Skip to content

Commit 97ba8d9

Browse files
tekknolagik0kubun
authored andcommitted
Insert PatchPoint after each Send
In case the callee writes to the caller's locals, we need to be able to immediately side-exit because our frame's invariants might have been invalidated. If in the course of optimization we rewrite the Send, we can choose to remove the PatchPoint too---but it's not an error to keep it around.
1 parent 8b72e07 commit 97ba8d9

File tree

2 files changed

+63
-25
lines changed

2 files changed

+63
-25
lines changed

zjit/src/codegen.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ fn gen_send_without_block(
378378
rb_vm_opt_send_without_block as *const u8,
379379
vec![EC, CFP, (cd as usize).into()],
380380
);
381+
// TODO(max): Add a PatchPoint here that can side-exit the function if the callee messed with
382+
// the frame's locals
381383

382384
Some(ret)
383385
}

zjit/src/hir.rs

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,17 @@ pub enum Invariant {
109109
klass: VALUE,
110110
/// The method ID of the method we want to assume unchanged
111111
method: ID,
112-
}
112+
},
113+
/// Any send target can reflectively inspect its call-stack and modify parent call frame
114+
/// locals. If that happens, we need to side-exit after the call returns because our
115+
/// assumptions and type checks may have been invalidated behind our backs.
116+
CalleeModifiedLocals {
117+
/// The Send instruction that caused this PatchPoint to be emitted. If by the time we are
118+
/// generating LIR the send is no longer a Send(WithoutBlock)(Direct) (meaning it has been
119+
/// optimized into a FixnumAdd or similar), we need not emit the PatchPoint; we know the
120+
/// function is well-behaved.
121+
send: InsnId,
122+
},
113123
}
114124

115125
impl Invariant {
@@ -160,6 +170,9 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> {
160170
self.ptr_map.map_id(method)
161171
)
162172
}
173+
Invariant::CalleeModifiedLocals { send } => {
174+
write!(f, "CalleeModifiedLocals({send})")
175+
}
163176
}
164177
}
165178
}
@@ -712,6 +725,7 @@ impl Function {
712725
let insn_id = self.union_find.find_const(insn_id);
713726
use Insn::*;
714727
match &self.insns[insn_id.0] {
728+
PatchPoint(Invariant::CalleeModifiedLocals { send }) => PatchPoint(Invariant::CalleeModifiedLocals { send: find!(*send) }),
715729
result@(PutSelf | Const {..} | Param {..} | NewArray {..} | GetConstantPath {..}
716730
| Jump(_) | PatchPoint {..}) => result.clone(),
717731
Snapshot { state: FrameState { iseq, insn_idx, pc, stack, locals } } =>
@@ -1472,7 +1486,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
14721486
};
14731487
let right = state.stack_pop()?;
14741488
let left = state.stack_pop()?;
1475-
state.stack_push(fun.push_insn(block, Insn::SendWithoutBlock { self_val: left, call_info: CallInfo { method_name: $method_name.into() }, cd, args: vec![right], state: exit_id }));
1489+
let send = fun.push_insn(block, Insn::SendWithoutBlock { self_val: left, call_info: CallInfo { method_name: $method_name.into() }, cd, args: vec![right], state: exit_id });
1490+
state.stack_push(send);
1491+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
14761492
}
14771493
};
14781494
}
@@ -1565,7 +1581,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
15651581
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
15661582
let recv = state.stack_pop()?;
15671583
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
1568-
state.stack_push(fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name: "nil?".into() }, cd, args: vec![], state: exit_id }));
1584+
let send = fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name: "nil?".into() }, cd, args: vec![], state: exit_id });
1585+
state.stack_push(send);
1586+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
15691587
}
15701588
YARVINSN_getlocal_WC_0 => {
15711589
let ep_offset = get_arg(pc, 0).as_u32();
@@ -1637,15 +1655,18 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
16371655
let right = state.stack_pop()?;
16381656
let left = state.stack_pop()?;
16391657
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
1640-
state.stack_push(fun.push_insn(block, Insn::SendWithoutBlock { self_val: left, call_info: CallInfo { method_name: "<<".into() }, cd, args: vec![right], state: exit_id }));
1658+
let send = fun.push_insn(block, Insn::SendWithoutBlock { self_val: left, call_info: CallInfo { method_name: "<<".into() }, cd, args: vec![right], state: exit_id });
1659+
state.stack_push(send);
1660+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
16411661
}
16421662
YARVINSN_opt_aset => {
16431663
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
16441664
let set = state.stack_pop()?;
16451665
let obj = state.stack_pop()?;
16461666
let recv = state.stack_pop()?;
16471667
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
1648-
fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name: "[]=".into() }, cd, args: vec![obj, set], state: exit_id });
1668+
let send = fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name: "[]=".into() }, cd, args: vec![obj, set], state: exit_id });
1669+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
16491670
state.stack_push(set);
16501671
}
16511672

