Implement optimized send

This commit is contained in:
Takashi Kokubun 2023-02-16 22:29:58 -08:00
parent 2603d7a0b7
commit 2cc4f506ba
Notes: git 2023-03-06 07:29:57 +00:00
9 changed files with 247 additions and 73 deletions

View file

@ -1,5 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
module RubyVM::MJIT module RubyVM::MJIT
# 8-bit memory access
class BytePtr < Data.define(:reg, :disp); end
# 32-bit memory access # 32-bit memory access
class DwordPtr < Data.define(:reg, :disp); end class DwordPtr < Data.define(:reg, :disp); end
@ -66,7 +69,7 @@ module RubyVM::MJIT
def add(dst, src) def add(dst, src)
case [dst, src] case [dst, src]
# ADD r/m64, imm8 (Mod 00: [reg]) # ADD r/m64, imm8 (Mod 00: [reg])
in [[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm) in [Array[Symbol => dst_reg], Integer => src_imm] if r64?(dst_reg) && imm8?(src_imm)
# REX.W + 83 /0 ib # REX.W + 83 /0 ib
# MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32 # MI: Operand 1: ModRM:r/m (r, w), Operand 2: imm8/16/32
insn( insn(
@ -132,7 +135,7 @@ module RubyVM::MJIT
imm: imm32(src_imm), imm: imm32(src_imm),
) )
# AND r64, r/m64 (Mod 01: [reg]+disp8) # AND r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 23 /r # REX.W + 23 /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -274,7 +277,7 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg], mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg],
) )
# CMOVZ r64, r/m64 (Mod 01: [reg]+disp8) # CMOVZ r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 0F 44 /r # REX.W + 0F 44 /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -290,6 +293,16 @@ module RubyVM::MJIT
def cmp(left, right) def cmp(left, right)
case [left, right] case [left, right]
# CMP r/m8, imm8 (Mod 01: [reg]+disp8)
in [BytePtr[reg: left_reg, disp: left_disp], Integer => right_imm] if r64?(left_reg) && imm8?(left_disp) && imm8?(right_imm)
# 80 /7 ib
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn(
opcode: 0x80,
mod_rm: ModRM[mod: Mod01, reg: 7, rm: left_reg],
disp: left_disp,
imm: imm8(right_imm),
)
# CMP r/m32, imm32 (Mod 01: [reg]+disp8) # CMP r/m32, imm32 (Mod 01: [reg]+disp8)
in [DwordPtr[reg: left_reg, disp: left_disp], Integer => right_imm] if imm8?(left_disp) && imm32?(right_imm) in [DwordPtr[reg: left_reg, disp: left_disp], Integer => right_imm] if imm8?(left_disp) && imm32?(right_imm)
# 81 /7 id # 81 /7 id
@ -301,7 +314,7 @@ module RubyVM::MJIT
imm: imm32(right_imm), imm: imm32(right_imm),
) )
# CMP r/m64, imm8 (Mod 01: [reg]+disp8) # CMP r/m64, imm8 (Mod 01: [reg]+disp8)
in [[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if r64?(left_reg) && imm8?(left_disp) && imm8?(right_imm) in [Array[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if r64?(left_reg) && imm8?(left_disp) && imm8?(right_imm)
# REX.W + 83 /7 ib # REX.W + 83 /7 ib
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32 # MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn( insn(
@ -321,8 +334,18 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod11, reg: 7, rm: left_reg], mod_rm: ModRM[mod: Mod11, reg: 7, rm: left_reg],
imm: imm8(right_imm), imm: imm8(right_imm),
) )
# CMP r/m64, imm32 (Mod 11: reg)
in [Symbol => left_reg, Integer => right_imm] if r64?(left_reg) && imm32?(right_imm)
# REX.W + 81 /7 id
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn(
prefix: REX_W,
opcode: 0x81,
mod_rm: ModRM[mod: Mod11, reg: 7, rm: left_reg],
imm: imm32(right_imm),
)
# CMP r/m64, r64 (Mod 01: [reg]+disp8) # CMP r/m64, r64 (Mod 01: [reg]+disp8)
in [[Symbol => left_reg, Integer => left_disp], Symbol => right_reg] if r64?(right_reg) in [Array[Symbol => left_reg, Integer => left_disp], Symbol => right_reg] if r64?(right_reg)
# REX.W + 39 /r # REX.W + 39 /r
# MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r) # MR: Operand 1: ModRM:r/m (r), Operand 2: ModRM:reg (r)
insn( insn(
@ -393,7 +416,7 @@ module RubyVM::MJIT
# E9 cd # E9 cd
insn(opcode: 0xe9, imm: rel32(dst_addr)) insn(opcode: 0xe9, imm: rel32(dst_addr))
# JMP r/m64 (Mod 01: [reg]+disp8) # JMP r/m64 (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, Integer => dst_disp] if imm8?(dst_disp) in Array[Symbol => dst_reg, Integer => dst_disp] if imm8?(dst_disp)
# FF /4 # FF /4
insn(opcode: 0xff, mod_rm: ModRM[mod: Mod01, reg: 4, rm: dst_reg], disp: dst_disp) insn(opcode: 0xff, mod_rm: ModRM[mod: Mod01, reg: 4, rm: dst_reg], disp: dst_disp)
# JMP r/m64 (Mod 11: reg) # JMP r/m64 (Mod 11: reg)
@ -456,7 +479,7 @@ module RubyVM::MJIT
def lea(dst, src) def lea(dst, src)
case [dst, src] case [dst, src]
# LEA r64,m (Mod 01: [reg]+disp8) # LEA r64,m (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 8D /r # REX.W + 8D /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -466,7 +489,7 @@ module RubyVM::MJIT
disp: imm8(src_disp), disp: imm8(src_disp),
) )
# LEA r64,m (Mod 10: [reg]+disp32) # LEA r64,m (Mod 10: [reg]+disp32)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm32?(src_disp) in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm32?(src_disp)
# REX.W + 8D /r # REX.W + 8D /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -485,7 +508,7 @@ module RubyVM::MJIT
in Symbol => dst_reg in Symbol => dst_reg
case src case src
# MOV r64, r/m64 (Mod 00: [reg]) # MOV r64, r/m64 (Mod 00: [reg])
in [Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg) in Array[Symbol => src_reg] if r64?(dst_reg) && r64?(src_reg)
# REX.W + 8B /r # REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -494,7 +517,7 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod00, reg: dst_reg, rm: src_reg], mod_rm: ModRM[mod: Mod00, reg: dst_reg, rm: src_reg],
) )
# MOV r64, r/m64 (Mod 01: [reg]+disp8) # MOV r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => src_reg, Integer => src_disp] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) in Array[Symbol => src_reg, Integer => src_disp] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 8B /r # REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -504,7 +527,7 @@ module RubyVM::MJIT
disp: src_disp, disp: src_disp,
) )
# MOV r64, r/m64 (Mod 10: [reg]+disp16) # MOV r64, r/m64 (Mod 10: [reg]+disp16)
in [Symbol => src_reg, Integer => src_disp] if r64?(dst_reg) && r64?(src_reg) && imm32?(src_disp) in Array[Symbol => src_reg, Integer => src_disp] if r64?(dst_reg) && r64?(src_reg) && imm32?(src_disp)
# REX.W + 8B /r # REX.W + 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -523,7 +546,7 @@ module RubyVM::MJIT
mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg], mod_rm: ModRM[mod: Mod11, reg: dst_reg, rm: src_reg],
) )
# MOV r32 r/m32 (Mod 01: [reg]+disp8) # MOV r32 r/m32 (Mod 01: [reg]+disp8)
in [Symbol => src_reg, Integer => src_disp] if r32?(dst_reg) && imm8?(src_disp) in Array[Symbol => src_reg, Integer => src_disp] if r32?(dst_reg) && imm8?(src_disp)
# 8B /r # 8B /r
# RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -563,7 +586,7 @@ module RubyVM::MJIT
else else
raise NotImplementedError, "mov: not-implemented operands: #{dst.inspect}, #{src.inspect}" raise NotImplementedError, "mov: not-implemented operands: #{dst.inspect}, #{src.inspect}"
end end
in [Symbol => dst_reg] in Array[Symbol => dst_reg]
case src case src
# MOV r/m64, imm32 (Mod 00: [reg]) # MOV r/m64, imm32 (Mod 00: [reg])
in Integer => src_imm if r64?(dst_reg) && imm32?(src_imm) in Integer => src_imm if r64?(dst_reg) && imm32?(src_imm)
@ -587,7 +610,7 @@ module RubyVM::MJIT
else else
raise NotImplementedError, "mov: not-implemented operands: #{dst.inspect}, #{src.inspect}" raise NotImplementedError, "mov: not-implemented operands: #{dst.inspect}, #{src.inspect}"
end end
in [Symbol => dst_reg, Integer => dst_disp] in Array[Symbol => dst_reg, Integer => dst_disp]
# Optimize encoding when disp is 0 # Optimize encoding when disp is 0
return mov([dst_reg], src) if dst_disp == 0 return mov([dst_reg], src) if dst_disp == 0
@ -645,7 +668,7 @@ module RubyVM::MJIT
def or(dst, src) def or(dst, src)
case [dst, src] case [dst, src]
# OR r64, r/m64 (Mod 01: [reg]+disp8) # OR r64, r/m64 (Mod 01: [reg]+disp8)
in [Symbol => dst_reg, [Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp) in [Symbol => dst_reg, Array[Symbol => src_reg, Integer => src_disp]] if r64?(dst_reg) && r64?(src_reg) && imm8?(src_disp)
# REX.W + 0B /r # REX.W + 0B /r
# RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r) # RM: Operand 1: ModRM:reg (r, w), Operand 2: ModRM:r/m (r)
insn( insn(
@ -734,7 +757,7 @@ module RubyVM::MJIT
def test(left, right) def test(left, right)
case [left, right] case [left, right]
# TEST r/m8*, imm8 (Mod 01: [reg]+disp8) # TEST r/m8*, imm8 (Mod 01: [reg]+disp8)
in [[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm8?(right_imm) && right_imm >= 0 in [Array[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm8?(right_imm) && right_imm >= 0
# REX + F6 /0 ib # REX + F6 /0 ib
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32 # MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn( insn(
@ -744,7 +767,7 @@ module RubyVM::MJIT
imm: imm8(right_imm), imm: imm8(right_imm),
) )
# TEST r/m64, imm32 (Mod 01: [reg]+disp8) # TEST r/m64, imm32 (Mod 01: [reg]+disp8)
in [[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm32?(right_imm) in [Array[Symbol => left_reg, Integer => left_disp], Integer => right_imm] if imm32?(right_imm)
# REX.W + F7 /0 id # REX.W + F7 /0 id
# MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32 # MI: Operand 1: ModRM:r/m (r), Operand 2: imm8/16/32
insn( insn(

View file

@ -170,6 +170,17 @@ module RubyVM::MJIT
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
# If previous instruction requested to record the boundary
if jit.record_boundary_patch_point
# Generate an exit to this instruction and record it
exit_pos = Assembler.new.then do |ocb_asm|
@exit_compiler.compile_side_exit(jit.pc, ctx, ocb_asm)
@ocb.write(ocb_asm)
end
Invariants.record_global_inval_patch(asm, exit_pos)
jit.record_boundary_patch_point = false
end
case status = @insn_compiler.compile(jit, ctx, asm, insn) case status = @insn_compiler.compile(jit, ctx, asm, insn)
when KeepCompiling when KeepCompiling
index += insn.len index += insn.len
@ -177,7 +188,7 @@ module RubyVM::MJIT
# TODO: pad nops if entry exit exists # TODO: pad nops if entry exit exists
break break
when CantCompile when CantCompile
@exit_compiler.compile_side_exit(jit, ctx, asm) @exit_compiler.compile_side_exit(jit.pc, ctx, asm)
break break
else else
raise "compiling #{insn.name} returned unexpected status: #{status.inspect}" raise "compiling #{insn.name} returned unexpected status: #{status.inspect}"

View file

@ -63,15 +63,15 @@ module RubyVM::MJIT
# @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_side_exit(jit, ctx, asm) def compile_side_exit(pc, ctx, asm)
# Increment per-insn exit counter # Increment per-insn exit counter
incr_insn_exit(jit.pc, asm) incr_insn_exit(pc, asm)
# Fix pc/sp offsets for the interpreter # Fix pc/sp offsets for the interpreter
save_pc_and_sp(jit.pc, ctx.dup, asm) # dup to avoid sp_offset update save_pc_and_sp(pc, ctx.dup, asm) # dup to avoid sp_offset update
# Restore callee-saved registers # Restore callee-saved registers
asm.comment("exit to interpreter on #{pc_to_insn(jit.pc).name}") asm.comment("exit to interpreter on #{pc_to_insn(pc).name}")
asm.pop(SP) asm.pop(SP)
asm.pop(EC) asm.pop(EC)
asm.pop(CFP) asm.pop(CFP)

View file

@ -966,7 +966,7 @@ module RubyVM::MJIT
recv_opnd = ctx.stack_opnd(1) recv_opnd = ctx.stack_opnd(1)
not_array_exit = counted_exit(side_exit, :optaref_recv_not_array) not_array_exit = counted_exit(side_exit, :optaref_recv_not_array)
if jit_guard_known_class(jit, ctx, asm, comptime_recv.class, recv_opnd, comptime_recv, not_array_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_recv.class, recv_opnd, comptime_recv, not_array_exit) == CantCompile
return CantCompile return CantCompile
end end
@ -1001,7 +1001,7 @@ module RubyVM::MJIT
# Guard that the receiver is a Hash # Guard that the receiver is a Hash
not_hash_exit = counted_exit(side_exit, :optaref_recv_not_hash) not_hash_exit = counted_exit(side_exit, :optaref_recv_not_hash)
if jit_guard_known_class(jit, ctx, asm, comptime_recv.class, recv_opnd, comptime_recv, not_hash_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_recv.class, recv_opnd, comptime_recv, not_hash_exit) == CantCompile
return CantCompile return CantCompile
end end
@ -1051,12 +1051,12 @@ module RubyVM::MJIT
side_exit = side_exit(jit, ctx) side_exit = side_exit(jit, ctx)
# Guard receiver is an Array # Guard receiver is an Array
if jit_guard_known_class(jit, ctx, asm, comptime_recv.class, recv, comptime_recv, side_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_recv.class, recv, comptime_recv, side_exit) == CantCompile
return CantCompile return CantCompile
end end
# Guard key is a fixnum # Guard key is a fixnum
if jit_guard_known_class(jit, ctx, asm, comptime_key.class, key, comptime_key, side_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_key.class, key, comptime_key, side_exit) == CantCompile
return CantCompile return CantCompile
end end
@ -1090,7 +1090,7 @@ module RubyVM::MJIT
side_exit = side_exit(jit, ctx) side_exit = side_exit(jit, ctx)
# Guard receiver is a Hash # Guard receiver is a Hash
if jit_guard_known_class(jit, ctx, asm, comptime_recv.class, recv, comptime_recv, side_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_recv.class, recv, comptime_recv, side_exit) == CantCompile
return CantCompile return CantCompile
end end
@ -1425,7 +1425,7 @@ module RubyVM::MJIT
# @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 jit_guard_known_class(jit, ctx, asm, known_klass, obj_opnd, comptime_obj, side_exit, limit: 5) def jit_guard_known_klass(jit, ctx, asm, known_klass, obj_opnd, comptime_obj, side_exit, limit: 5)
# Only memory operand is supported for now # Only memory operand is supported for now
assert_equal(true, obj_opnd.is_a?(Array)) assert_equal(true, obj_opnd.is_a?(Array))
@ -1445,9 +1445,13 @@ module RubyVM::MJIT
asm.comment('guard object is fixnum') asm.comment('guard object is fixnum')
asm.test(obj_opnd, C.RUBY_FIXNUM_FLAG) asm.test(obj_opnd, C.RUBY_FIXNUM_FLAG)
jit_chain_guard(:jz, jit, ctx, asm, side_exit, limit:) jit_chain_guard(:jz, jit, ctx, asm, side_exit, limit:)
elsif known_klass == Symbol elsif known_klass == Symbol && static_symbol?(comptime_obj)
asm.incr_counter(:send_guard_symbol) # We will guard STATIC vs DYNAMIC as though they were separate classes
return CantCompile # DYNAMIC symbols can be handled by the general else case below
asm.comment('guard object is static symbol')
assert_equal(8, C.RUBY_SPECIAL_SHIFT)
asm.cmp(BytePtr[*obj_opnd], C.RUBY_SYMBOL_FLAG)
jit_chain_guard(:jne, jit, ctx, asm, side_exit, limit:)
elsif known_klass == Float elsif known_klass == Float
asm.incr_counter(:send_guard_float) asm.incr_counter(:send_guard_float)
return CantCompile return CantCompile
@ -1471,7 +1475,7 @@ module RubyVM::MJIT
# Bail if receiver class is different from known_klass # Bail if receiver class is different from known_klass
klass_opnd = [obj_opnd, C.RBasic.offsetof(:klass)] klass_opnd = [obj_opnd, C.RBasic.offsetof(:klass)]
asm.comment('guard known class') asm.comment("guard known class #{known_klass}")
asm.mov(:rcx, to_value(known_klass)) asm.mov(:rcx, to_value(known_klass))
asm.cmp(klass_opnd, :rcx) asm.cmp(klass_opnd, :rcx)
jit_chain_guard(:jne, jit, ctx, asm, side_exit, limit:) jit_chain_guard(:jne, jit, ctx, asm, side_exit, limit:)
@ -1593,7 +1597,7 @@ module RubyVM::MJIT
end end
# Guard that a is a String # Guard that a is a String
if jit_guard_known_class(jit, ctx, asm, comptime_a.class, a_opnd, comptime_a, side_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_a.class, a_opnd, comptime_a, side_exit) == CantCompile
return false return false
end end
@ -1609,7 +1613,7 @@ module RubyVM::MJIT
# Otherwise guard that b is a T_STRING (from type info) or String (from runtime guard) # Otherwise guard that b is a T_STRING (from type info) or String (from runtime guard)
# Note: any T_STRING is valid here, but we check for a ::String for simplicity # Note: any T_STRING is valid here, but we check for a ::String for simplicity
# To pass a mutable static variable (rb_cString) requires an unsafe block # To pass a mutable static variable (rb_cString) requires an unsafe block
if jit_guard_known_class(jit, ctx, asm, comptime_b.class, b_opnd, comptime_b, side_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_b.class, b_opnd, comptime_b, side_exit) == CantCompile
return false return false
end end
@ -1639,6 +1643,7 @@ module RubyVM::MJIT
# @param ctx [RubyVM::MJIT::Context] # @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler] # @param asm [RubyVM::MJIT::Assembler]
def jit_prepare_routine_call(jit, ctx, asm) def jit_prepare_routine_call(jit, ctx, asm)
jit.record_boundary_patch_point = true
jit_save_pc(jit, asm) jit_save_pc(jit, asm)
jit_save_sp(jit, ctx, asm) jit_save_sp(jit, ctx, asm)
end end
@ -1672,6 +1677,17 @@ module RubyVM::MJIT
reset_depth.chain_depth = 0 reset_depth.chain_depth = 0
next_pc = jit.pc + jit.insn.len * C.VALUE.size next_pc = jit.pc + jit.insn.len * C.VALUE.size
# We are at the end of the current instruction. Record the boundary.
if jit.record_boundary_patch_point
exit_pos = Assembler.new.then do |ocb_asm|
@exit_compiler.compile_side_exit(next_pc, ctx, ocb_asm)
@ocb.write(ocb_asm)
end
Invariants.record_global_inval_patch(asm, exit_pos)
jit.record_boundary_patch_point = false
end
stub_next_block(jit.iseq, next_pc, reset_depth, asm, comment: 'jump_to_next_insn') stub_next_block(jit.iseq, next_pc, reset_depth, asm, comment: 'jump_to_next_insn')
end end
@ -1781,7 +1797,8 @@ module RubyVM::MJIT
# @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 jit_call_method(jit, ctx, asm, mid, argc, flags) # @param send_shift [Integer] The number of shifts needed for VM_CALL_OPT_SEND
def jit_call_method(jit, ctx, asm, mid, argc, flags, send_shift: 0)
# Specialize on a compile-time receiver, and split a block for chain guards # Specialize on a compile-time receiver, and split a block for chain guards
unless jit.at_current_insn? unless jit.at_current_insn?
defer_compilation(jit, ctx, asm) defer_compilation(jit, ctx, asm)
@ -1791,22 +1808,22 @@ module RubyVM::MJIT
# Generate a side exit # Generate a side exit
side_exit = side_exit(jit, ctx) side_exit = side_exit(jit, ctx)
# Calculate a receiver index # kw_splat is not supported yet
if flags & C.VM_CALL_KW_SPLAT != 0 if flags & C.VM_CALL_KW_SPLAT != 0
# recv_index calculation may not work for this
asm.incr_counter(:send_kw_splat) asm.incr_counter(:send_kw_splat)
return CantCompile return CantCompile
end end
recv_index = argc # TODO: +1 for VM_CALL_ARGS_BLOCKARG
# Get a compile-time receiver and its class # Get a compile-time receiver and its class
comptime_recv = jit.peek_at_stack(recv_index) recv_idx = argc + (flags & C.VM_CALL_ARGS_BLOCKARG != 0 ? 1 : 0)
recv_idx += send_shift
comptime_recv = jit.peek_at_stack(recv_idx)
comptime_recv_klass = C.rb_class_of(comptime_recv) comptime_recv_klass = C.rb_class_of(comptime_recv)
# Guard the receiver class (part of vm_search_method_fastpath) # Guard the receiver class (part of vm_search_method_fastpath)
recv_opnd = ctx.stack_opnd(recv_index) recv_opnd = ctx.stack_opnd(recv_idx)
megamorphic_exit = counted_exit(side_exit, :send_klass_megamorphic) megamorphic_exit = counted_exit(side_exit, :send_klass_megamorphic)
if jit_guard_known_class(jit, ctx, asm, comptime_recv_klass, recv_opnd, comptime_recv, megamorphic_exit) == CantCompile if jit_guard_known_klass(jit, ctx, asm, comptime_recv_klass, recv_opnd, comptime_recv, megamorphic_exit) == CantCompile
return CantCompile return CantCompile
end end
@ -1838,27 +1855,27 @@ module RubyVM::MJIT
# Invalidate on redefinition (part of vm_search_method_fastpath) # Invalidate on redefinition (part of vm_search_method_fastpath)
Invariants.assume_method_lookup_stable(jit, cme) Invariants.assume_method_lookup_stable(jit, cme)
jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd) jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, send_shift:)
end end
# vm_call_method_each_type # vm_call_method_each_type
# @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 jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd) def jit_call_method_each_type(jit, ctx, asm, argc, flags, cme, comptime_recv, recv_opnd, send_shift:)
case cme.def.type case cme.def.type
when C.VM_METHOD_TYPE_ISEQ when C.VM_METHOD_TYPE_ISEQ
jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc) jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, send_shift:)
when C.VM_METHOD_TYPE_NOTIMPLEMENTED when C.VM_METHOD_TYPE_NOTIMPLEMENTED
asm.incr_counter(:send_notimplemented) asm.incr_counter(:send_notimplemented)
return CantCompile return CantCompile
when C.VM_METHOD_TYPE_CFUNC when C.VM_METHOD_TYPE_CFUNC
jit_call_cfunc(jit, ctx, asm, cme, flags, argc) jit_call_cfunc(jit, ctx, asm, cme, flags, argc, send_shift:)
when C.VM_METHOD_TYPE_ATTRSET when C.VM_METHOD_TYPE_ATTRSET
asm.incr_counter(:send_attrset) asm.incr_counter(:send_attrset)
return CantCompile return CantCompile
when C.VM_METHOD_TYPE_IVAR when C.VM_METHOD_TYPE_IVAR
jit_call_ivar(jit, ctx, asm, cme, flags, argc, comptime_recv, recv_opnd) jit_call_ivar(jit, ctx, asm, cme, flags, argc, comptime_recv, recv_opnd, send_shift:)
when C.VM_METHOD_TYPE_MISSING when C.VM_METHOD_TYPE_MISSING
asm.incr_counter(:send_missing) asm.incr_counter(:send_missing)
return CantCompile return CantCompile
@ -1869,7 +1886,7 @@ module RubyVM::MJIT
asm.incr_counter(:send_alias) asm.incr_counter(:send_alias)
return CantCompile return CantCompile
when C.VM_METHOD_TYPE_OPTIMIZED when C.VM_METHOD_TYPE_OPTIMIZED
jit_call_optimized(jit, ctx, asm, cme, flags, argc) jit_call_optimized(jit, ctx, asm, cme, flags, argc, send_shift:)
when C.VM_METHOD_TYPE_UNDEF when C.VM_METHOD_TYPE_UNDEF
asm.incr_counter(:send_undef) asm.incr_counter(:send_undef)
return CantCompile return CantCompile
@ -1889,7 +1906,7 @@ module RubyVM::MJIT
# @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 jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc) def jit_call_iseq_setup(jit, ctx, asm, cme, flags, argc, send_shift:)
iseq = def_iseq_ptr(cme.def) iseq = def_iseq_ptr(cme.def)
opt_pc = jit_callee_setup_arg(jit, ctx, asm, flags, argc, iseq) opt_pc = jit_callee_setup_arg(jit, ctx, asm, flags, argc, iseq)
if opt_pc == CantCompile if opt_pc == CantCompile
@ -1902,19 +1919,24 @@ module RubyVM::MJIT
asm.incr_counter(:send_tailcall) asm.incr_counter(:send_tailcall)
return CantCompile return CantCompile
end end
jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq) jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, send_shift:)
end end
# vm_call_iseq_setup_normal (vm_call_iseq_setup_2 -> vm_call_iseq_setup_normal) # vm_call_iseq_setup_normal (vm_call_iseq_setup_2 -> vm_call_iseq_setup_normal)
# @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 jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq) def jit_call_iseq_setup_normal(jit, ctx, asm, cme, flags, argc, iseq, send_shift:)
# We will not have side exits from here. Adjust the stack.
if flags & C.VM_CALL_OPT_SEND != 0
jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:)
end
# Save caller SP and PC before pushing a callee frame for backtrace and side exits # Save caller SP and PC before pushing a callee frame for backtrace and side exits
asm.comment('save SP to caller CFP') asm.comment('save SP to caller CFP')
# Not setting this to SP register. This cfp->sp will be copied to SP on leave insn. recv_idx = argc + (flags & C.VM_CALL_ARGS_BLOCKARG != 0 ? 1 : 0)
sp_index = -(1 + argc) # Pop receiver and arguments for side exits # TODO: subtract one more for VM_CALL_ARGS_BLOCKARG # Skip setting this to SP register. This cfp->sp will be copied to SP on leave insn.
asm.lea(:rax, ctx.sp_opnd(C.VALUE.size * sp_index)) asm.lea(:rax, ctx.sp_opnd(C.VALUE.size * -(1 + recv_idx))) # Pop receiver and arguments to prepare for side exits
asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], :rax) asm.mov([CFP, C.rb_control_frame_t.offsetof(:sp)], :rax)
jit_save_pc(jit, asm, comment: 'save PC to caller CFP') jit_save_pc(jit, asm, comment: 'save PC to caller CFP')
@ -1937,7 +1959,7 @@ module RubyVM::MJIT
# @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 jit_call_cfunc(jit, ctx, asm, cme, flags, argc) def jit_call_cfunc(jit, ctx, asm, cme, flags, argc, send_shift:)
if jit_caller_setup_arg(jit, ctx, asm, flags) == CantCompile if jit_caller_setup_arg(jit, ctx, asm, flags) == CantCompile
return CantCompile return CantCompile
end end
@ -1945,14 +1967,14 @@ module RubyVM::MJIT
return CantCompile return CantCompile
end end
jit_call_cfunc_with_frame(jit, ctx, asm, cme, flags, argc) jit_call_cfunc_with_frame(jit, ctx, asm, cme, flags, argc, send_shift:)
end end
# jit_call_cfunc_with_frame # jit_call_cfunc_with_frame
# @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 jit_call_cfunc_with_frame(jit, ctx, asm, cme, flags, argc) def jit_call_cfunc_with_frame(jit, ctx, asm, cme, flags, argc, send_shift:)
cfunc = cme.def.body.cfunc cfunc = cme.def.body.cfunc
if argc + 1 > 6 if argc + 1 > 6
@ -1981,6 +2003,11 @@ module RubyVM::MJIT
return CantCompile return CantCompile
end end
# We will not have side exits from here. Adjust the stack.
if flags & C.VM_CALL_OPT_SEND != 0
jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:)
end
# Check interrupts before SP motion to safely side-exit with the original SP. # Check interrupts before SP motion to safely side-exit with the original SP.
jit_check_ints(jit, ctx, asm) jit_check_ints(jit, ctx, asm)
@ -2026,11 +2053,11 @@ module RubyVM::MJIT
EndBlock EndBlock
end end
# vm_call_ivar # vm_call_ivar (+ part of vm_call_method_each_type)
# @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 jit_call_ivar(jit, ctx, asm, cme, flags, argc, comptime_recv, recv_opnd) def jit_call_ivar(jit, ctx, asm, cme, flags, argc, comptime_recv, recv_opnd, send_shift:)
if flags & C.VM_CALL_ARGS_SPLAT != 0 if flags & C.VM_CALL_ARGS_SPLAT != 0
asm.incr_counter(:send_ivar_splat) asm.incr_counter(:send_ivar_splat)
return CantCompile return CantCompile
@ -2041,6 +2068,7 @@ module RubyVM::MJIT
return CantCompile return CantCompile
end end
# We don't support jit_call_opt_send_shift_stack for this yet.
if flags & C.VM_CALL_OPT_SEND != 0 if flags & C.VM_CALL_OPT_SEND != 0
asm.incr_counter(:send_ivar_opt_send) asm.incr_counter(:send_ivar_opt_send)
return CantCompile return CantCompile
@ -2048,7 +2076,7 @@ module RubyVM::MJIT
ivar_id = cme.def.body.attr.id ivar_id = cme.def.body.attr.id
if flags & C.VM_CALL_OPT_SEND != 0 if flags & C.VM_CALL_ARGS_BLOCKARG != 0
asm.incr_counter(:send_ivar_blockarg) asm.incr_counter(:send_ivar_blockarg)
return CantCompile return CantCompile
end end
@ -2060,11 +2088,10 @@ module RubyVM::MJIT
# @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 jit_call_optimized(jit, ctx, asm, cme, flags, argc) def jit_call_optimized(jit, ctx, asm, cme, flags, argc, send_shift:)
case cme.def.body.optimized.type case cme.def.body.optimized.type
when C.OPTIMIZED_METHOD_TYPE_SEND when C.OPTIMIZED_METHOD_TYPE_SEND
asm.incr_counter(:send_optimized_send) jit_call_opt_send(jit, ctx, asm, cme, flags, argc, send_shift:)
return CantCompile
when C.OPTIMIZED_METHOD_TYPE_CALL when C.OPTIMIZED_METHOD_TYPE_CALL
asm.incr_counter(:send_optimized_call) asm.incr_counter(:send_optimized_call)
return CantCompile return CantCompile
@ -2083,6 +2110,87 @@ module RubyVM::MJIT
end end
end end
# vm_call_opt_send
# @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler]
def jit_call_opt_send(jit, ctx, asm, cme, flags, argc, send_shift:)
if jit_caller_setup_arg(jit, ctx, asm, flags) == CantCompile
return CantCompile
end
if argc == 0
asm.incr_counter(:send_optimized_send_no_args)
return CantCompile
end
argc -= 1
# We aren't handling `send(:send, ...)` yet. This might work, but not tested yet.
if send_shift > 0
asm.incr_counter(:send_optimized_send_send)
return CantCompile
end
# Ideally, we want to shift the stack here, but it's not safe until you reach the point
# where you never exit. `send_shift` signals to lazily shift the stack by this amount.
send_shift += 1
kw_splat = flags & C.VM_CALL_KW_SPLAT != 0
jit_call_symbol(jit, ctx, asm, cme, C.VM_CALL_FCALL, argc, kw_splat, send_shift:)
end
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler]
def jit_call_opt_send_shift_stack(ctx, asm, argc, send_shift:)
# We don't support `send(:send, ...)` for now.
assert_equal(1, send_shift)
asm.comment('shift stack')
(0...argc).reverse_each do |i|
opnd = ctx.stack_opnd(i)
opnd2 = ctx.stack_opnd(i + 1)
asm.mov(:rax, opnd)
asm.mov(opnd2, :rax)
end
ctx.stack_pop(1)
end
# vm_call_symbol
# @param jit [RubyVM::MJIT::JITState]
# @param ctx [RubyVM::MJIT::Context]
# @param asm [RubyVM::MJIT::Assembler]
def jit_call_symbol(jit, ctx, asm, cme, flags, argc, kw_splat, send_shift:)
flags |= C.VM_CALL_OPT_SEND | (kw_splat ? C.VM_CALL_KW_SPLAT : 0)
comptime_symbol = jit.peek_at_stack(argc)
if comptime_symbol.class != String && !static_symbol?(comptime_symbol)
asm.incr_counter(:send_optimized_send_not_sym_or_str)
return CantCompile
end
mid = C.get_symbol_id(comptime_symbol)
if mid == 0
asm.incr_counter(:send_optimized_send_null_mid)
return CantCompile
end
asm.comment("Guard #{comptime_symbol.inspect} is on stack")
mid_changed_exit = counted_exit(side_exit(jit, ctx), :send_optimized_send_mid_changed)
if jit_guard_known_klass(jit, ctx, asm, comptime_symbol.class, ctx.stack_opnd(argc), comptime_symbol, mid_changed_exit) == CantCompile
return CantCompile
end
asm.mov(C_ARGS[0], ctx.stack_opnd(argc))
asm.call(C.rb_get_symbol_id)
asm.cmp(C_RET, mid)
jit_chain_guard(:jne, jit, ctx, asm, mid_changed_exit)
if flags & C.VM_CALL_FCALL != 0
return jit_call_method(jit, ctx, asm, mid, argc, flags, send_shift:)
end
raise NotImplementedError # unreachable for now
end
# vm_push_frame # vm_push_frame
# #
# Frame structure: # Frame structure:
@ -2253,8 +2361,11 @@ module RubyVM::MJIT
end end
def fixnum?(obj) def fixnum?(obj)
flag = C.RUBY_FIXNUM_FLAG (C.to_value(obj) & C.RUBY_FIXNUM_FLAG) == C.RUBY_FIXNUM_FLAG
(C.to_value(obj) & flag) == flag end
def static_symbol?(obj)
(C.to_value(obj) & 0xff) == C.RUBY_SYMBOL_FLAG
end end
# @param jit [RubyVM::MJIT::JITState] # @param jit [RubyVM::MJIT::JITState]
@ -2296,7 +2407,7 @@ module RubyVM::MJIT
return side_exit return side_exit
end end
asm = Assembler.new asm = Assembler.new
@exit_compiler.compile_side_exit(jit, ctx, asm) @exit_compiler.compile_side_exit(jit.pc, ctx, asm)
jit.side_exits[jit.pc] = @ocb.write(asm) jit.side_exits[jit.pc] = @ocb.write(asm)
end end

