Skip to content

Commit

Permalink
RJIT: Optimize definedivar using shapes
Browse files Browse the repository at this point in the history
  • Loading branch information
k0kubun committed Mar 28, 2023
1 parent 82f46dd commit 7600e6b
Showing 1 changed file with 57 additions and 19 deletions.
76 changes: 57 additions & 19 deletions lib/ruby_vm/rjit/insn_compiler.rb
Expand Up @@ -1237,34 +1237,72 @@ def defined(jit, ctx, asm)
# @param ctx [RubyVM::RJIT::Context]
# @param asm [RubyVM::RJIT::Assembler]
def definedivar(jit, ctx, asm)
# Defer compilation so we can specialize base on a runtime receiver
unless jit.at_current_insn?
defer_compilation(jit, ctx, asm)
return EndBlock
end

ivar_name = jit.operand(0)
# Value that will be pushed on the stack if the ivar is defined. In practice this is always the
# string "instance-variable". If the ivar is not defined, nil will be pushed instead.
pushval = jit.operand(2)

# Get the receiver
asm.mov(:rcx, [CFP, C.rb_control_frame_t.offsetof(:self)])
recv = :rcx
asm.mov(recv, [CFP, C.rb_control_frame_t.offsetof(:self)])

# Save the PC and SP because the callee may allocate
# Note that this modifies REG_SP, which is why we do it first
jit_prepare_routine_call(jit, ctx, asm) # clobbers :rax
# Specialize base on compile time values
comptime_receiver = jit.peek_at_self

# Call rb_ivar_defined(recv, ivar_name)
asm.mov(C_ARGS[0], :rcx)
asm.mov(C_ARGS[1], ivar_name)
asm.call(C.rb_ivar_defined)
if shape_too_complex?(comptime_receiver)
# Fall back to calling rb_ivar_defined

# if (rb_ivar_defined(recv, ivar_name)) {
# val = pushval;
# }
asm.test(C_RET, 255)
asm.mov(:rax, Qnil)
asm.mov(:rcx, pushval)
asm.cmovnz(:rax, :rcx)
# Save the PC and SP because the callee may allocate
# Note that this modifies REG_SP, which is why we do it first
jit_prepare_routine_call(jit, ctx, asm) # clobbers :rax

# Push the return value onto the stack
stack_ret = ctx.stack_push
asm.mov(stack_ret, :rax)
# Call rb_ivar_defined(recv, ivar_name)
asm.mov(C_ARGS[0], recv)
asm.mov(C_ARGS[1], ivar_name)
asm.call(C.rb_ivar_defined)

# if (rb_ivar_defined(recv, ivar_name)) {
# val = pushval;
# }
asm.test(C_RET, 255)
asm.mov(:rax, Qnil)
asm.mov(:rcx, pushval)
asm.cmovnz(:rax, :rcx)

KeepCompiling
# Push the return value onto the stack
stack_ret = ctx.stack_push
asm.mov(stack_ret, :rax)

return KeepCompiling
end

shape_id = C.rb_shape_get_shape_id(comptime_receiver)
ivar_exists = C.rb_shape_get_iv_index(shape_id, ivar_name)

side_exit = side_exit(jit, ctx)

# Guard heap object (recv_opnd must be used before stack_pop)
guard_object_is_heap(asm, recv, side_exit)

shape_opnd = DwordPtr[recv, C.rb_shape_id_offset]

asm.comment('guard shape')
asm.cmp(shape_opnd, shape_id)
jit_chain_guard(:jne, jit, ctx, asm, side_exit)

result = ivar_exists ? pushval : Qnil
putobject(jit, ctx, asm, val: result)

# Jump to next instruction. This allows guard chains to share the same successor.
jump_to_next_insn(jit, ctx, asm)

return EndBlock
end

# checkmatch
Expand Down

0 comments on commit 7600e6b

Please sign in to comment.