diff options
author | Takashi Kokubun <takashikkbn@gmail.com> | 2023-01-02 22:53:14 -0800 |
---|---|---|
committer | Takashi Kokubun <takashikkbn@gmail.com> | 2023-03-05 22:11:20 -0800 |
commit | a8dec34961d7734ad975e44efa8aae361a1c4f5a (patch) | |
tree | 5849c716f37c959d9276ddb8c598fe1142a7f6fa /lib | |
parent | 21696ad81ec40cf9cd146836dc8c945f1e485774 (diff) | |
download | ruby-a8dec34961d7734ad975e44efa8aae361a1c4f5a.tar.gz |
Implement initial opt_lt
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ruby_vm/mjit/assembler.rb | 63 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/compiler.rb | 13 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/context.rb | 10 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/exit_compiler.rb | 8 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/insn_compiler.rb | 54 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/jit_state.rb | 18 |
6 files changed, 132 insertions, 34 deletions
diff --git a/lib/ruby_vm/mjit/assembler.rb b/lib/ruby_vm/mjit/assembler.rb index c55df0235d..bc42ad1a6a 100644 --- a/lib/ruby_vm/mjit/assembler.rb +++ b/lib/ruby_vm/mjit/assembler.rb @@ -6,7 +6,7 @@ module RubyVM::MJIT # A thin Fiddle wrapper to write bytes to memory ByteWriter = CType::Immediate.parse('char') - # Used for rel8 jumps + # rel8 jumps are made with labels class Label < Data.define(:id, :name); end # rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32 @@ -20,7 +20,6 @@ module RubyVM::MJIT Mod10 = 0b10 # Mod 10: [reg]+disp16 Mod11 = 0b11 # Mod 11: reg - ### prefix ### # REX = 0100WR0B REX_B = 0b01000001 REX_R = 0b01000100 @@ -94,6 +93,50 @@ module RubyVM::MJIT insn(opcode: 0xe8, imm: rel32(addr)) end + def cmovl(dst, src) + case [dst, src] + # CMOVL r64, r/m64 (Mod 11: reg) + in [Symbol => dst_reg, Symbol => src_reg] + # REX.W + 0F 4C /r + # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) + insn( + prefix: REX_W, + opcode: [0x0f, 0x4c], + mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg], + ) + else + raise NotImplementedError, "cmovl: not-implemented operands: #{dst.inspect}, #{src.inspect}" + end + end + + def cmp(left, right) + case [left, right] + # CMP r/m64 r64 (Mod 01: [reg]+disp8) + in [[Symbol => left_reg, Integer => left_disp], Symbol => right_reg] + # REX.W + 39 /r + # MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r) + insn( + prefix: REX_W, + opcode: 0x39, + mod_rm: ModRM[mod: Mod01, reg: right_reg, rm: left_reg], + disp: left_disp, + ) + else + raise NotImplementedError, "cmp: not-implemented operands: #{left.inspect}, #{right.inspect}" + end + end + + def je(dst) + case dst + # JE rel32 + in Integer => dst_addr + # 0F 84 cd + insn(opcode: [0x0f, 0x84], imm: rel32(dst_addr)) + else + raise NotImplementedError, "je: not-implemented operands: #{dst.inspect}" + end + end + def jmp(dst) case dst # JMP rel32 @@ -285,6 +328,16 @@ module RubyVM::MJIT def test(left, right) case [left, right] + # TEST r/m8*, imm8 (Mod 01: [reg]+disp8) + in [[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm8?(right_imm) + # REX + F6 /0 ib + # MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32 + insn( + opcode: 0xf6, + mod_rm: ModRM[mod: Mod01, reg: 0, rm: left_reg], + disp: left_disp, + imm: imm8(right_imm), + ) # TEST r/m32, r32 (Mod 11: reg) in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg) # 85 /r @@ -294,7 +347,7 @@ module RubyVM::MJIT mod_rm: ModRM[mod: Mod11, reg: right_reg, rm: left_reg], ) else - raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}" + raise NotImplementedError, "test: not-implemented operands: #{left.inspect}, #{right.inspect}" end end @@ -442,7 +495,7 @@ module RubyVM::MJIT unless imm8?(imm) raise ArgumentError, "unexpected imm8: #{imm}" end - imm_bytes(imm, 1) + [imm].pack('c').unpack('c*') # TODO: consider uimm end # id: 4 bytes @@ -450,7 +503,7 @@ module RubyVM::MJIT unless imm32?(imm) raise ArgumentError, "unexpected imm32: #{imm}" end - [imm].pack('l').unpack('c*') + [imm].pack('l').unpack('c*') # TODO: consider uimm end # io: 8 bytes diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb index 56e23a1f77..0e018a31f2 100644 --- a/lib/ruby_vm/mjit/compiler.rb +++ b/lib/ruby_vm/mjit/compiler.rb @@ -16,6 +16,8 @@ module RubyVM::MJIT EndBlock = :EndBlock # Ruby constants + Qtrue = Fiddle::Qtrue + Qfalse = Fiddle::Qfalse Qnil = Fiddle::Qnil Qundef = Fiddle::Qundef @@ -25,6 +27,8 @@ module RubyVM::MJIT CFP = :r15 SP = :rbx + # Scratch registers: rax, rcx + class Compiler attr_accessor :write_pos @@ -134,14 +138,5 @@ module RubyVM::MJIT index += insn.len end end - - # vm_core.h: pathobj_path - def pathobj_path(pathobj) - if pathobj.is_a?(String) - pathobj - else - pathobj.first - end - end end end diff --git a/lib/ruby_vm/mjit/context.rb b/lib/ruby_vm/mjit/context.rb index 8433cc5f95..2799b63eb3 100644 --- a/lib/ruby_vm/mjit/context.rb +++ b/lib/ruby_vm/mjit/context.rb @@ -2,14 +2,14 @@ class RubyVM::MJIT::Context < Struct.new( :stack_size, # @param [Integer] The number of values on the stack :sp_offset, # @param [Integer] JIT sp offset relative to the interpreter's sp ) - def initialize(*) - super - self.stack_size ||= 0 - self.sp_offset ||= 0 - end + def initialize(stack_size: 0, sp_offset: 0) = super def stack_push(size) self.stack_size += size self.sp_offset += size end + + def stack_pop(size) + stack_push(-size) + end end diff --git a/lib/ruby_vm/mjit/exit_compiler.rb b/lib/ruby_vm/mjit/exit_compiler.rb index 4577f65e20..7e1c605d25 100644 --- a/lib/ruby_vm/mjit/exit_compiler.rb +++ b/lib/ruby_vm/mjit/exit_compiler.rb @@ -32,10 +32,10 @@ module RubyVM::MJIT incr_insn_exit(jit.pc) # Fix pc/sp offsets for the interpreter - save_pc_and_sp(jit, ctx, asm) + save_pc_and_sp(jit, ctx.dup, asm) # dup to avoid sp_offset update # Restore callee-saved registers - asm.comment('exit to interpreter') + asm.comment("exit to interpreter on #{pc_to_insn(jit.pc).name}") asm.pop(SP) asm.pop(EC) asm.pop(CFP) @@ -65,6 +65,10 @@ module RubyVM::MJIT private + def pc_to_insn(pc) + Compiler.decode_insn(C.VALUE.new(pc).*) + end + # @param pc [Integer] def incr_insn_exit(pc) if C.mjit_opts.stats diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb index 262d33387a..2950536e8e 100644 --- a/lib/ruby_vm/mjit/insn_compiler.rb +++ b/lib/ruby_vm/mjit/insn_compiler.rb @@ -1,7 +1,4 @@ module RubyVM::MJIT - # scratch regs: rax - # - # 5/101 class InsnCompiler # @param ocb [CodeBlock] # @param exit_compiler [RubyVM::MJIT::ExitCompiler] @@ -20,6 +17,7 @@ module RubyVM::MJIT asm.incr_counter(:mjit_insns_count) asm.comment("Insn: #{insn.name}") + # 5/101 case insn.name # nop # getlocal @@ -149,6 +147,7 @@ module RubyVM::MJIT # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] def putnil(jit, ctx, asm) + raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this asm.mov([SP, C.VALUE.size * ctx.stack_size], Qnil) ctx.stack_push(1) KeepCompiling @@ -165,6 +164,7 @@ module RubyVM::MJIT # Push it to the stack # TODO: GC offsets + raise 'sp_offset != stack_size' if ctx.sp_offset != ctx.stack_size # TODO: handle this 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 @@ -226,7 +226,7 @@ module RubyVM::MJIT asm.comment('RUBY_VM_CHECK_INTS(ec)') asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)]) asm.test(:eax, :eax) - asm.jnz(compile_side_exit(jit, ctx)) + asm.jnz(side_exit(jit, ctx)) asm.comment('pop stack frame') asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1 @@ -268,10 +268,38 @@ module RubyVM::MJIT return EndBlock end - unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT) - return CantCompile + 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 + + asm.comment('guard recv is fixnum'); + asm.test([SP, C.VALUE.size * recv_index], C.RUBY_FIXNUM_FLAG) + asm.je(side_exit(jit, ctx)) + + asm.comment('guard obj is fixnum'); + 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 end - CantCompile end # opt_le @@ -330,6 +358,11 @@ module RubyVM::MJIT end end + def fixnum?(obj) + flag = C.RUBY_FIXNUM_FLAG + (C.to_value(obj) & flag) == flag + end + # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] # @param asm [RubyVM::MJIT::Assembler] @@ -354,10 +387,13 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - def compile_side_exit(jit, ctx) + def side_exit(jit, ctx) + if side_exit = jit.side_exits[jit.pc] + return side_exit + end asm = Assembler.new @exit_compiler.compile_side_exit(jit, ctx, asm) - @ocb.write(asm) + jit.side_exits[jit.pc] = @ocb.write(asm) end end end diff --git a/lib/ruby_vm/mjit/jit_state.rb b/lib/ruby_vm/mjit/jit_state.rb index 97e5b7ffdd..97331e8108 100644 --- a/lib/ruby_vm/mjit/jit_state.rb +++ b/lib/ruby_vm/mjit/jit_state.rb @@ -1,10 +1,13 @@ module RubyVM::MJIT class JITState < Struct.new( - :iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t` - :pc, # @param [Integer] The JIT target PC - :cfp, # @param `RubyVM::MJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before MJIT is called) - :block, # @param [RubyVM::MJIT::Block] + :iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t` + :pc, # @param [Integer] The JIT target PC + :cfp, # @param `RubyVM::MJIT::CPointer::Struct_rb_control_frame_t` The JIT source CFP (before MJIT is called) + :block, # @param [RubyVM::MJIT::Block] + :side_exits, # @param [Hash{ Integer => Integer }] { PC => address } ) + def initialize(side_exits: {}, **) = super + def operand(index) C.VALUE.new(pc)[index + 1] end @@ -12,5 +15,12 @@ module RubyVM::MJIT def at_current_insn? pc == cfp.pc.to_i end + + def peek_at_stack(offset_from_top) + raise 'not at current insn' unless at_current_insn? + offset = -(1 + offset_from_top) + value = (cfp.sp + offset).* + C.to_ruby(value) + end end end |