summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <[email protected]>2023-03-03 23:00:30 -0800
committerTakashi Kokubun <[email protected]>2023-03-05 23:28:59 -0800
commitcc646d3262955b10907d984d4de72c1b23cf0c89 (patch)
tree880b507ec68da626fcbb476ff97f2695db7d24e6
parent6b38d1ce7bfb37e3891eaa2155c906889c6ef28e (diff)
Implement getblockparam
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/7448
-rw-r--r--lib/ruby_vm/mjit/assembler.rb10
-rw-r--r--lib/ruby_vm/mjit/insn_compiler.rb74
-rw-r--r--mjit_c.rb4
3 files changed, 86 insertions, 2 deletions
diff --git a/lib/ruby_vm/mjit/assembler.rb b/lib/ruby_vm/mjit/assembler.rb
index 20cad72a39..0cf90dd4fa 100644
--- a/lib/ruby_vm/mjit/assembler.rb
+++ b/lib/ruby_vm/mjit/assembler.rb
@@ -700,6 +700,16 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod11, reg: 1, rm: dst_reg],
imm: imm8(src_imm),
)
+ # OR r/m64, imm32 (Mod 11: reg)
+ in [Symbol => dst_reg, Integer => src_imm] if r64?(dst_reg) && imm32?(src_imm)
+ # REX.W + 81 /1 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: 1, rm: dst_reg],
+ imm: imm32(src_imm),
+ )
# OR r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 0B /r
diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb
index 505100e327..ee96b8ff2d 100644
--- a/lib/ruby_vm/mjit/insn_compiler.rb
+++ b/lib/ruby_vm/mjit/insn_compiler.rb
@@ -29,7 +29,7 @@ module RubyVM::MJIT
when :nop then nop(jit, ctx, asm)
when :getlocal then getlocal(jit, ctx, asm)
when :setlocal then setlocal(jit, ctx, asm)
- # getblockparam
+ when :getblockparam then getblockparam(jit, ctx, asm)
# setblockparam
when :getblockparamproxy then getblockparamproxy(jit, ctx, asm)
# getspecial
@@ -163,7 +163,77 @@ module RubyVM::MJIT
jit_setlocal_generic(jit, ctx, asm, idx:, level:)
end
- # getblockparam
+ # @param jit [RubyVM::MJIT::JITState]
+ # @param ctx [RubyVM::MJIT::Context]
+ # @param asm [RubyVM::MJIT::Assembler]
+ def getblockparam(jit, ctx, asm)
+ # EP level
+ level = jit.operand(1)
+
+ # Save the PC and SP because we might allocate
+ jit_prepare_routine_call(jit, ctx, asm)
+
+ # A mirror of the interpreter code. Checking for the case
+ # where it's pushing rb_block_param_proxy.
+ side_exit = side_exit(jit, ctx)
+
+ # Load environment pointer EP from CFP
+ ep_reg = :rax
+ jit_get_ep(asm, level, reg: ep_reg)
+
+ # Bail when VM_ENV_FLAGS(ep, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM) is non zero
+ # FIXME: This is testing bits in the same place that the WB check is testing.
+ # We should combine these at some point
+ asm.test([ep_reg, C.VALUE.size * C.VM_ENV_DATA_INDEX_FLAGS], C.VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM)
+
+ # If the frame flag has been modified, then the actual proc value is
+ # already in the EP and we should just use the value.
+ frame_flag_modified = asm.new_label('frame_flag_modified')
+ asm.jnz(frame_flag_modified)
+
+ # This instruction writes the block handler to the EP. If we need to
+ # fire a write barrier for the write, then exit (we'll let the
+ # interpreter handle it so it can fire the write barrier).
+ # flags & VM_ENV_FLAG_WB_REQUIRED
+ asm.test([ep_reg, C.VALUE.size * C.VM_ENV_DATA_INDEX_FLAGS], C.VM_ENV_FLAG_WB_REQUIRED)
+
+ # if (flags & VM_ENV_FLAG_WB_REQUIRED) != 0
+ asm.jnz(side_exit)
+
+ # Convert the block handler in to a proc
+ # call rb_vm_bh_to_procval(const rb_execution_context_t *ec, VALUE block_handler)
+ asm.mov(C_ARGS[0], EC)
+ # The block handler for the current frame
+ # note, VM_ASSERT(VM_ENV_LOCAL_P(ep))
+ asm.mov(C_ARGS[1], [ep_reg, C.VALUE.size * C.VM_ENV_DATA_INDEX_SPECVAL])
+ asm.call(C.rb_vm_bh_to_procval)
+
+ # Load environment pointer EP from CFP (again)
+ ep_reg = :rcx
+ jit_get_ep(asm, level, reg: ep_reg)
+
+ # Write the value at the environment pointer
+ idx = jit.operand(0)
+ offs = -(C.VALUE.size * idx)
+ asm.mov([ep_reg, offs], C_RET);
+
+ # Set the frame modified flag
+ asm.mov(:rax, [ep_reg, C.VALUE.size * C.VM_ENV_DATA_INDEX_FLAGS]) # flag_check
+ asm.or(:rax, C.VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM) # modified_flag
+ asm.mov([ep_reg, C.VALUE.size * C.VM_ENV_DATA_INDEX_FLAGS], :rax)
+
+ asm.write_label(frame_flag_modified)
+
+ # Push the proc on the stack
+ stack_ret = ctx.stack_push
+ ep_reg = :rax
+ jit_get_ep(asm, level, reg: ep_reg)
+ asm.mov(:rax, [ep_reg, offs])
+ asm.mov(stack_ret, :rax)
+
+ KeepCompiling
+ end
+
# setblockparam
# @param jit [RubyVM::MJIT::JITState]
diff --git a/mjit_c.rb b/mjit_c.rb
index a0259afe91..16e034b2d3 100644
--- a/mjit_c.rb
+++ b/mjit_c.rb
@@ -380,6 +380,10 @@ module RubyVM::MJIT # :nodoc: all
}
end
+ def rb_vm_bh_to_procval
+ Primitive.cexpr! 'SIZET2NUM((size_t)rb_vm_bh_to_procval)'
+ end
+
#========================================================================================
#
# Old stuff