mirror of
https://github.com/ruby/ruby.git
synced 2026-01-27 04:24:23 +00:00
ZJIT: Introduce GetLEP HIR instruction (#15917)
This PR is a follow-up to #15816. There, I introduced the `GuardSuperMethodEntry` HIR instruction and that needed the LEP. The LEP was also used by `GetBlockHandler`. Consequently, the codegen for `invokesuper` ended up loading the LEP twice. By introducing a new HIR instruction, we can load the LEP once and use it in both `GetBlockHandler` and `GuardSuperMethodEntry`. I also updated `IsBlockGiven`, which conditionally loaded the LEP. To ensure we only use `GetLEP` in the cases we need it, I lifted most of the `IsBlockGiven` handler to HIR. As an added benefit, this addressed a TODO that @tekknolagi had written: when `block_given?` is called outside of a method we can rewrite to a constant `false`. We could use `GetLEP` in the handling of `Defined`, but that looked a bit more involved and I wanted to keep this PR focused, so I'm suggesting we handle that as future work.
This commit is contained in:
parent
6d0b47de86
commit
cfa97af7e1
Notes:
git
2026-01-21 19:07:59 +00:00
Merged-By: tekknolagi <donotemailthisaddress@bernsteinbear.com>
@ -532,8 +532,8 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
Insn::GuardNotShared { recv, state } => gen_guard_not_shared(jit, asm, opnd!(recv), &function.frame_state(*state)),
|
||||
&Insn::GuardLess { left, right, state } => gen_guard_less(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
|
||||
&Insn::GuardGreaterEq { left, right, state } => gen_guard_greater_eq(jit, asm, opnd!(left), opnd!(right), &function.frame_state(state)),
|
||||
&Insn::GuardSuperMethodEntry { cme, state } => no_output!(gen_guard_super_method_entry(jit, asm, cme, &function.frame_state(state))),
|
||||
Insn::GetBlockHandler => gen_get_block_handler(jit, asm),
|
||||
&Insn::GuardSuperMethodEntry { lep, cme, state } => no_output!(gen_guard_super_method_entry(jit, asm, opnd!(lep), cme, &function.frame_state(state))),
|
||||
Insn::GetBlockHandler { lep } => gen_get_block_handler(asm, opnd!(lep)),
|
||||
Insn::PatchPoint { invariant, state } => no_output!(gen_patch_point(jit, asm, invariant, &function.frame_state(*state))),
|
||||
Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => gen_ccall(asm, *cfunc, *name, opnd!(recv), opnds!(args)),
|
||||
// Give up CCallWithFrame for 7+ args since asm.ccall() supports at most 6 args (recv + args).
|
||||
@ -576,11 +576,12 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio
|
||||
&Insn::GuardShape { val, shape, state } => gen_guard_shape(jit, asm, opnd!(val), shape, &function.frame_state(state)),
|
||||
Insn::LoadPC => gen_load_pc(asm),
|
||||
Insn::LoadEC => gen_load_ec(),
|
||||
Insn::GetLEP => gen_get_lep(jit, asm),
|
||||
Insn::LoadSelf => gen_load_self(),
|
||||
&Insn::LoadField { recv, id, offset, return_type } => gen_load_field(asm, opnd!(recv), id, offset, return_type),
|
||||
&Insn::StoreField { recv, id, offset, val } => no_output!(gen_store_field(asm, opnd!(recv), id, offset, opnd!(val), function.type_of(val))),
|
||||
&Insn::WriteBarrier { recv, val } => no_output!(gen_write_barrier(asm, opnd!(recv), opnd!(val), function.type_of(val))),
|
||||
&Insn::IsBlockGiven => gen_is_block_given(jit, asm),
|
||||
&Insn::IsBlockGiven { lep } => gen_is_block_given(asm, opnd!(lep)),
|
||||
Insn::ArrayInclude { elements, target, state } => gen_array_include(jit, asm, opnds!(elements), opnd!(target), &function.frame_state(*state)),
|
||||
Insn::ArrayPackBuffer { elements, fmt, buffer, state } => gen_array_pack_buffer(jit, asm, opnds!(elements), opnd!(fmt), opnd!(buffer), &function.frame_state(*state)),
|
||||
&Insn::DupArrayInclude { ary, target, state } => gen_dup_array_include(jit, asm, ary, opnd!(target), &function.frame_state(state)),
|
||||
@ -688,16 +689,10 @@ fn gen_defined(jit: &JITState, asm: &mut Assembler, op_type: usize, obj: VALUE,
|
||||
}
|
||||
|
||||
/// Similar to gen_defined for DEFINED_YIELD
|
||||
fn gen_is_block_given(jit: &JITState, asm: &mut Assembler) -> Opnd {
|
||||
let local_iseq = unsafe { rb_get_iseq_body_local_iseq(jit.iseq) };
|
||||
if unsafe { rb_get_iseq_body_type(local_iseq) } == ISEQ_TYPE_METHOD {
|
||||
let lep = gen_get_lep(jit, asm);
|
||||
let block_handler = asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL));
|
||||
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
|
||||
asm.csel_e(Qfalse.into(), Qtrue.into())
|
||||
} else {
|
||||
Qfalse.into()
|
||||
}
|
||||
fn gen_is_block_given(asm: &mut Assembler, lep: Opnd) -> Opnd {
|
||||
let block_handler = asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL));
|
||||
asm.cmp(block_handler, VM_BLOCK_HANDLER_NONE.into());
|
||||
asm.csel_e(Qfalse.into(), Qtrue.into())
|
||||
}
|
||||
|
||||
fn gen_unbox_fixnum(asm: &mut Assembler, val: Opnd) -> Opnd {
|
||||
@ -803,11 +798,11 @@ fn gen_guard_greater_eq(jit: &JITState, asm: &mut Assembler, left: Opnd, right:
|
||||
fn gen_guard_super_method_entry(
|
||||
jit: &JITState,
|
||||
asm: &mut Assembler,
|
||||
lep: Opnd,
|
||||
cme: *const rb_callable_method_entry_t,
|
||||
state: &FrameState,
|
||||
) {
|
||||
asm_comment!(asm, "guard super method entry");
|
||||
let lep = gen_get_lep(jit, asm);
|
||||
let ep_me_opnd = Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_ME_CREF);
|
||||
let ep_me = asm.load(ep_me_opnd);
|
||||
asm.cmp(ep_me, Opnd::UImm(cme as u64));
|
||||
@ -815,9 +810,8 @@ fn gen_guard_super_method_entry(
|
||||
}
|
||||
|
||||
/// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP).
|
||||
fn gen_get_block_handler(jit: &JITState, asm: &mut Assembler) -> Opnd {
|
||||
fn gen_get_block_handler(asm: &mut Assembler, lep: Opnd) -> Opnd {
|
||||
asm_comment!(asm, "get block handler from LEP");
|
||||
let lep = gen_get_lep(jit, asm);
|
||||
asm.load(Opnd::mem(64, lep, SIZEOF_VALUE_I32 * VM_ENV_DATA_INDEX_SPECVAL))
|
||||
}
|
||||
|
||||
|
||||
@ -318,8 +318,14 @@ fn inline_kernel_itself(_fun: &mut hir::Function, _block: hir::BlockId, recv: hi
|
||||
|
||||
fn inline_kernel_block_given_p(fun: &mut hir::Function, block: hir::BlockId, _recv: hir::InsnId, args: &[hir::InsnId], _state: hir::InsnId) -> Option<hir::InsnId> {
|
||||
let &[] = args else { return None; };
|
||||
// TODO(max): In local iseq types that are not ISEQ_TYPE_METHOD, rewrite to Constant false.
|
||||
Some(fun.push_insn(block, hir::Insn::IsBlockGiven))
|
||||
|
||||
let local_iseq = unsafe { rb_get_iseq_body_local_iseq(fun.iseq()) };
|
||||
if unsafe { rb_get_iseq_body_type(local_iseq) } == ISEQ_TYPE_METHOD {
|
||||
let lep = fun.push_insn(block, hir::Insn::GetLEP);
|
||||
Some(fun.push_insn(block, hir::Insn::IsBlockGiven { lep }))
|
||||
} else {
|
||||
Some(fun.push_insn(block, hir::Insn::Const { val: hir::Const::Value(Qfalse) }))
|
||||
}
|
||||
}
|
||||
|
||||
fn inline_array_aref(fun: &mut hir::Function, block: hir::BlockId, recv: hir::InsnId, args: &[hir::InsnId], state: hir::InsnId) -> Option<hir::InsnId> {
|
||||
|
||||
@ -802,7 +802,7 @@ pub enum Insn {
|
||||
GetConstantPath { ic: *const iseq_inline_constant_cache, state: InsnId },
|
||||
/// Kernel#block_given? but without pushing a frame. Similar to [`Insn::Defined`] with
|
||||
/// `DEFINED_YIELD`
|
||||
IsBlockGiven,
|
||||
IsBlockGiven { lep: InsnId },
|
||||
/// Test the bit at index of val, a Fixnum.
|
||||
/// Return Qtrue if the bit is set, else Qfalse.
|
||||
FixnumBitCheck { val: InsnId, index: u8 },
|
||||
@ -849,6 +849,10 @@ pub enum Insn {
|
||||
/// Set a class variable `id` to `val`
|
||||
SetClassVar { id: ID, val: InsnId, ic: *const iseq_inline_cvar_cache_entry, state: InsnId },
|
||||
|
||||
/// Get the EP of the ISeq of the containing method, or "local level", skipping over block-level EPs.
|
||||
/// Equivalent of GET_LEP() macro.
|
||||
GetLEP,
|
||||
|
||||
/// Own a FrameState so that instructions can look up their dominating FrameState when
|
||||
/// generating deopt side-exits and frame reconstruction metadata. Does not directly generate
|
||||
/// any code.
|
||||
@ -1012,9 +1016,9 @@ pub enum Insn {
|
||||
GuardLess { left: InsnId, right: InsnId, state: InsnId },
|
||||
/// Side-exit if the method entry at ep[VM_ENV_DATA_INDEX_ME_CREF] doesn't match the expected CME.
|
||||
/// Used to ensure super calls are made from the expected method context.
|
||||
GuardSuperMethodEntry { cme: *const rb_callable_method_entry_t, state: InsnId },
|
||||
GuardSuperMethodEntry { lep: InsnId, cme: *const rb_callable_method_entry_t, state: InsnId },
|
||||
/// Get the block handler from ep[VM_ENV_DATA_INDEX_SPECVAL] at the local EP (LEP).
|
||||
GetBlockHandler,
|
||||
GetBlockHandler { lep: InsnId },
|
||||
|
||||
/// Generate no code (or padding if necessary) and insert a patch point
|
||||
/// that can be rewritten to a side exit when the Invariant is broken.
|
||||
@ -1131,6 +1135,7 @@ impl Insn {
|
||||
Insn::DefinedIvar { .. } => effects::Any,
|
||||
Insn::LoadPC { .. } => Effect::read_write(abstract_heaps::PC, abstract_heaps::Empty),
|
||||
Insn::LoadEC { .. } => effects::Empty,
|
||||
Insn::GetLEP { .. } => effects::Empty,
|
||||
Insn::LoadSelf { .. } => Effect::read_write(abstract_heaps::Frame, abstract_heaps::Empty),
|
||||
Insn::LoadField { .. } => Effect::read_write(abstract_heaps::Other, abstract_heaps::Empty),
|
||||
Insn::StoreField { .. } => effects::Any,
|
||||
@ -1510,11 +1515,11 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||
Insn::GuardNotShared { recv, .. } => write!(f, "GuardNotShared {recv}"),
|
||||
Insn::GuardLess { left, right, .. } => write!(f, "GuardLess {left}, {right}"),
|
||||
Insn::GuardGreaterEq { left, right, .. } => write!(f, "GuardGreaterEq {left}, {right}"),
|
||||
Insn::GuardSuperMethodEntry { cme, .. } => write!(f, "GuardSuperMethodEntry {:p}", self.ptr_map.map_ptr(cme)),
|
||||
Insn::GetBlockHandler => write!(f, "GetBlockHandler"),
|
||||
Insn::GuardSuperMethodEntry { lep, cme, .. } => write!(f, "GuardSuperMethodEntry {lep}, {:p}", self.ptr_map.map_ptr(cme)),
|
||||
Insn::GetBlockHandler { lep } => write!(f, "GetBlockHandler {lep}"),
|
||||
Insn::PatchPoint { invariant, .. } => { write!(f, "PatchPoint {}", invariant.print(self.ptr_map)) },
|
||||
Insn::GetConstantPath { ic, .. } => { write!(f, "GetConstantPath {:p}", self.ptr_map.map_ptr(ic)) },
|
||||
Insn::IsBlockGiven => { write!(f, "IsBlockGiven") },
|
||||
Insn::IsBlockGiven { lep } => { write!(f, "IsBlockGiven {lep}") },
|
||||
Insn::FixnumBitCheck {val, index} => { write!(f, "FixnumBitCheck {val}, {index}") },
|
||||
Insn::CCall { cfunc, recv, args, name, return_type: _, elidable: _ } => {
|
||||
write!(f, "CCall {recv}, :{}@{:p}", name.contents_lossy(), self.ptr_map.map_ptr(cfunc))?;
|
||||
@ -1562,6 +1567,7 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> {
|
||||
Insn::GetIvar { self_val, id, .. } => write!(f, "GetIvar {self_val}, :{}", id.contents_lossy()),
|
||||
Insn::LoadPC => write!(f, "LoadPC"),
|
||||
Insn::LoadEC => write!(f, "LoadEC"),
|
||||
Insn::GetLEP => write!(f, "GetLEP"),
|
||||
Insn::LoadSelf => write!(f, "LoadSelf"),
|
||||
&Insn::LoadField { recv, id, offset, return_type: _ } => write!(f, "LoadField {recv}, :{}@{:p}", id.contents_lossy(), self.ptr_map.map_offset(offset)),
|
||||
&Insn::StoreField { recv, id, offset, val } => write!(f, "StoreField {recv}, :{}@{:p}, {val}", id.contents_lossy(), self.ptr_map.map_offset(offset)),
|
||||
@ -1969,6 +1975,10 @@ impl Function {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iseq(&self) -> *const rb_iseq_t {
|
||||
self.iseq
|
||||
}
|
||||
|
||||
// Add an instruction to the function without adding it to any block
|
||||
fn new_insn(&mut self, insn: Insn) -> InsnId {
|
||||
let id = InsnId(self.insns.len());
|
||||
@ -2119,7 +2129,6 @@ impl Function {
|
||||
result@(Const {..}
|
||||
| Param
|
||||
| GetConstantPath {..}
|
||||
| IsBlockGiven
|
||||
| PatchPoint {..}
|
||||
| PutSpecialObject {..}
|
||||
| GetGlobal {..}
|
||||
@ -2128,6 +2137,7 @@ impl Function {
|
||||
| EntryPoint {..}
|
||||
| LoadPC
|
||||
| LoadEC
|
||||
| GetLEP
|
||||
| LoadSelf
|
||||
| IncrCounterPtr {..}
|
||||
| IncrCounter(_)) => result.clone(),
|
||||
@ -2173,8 +2183,9 @@ impl Function {
|
||||
&GuardNotShared { recv, state } => GuardNotShared { recv: find!(recv), state },
|
||||
&GuardGreaterEq { left, right, state } => GuardGreaterEq { left: find!(left), right: find!(right), state },
|
||||
&GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state },
|
||||
&GuardSuperMethodEntry { cme, state } => GuardSuperMethodEntry { cme, state },
|
||||
&GetBlockHandler => GetBlockHandler,
|
||||
&GuardSuperMethodEntry { lep, cme, state } => GuardSuperMethodEntry { lep: find!(lep), cme, state },
|
||||
&GetBlockHandler { lep } => GetBlockHandler { lep: find!(lep) },
|
||||
&IsBlockGiven { lep } => IsBlockGiven { lep: find!(lep) },
|
||||
&FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state },
|
||||
&FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state },
|
||||
&FixnumMult { left, right, state } => FixnumMult { left: find!(left), right: find!(right), state },
|
||||
@ -2446,7 +2457,7 @@ impl Function {
|
||||
Insn::Defined { pushval, .. } => Type::from_value(*pushval).union(types::NilClass),
|
||||
Insn::DefinedIvar { pushval, .. } => Type::from_value(*pushval).union(types::NilClass),
|
||||
Insn::GetConstantPath { .. } => types::BasicObject,
|
||||
Insn::IsBlockGiven => types::BoolExact,
|
||||
Insn::IsBlockGiven { .. } => types::BoolExact,
|
||||
Insn::FixnumBitCheck { .. } => types::BoolExact,
|
||||
Insn::ArrayMax { .. } => types::BasicObject,
|
||||
Insn::ArrayInclude { .. } => types::BoolExact,
|
||||
@ -2457,6 +2468,7 @@ impl Function {
|
||||
Insn::GetIvar { .. } => types::BasicObject,
|
||||
Insn::LoadPC => types::CPtr,
|
||||
Insn::LoadEC => types::CPtr,
|
||||
Insn::GetLEP => types::CPtr,
|
||||
Insn::LoadSelf => types::BasicObject,
|
||||
&Insn::LoadField { return_type, .. } => return_type,
|
||||
Insn::GetSpecialSymbol { .. } => types::BasicObject,
|
||||
@ -2468,7 +2480,7 @@ impl Function {
|
||||
Insn::AnyToString { .. } => types::String,
|
||||
Insn::GetLocal { rest_param: true, .. } => types::ArrayExact,
|
||||
Insn::GetLocal { .. } => types::BasicObject,
|
||||
Insn::GetBlockHandler => types::RubyValue,
|
||||
Insn::GetBlockHandler { .. } => types::RubyValue,
|
||||
// The type of Snapshot doesn't really matter; it's never materialized. It's used only
|
||||
// as a reference for FrameState, which we use to generate side-exit code.
|
||||
Insn::Snapshot { .. } => types::Any,
|
||||
@ -3413,10 +3425,15 @@ impl Function {
|
||||
});
|
||||
|
||||
// Guard that we're calling `super` from the expected method context.
|
||||
self.push_insn(block, Insn::GuardSuperMethodEntry { cme: current_cme, state });
|
||||
let lep = self.push_insn(block, Insn::GetLEP);
|
||||
self.push_insn(block, Insn::GuardSuperMethodEntry {
|
||||
lep,
|
||||
cme: current_cme,
|
||||
state
|
||||
});
|
||||
|
||||
// Guard that no block is being passed (implicit or explicit).
|
||||
let block_handler = self.push_insn(block, Insn::GetBlockHandler);
|
||||
let block_handler = self.push_insn(block, Insn::GetBlockHandler { lep });
|
||||
self.push_insn(block, Insn::GuardBitEquals {
|
||||
val: block_handler,
|
||||
expected: Const::Value(VALUE(VM_BLOCK_HANDLER_NONE as usize)),
|
||||
@ -4357,14 +4374,17 @@ impl Function {
|
||||
| &Insn::EntryPoint { .. }
|
||||
| &Insn::LoadPC
|
||||
| &Insn::LoadEC
|
||||
| &Insn::GetLEP
|
||||
| &Insn::LoadSelf
|
||||
| &Insn::GetLocal { .. }
|
||||
| &Insn::GetBlockHandler
|
||||
| &Insn::PutSpecialObject { .. }
|
||||
| &Insn::IsBlockGiven
|
||||
| &Insn::IncrCounter(_)
|
||||
| &Insn::IncrCounterPtr { .. } =>
|
||||
{}
|
||||
&Insn::GetBlockHandler { lep }
|
||||
| &Insn::IsBlockGiven { lep } => {
|
||||
worklist.push_back(lep);
|
||||
}
|
||||
&Insn::PatchPoint { state, .. }
|
||||
| &Insn::CheckInterrupts { state }
|
||||
| &Insn::GetConstantPath { ic: _, state } => {
|
||||
@ -4589,12 +4609,15 @@ impl Function {
|
||||
worklist.push_back(val);
|
||||
}
|
||||
&Insn::GuardBlockParamProxy { state, .. } |
|
||||
&Insn::GuardSuperMethodEntry { state, .. } |
|
||||
&Insn::GetGlobal { state, .. } |
|
||||
&Insn::GetSpecialSymbol { state, .. } |
|
||||
&Insn::GetSpecialNumber { state, .. } |
|
||||
&Insn::ObjectAllocClass { state, .. } |
|
||||
&Insn::SideExit { state, .. } => worklist.push_back(state),
|
||||
&Insn::GuardSuperMethodEntry { lep, state, .. } => {
|
||||
worklist.push_back(lep);
|
||||
worklist.push_back(state);
|
||||
}
|
||||
&Insn::UnboxFixnum { val } => worklist.push_back(val),
|
||||
&Insn::FixnumAref { recv, index } => {
|
||||
worklist.push_back(recv);
|
||||
@ -5099,17 +5122,18 @@ impl Function {
|
||||
| Insn::PutSpecialObject { .. }
|
||||
| Insn::LoadField { .. }
|
||||
| Insn::GetConstantPath { .. }
|
||||
| Insn::IsBlockGiven
|
||||
| Insn::IsBlockGiven { .. }
|
||||
| Insn::GetGlobal { .. }
|
||||
| Insn::LoadPC
|
||||
| Insn::LoadEC
|
||||
| Insn::GetLEP
|
||||
| Insn::LoadSelf
|
||||
| Insn::Snapshot { .. }
|
||||
| Insn::Jump { .. }
|
||||
| Insn::EntryPoint { .. }
|
||||
| Insn::GuardBlockParamProxy { .. }
|
||||
| Insn::GuardSuperMethodEntry { .. }
|
||||
| Insn::GetBlockHandler
|
||||
| Insn::GetBlockHandler { .. }
|
||||
| Insn::PatchPoint { .. }
|
||||
| Insn::SideExit { .. }
|
||||
| Insn::IncrCounter { .. }
|
||||
|
||||
@ -2653,10 +2653,11 @@ mod hir_opt_tests {
|
||||
PatchPoint NoSingletonClass(Object@0x1000)
|
||||
PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010)
|
||||
v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)]
|
||||
v20:BoolExact = IsBlockGiven
|
||||
v20:CPtr = GetLEP
|
||||
v21:BoolExact = IsBlockGiven v20
|
||||
IncrCounter inline_cfunc_optimized_send_count
|
||||
CheckInterrupts
|
||||
Return v20
|
||||
Return v21
|
||||
");
|
||||
}
|
||||
|
||||
@ -2679,7 +2680,7 @@ mod hir_opt_tests {
|
||||
PatchPoint NoSingletonClass(Object@0x1000)
|
||||
PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010)
|
||||
v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)]
|
||||
v20:BoolExact = IsBlockGiven
|
||||
v20:FalseClass = Const Value(false)
|
||||
IncrCounter inline_cfunc_optimized_send_count
|
||||
CheckInterrupts
|
||||
Return v20
|
||||
@ -10943,12 +10944,13 @@ mod hir_opt_tests {
|
||||
Jump bb2(v4)
|
||||
bb2(v6:BasicObject):
|
||||
PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010)
|
||||
GuardSuperMethodEntry 0x1038
|
||||
v18:RubyValue = GetBlockHandler
|
||||
v19:FalseClass = GuardBitEquals v18, Value(false)
|
||||
v20:BasicObject = SendWithoutBlockDirect v6, :foo (0x1040)
|
||||
v17:CPtr = GetLEP
|
||||
GuardSuperMethodEntry v17, 0x1038
|
||||
v19:RubyValue = GetBlockHandler v17
|
||||
v20:FalseClass = GuardBitEquals v19, Value(false)
|
||||
v21:BasicObject = SendWithoutBlockDirect v6, :foo (0x1040)
|
||||
CheckInterrupts
|
||||
Return v20
|
||||
Return v21
|
||||
");
|
||||
}
|
||||
|
||||
@ -10986,17 +10988,18 @@ mod hir_opt_tests {
|
||||
Jump bb2(v5, v6)
|
||||
bb2(v8:BasicObject, v9:BasicObject):
|
||||
PatchPoint MethodRedefined(A@0x1000, foo@0x1008, cme:0x1010)
|
||||
GuardSuperMethodEntry 0x1038
|
||||
v27:RubyValue = GetBlockHandler
|
||||
v28:FalseClass = GuardBitEquals v27, Value(false)
|
||||
v29:BasicObject = SendWithoutBlockDirect v8, :foo (0x1040), v9
|
||||
v26:CPtr = GetLEP
|
||||
GuardSuperMethodEntry v26, 0x1038
|
||||
v28:RubyValue = GetBlockHandler v26
|
||||
v29:FalseClass = GuardBitEquals v28, Value(false)
|
||||
v30:BasicObject = SendWithoutBlockDirect v8, :foo (0x1040), v9
|
||||
v17:Fixnum[1] = Const Value(1)
|
||||
PatchPoint MethodRedefined(Integer@0x1048, +@0x1050, cme:0x1058)
|
||||
v32:Fixnum = GuardType v29, Fixnum
|
||||
v33:Fixnum = FixnumAdd v32, v17
|
||||
v33:Fixnum = GuardType v30, Fixnum
|
||||
v34:Fixnum = FixnumAdd v33, v17
|
||||
IncrCounter inline_cfunc_optimized_send_count
|
||||
CheckInterrupts
|
||||
Return v33
|
||||
Return v34
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user