Partly implement BOP assumption
This commit is contained in:
parent
00c659d246
commit
21696ad81e
@ -31,12 +31,13 @@ module RubyVM::MJIT
|
||||
@labels = {}
|
||||
@label_id = 0
|
||||
@comments = Hash.new { |h, k| h[k] = [] }
|
||||
@blocks = Hash.new { |h, k| h[k] = [] }
|
||||
@stub_starts = Hash.new { |h, k| h[k] = [] }
|
||||
@stub_ends = Hash.new { |h, k| h[k] = [] }
|
||||
end
|
||||
|
||||
def assemble(addr)
|
||||
set_stub_addrs(addr)
|
||||
set_code_addrs(addr)
|
||||
resolve_rel32(addr)
|
||||
resolve_labels
|
||||
|
||||
@ -307,6 +308,12 @@ module RubyVM::MJIT
|
||||
@comments[@bytes.size] << message
|
||||
end
|
||||
|
||||
# Mark the starting address of a block
|
||||
def block(block)
|
||||
@blocks[@bytes.size] << block
|
||||
end
|
||||
|
||||
# Mark the starting/ending addresses of a stub
|
||||
def stub(stub)
|
||||
@stub_starts[@bytes.size] << stub
|
||||
yield
|
||||
@ -495,8 +502,11 @@ module RubyVM::MJIT
|
||||
[Rel32.new(addr), Rel32Pad, Rel32Pad, Rel32Pad]
|
||||
end
|
||||
|
||||
def set_stub_addrs(write_addr)
|
||||
def set_code_addrs(write_addr)
|
||||
(@bytes.size + 1).times do |index|
|
||||
@blocks.fetch(index, []).each do |block|
|
||||
block.start_addr = write_addr + index
|
||||
end
|
||||
@stub_starts.fetch(index, []).each do |stub|
|
||||
stub.start_addr = write_addr + index
|
||||
end
|
||||
|
6
lib/ruby_vm/mjit/block.rb
Normal file
6
lib/ruby_vm/mjit/block.rb
Normal file
@ -0,0 +1,6 @@
|
||||
class RubyVM::MJIT::Block < Struct.new(
|
||||
:pc, # @param [Integer] Starting PC
|
||||
:start_addr, # @param [Integer] Starting address of this block's JIT code
|
||||
:entry_exit, # @param [Integer] Address of entry exit (optional)
|
||||
)
|
||||
end
|
@ -1,10 +1,12 @@
|
||||
require 'ruby_vm/mjit/assembler'
|
||||
require 'ruby_vm/mjit/block'
|
||||
require 'ruby_vm/mjit/block_stub'
|
||||
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/invariants'
|
||||
require 'ruby_vm/mjit/jit_state'
|
||||
|
||||
module RubyVM::MJIT
|
||||
@ -36,7 +38,7 @@ module RubyVM::MJIT
|
||||
@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, outlined: true)
|
||||
@exit_compiler = ExitCompiler.new
|
||||
@insn_compiler = InsnCompiler.new(@ocb)
|
||||
@insn_compiler = InsnCompiler.new(@ocb, @exit_compiler)
|
||||
end
|
||||
|
||||
# Compile an ISEQ from its entry point.
|
||||
@ -66,8 +68,7 @@ module RubyVM::MJIT
|
||||
# Prepare the jump target
|
||||
new_asm = Assembler.new.tap do |asm|
|
||||
jit = JITState.new(iseq: stub.iseq, cfp:)
|
||||
index = (stub.pc - stub.iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
||||
compile_block(asm, jit:, index:, ctx: stub.ctx)
|
||||
compile_block(asm, jit:, pc: stub.pc, ctx: stub.ctx)
|
||||
end
|
||||
|
||||
# Rewrite the stub
|
||||
@ -110,17 +111,24 @@ module RubyVM::MJIT
|
||||
end
|
||||
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def compile_block(asm, jit:, index: 0, ctx: Context.new)
|
||||
def compile_block(asm, jit:, pc: jit.iseq.body.iseq_encoded.to_i, ctx: Context.new)
|
||||
# Mark the block start address and prepare an exit code storage
|
||||
jit.block = Block.new(pc:)
|
||||
asm.block(jit.block)
|
||||
|
||||
# Compile each insn
|
||||
iseq = jit.iseq
|
||||
index = (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
||||
while index < iseq.body.iseq_size
|
||||
insn = self.class.decode_insn(iseq.body.iseq_encoded[index])
|
||||
jit.pc = (iseq.body.iseq_encoded + index).to_i
|
||||
|
||||
case @insn_compiler.compile(jit, ctx, asm, insn)
|
||||
when EndBlock
|
||||
# TODO: pad nops if entry exit exists
|
||||
break
|
||||
when CantCompile
|
||||
@exit_compiler.compile_exit(jit, ctx, asm)
|
||||
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
||||
break
|
||||
end
|
||||
index += insn.len
|
||||
|
@ -5,16 +5,31 @@ module RubyVM::MJIT
|
||||
@gc_refs = []
|
||||
end
|
||||
|
||||
# Used for invalidating a block on entry.
|
||||
# @param pc [Integer]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def compile_entry_exit(pc, asm, cause:)
|
||||
# Increment per-insn exit counter
|
||||
incr_insn_exit(pc)
|
||||
|
||||
# TODO: Saving pc and sp may be needed later
|
||||
|
||||
# Restore callee-saved registers
|
||||
asm.comment("#{cause}: entry exit")
|
||||
asm.pop(SP)
|
||||
asm.pop(EC)
|
||||
asm.pop(CFP)
|
||||
|
||||
asm.mov(:rax, Qundef)
|
||||
asm.ret
|
||||
end
|
||||
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def compile_exit(jit, ctx, asm)
|
||||
if C.mjit_opts.stats
|
||||
insn = decode_insn(C.VALUE.new(jit.pc).*)
|
||||
asm.comment("increment insn exit: #{insn.name}")
|
||||
asm.mov(:rax, (C.mjit_insn_exits + insn.bin).to_i)
|
||||
asm.add([:rax], 1) # TODO: lock
|
||||
end
|
||||
def compile_side_exit(jit, ctx, asm)
|
||||
# Increment per-insn exit counter
|
||||
incr_insn_exit(jit.pc)
|
||||
|
||||
# Fix pc/sp offsets for the interpreter
|
||||
save_pc_and_sp(jit, ctx, asm)
|
||||
@ -50,11 +65,21 @@ module RubyVM::MJIT
|
||||
|
||||
private
|
||||
|
||||
# @param pc [Integer]
|
||||
def incr_insn_exit(pc)
|
||||
if C.mjit_opts.stats
|
||||
insn = decode_insn(C.VALUE.new(pc).*)
|
||||
asm.comment("increment insn exit: #{insn.name}")
|
||||
asm.mov(:rax, (C.mjit_insn_exits + insn.bin).to_i)
|
||||
asm.add([:rax], 1) # TODO: lock
|
||||
end
|
||||
end
|
||||
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
# @param asm [RubyVM::MJIT::Assembler]
|
||||
def save_pc_and_sp(jit, ctx, asm)
|
||||
# Update pc
|
||||
# Update pc (TODO: manage PC offset?)
|
||||
asm.comment("save pc #{'and sp' if ctx.sp_offset != 0}")
|
||||
asm.mov(:rax, jit.pc) # rax = jit.pc
|
||||
asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
|
||||
|
@ -4,9 +4,11 @@ module RubyVM::MJIT
|
||||
# 5/101
|
||||
class InsnCompiler
|
||||
# @param ocb [CodeBlock]
|
||||
def initialize(ocb)
|
||||
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
||||
def initialize(ocb, exit_compiler)
|
||||
@ocb = ocb
|
||||
@exit_compiler = ExitCompiler.new
|
||||
@exit_compiler = exit_compiler
|
||||
@invariants = Invariants.new(ocb, exit_compiler)
|
||||
freeze
|
||||
end
|
||||
|
||||
@ -265,6 +267,10 @@ module RubyVM::MJIT
|
||||
defer_compilation(jit, ctx, asm)
|
||||
return EndBlock
|
||||
end
|
||||
|
||||
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
|
||||
return CantCompile
|
||||
end
|
||||
CantCompile
|
||||
end
|
||||
|
||||
@ -350,7 +356,7 @@ module RubyVM::MJIT
|
||||
# @param ctx [RubyVM::MJIT::Context]
|
||||
def compile_side_exit(jit, ctx)
|
||||
asm = Assembler.new
|
||||
@exit_compiler.compile_exit(jit, ctx, asm)
|
||||
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
||||
@ocb.write(asm)
|
||||
end
|
||||
end
|
||||
|
35
lib/ruby_vm/mjit/invariants.rb
Normal file
35
lib/ruby_vm/mjit/invariants.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require 'set'
|
||||
|
||||
module RubyVM::MJIT
|
||||
class Invariants
|
||||
# @param ocb [CodeBlock]
|
||||
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
||||
def initialize(ocb, exit_compiler)
|
||||
@ocb = ocb
|
||||
@exit_compiler = exit_compiler
|
||||
@bop_blocks = Set.new # TODO: actually invalidate this
|
||||
end
|
||||
|
||||
# @param jit [RubyVM::MJIT::JITState]
|
||||
# @param klass [Integer]
|
||||
# @param op [Integer]
|
||||
def assume_bop_not_redefined(jit, klass, op)
|
||||
return false unless C.BASIC_OP_UNREDEFINED_P(klass, op)
|
||||
|
||||
ensure_block_entry_exit(jit.block, cause: 'assume_bop_not_redefined')
|
||||
@bop_blocks << jit.block
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @param block [RubyVM::MJIT::Block]
|
||||
def ensure_block_entry_exit(block, cause:)
|
||||
if block.entry_exit.nil?
|
||||
asm = Assembler.new
|
||||
@exit_compiler.compile_entry_exit(block.pc, asm, cause:)
|
||||
block.entry_exit = @ocb.write(asm)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,8 +1,9 @@
|
||||
module RubyVM::MJIT
|
||||
class JITState < Struct.new(
|
||||
:iseq,
|
||||
: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)
|
||||
: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]
|
||||
)
|
||||
def operand(index)
|
||||
C.VALUE.new(pc)[index + 1]
|
||||
|
12
mjit_c.rb
12
mjit_c.rb
@ -65,6 +65,10 @@ module RubyVM::MJIT # :nodoc: all
|
||||
Primitive.cexpr! 'SIZET2NUM((size_t)obj)'
|
||||
end
|
||||
|
||||
def BASIC_OP_UNREDEFINED_P(op, klass)
|
||||
Primitive.cexpr! 'RBOOL(BASIC_OP_UNREDEFINED_P(NUM2INT(op), NUM2INT(klass)))'
|
||||
end
|
||||
|
||||
#========================================================================================
|
||||
#
|
||||
# Old stuff
|
||||
@ -241,6 +245,14 @@ module RubyVM::MJIT # :nodoc: all
|
||||
Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) }
|
||||
end
|
||||
|
||||
def C.BOP_LT
|
||||
Primitive.cexpr! %q{ UINT2NUM(BOP_LT) }
|
||||
end
|
||||
|
||||
def C.INTEGER_REDEFINED_OP_FLAG
|
||||
Primitive.cexpr! %q{ UINT2NUM(INTEGER_REDEFINED_OP_FLAG) }
|
||||
end
|
||||
|
||||
def C.RUBY_EVENT_CLASS
|
||||
Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
|
||||
end
|
||||
|
@ -353,6 +353,8 @@ generator = BindingGenerator.new(
|
||||
VM_METHOD_TYPE_ISEQ
|
||||
],
|
||||
UINT: %w[
|
||||
BOP_LT
|
||||
INTEGER_REDEFINED_OP_FLAG
|
||||
RUBY_EVENT_CLASS
|
||||
SHAPE_CAPACITY_CHANGE
|
||||
SHAPE_FLAG_SHIFT
|
||||
|
Loading…
x
Reference in New Issue
Block a user