diff options
author | Takashi Kokubun <[email protected]> | 2022-12-30 22:16:07 -0800 |
---|---|---|
committer | Takashi Kokubun <[email protected]> | 2023-03-05 22:11:20 -0800 |
commit | 36cec59f0a54b817ae6b3836fb0e97e342b999ce (patch) | |
tree | 2978e1388c2c093a521456a9c08cf507ca0df7e0 | |
parent | 7abff797b434ead0653c89f5429490bd0f716f88 (diff) |
Implement ocb
-rw-r--r-- | lib/ruby_vm/mjit/assembler.rb (renamed from lib/ruby_vm/mjit/x86_assembler.rb) | 63 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/code_block.rb | 2 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/compiler.rb | 15 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/exit_compiler.rb | 2 | ||||
-rw-r--r-- | lib/ruby_vm/mjit/insn_compiler.rb | 27 |
5 files changed, 79 insertions, 30 deletions
diff --git a/lib/ruby_vm/mjit/x86_assembler.rb b/lib/ruby_vm/mjit/assembler.rb index 9856d4e4ac..4620430faf 100644 --- a/lib/ruby_vm/mjit/x86_assembler.rb +++ b/lib/ruby_vm/mjit/assembler.rb @@ -1,9 +1,14 @@ # frozen_string_literal: true -# https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf module RubyVM::MJIT - class X86Assembler + # https://www.intel.com/content/dam/develop/public/us/en/documents/325383-sdm-vol-2abcd.pdf + # Mostly an x86_64 assembler, but this also has some stuff that is useful for any architecture. + class Assembler class Label < Data.define(:id, :name); end + # rel32 is inserted as [Rel32, Rel32Pad..] and converted on #resolve_rel32 + class Rel32 < Data.define(:addr); end + Rel32Pad = Object.new + ByteWriter = CType::Immediate.parse('char') ### prefix ### @@ -18,7 +23,9 @@ module RubyVM::MJIT end def assemble(addr) - link_labels + resolve_rel32(addr) + resolve_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. @@ -65,12 +72,26 @@ module RubyVM::MJIT end end - # JZ rel8 - # @param [RubyVM::MJIT::X86Assembler::Label] label - def jz(label) - # 74 cb - insn(opcode: 0x74) - @bytes.push(label) + def jnz(dst) + case dst + # JNZ rel32 + in Integer => addr + # 0F 85 cd + insn(opcode: [0x0f, 0x85], imm: rel32(addr)) + else + raise NotImplementedError, "jnz: not-implemented operands: #{dst.inspect}" + end + end + + def jz(dst) + case dst + # JZ rel8 + in Label => label + # 74 cb + insn(opcode: 0x74, imm: label) + else + raise NotImplementedError, "jz: not-implemented operands: #{dst.inspect}" + end end def mov(dst, src) @@ -244,7 +265,7 @@ module RubyVM::MJIT Label.new(id: @label_id += 1, name:) end - # @param [RubyVM::MJIT::X86Assembler::Label] label + # @param [RubyVM::MJIT::Assembler::Label] label def write_label(label) @labels[label] = @bytes.size end @@ -267,7 +288,7 @@ module RubyVM::MJIT if prefix @bytes.push(prefix) end - @bytes.push(opcode) + @bytes.push(*Array(opcode)) if mod_rm @bytes.push(mod_rm) end @@ -375,7 +396,25 @@ module RubyVM::MJIT reg.start_with?('r') end - def link_labels + def rel32(addr) + [Rel32.new(addr), Rel32Pad, Rel32Pad, Rel32Pad] + end + + def resolve_rel32(write_addr) + @bytes.each_with_index do |byte, index| + if byte.is_a?(Rel32) + src_addr = write_addr + index + 4 # offset 4 bytes for rel32 itself + dst_addr = byte.addr + rel32 = dst_addr - src_addr + raise "unexpected offset: #{rel32}" unless imm32?(rel32) + imm32(rel32).each_with_index do |rel_byte, rel_index| + @bytes[index + rel_index] = rel_byte + end + end + end + end + + def resolve_labels @bytes.each_with_index do |byte, index| if byte.is_a?(Label) src_index = index + 1 # offset 1 byte for rel8 itself diff --git a/lib/ruby_vm/mjit/code_block.rb b/lib/ruby_vm/mjit/code_block.rb index 17e2a0053d..464d13ba8d 100644 --- a/lib/ruby_vm/mjit/code_block.rb +++ b/lib/ruby_vm/mjit/code_block.rb @@ -9,7 +9,7 @@ module RubyVM::MJIT @write_pos = 0 end - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def write(asm) return 0 if @write_pos + asm.size >= @mem_size diff --git a/lib/ruby_vm/mjit/compiler.rb b/lib/ruby_vm/mjit/compiler.rb index f872c05bce..e114d43e0d 100644 --- a/lib/ruby_vm/mjit/compiler.rb +++ b/lib/ruby_vm/mjit/compiler.rb @@ -1,10 +1,10 @@ +require 'ruby_vm/mjit/assembler' require 'ruby_vm/mjit/code_block' require 'ruby_vm/mjit/context' require 'ruby_vm/mjit/exit_compiler' require 'ruby_vm/mjit/insn_compiler' require 'ruby_vm/mjit/instruction' require 'ruby_vm/mjit/jit_state' -require 'ruby_vm/mjit/x86_assembler' module RubyVM::MJIT # Compilation status @@ -31,9 +31,10 @@ module RubyVM::MJIT # @param mem_block [Integer] JIT buffer address # @param mem_size [Integer] JIT buffer size def initialize(mem_block, mem_size) - @cb = CodeBlock.new(mem_block:, mem_size:) + @cb = CodeBlock.new(mem_block: mem_block, mem_size: mem_size / 2) + @ocb = CodeBlock.new(mem_block: mem_block + mem_size / 2, mem_size: mem_size / 2) @exit_compiler = ExitCompiler.new - @insn_compiler = InsnCompiler.new + @insn_compiler = InsnCompiler.new(@ocb) end # @param iseq [RubyVM::MJIT::CPointer::Struct] @@ -41,7 +42,7 @@ module RubyVM::MJIT # TODO: Support has_opt return if iseq.body.param.flags.has_opt - asm = X86Assembler.new + asm = Assembler.new asm.comment("Block: #{iseq.body.location.label}@#{pathobj_path(iseq.body.location.pathobj)}:#{iseq.body.location.first_lineno}") compile_prologue(asm) compile_block(asm, iseq) @@ -58,7 +59,7 @@ module RubyVM::MJIT # Callee-saved: rbx, rsp, rbp, r12, r13, r14, r15 # Caller-saved: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 # - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def compile_prologue(asm) asm.comment("MJIT entry") @@ -69,7 +70,7 @@ module RubyVM::MJIT asm.mov(SP, [CFP, C.rb_control_frame_t.offsetof(:sp)]) # rbx = cfp->sp end - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def compile_block(asm, iseq) jit = JITState.new ctx = Context.new @@ -92,7 +93,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def compile_insn(jit, ctx, asm, insn) asm.incr_counter(:mjit_insns_count) asm.comment("Insn: #{insn.name}") diff --git a/lib/ruby_vm/mjit/exit_compiler.rb b/lib/ruby_vm/mjit/exit_compiler.rb index bd1cf67a3e..7f5219ea5b 100644 --- a/lib/ruby_vm/mjit/exit_compiler.rb +++ b/lib/ruby_vm/mjit/exit_compiler.rb @@ -4,7 +4,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def compile_exit(jit, ctx, asm) if C.mjit_opts.stats insn = decode_insn(C.VALUE.new(jit.pc).*) diff --git a/lib/ruby_vm/mjit/insn_compiler.rb b/lib/ruby_vm/mjit/insn_compiler.rb index 4c9c24eca6..4c4c86c5f0 100644 --- a/lib/ruby_vm/mjit/insn_compiler.rb +++ b/lib/ruby_vm/mjit/insn_compiler.rb @@ -3,9 +3,12 @@ module RubyVM::MJIT # cfp: rsi # sp: rbx # scratch regs: rax + # + # 4/101 class InsnCompiler - # 4/101 - def initialize + # @param ocb [CodeBlock] + def initialize(ocb) + @ocb = ocb @exit_compiler = ExitCompiler.new freeze end @@ -30,7 +33,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def putnil(jit, ctx, asm) asm.mov([SP, C.VALUE.size * ctx.stack_size], Qnil) ctx.stack_size += 1 @@ -41,7 +44,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def putobject(jit, ctx, asm) # Get operands val = jit.operand(0) @@ -102,16 +105,14 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def leave(jit, ctx, asm) assert_eq!(ctx.stack_size, 1) asm.comment('RUBY_VM_CHECK_INTS(ec)') 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)) - @exit_compiler.compile_exit(jit, ctx, asm) # TODO: use ocb - asm.write_label(not_interrupted) + asm.jnz(compile_side_exit(jit, ctx)) asm.comment('pop stack frame') asm.add(CFP, C.rb_control_frame_t.size) # cfp = cfp + 1 @@ -164,7 +165,7 @@ module RubyVM::MJIT # @param jit [RubyVM::MJIT::JITState] # @param ctx [RubyVM::MJIT::Context] - # @param asm [RubyVM::MJIT::X86Assembler] + # @param asm [RubyVM::MJIT::Assembler] def getlocal_WC_0(jit, ctx, asm) # Get operands idx = jit.operand(0) @@ -195,5 +196,13 @@ module RubyVM::MJIT raise "'#{left.inspect}' was not '#{right.inspect}'" end end + + # @param jit [RubyVM::MJIT::JITState] + # @param ctx [RubyVM::MJIT::Context] + def compile_side_exit(jit, ctx) + asm = Assembler.new + @exit_compiler.compile_exit(jit, ctx, asm) + @ocb.write(asm) + end end end |