@@ -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
115125impl 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