summaryrefslogtreecommitdiff
path: root/lib/ruby_vm/mjit/insn_compiler.rb
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/insn_compiler.rb
parente078a4a9649240bbac2119891e4bd6c0a4952551 (diff)
Implement duparray and expandarray
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/7448
Diffstat (limited to 'lib/ruby_vm/mjit/insn_compiler.rb')
-rw-r--r--lib/ruby_vm/mjit/insn_compiler.rb129
1 files changed, 124 insertions, 5 deletions
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}'"