summaryrefslogtreecommitdiff
path: root/lib/ruby_vm/mjit
diff options
context:
space:
mode:
authorTakashi Kokubun <[email protected]>2023-02-15 21:26:04 -0800
committerTakashi Kokubun <[email protected]>2023-03-05 23:28:59 -0800
commitd120394df30a3dd1878f183966489c100755e4b4 (patch)
tree59a73d73e8dbccc6740ea975b6c4109647a3b77a /lib/ruby_vm/mjit
parente078a4a9649240bbac2119891e4bd6c0a4952551 (diff)
Implement duparray and expandarray
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/7448
Diffstat (limited to 'lib/ruby_vm/mjit')
-rw-r--r--lib/ruby_vm/mjit/assembler.rb63
-rw-r--r--lib/ruby_vm/mjit/insn_compiler.rb129
-rw-r--r--lib/ruby_vm/mjit/stats.rb1
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])}"