View file

@ -5,8 +5,9 @@ module RubyVM::MJIT
: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] :block, # @param [RubyVM::MJIT::Block]
:side_exits, # @param [Hash{ Integer => Integer }] { PC => address } :side_exits, # @param [Hash{ Integer => Integer }] { PC => address }
:record_boundary_patch_point, # @param [TrueClass,FalseClass]
) )
def initialize(side_exits: {}, **) = super def initialize(side_exits: {}, record_boundary_patch_point: false, **) = super
def insn def insn
Compiler.decode_insn(C.VALUE.new(pc).*) Compiler.decode_insn(C.VALUE.new(pc).*)

View file

@ -117,6 +117,7 @@ mjit_for_each_iseq(rb_execution_context_t *ec, VALUE self, VALUE block)
} }
extern bool rb_simple_iseq_p(const rb_iseq_t *iseq); extern bool rb_simple_iseq_p(const rb_iseq_t *iseq);
extern ID rb_get_symbol_id(VALUE name);
#include "mjit_c.rbinc" #include "mjit_c.rbinc"

View file

@ -144,13 +144,18 @@ MJIT_RUNTIME_COUNTERS(
send_ivar_opt_send, send_ivar_opt_send,
send_ivar_blockarg, send_ivar_blockarg,
send_optimized_send,
send_optimized_call, send_optimized_call,
send_optimized_block_call, send_optimized_block_call,
send_optimized_struct_aref, send_optimized_struct_aref,
send_optimized_struct_aset, send_optimized_struct_aset,
send_optimized_unknown_type, send_optimized_unknown_type,
send_optimized_send_no_args,
send_optimized_send_not_sym_or_str,
send_optimized_send_mid_changed,
send_optimized_send_null_mid,
send_optimized_send_send,
send_guard_symbol, send_guard_symbol,
send_guard_float, send_guard_float,

View file

@ -180,6 +180,14 @@ module RubyVM::MJIT # :nodoc: all
Primitive.cexpr! 'SIZET2NUM((size_t)rb_hash_aset)' Primitive.cexpr! 'SIZET2NUM((size_t)rb_hash_aset)'
end end
def get_symbol_id(name)
Primitive.cexpr! 'SIZET2NUM((size_t)rb_get_symbol_id(name))'
end
def rb_get_symbol_id
Primitive.cexpr! 'SIZET2NUM((size_t)rb_get_symbol_id)'
end
#======================================================================================== #========================================================================================
# #
# Old stuff # Old stuff
@ -629,6 +637,14 @@ module RubyVM::MJIT # :nodoc: all
Primitive.cexpr! %q{ ULONG2NUM(RUBY_IMMEDIATE_MASK) } Primitive.cexpr! %q{ ULONG2NUM(RUBY_IMMEDIATE_MASK) }
end end
def C.RUBY_SPECIAL_SHIFT
Primitive.cexpr! %q{ ULONG2NUM(RUBY_SPECIAL_SHIFT) }
end
def C.RUBY_SYMBOL_FLAG
Primitive.cexpr! %q{ ULONG2NUM(RUBY_SYMBOL_FLAG) }
end
def C.RUBY_T_ARRAY def C.RUBY_T_ARRAY
Primitive.cexpr! %q{ ULONG2NUM(RUBY_T_ARRAY) } Primitive.cexpr! %q{ ULONG2NUM(RUBY_T_ARRAY) }
end end
@ -1165,12 +1181,16 @@ module RubyVM::MJIT # :nodoc: all
send_ivar_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_splat)")], send_ivar_splat: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_splat)")],
send_ivar_opt_send: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_opt_send)")], send_ivar_opt_send: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_opt_send)")],
send_ivar_blockarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_blockarg)")], send_ivar_blockarg: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_ivar_blockarg)")],
send_optimized_send: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send)")],
send_optimized_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_call)")], send_optimized_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_call)")],
send_optimized_block_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_block_call)")], send_optimized_block_call: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_block_call)")],
send_optimized_struct_aref: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aref)")], send_optimized_struct_aref: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aref)")],
send_optimized_struct_aset: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aset)")], send_optimized_struct_aset: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_struct_aset)")],
send_optimized_unknown_type: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_unknown_type)")], send_optimized_unknown_type: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_unknown_type)")],
send_optimized_send_no_args: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_no_args)")],
send_optimized_send_not_sym_or_str: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_not_sym_or_str)")],
send_optimized_send_mid_changed: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_mid_changed)")],
send_optimized_send_null_mid: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_null_mid)")],
send_optimized_send_send: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_optimized_send_send)")],
send_guard_symbol: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_guard_symbol)")], send_guard_symbol: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_guard_symbol)")],
send_guard_float: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_guard_float)")], send_guard_float: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), send_guard_float)")],
getivar_megamorphic: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_megamorphic)")], getivar_megamorphic: [CType::Immediate.parse("size_t"), Primitive.cexpr!("OFFSETOF((*((struct rb_mjit_runtime_counters *)NULL)), getivar_megamorphic)")],

View file

@ -424,6 +424,8 @@ generator = BindingGenerator.new(
INVALID_SHAPE_ID INVALID_SHAPE_ID
OBJ_TOO_COMPLEX_SHAPE_ID OBJ_TOO_COMPLEX_SHAPE_ID
RUBY_FIXNUM_FLAG RUBY_FIXNUM_FLAG
RUBY_SYMBOL_FLAG
RUBY_SPECIAL_SHIFT
RUBY_IMMEDIATE_MASK RUBY_IMMEDIATE_MASK
RARRAY_EMBED_LEN_MASK RARRAY_EMBED_LEN_MASK
RARRAY_EMBED_LEN_SHIFT RARRAY_EMBED_LEN_SHIFT