Implement getblockparam

This commit is contained in:
Takashi Kokubun 2023-03-03 23:00:30 -08:00
parent 6b38d1ce7b
commit cc646d3262
Notes: git 2023-03-06 07:29:37 +00:00
3 changed files with 86 additions and 2 deletions

View File

@ -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

View File

@ -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]

View File

@ -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