Partly implement BOP assumption
This commit is contained in:
parent
00c659d246
commit
21696ad81e
@ -31,12 +31,13 @@ module RubyVM::MJIT
|
|||||||
@labels = {}
|
@labels = {}
|
||||||
@label_id = 0
|
@label_id = 0
|
||||||
@comments = Hash.new { |h, k| h[k] = [] }
|
@comments = Hash.new { |h, k| h[k] = [] }
|
||||||
|
@blocks = Hash.new { |h, k| h[k] = [] }
|
||||||
@stub_starts = Hash.new { |h, k| h[k] = [] }
|
@stub_starts = Hash.new { |h, k| h[k] = [] }
|
||||||
@stub_ends = Hash.new { |h, k| h[k] = [] }
|
@stub_ends = Hash.new { |h, k| h[k] = [] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def assemble(addr)
|
def assemble(addr)
|
||||||
set_stub_addrs(addr)
|
set_code_addrs(addr)
|
||||||
resolve_rel32(addr)
|
resolve_rel32(addr)
|
||||||
resolve_labels
|
resolve_labels
|
||||||
|
|
||||||
@ -307,6 +308,12 @@ module RubyVM::MJIT
|
|||||||
@comments[@bytes.size] << message
|
@comments[@bytes.size] << message
|
||||||
end
|
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)
|
def stub(stub)
|
||||||
@stub_starts[@bytes.size] << stub
|
@stub_starts[@bytes.size] << stub
|
||||||
yield
|
yield
|
||||||
@ -495,8 +502,11 @@ module RubyVM::MJIT
|
|||||||
[Rel32.new(addr), Rel32Pad, Rel32Pad, Rel32Pad]
|
[Rel32.new(addr), Rel32Pad, Rel32Pad, Rel32Pad]
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_stub_addrs(write_addr)
|
def set_code_addrs(write_addr)
|
||||||
(@bytes.size + 1).times do |index|
|
(@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_starts.fetch(index, []).each do |stub|
|
||||||
stub.start_addr = write_addr + index
|
stub.start_addr = write_addr + index
|
||||||
end
|
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/assembler'
|
||||||
|
require 'ruby_vm/mjit/block'
|
||||||
require 'ruby_vm/mjit/block_stub'
|
require 'ruby_vm/mjit/block_stub'
|
||||||
require 'ruby_vm/mjit/code_block'
|
require 'ruby_vm/mjit/code_block'
|
||||||
require 'ruby_vm/mjit/context'
|
require 'ruby_vm/mjit/context'
|
||||||
require 'ruby_vm/mjit/exit_compiler'
|
require 'ruby_vm/mjit/exit_compiler'
|
||||||
require 'ruby_vm/mjit/insn_compiler'
|
require 'ruby_vm/mjit/insn_compiler'
|
||||||
require 'ruby_vm/mjit/instruction'
|
require 'ruby_vm/mjit/instruction'
|
||||||
|
require 'ruby_vm/mjit/invariants'
|
||||||
require 'ruby_vm/mjit/jit_state'
|
require 'ruby_vm/mjit/jit_state'
|
||||||
|
|
||||||
module RubyVM::MJIT
|
module RubyVM::MJIT
|
||||||
@ -36,7 +38,7 @@ module RubyVM::MJIT
|
|||||||
@cb = CodeBlock.new(mem_block: mem_block, mem_size: mem_size / 2)
|
@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)
|
@ocb = CodeBlock.new(mem_block: mem_block + mem_size / 2, mem_size: mem_size / 2, outlined: true)
|
||||||
@exit_compiler = ExitCompiler.new
|
@exit_compiler = ExitCompiler.new
|
||||||
@insn_compiler = InsnCompiler.new(@ocb)
|
@insn_compiler = InsnCompiler.new(@ocb, @exit_compiler)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Compile an ISEQ from its entry point.
|
# Compile an ISEQ from its entry point.
|
||||||
@ -66,8 +68,7 @@ module RubyVM::MJIT
|
|||||||
# Prepare the jump target
|
# Prepare the jump target
|
||||||
new_asm = Assembler.new.tap do |asm|
|
new_asm = Assembler.new.tap do |asm|
|
||||||
jit = JITState.new(iseq: stub.iseq, cfp:)
|
jit = JITState.new(iseq: stub.iseq, cfp:)
|
||||||
index = (stub.pc - stub.iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
compile_block(asm, jit:, pc: stub.pc, ctx: stub.ctx)
|
||||||
compile_block(asm, jit:, index:, ctx: stub.ctx)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rewrite the stub
|
# Rewrite the stub
|
||||||
@ -110,17 +111,24 @@ module RubyVM::MJIT
|
|||||||
end
|
end
|
||||||
|
|
||||||
# @param asm [RubyVM::MJIT::Assembler]
|
# @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
|
iseq = jit.iseq
|
||||||
|
index = (pc - iseq.body.iseq_encoded.to_i) / C.VALUE.size
|
||||||
while index < iseq.body.iseq_size
|
while index < iseq.body.iseq_size
|
||||||
insn = self.class.decode_insn(iseq.body.iseq_encoded[index])
|
insn = self.class.decode_insn(iseq.body.iseq_encoded[index])
|
||||||
jit.pc = (iseq.body.iseq_encoded + index).to_i
|
jit.pc = (iseq.body.iseq_encoded + index).to_i
|
||||||
|
|
||||||
case @insn_compiler.compile(jit, ctx, asm, insn)
|
case @insn_compiler.compile(jit, ctx, asm, insn)
|
||||||
when EndBlock
|
when EndBlock
|
||||||
|
# TODO: pad nops if entry exit exists
|
||||||
break
|
break
|
||||||
when CantCompile
|
when CantCompile
|
||||||
@exit_compiler.compile_exit(jit, ctx, asm)
|
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
index += insn.len
|
index += insn.len
|
||||||
|
@ -5,16 +5,31 @@ module RubyVM::MJIT
|
|||||||
@gc_refs = []
|
@gc_refs = []
|
||||||
end
|
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 jit [RubyVM::MJIT::JITState]
|
||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @param ctx [RubyVM::MJIT::Context]
|
||||||
# @param asm [RubyVM::MJIT::Assembler]
|
# @param asm [RubyVM::MJIT::Assembler]
|
||||||
def compile_exit(jit, ctx, asm)
|
def compile_side_exit(jit, ctx, asm)
|
||||||
if C.mjit_opts.stats
|
# Increment per-insn exit counter
|
||||||
insn = decode_insn(C.VALUE.new(jit.pc).*)
|
incr_insn_exit(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
|
|
||||||
|
|
||||||
# Fix pc/sp offsets for the interpreter
|
# Fix pc/sp offsets for the interpreter
|
||||||
save_pc_and_sp(jit, ctx, asm)
|
save_pc_and_sp(jit, ctx, asm)
|
||||||
@ -50,11 +65,21 @@ module RubyVM::MJIT
|
|||||||
|
|
||||||
private
|
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 jit [RubyVM::MJIT::JITState]
|
||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @param ctx [RubyVM::MJIT::Context]
|
||||||
# @param asm [RubyVM::MJIT::Assembler]
|
# @param asm [RubyVM::MJIT::Assembler]
|
||||||
def save_pc_and_sp(jit, ctx, asm)
|
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.comment("save pc #{'and sp' if ctx.sp_offset != 0}")
|
||||||
asm.mov(:rax, jit.pc) # rax = jit.pc
|
asm.mov(:rax, jit.pc) # rax = jit.pc
|
||||||
asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
|
asm.mov([CFP, C.rb_control_frame_t.offsetof(:pc)], :rax) # cfp->pc = rax
|
||||||
|
@ -4,9 +4,11 @@ module RubyVM::MJIT
|
|||||||
# 5/101
|
# 5/101
|
||||||
class InsnCompiler
|
class InsnCompiler
|
||||||
# @param ocb [CodeBlock]
|
# @param ocb [CodeBlock]
|
||||||
def initialize(ocb)
|
# @param exit_compiler [RubyVM::MJIT::ExitCompiler]
|
||||||
|
def initialize(ocb, exit_compiler)
|
||||||
@ocb = ocb
|
@ocb = ocb
|
||||||
@exit_compiler = ExitCompiler.new
|
@exit_compiler = exit_compiler
|
||||||
|
@invariants = Invariants.new(ocb, exit_compiler)
|
||||||
freeze
|
freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -265,6 +267,10 @@ module RubyVM::MJIT
|
|||||||
defer_compilation(jit, ctx, asm)
|
defer_compilation(jit, ctx, asm)
|
||||||
return EndBlock
|
return EndBlock
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unless @invariants.assume_bop_not_redefined(jit, C.INTEGER_REDEFINED_OP_FLAG, C.BOP_LT)
|
||||||
|
return CantCompile
|
||||||
|
end
|
||||||
CantCompile
|
CantCompile
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -350,7 +356,7 @@ module RubyVM::MJIT
|
|||||||
# @param ctx [RubyVM::MJIT::Context]
|
# @param ctx [RubyVM::MJIT::Context]
|
||||||
def compile_side_exit(jit, ctx)
|
def compile_side_exit(jit, ctx)
|
||||||
asm = Assembler.new
|
asm = Assembler.new
|
||||||
@exit_compiler.compile_exit(jit, ctx, asm)
|
@exit_compiler.compile_side_exit(jit, ctx, asm)
|
||||||
@ocb.write(asm)
|
@ocb.write(asm)
|
||||||
end
|
end
|
||||||
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
|
module RubyVM::MJIT
|
||||||
class JITState < Struct.new(
|
class JITState < Struct.new(
|
||||||
:iseq,
|
:iseq, # @param `RubyVM::MJIT::CPointer::Struct_rb_iseq_t`
|
||||||
:pc, # @param [Integer] The JIT target PC
|
: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)
|
: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)
|
def operand(index)
|
||||||
C.VALUE.new(pc)[index + 1]
|
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)'
|
Primitive.cexpr! 'SIZET2NUM((size_t)obj)'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def BASIC_OP_UNREDEFINED_P(op, klass)
|
||||||
|
Primitive.cexpr! 'RBOOL(BASIC_OP_UNREDEFINED_P(NUM2INT(op), NUM2INT(klass)))'
|
||||||
|
end
|
||||||
|
|
||||||
#========================================================================================
|
#========================================================================================
|
||||||
#
|
#
|
||||||
# Old stuff
|
# Old stuff
|
||||||
@ -241,6 +245,14 @@ module RubyVM::MJIT # :nodoc: all
|
|||||||
Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) }
|
Primitive.cexpr! %q{ INT2NUM(VM_METHOD_TYPE_ISEQ) }
|
||||||
end
|
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
|
def C.RUBY_EVENT_CLASS
|
||||||
Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
|
Primitive.cexpr! %q{ UINT2NUM(RUBY_EVENT_CLASS) }
|
||||||
end
|
end
|
||||||
|
@ -353,6 +353,8 @@ generator = BindingGenerator.new(
|
|||||||
VM_METHOD_TYPE_ISEQ
|
VM_METHOD_TYPE_ISEQ
|
||||||
],
|
],
|
||||||
UINT: %w[
|
UINT: %w[
|
||||||
|
BOP_LT
|
||||||
|
INTEGER_REDEFINED_OP_FLAG
|
||||||
RUBY_EVENT_CLASS
|
RUBY_EVENT_CLASS
|
||||||
SHAPE_CAPACITY_CHANGE
|
SHAPE_CAPACITY_CHANGE
|
||||||
SHAPE_FLAG_SHIFT
|
SHAPE_FLAG_SHIFT
|
||||||
|
Loading…
x
Reference in New Issue
Block a user