2022-12-17 13:39:35 -08:00
|
|
|
module RubyVM::MJIT
|
2022-12-26 14:09:45 -08:00
|
|
|
class InsnCompiler
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param ocb [CodeBlock]
|
2023-01-02 14:11:06 -08:00
|
|
|
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
|
|
|
def initialize(ocb, exit_compiler)
|
2022-12-30 22:16:07 -08:00
|
|
|
@ocb = ocb
|
2023-01-02 14:11:06 -08:00
|
|
|
@exit_compiler = exit_compiler
|
|
|
|
@invariants = Invariants.new(ocb, exit_compiler)
|
2022-12-30 21:27:12 -08:00
|
|
|
freeze
|
|
|
|
end
|
2022-12-28 13:50:24 -08:00
|
|
|
|
2023-01-02 13:30:56 -08:00
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
|
|
|
# @param insn `RubyVM::MJIT::Instruction`
|
|
|
|
def compile(jit, ctx, asm, insn)
|
|
|
|
asm.incr_counter(:mjit_insns_count)
|
|
|
|
asm.comment("Insn: #{insn.name}")
|
|
|
|
|
2023-01-02 22:53:14 -08:00
|
|
|
# 5/101
|
2023-01-02 13:30:56 -08:00
|
|
|
case insn.name
|
|
|
|
# nop
|
|
|
|
# getlocal
|
|
|
|
# setlocal
|
|
|
|
# getblockparam
|
|
|
|
# setblockparam
|
|
|
|
# getblockparamproxy
|
|
|
|
# getspecial
|
|
|
|
# setspecial
|
|
|
|
# getinstancevariable
|
|
|
|
# setinstancevariable
|
|
|
|
# getclassvariable
|
|
|
|
# setclassvariable
|
|
|
|
# opt_getconstant_path
|
|
|
|
# getconstant
|
|
|
|
# setconstant
|
|
|
|
# getglobal
|
|
|
|
# setglobal
|
|
|
|
when :putnil then putnil(jit, ctx, asm)
|
|
|
|
# putself
|
|
|
|
when :putobject then putobject(jit, ctx, asm)
|
|
|
|
# putspecialobject
|
|
|
|
# putstring
|
|
|
|
# concatstrings
|
|
|
|
# anytostring
|
|
|
|
# toregexp
|
|
|
|
# intern
|
|
|
|
# newarray
|
|
|
|
# newarraykwsplat
|
|
|
|
# duparray
|
|
|
|
# duphash
|
|
|
|
# expandarray
|
|
|
|
# concatarray
|
|
|
|
# splatarray
|
|
|
|
# newhash
|
|
|
|
# newrange
|
|
|
|
# pop
|
|
|
|
# dup
|
|
|
|
# dupn
|
|
|
|
# swap
|
|
|
|
# opt_reverse
|
|
|
|
# topn
|
|
|
|
# setn
|
|
|
|
# adjuststack
|
|
|
|
# defined
|
|
|
|
# checkmatch
|
|
|
|
# checkkeyword
|
|
|
|
# checktype
|
|
|
|
# defineclass
|
|
|
|
# definemethod
|
|
|
|
# definesmethod
|
|
|
|
# send
|
|
|
|
# opt_send_without_block
|
|
|
|
# objtostring
|
|
|
|
# opt_str_freeze
|
|
|
|
# opt_nil_p
|
|
|
|
# opt_str_uminus
|
|
|
|
# opt_newarray_max
|
|
|
|
# opt_newarray_min
|
|
|
|
# invokesuper
|
|
|
|
# invokeblock
|
|
|
|
when :leave then leave(jit, ctx, asm)
|
|
|
|
# throw
|
|
|
|
# jump
|
|
|
|
# branchif
|
|
|
|
# branchunless
|
|
|
|
# branchnil
|
|
|
|
# once
|
|
|
|
# opt_case_dispatch
|
|
|
|
# opt_plus
|
|
|
|
# opt_minus
|
|
|
|
# opt_mult
|
|
|
|
# opt_div
|
|
|
|
# opt_mod
|
|
|
|
# opt_eq
|
|
|
|
# opt_neq
|
|
|
|
when :opt_lt then opt_lt(jit, ctx, asm)
|
|
|
|
# opt_le
|
|
|
|
# opt_gt
|
|
|
|
# opt_ge
|
|
|
|
# opt_ltlt
|
|
|
|
# opt_and
|
|
|
|
# opt_or
|
|
|
|
# opt_aref
|
|
|
|
# opt_aset
|
|
|
|
# opt_aset_with
|
|
|
|
# opt_aref_with
|
|
|
|
# opt_length
|
|
|
|
# opt_size
|
|
|
|
# opt_empty_p
|
|
|
|
# opt_succ
|
|
|
|
# opt_not
|
|
|
|
# opt_regexpmatch2
|
|
|
|
# invokebuiltin
|
|
|
|
# opt_invokebuiltin_delegate
|
|
|
|
# opt_invokebuiltin_delegate_leave
|
|
|
|
when :getlocal_WC_0 then getlocal_WC_0(jit, ctx, asm)
|
|
|
|
else CantCompile
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
#
|
|
|
|
# Insns
|
|
|
|
#
|
|
|
|
|
2022-12-28 13:50:24 -08:00
|
|
|
# nop
|
|
|
|
# getlocal
|
|
|
|
# setlocal
|
|
|
|
# getblockparam
|
|
|
|
# setblockparam
|
|
|
|
# getblockparamproxy
|
|
|
|
# getspecial
|
|
|
|
# setspecial
|
|
|
|
# getinstancevariable
|
|
|
|
# setinstancevariable
|
|
|
|
# getclassvariable
|
|
|
|
# setclassvariable
|
|
|
|
# opt_getconstant_path
|
|
|
|
# getconstant
|
|
|
|
# setconstant
|
|
|
|
# getglobal
|
|
|
|
# setglobal
|
|
|
|
|
2022-12-26 14:09:45 -08:00
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
2022-12-23 14:17:32 -08:00
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
2022-12-26 14:09:45 -08:00
|
|
|
def putnil(jit, ctx, asm)
|
2023-01-02 22:53:14 -08:00
|
|
|
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
|
2022-12-28 13:16:14 -08:00
|
|
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], Qnil)
|
2022-12-31 13:41:32 -08:00
|
|
|
ctx.stack_push(1)
|
2022-12-17 13:39:35 -08:00
|
|
|
KeepCompiling
|
|
|
|
end
|
|
|
|
|
2022-12-28 13:50:24 -08:00
|
|
|
# putself
|
2022-12-28 14:43:04 -08:00
|
|
|
|
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
2022-12-28 14:43:04 -08:00
|
|
|
def putobject(jit, ctx, asm)
|
|
|
|
# Get operands
|
|
|
|
val = jit.operand(0)
|
|
|
|
|
|
|
|
# Push it to the stack
|
|
|
|
# TODO: GC offsets
|
2023-01-02 22:53:14 -08:00
|
|
|
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
|
2022-12-28 14:43:04 -08:00
|
|
|
if asm.imm32?(val)
|
|
|
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], val)
|
|
|
|
else # 64-bit immediates can't be directly written to memory
|
|
|
|
asm.mov(:rax, val)
|
|
|
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], :rax)
|
|
|
|
end
|
|
|
|
|
2022-12-31 13:41:32 -08:00
|
|
|
ctx.stack_push(1)
|
2022-12-28 14:43:04 -08:00
|
|
|
KeepCompiling
|
|
|
|
end
|
|
|
|
|
2022-12-28 13:50:24 -08:00
|
|
|
# putspecialobject
|
|
|
|
# putstring
|
|
|
|
# concatstrings
|
|
|
|
# anytostring
|
|
|
|
# toregexp
|
|
|
|
# intern
|
|
|
|
# newarray
|
|
|
|
# newarraykwsplat
|
|
|
|
# duparray
|
|
|
|
# duphash
|
|
|
|
# expandarray
|
|
|
|
# concatarray
|
|
|
|
# splatarray
|
|
|
|
# newhash
|
|
|
|
# newrange
|
|
|
|
# pop
|
|
|
|
# dup
|
|
|
|
# dupn
|
|
|
|
# swap
|
|
|
|
# opt_reverse
|
|
|
|
# topn
|
|
|
|
# setn
|
|
|
|
# adjuststack
|
|
|
|
# defined
|
|
|
|
# checkmatch
|
|
|
|
# checkkeyword
|
|
|
|
# checktype
|
|
|
|
# defineclass
|
|
|
|
# definemethod
|
|
|
|
# definesmethod
|
|
|
|
# send
|
|
|
|
# opt_send_without_block
|
|
|
|
# objtostring
|
|
|
|
# opt_str_freeze
|
|
|
|
# opt_nil_p
|
|
|
|
# opt_str_uminus
|
|
|
|
# opt_newarray_max
|
|
|
|
# opt_newarray_min
|
|
|
|
# invokesuper
|
|
|
|
# invokeblock
|
|
|
|
|
2022-12-26 14:09:45 -08:00
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
2022-12-23 14:17:32 -08:00
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
2022-12-26 14:09:45 -08:00
|
|
|
def leave(jit, ctx, asm)
|
2022-12-23 14:17:32 -08:00
|
|
|
assert_eq!(ctx.stack_size, 1)
|
2022-12-26 14:09:45 -08:00
|
|
|
|
2022-12-28 13:16:02 -08:00
|
|
|
asm.comment('RUBY_VM_CHECK_INTS(ec)')
|
2022-12-26 14:09:45 -08:00
|
|
|
asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)])
|
|
|
|
asm.test(:eax, :eax)
|
2023-01-02 22:53:14 -08:00
|
|
|
asm.jnz(side_exit(jit, ctx))
|
2022-12-23 14:17:32 -08:00
|
|
|
|
2022-12-28 13:16:02 -08:00
|
|
|
asm.comment('pop stack frame')
|
2022-12-23 16:23:21 -08:00
|
|
|
asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1
|
|
|
|
asm.mov([EC, C.rb_execution_context_t.offsetof(:cfp)], CFP) # ec->cfp = cfp
|
2022-12-17 13:39:35 -08:00
|
|
|
|
2022-12-23 14:46:39 -08:00
|
|
|
# Return a value
|
2022-12-23 16:23:21 -08:00
|
|
|
asm.mov(:rax, [SP])
|
2022-12-23 14:46:39 -08:00
|
|
|
|
|
|
|
# Restore callee-saved registers
|
2022-12-23 16:23:21 -08:00
|
|
|
asm.pop(SP)
|
2022-12-31 14:14:53 -08:00
|
|
|
asm.pop(EC)
|
|
|
|
asm.pop(CFP)
|
2022-12-23 14:46:39 -08:00
|
|
|
|
2022-12-17 13:39:35 -08:00
|
|
|
asm.ret
|
|
|
|
EndBlock
|
|
|
|
end
|
2022-12-23 14:17:32 -08:00
|
|
|
|
2022-12-28 13:50:24 -08:00
|
|
|
# throw
|
|
|
|
# jump
|
|
|
|
# branchif
|
|
|
|
# branchunless
|
|
|
|
# branchnil
|
|
|
|
# once
|
|
|
|
# opt_case_dispatch
|
|
|
|
# opt_plus
|
|
|
|
# opt_minus
|
|
|
|
# opt_mult
|
|
|
|
# opt_div
|
|
|
|
# opt_mod
|
|
|
|
# opt_eq
|
|
|
|
# opt_neq
|
2022-12-31 13:41:32 -08:00
|
|
|
|
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
|
|
|
def opt_lt(jit, ctx, asm)
|
|
|
|
unless jit.at_current_insn?
|
|
|
|
defer_compilation(jit, ctx, asm)
|
|
|
|
return EndBlock
|
|
|
|
end
|
2023-01-02 14:11:06 -08:00
|
|
|
|
2023-01-02 22:53:14 -08:00
|
|
|
comptime_recv = jit.peek_at_stack(1)
|
|
|
|
comptime_obj = jit.peek_at_stack(0)
|
|
|
|
|
|
|
|
if fixnum?(comptime_recv) && fixnum?(comptime_obj)
|
|
|
|
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
|
|
|
|
return CantCompile
|
|
|
|
end
|
|
|
|
|
|
|
|
raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this
|
|
|
|
recv_index = ctx.stack_size - 2
|
|
|
|
obj_index = ctx.stack_size - 1
|
|
|
|
|
2023-01-03 23:51:37 -08:00
|
|
|
asm.comment('guard recv is fixnum') # TODO: skip this with type information
|
2023-01-02 22:53:14 -08:00
|
|
|
asm.test([SP, C.VALUE.size * recv_index], C.RUBY_FIXNUM_FLAG)
|
|
|
|
asm.je(side_exit(jit, ctx))
|
|
|
|
|
2023-01-03 23:51:37 -08:00
|
|
|
asm.comment('guard obj is fixnum') # TODO: skip this with type information
|
2023-01-02 22:53:14 -08:00
|
|
|
asm.test([SP, C.VALUE.size * obj_index], C.RUBY_FIXNUM_FLAG)
|
|
|
|
asm.je(side_exit(jit, ctx))
|
|
|
|
|
|
|
|
asm.mov(:rax, [SP, C.VALUE.size * obj_index])
|
|
|
|
asm.cmp([SP, C.VALUE.size * recv_index], :rax)
|
|
|
|
asm.mov(:rax, Qfalse)
|
|
|
|
asm.mov(:rcx, Qtrue)
|
|
|
|
asm.cmovl(:rax, :rcx)
|
|
|
|
asm.mov([SP, C.VALUE.size * recv_index], :rax)
|
|
|
|
|
|
|
|
ctx.stack_pop(1)
|
|
|
|
KeepCompiling
|
|
|
|
else
|
|
|
|
CantCompile # TODO: delegate to send
|
2023-01-02 14:11:06 -08:00
|
|
|
end
|
2022-12-31 13:41:32 -08:00
|
|
|
end
|
|
|
|
|
2022-12-28 13:50:24 -08:00
|
|
|
# opt_le
|
|
|
|
# opt_gt
|
|
|
|
# opt_ge
|
|
|
|
# opt_ltlt
|
|
|
|
# opt_and
|
|
|
|
# opt_or
|
|
|
|
# opt_aref
|
|
|
|
# opt_aset
|
|
|
|
# opt_aset_with
|
|
|
|
# opt_aref_with
|
|
|
|
# opt_length
|
|
|
|
# opt_size
|
|
|
|
# opt_empty_p
|
|
|
|
# opt_succ
|
|
|
|
# opt_not
|
|
|
|
# opt_regexpmatch2
|
|
|
|
# invokebuiltin
|
|
|
|
# opt_invokebuiltin_delegate
|
|
|
|
# opt_invokebuiltin_delegate_leave
|
|
|
|
|
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
2022-12-28 13:50:24 -08:00
|
|
|
def getlocal_WC_0(jit, ctx, asm)
|
|
|
|
# Get operands
|
|
|
|
idx = jit.operand(0)
|
|
|
|
level = 0
|
|
|
|
|
|
|
|
# Get EP
|
|
|
|
asm.mov(:rax, [CFP, C.rb_control_frame_t.offsetof(:ep)])
|
|
|
|
|
|
|
|
# Get a local variable
|
|
|
|
asm.mov(:rax, [:rax, -idx * C.VALUE.size])
|
|
|
|
|
|
|
|
# Push it to the stack
|
|
|
|
asm.mov([SP, C.VALUE.size * ctx.stack_size], :rax)
|
2022-12-31 13:41:32 -08:00
|
|
|
ctx.stack_push(1)
|
2022-12-28 13:50:24 -08:00
|
|
|
KeepCompiling
|
|
|
|
end
|
|
|
|
|
|
|
|
# getlocal_WC_1
|
|
|
|
# setlocal_WC_0
|
|
|
|
# setlocal_WC_1
|
|
|
|
# putobject_INT2FIX_0_
|
|
|
|
# putobject_INT2FIX_1_
|
|
|
|
|
2023-01-02 13:30:56 -08:00
|
|
|
#
|
|
|
|
# Helpers
|
|
|
|
#
|
2022-12-23 14:17:32 -08:00
|
|
|
|
|
|
|
def assert_eq!(left, right)
|
|
|
|
if left != right
|
|
|
|
raise "'#{left.inspect}' was not '#{right.inspect}'"
|
|
|
|
end
|
|
|
|
end
|
2022-12-30 22:16:07 -08:00
|
|
|
|
2023-01-02 22:53:14 -08:00
|
|
|
def fixnum?(obj)
|
|
|
|
flag = C.RUBY_FIXNUM_FLAG
|
|
|
|
(C.to_value(obj) & flag) == flag
|
|
|
|
end
|
|
|
|
|
2022-12-31 13:41:32 -08:00
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
|
|
|
# @param asm [RubyVM::MJIT::Assembler]
|
|
|
|
def defer_compilation(jit, ctx, asm)
|
|
|
|
# Make a stub to compile the current insn
|
|
|
|
block_stub = BlockStub.new(
|
|
|
|
iseq: jit.iseq,
|
|
|
|
pc: jit.pc,
|
|
|
|
ctx: ctx.dup,
|
|
|
|
)
|
|
|
|
|
|
|
|
stub_hit = Assembler.new.then do |ocb_asm|
|
2023-01-04 00:12:16 -08:00
|
|
|
@exit_compiler.compile_jump_stub(jit, ctx, ocb_asm, block_stub)
|
2022-12-31 13:41:32 -08:00
|
|
|
@ocb.write(ocb_asm)
|
|
|
|
end
|
|
|
|
|
|
|
|
asm.comment('defer_compilation: block stub')
|
2023-01-01 23:13:43 -08:00
|
|
|
asm.stub(block_stub) do
|
|
|
|
asm.jmp(stub_hit)
|
|
|
|
end
|
2022-12-31 13:41:32 -08:00
|
|
|
end
|
|
|
|
|
2022-12-30 22:16:07 -08:00
|
|
|
# @param jit [RubyVM::MJIT::JITState]
|
|
|
|
# @param ctx [RubyVM::MJIT::Context]
|
2023-01-02 22:53:14 -08:00
|
|
|
def side_exit(jit, ctx)
|
|
|
|
if side_exit = jit.side_exits[jit.pc]
|
|
|
|
return side_exit
|
|
|
|
end
|
2022-12-30 22:16:07 -08:00
|
|
|
asm = Assembler.new
|
2023-01-02 14:11:06 -08:00
|
|
|
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
2023-01-02 22:53:14 -08:00
|
|
|
jit.side_exits[jit.pc] = @ocb.write(asm)
|
2022-12-30 22:16:07 -08:00
|
|
|
end
|
2022-12-17 13:39:35 -08:00
|
|
|
end
|
|
|
|
end
|