@@ -1672,7 +1693,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
16721693

16731694
let recv = state.stack_pop()?;
16741695
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
1675-
state.stack_push(fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name }, cd, args, state: exit_id }));
1696+
let send = fun.push_insn(block, Insn::SendWithoutBlock { self_val: recv, call_info: CallInfo { method_name }, cd, args, state: exit_id });
1697+
state.stack_push(send);
1698+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
16761699
}
16771700
YARVINSN_send => {
16781701
let cd: *const rb_call_data = get_arg(pc, 0).as_ptr();
@@ -1692,7 +1715,9 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> {
16921715

16931716
let recv = state.stack_pop()?;
16941717
let exit_id = fun.push_insn(block, Insn::Snapshot { state: exit_state.clone() });
1695-
state.stack_push(fun.push_insn(block, Insn::Send { self_val: recv, call_info: CallInfo { method_name }, cd, blockiseq, args, state: exit_id }));
1718+
let send = fun.push_insn(block, Insn::Send { self_val: recv, call_info: CallInfo { method_name }, cd, blockiseq, args, state: exit_id });
1719+
state.stack_push(send);
1720+
fun.push_insn(block, Insn::PatchPoint(Invariant::CalleeModifiedLocals { send }));
16961721
}
16971722
_ => return Err(ParseError::UnknownOpcode(insn_name(opcode as usize))),
16981723
}
@@ -2125,6 +2150,7 @@ mod tests {
21252150
v1:Fixnum[1] = Const Value(1)
21262151
v2:Fixnum[2] = Const Value(2)
21272152
v4:BasicObject = SendWithoutBlock v1, :+, v2
2153+
PatchPoint CalleeModifiedLocals(v4)
21282154
Return v4
21292155
"#]]);
21302156
}
@@ -2478,6 +2504,7 @@ mod tests {
24782504
v2:Fixnum[2] = Const Value(2)
24792505
v3:Fixnum[3] = Const Value(3)
24802506
v5:BasicObject = SendWithoutBlock v1, :bar, v2, v3
2507+
PatchPoint CalleeModifiedLocals(v5)
24812508
Return v5
24822509
"#]]);
24832510
}
@@ -2496,6 +2523,7 @@ mod tests {
24962523
fn test:
24972524
bb0(v0:BasicObject):
24982525
v3:BasicObject = Send v0, 0x1000, :each
2526+
PatchPoint CalleeModifiedLocals(v3)
24992527
Return v3
25002528
"#]]);
25012529
}
@@ -2518,6 +2546,7 @@ mod tests {
25182546
v8:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010))
25192547
v9:StringExact = StringCopy v8
25202548
v11:BasicObject = SendWithoutBlock v1, :unknown_method, v3, v5, v7, v9
2549+
PatchPoint CalleeModifiedLocals(v11)
25212550
Return v11
25222551
"#]]);
25232552
}
@@ -2698,9 +2727,10 @@ mod opt_tests {
26982727
bb0():
26992728
v1:BasicObject = PutSelf
27002729
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008)
2701-
v6:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2702-
v7:BasicObject = SendWithoutBlockDirect v6, :foo (0x1018)
2703-
Return v7
2730+
v7:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2731+
v8:BasicObject = SendWithoutBlockDirect v7, :foo (0x1018)
2732+
PatchPoint CalleeModifiedLocals(v8)
2733+
Return v8
27042734
"#]]);
27052735
}
27062736

