summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Kokubun <takashikkbn@gmail.com>2022-12-26 14:09:45 -0800
committerTakashi Kokubun <takashikkbn@gmail.com>2023-03-05 22:11:20 -0800
commit3b398513bea65bb0237bb8b7a383ba2874bb0b84 (patch)
tree87954f8ae73692ebd9709c6a38a1def8bad02e64
parent114f8d3e330735fda394c58d7a373913f9ccb93b (diff)
downloadruby-3b398513bea65bb0237bb8b7a383ba2874bb0b84.tar.gz
Check interrupts on leave
-rw-r--r--lib/mjit/insn_compiler.rb (renamed from lib/mjit/codegen.rb)16
-rw-r--r--lib/mjit/jit_state.rb4
-rw-r--r--lib/mjit/x86_assembler.rb78
-rw-r--r--lib/ruby_vm/mjit/compiler.rb63
4 files changed, 121 insertions, 40 deletions
diff --git a/lib/mjit/codegen.rb b/lib/mjit/insn_compiler.rb
index 757e08c1a0..8fd8dfcef8 100644
--- a/lib/mjit/codegen.rb
+++ b/lib/mjit/insn_compiler.rb
@@ -3,20 +3,28 @@ module RubyVM::MJIT
# cfp: rsi
# sp: rbx
# scratch regs: rax
- class Codegen
+ class InsnCompiler
+ # @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::X86Assembler]
- def putnil(ctx, asm)
+ def putnil(jit, ctx, asm)
asm.mov([SP], Qnil)
ctx.stack_size += 1
KeepCompiling
end
+ # @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::X86Assembler]
- def leave(ctx, asm)
+ def leave(jit, ctx, asm)
assert_eq!(ctx.stack_size, 1)
- # TODO: Check interrupts
+
+ # Check interrupts
+ asm.mov(:eax, [EC, C.rb_execution_context_t.offsetof(:interrupt_flag)])
+ asm.test(:eax, :eax)
+ asm.jz(not_interrupted = asm.new_label(:not_interrupted))
+ Compiler.compile_exit(jit, ctx, asm) # TODO: use ocb
+ asm.write_label(not_interrupted)
# Pop the current frame (ec->cfp++)
asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1
diff --git a/lib/mjit/jit_state.rb b/lib/mjit/jit_state.rb
new file mode 100644
index 0000000000..819ecc128b
--- /dev/null
+++ b/lib/mjit/jit_state.rb
@@ -0,0 +1,4 @@
+class RubyVM::MJIT::JITState < Struct.new(
+ :pc, # @param [Integer]
+)
+end
diff --git a/lib/mjit/x86_assembler.rb b/lib/mjit/x86_assembler.rb
index c6c0cc2e19..1d010411db 100644
--- a/lib/mjit/x86_assembler.rb
+++ b/lib/mjit/x86_assembler.rb
@@ -2,6 +2,8 @@
# https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf
module RubyVM::MJIT
class X86Assembler
+ class Label < Data.define(:id, :name); end
+
ByteWriter = CType::Immediate.parse('char')
### prefix ###
@@ -10,9 +12,12 @@ module RubyVM::MJIT
def initialize
@bytes = []
+ @label_id = 0
+ @labels = {}
end
def compile(addr)
+ link_labels
writer = ByteWriter.new(addr)
# If you pack bytes containing \x00, Ruby fails to recognize bytes after \x00.
# So writing byte by byte to avoid hitting that situation.
@@ -41,8 +46,25 @@ module RubyVM::MJIT
end
end
+ # JZ rel8
+ # @param [RubyVM::MJIT::X86Assembler::Label] label
+ def jz(label)
+ # 74 cb
+ insn(opcode: 0x74)
+ @bytes.push(label)
+ end
+
def mov(dst, src)
case [dst, src]
+ # MOV r32 r/m32 (Mod 01)
+ in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r32?(dst_reg) && imm8?(src_disp)
+ # 8B /r
+ # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
+ insn(
+ opcode: 0x8b,
+ mod_rm: mod_rm(mod: 0b01, reg: reg_code(dst_reg), rm: reg_code(src_reg)), # Mod 01: [reg]+disp8
+ disp: src_disp,
+ )
# MOV r/m64, imm32 (Mod 00)
in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg)
# REX.W + C7 /0 id
@@ -73,14 +95,14 @@ module RubyVM::MJIT
imm: imm64(src_imm),
)
# MOV r/m64, r64
- in [[Symbol => dst_reg, Integer => dst_offset], Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) && imm8?(dst_offset)
+ in [[Symbol => dst_reg, Integer => dst_disp], Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) && imm8?(dst_disp)
# REX.W + 89 /r
# MR: Operand 1: ModRM:r/m (w), Operand 2: ModRM:reg (r)
insn(
prefix: REX_W,
opcode: 0x89,
mod_rm: mod_rm(mod: 0b01, reg: reg_code(src_reg), rm: reg_code(dst_reg)), # Mod 01: [reg]+disp8
- disp: dst_offset,
+ disp: dst_disp,
)
# MOV r64, r/m64 (Mod 00)
in [Symbol => dst_reg, [Symbol => src_reg]] if r64?(dst_reg) && r64?(src_reg)
@@ -136,6 +158,30 @@ module RubyVM::MJIT
insn(opcode: 0xc3)
end
+ def test(left, right)
+ case [left, right]
+ # TEST r/m32, r32 (Mod 11)
+ in [Symbol => left_reg, Symbol => right_reg] if r32?(left_reg) && r32?(right_reg)
+ # 85 /r
+ # MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r)
+ insn(
+ opcode: 0x85,
+ mod_rm: mod_rm(mod: 0b11, reg: reg_code(right_reg), rm: reg_code(left_reg)), # Mod 11: reg
+ )
+ else
+ raise NotImplementedError, "pop: not-implemented operands: #{dst.inspect}"
+ end
+ end
+
+ def new_label(name)
+ Label.new(id: @label_id += 1, name:)
+ end
+
+ # @param [RubyVM::MJIT::X86Assembler::Label] label
+ def write_label(label)
+ @labels[label] = @bytes.size
+ end
+
private
def insn(prefix: nil, opcode:, mod_rm: nil, disp: nil, imm: nil)
@@ -235,24 +281,40 @@ module RubyVM::MJIT
end
def imm8?(imm)
- # TODO: consider negative values
- imm <= 0xff
+ raise "negative imm not supported: #{imm}" if imm.negative? # TODO: support this
+ imm <= 0x7f # TODO: consider uimm
end
def imm32?(imm)
- # TODO: consider negative values
+ raise "negative imm not supported: #{imm}" if imm.negative? # TODO: support this
# TODO: consider rejecting small values
- imm <= 0xffff_ffff
+ imm <= 0x7fff_ffff # TODO: consider uimm
end
def imm64?(imm)
- # TODO: consider negative values
+ raise "negative imm not supported: #{imm}" if imm.negative? # TODO: support this
# TODO: consider rejecting small values
- imm <= 0xffff_ffff_ffff_ffff
+ imm <= 0x7fff_ffff_ffff_ffff # TODO: consider uimm
+ end
+
+ def r32?(reg)
+ reg.start_with?('e')
end
def r64?(reg)
reg.start_with?('r')
end
+
+ def link_labels
+ @bytes.each_with_index do |byte, index|
+ if byte.is_a?(Label)
+ src_index = index + 1 # offset 1 byte for rel8 itself
+ dst_index = @labels.fetch(byte)
+ rel8 = dst_index - src_index
+ raise "unexpected offset: #{rel8}" unless imm8?(rel8)
+ @bytes[index] = rel8
+ end
+ end
+ end
end
end
diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb
index c744c533a0..6641c749b6 100644
--- a/lib/ruby_vm/mjit/compiler.rb
+++ b/lib/ruby_vm/mjit/compiler.rb
@@ -1,6 +1,7 @@
-require 'mjit/codegen'
require 'mjit/context'
+require 'mjit/insn_compiler'
require 'mjit/instruction'
+require 'mjit/jit_state'
require 'mjit/x86_assembler'
module RubyVM::MJIT
@@ -21,11 +22,32 @@ module RubyVM::MJIT
class Compiler
attr_accessor :write_pos
+ # @param jit [RubyVM::MJIT::JITState]
+ # @param ctx [RubyVM::MJIT::Context]
+ # @param asm [RubyVM::MJIT::X86Assembler]
+ def self.compile_exit(jit, ctx, asm)
+ # update pc
+ asm.mov(:rax, jit.pc) # rax = jit.pc
+ asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
+
+ # update sp
+ if ctx.stack_size > 0
+ asm.add(SP, C.VALUE.size * ctx.stack_size) # rbx += stack_size
+ asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], SP) # cfp->sp = rbx
+ end
+
+ # Restore callee-saved registers
+ asm.pop(SP)
+
+ asm.mov(:rax, Qundef)
+ asm.ret
+ end
+
# @param mem_block [Integer] JIT buffer address
def initialize(mem_block)
@mem_block = mem_block
@write_pos = 0
- @codegen = Codegen.new
+ @insn_compiler = InsnCompiler.new
end
# @param iseq [RubyVM::MJIT::CPointer::Struct]
@@ -79,58 +101,43 @@ module RubyVM::MJIT
# @param asm [RubyVM::MJIT::X86Assembler]
def compile_block(asm, iseq)
+ jit = JITState.new
ctx = Context.new
+
index = 0
while index < iseq.body.iseq_size
insn = decode_insn(iseq.body.iseq_encoded[index])
- case compile_insn(ctx, asm, insn)
+ jit.pc = (iseq.body.iseq_encoded + index).to_i
+
+ case compile_insn(jit, ctx, asm, insn)
when EndBlock
break
when CantCompile
- compile_exit(ctx, asm, (iseq.body.iseq_encoded + index).to_i)
+ self.class.compile_exit(jit, ctx, asm)
break
end
index += insn.len
end
end
+ # @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::X86Assembler]
- def compile_insn(ctx, asm, insn)
+ def compile_insn(jit, ctx, asm, insn)
case insn.name
- when :putnil then @codegen.putnil(ctx, asm)
- when :leave then @codegen.leave(ctx, asm)
+ when :putnil then @insn_compiler.putnil(jit, ctx, asm)
+ when :leave then @insn_compiler.leave(jit, ctx, asm)
else CantCompile
end
end
- # @param ctx [RubyVM::MJIT::Context]
- # @param asm [RubyVM::MJIT::X86Assembler]
- def compile_exit(ctx, asm, exit_pc)
- # update pc
- asm.mov(:rax, exit_pc) # rax = exit_pc
- asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
-
- # update sp
- if ctx.stack_size > 0
- asm.add(SP, C.VALUE.size * ctx.stack_size) # rbx += stack_size
- asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], SP) # cfp->sp = rbx
- end
-
- # Restore callee-saved registers
- asm.pop(SP)
-
- asm.mov(:rax, Qundef)
- asm.ret
- end
-
def decode_insn(encoded)
INSNS.fetch(C.rb_vm_insn_decode(encoded))
end
def dump_disasm(from, to)
C.dump_disasm(from, to).each do |address, mnemonic, op_str|
- puts " 0x#{"%p" % address}: #{mnemonic} #{op_str}"
+ puts " 0x#{"%x" % address}: #{mnemonic} #{op_str}"
end
puts
end