diff options
author | Takashi Kokubun <[email protected]> | 2023-02-15 21:26:04 -0800 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2023-03-05 23:28:59 -0800 |
commit | d120394df30a3dd1878f183966489c100755e4b4 (patch) | |
tree | 59a73d73e8dbccc6740ea975b6c4109647a3b77a /lib | |
parent | e078a4a9649240bbac2119891e4bd6c0a4952551 (diff) |
Implement duparray and expandarray
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/7448
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ruby_vm/mjit/assembler.rb | 63 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/insn_compiler.rb | 129 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/stats.rb | 1 |
3 files changed, 188 insertions, 5 deletions
diff --git a/lib/ruby_vm/mjit/assembler.rb b/lib/ruby_vm/mjit/assembler.rb index 3ffb3e45ad..abf0ae6c08 100644 --- a/lib/ruby_vm/mjit/assembler.rb +++ b/lib/ruby_vm/mjit/assembler.rb @@ -121,6 +121,16 @@ module RubyVM::MJIT mod_rm: ModRM[mod: Mod11, reg: 4, rm: dst_reg], imm: imm8(src_imm), ) + # AND r/m64, imm32 (Mod 11: reg) + in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm32?(src_imm) + # REX.W + 81 /4 id + # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 + insn( + prefix: REX_W, + opcode: 0x81, + mod_rm: ModRM[mod: Mod11, reg: 4, rm: dst_reg], + imm: imm32(src_imm), + ) # AND r64, r/m64 (Mod 01: [reg]+disp8) in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) # REX.W + 23 /r @@ -236,6 +246,48 @@ module RubyVM::MJIT end end + def cmovnz(dst, src) + case [dst, src] + # CMOVNZ r64, r/m64 (Mod 11: reg) + in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) + # REX.W + 0F 45 /r + # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) + insn( + prefix: REX_W, + opcode: [0x0f, 0x45], + mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg], + ) + else + raise NotImplementedError, "cmovnz: not-implemented operands: #{dst.inspect}, #{src.inspect}" + end + end + + def cmovz(dst, src) + case [dst, src] + # CMOVZ r64, r/m64 (Mod 11: reg) + in [Symbol => dst_reg, Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) + # REX.W + 0F 44 /r + # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) + insn( + prefix: REX_W, + opcode: [0x0f, 0x44], + mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg], + ) + # CMOVZ r64, r/m64 (Mod 01: [reg]+disp8) + in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) + # REX.W + 0F 44 /r + # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) + insn( + prefix: REX_W, + opcode: [0x0f, 0x44], + mod_rm: ModRM[mod: Mod01, reg: dst_reg, rm: src_reg], + disp: imm8(src_disp), + ) + else + raise NotImplementedError, "cmovz: not-implemented operands: #{dst.inspect}, #{src.inspect}" + end + end + def cmp(left, right) case [left, right] # CMP r/m32, imm32 (Mod 01: [reg]+disp8) @@ -319,6 +371,17 @@ module RubyVM::MJIT end end + def jl(dst) + case dst + # JL rel32 + in Integer => dst_addr + # 0F 8C cd + insn(opcode: [0x0f, 0x8c], imm: rel32(dst_addr)) + else + raise NotImplementedError, "jl: not-implemented operands: #{dst.inspect}" + end + end + def jmp(dst) case dst # JZ rel8 diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb index e723e3f376..e9c4b0e56f 100644 --- a/lib/ruby_vm/mjit/insn_compiler.rb +++ b/lib/ruby_vm/mjit/insn_compiler.rb @@ -23,7 +23,7 @@ module RubyVM::MJIT asm.incr_counter(:mjit_insns_count) asm.comment("Insn: #{insn.name}") - # 46/101 + # 48/101 case insn.name when :nop then nop(jit, ctx, asm) when :getlocal then getlocal(jit, ctx, asm) @@ -53,9 +53,9 @@ module RubyVM::MJIT # intern # newarray # newarraykwsplat - # duparray + when :duparray then duparray(jit, ctx, asm) # duphash - # expandarray + when :expandarray then expandarray(jit, ctx, asm) # concatarray # splatarray # newhash @@ -299,9 +299,97 @@ module RubyVM::MJIT # intern # newarray # newarraykwsplat - # duparray + + # @param jit [RubyVM::MJIT::JITState] + # @param ctx [RubyVM::MJIT::Context] + # @param asm [RubyVM::MJIT::Assembler] + def duparray(jit, ctx, asm) + ary = jit.operand(0) + + # Save the PC and SP because we are allocating + jit_prepare_routine_call(jit, ctx, asm) + + # call rb_ary_resurrect(VALUE ary); + asm.comment('call rb_ary_resurrect') + asm.mov(C_ARGS[0], ary) + asm.call(C.rb_ary_resurrect) + + stack_ret = ctx.stack_push + asm.mov(stack_ret, C_RET) + + KeepCompiling + end + # duphash - # expandarray + + # @param jit [RubyVM::MJIT::JITState] + # @param ctx [RubyVM::MJIT::Context] + # @param asm [RubyVM::MJIT::Assembler] + def expandarray(jit, ctx, asm) + # Both arguments are rb_num_t which is unsigned + num = jit.operand(0) + flag = jit.operand(1) + + # If this instruction has the splat flag, then bail out. + if flag & 0x01 != 0 + asm.incr_counter(:expandarray_splat) + return CantCompile + end + + # If this instruction has the postarg flag, then bail out. + if flag & 0x02 != 0 + asm.incr_counter(:expandarray_postarg) + return CantCompile + end + + side_exit = side_exit(jit, ctx) + + array_opnd = ctx.stack_pop(1) + + # num is the number of requested values. If there aren't enough in the + # array then we're going to push on nils. + # TODO: implement this + + # Move the array from the stack and check that it's an array. + asm.mov(:rax, array_opnd) + guard_object_is_heap(asm, :rax, counted_exit(side_exit, :expandarray_not_array)) + guard_object_is_array(asm, :rax, :rcx, counted_exit(side_exit, :expandarray_not_array)) + + # If we don't actually want any values, then just return. + if num == 0 + return KeepCompiling + end + + jit_array_len(asm, :rax, :rcx) + + # Only handle the case where the number of values in the array is greater + # than or equal to the number of values requested. + asm.cmp(:rcx, num) + asm.jl(counted_exit(side_exit, :expandarray_rhs_too_small)) + + # Conditionally load the address of the heap array into REG1. + # (struct RArray *)(obj)->as.heap.ptr + #asm.mov(:rax, array_opnd) + asm.mov(:rcx, [:rax, C.RBasic.offsetof(:flags)]) + asm.test(:rcx, C.RARRAY_EMBED_FLAG); + asm.mov(:rcx, [:rax, C.RArray.offsetof(:as, :heap, :ptr)]) + + # Load the address of the embedded array into REG1. + # (struct RArray *)(obj)->as.ary + asm.lea(:rax, [:rax, C.RArray.offsetof(:as, :ary)]) + + asm.cmovnz(:rcx, :rax) + + # Loop backward through the array and push each element onto the stack. + (num - 1).downto(0).each do |i| + top = ctx.stack_push + asm.mov(:rax, [:rcx, i * C.VALUE.size]) + asm.mov(top, :rax) + end + + KeepCompiling + end + # concatarray # splatarray # newhash @@ -1156,6 +1244,18 @@ module RubyVM::MJIT asm.je(side_exit) end + # @param asm [RubyVM::MJIT::Assembler] + def guard_object_is_array(asm, object_reg, flags_reg, side_exit) + asm.comment('guard object is array') + # Pull out the type mask + asm.mov(flags_reg, [object_reg, C.RBasic.offsetof(:flags)]) + asm.and(flags_reg, C.RUBY_T_MASK) + + # Compare the result with T_ARRAY + asm.cmp(flags_reg, C.RUBY_T_ARRAY) + asm.jne(side_exit) + end + # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] @@ -1957,6 +2057,25 @@ module RubyVM::MJIT end end + # Generate RARRAY_LEN. For array_opnd, use Opnd::Reg to reduce memory access, + # and use Opnd::Mem to save registers. + def jit_array_len(asm, array_reg, len_reg) + asm.comment('get array length for embedded or heap') + + # Pull out the embed flag to check if it's an embedded array. + asm.mov(len_reg, [array_reg, C.RBasic.offsetof(:flags)]) + + # Get the length of the array + asm.and(len_reg, C.RARRAY_EMBED_LEN_MASK) + asm.sar(len_reg, C.RARRAY_EMBED_LEN_SHIFT) + + # Conditionally move the length of the heap array + asm.test([array_reg, C.RBasic.offsetof(:flags)], C.RARRAY_EMBED_FLAG) + + # Select the array length value + asm.cmovz(len_reg, [array_reg, C.RArray.offsetof(:as, :heap, :len)]) + end + def assert_equal(left, right) if left != right raise "'#{left.inspect}' was not '#{right.inspect}'" diff --git a/lib/ruby_vm/mjit/stats.rb b/lib/ruby_vm/mjit/stats.rb index 932043337c..22be01fe34 100644 --- a/lib/ruby_vm/mjit/stats.rb +++ b/lib/ruby_vm/mjit/stats.rb @@ -38,6 +38,7 @@ module RubyVM::MJIT print_counters(stats, prefix: 'getivar_', prompt: 'getinstancevariable exit reasons') print_counters(stats, prefix: 'optaref_', prompt: 'opt_aref exit reasons') print_counters(stats, prefix: 'optgetconst_', prompt: 'opt_getconstant_path exit reasons') + print_counters(stats, prefix: 'expandarray_', prompt: 'expandarray exit reasons') $stderr.puts "compiled_block_count: #{format_number(13, stats[:compiled_block_count])}" $stderr.puts "side_exit_count: #{format_number(13, stats[:side_exit_count])}" |