@@ -2720,6 +2750,7 @@ mod opt_tests {
27202750
bb0():
27212751
v1:BasicObject = PutSelf
27222752
v3:BasicObject = SendWithoutBlock v1, :foo
2753+
PatchPoint CalleeModifiedLocals(v3)
27232754
Return v3
27242755
"#]]);
27252756
}
@@ -2740,9 +2771,10 @@ mod opt_tests {
27402771
bb0():
27412772
v1:BasicObject = PutSelf
27422773
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008)
2743-
v6:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2744-
v7:BasicObject = SendWithoutBlockDirect v6, :foo (0x1018)
2745-
Return v7
2774+
v7:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2775+
v8:BasicObject = SendWithoutBlockDirect v7, :foo (0x1018)
2776+
PatchPoint CalleeModifiedLocals(v8)
2777+
Return v8
27462778
"#]]);
27472779
}
27482780

@@ -2760,9 +2792,10 @@ mod opt_tests {
27602792
v1:BasicObject = PutSelf
27612793
v2:Fixnum[3] = Const Value(3)
27622794
PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008)
2763-
v7:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2764-
v8:BasicObject = SendWithoutBlockDirect v7, :Integer (0x1018), v2
2765-
Return v8
2795+
v8:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2796+
v9:BasicObject = SendWithoutBlockDirect v8, :Integer (0x1018), v2
2797+
PatchPoint CalleeModifiedLocals(v9)
2798+
Return v9
27662799
"#]]);
27672800
}
27682801

@@ -2783,9 +2816,10 @@ mod opt_tests {
27832816
v2:Fixnum[1] = Const Value(1)
27842817
v3:Fixnum[2] = Const Value(2)
27852818
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008)
2786-
v8:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2787-
v9:BasicObject = SendWithoutBlockDirect v8, :foo (0x1018), v2, v3
2788-
Return v9
2819+
v9:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2820+
v10:BasicObject = SendWithoutBlockDirect v9, :foo (0x1018), v2, v3
2821+
PatchPoint CalleeModifiedLocals(v10)
2822+
Return v10
27892823
"#]]);
27902824
}
27912825

@@ -2807,13 +2841,15 @@ mod opt_tests {
28072841
bb0():
28082842
v1:BasicObject = PutSelf
28092843
PatchPoint MethodRedefined(Object@0x1000, foo@0x1008)
2810-
v9:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2811-
v10:BasicObject = SendWithoutBlockDirect v9, :foo (0x1018)
2812-
v4:BasicObject = PutSelf
2844+
v11:BasicObject[VALUE(0x1010)] = GuardBitEquals v1, VALUE(0x1010)
2845+
v12:BasicObject = SendWithoutBlockDirect v11, :foo (0x1018)
2846+
PatchPoint CalleeModifiedLocals(v12)
2847+
v5:BasicObject = PutSelf
28132848
PatchPoint MethodRedefined(Object@0x1000, bar@0x1020)
2814-
v12:BasicObject[VALUE(0x1010)] = GuardBitEquals v4, VALUE(0x1010)
2815-
v13:BasicObject = SendWithoutBlockDirect v12, :bar (0x1018)
2816-
Return v13
2849+
v14:BasicObject[VALUE(0x1010)] = GuardBitEquals v5, VALUE(0x1010)
2850+
v15:BasicObject = SendWithoutBlockDirect v14, :bar (0x1018)
2851+
PatchPoint CalleeModifiedLocals(v15)
2852+
Return v15
28172853
"#]]);
28182854
}
28192855

0 commit comments

Comments
 (0)