@@ -10556,24 +10556,25 @@ mod hir_opt_tests {
1055610556 Jump bb2(v8, v9, v10, v11, v12)
1055710557 bb2(v14:BasicObject, v15:BasicObject, v16:BasicObject, v17:BasicObject, v18:NilClass):
1055810558 CheckInterrupts
10559+ SetLocal :formatted, l0, EP@3, v15
1055910560 PatchPoint SingleRactorMode
10560- v56 :HeapBasicObject = GuardType v14, HeapBasicObject
10561- v57 :CShape = LoadField v56 , :_shape_id@0x1000
10562- v58 :CShape[0x1001] = GuardBitEquals v57 , CShape(0x1001)
10563- StoreField v56 , :@formatted@0x1002, v15
10564- WriteBarrier v56 , v15
10565- v61 :CShape[0x1003] = Const CShape(0x1003)
10566- StoreField v56 , :_shape_id@0x1000, v61
10567- v45 :Class[VMFrozenCore] = Const Value(VALUE(0x1008))
10561+ v57 :HeapBasicObject = GuardType v14, HeapBasicObject
10562+ v58 :CShape = LoadField v57 , :_shape_id@0x1000
10563+ v59 :CShape[0x1001] = GuardBitEquals v58 , CShape(0x1001)
10564+ StoreField v57 , :@formatted@0x1002, v15
10565+ WriteBarrier v57 , v15
10566+ v62 :CShape[0x1003] = Const CShape(0x1003)
10567+ StoreField v57 , :_shape_id@0x1000, v62
10568+ v46 :Class[VMFrozenCore] = Const Value(VALUE(0x1008))
1056810569 PatchPoint NoSingletonClass(Class@0x1010)
1056910570 PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020)
10570- v66 :BasicObject = CCallWithFrame v45 , :RubyVM::FrozenCore.lambda@0x1048, block=0x1050
10571- v48 :BasicObject = GetLocal :a, l0, EP@6
10572- v49 :BasicObject = GetLocal :_b, l0, EP@5
10573- v50 :BasicObject = GetLocal :_c, l0, EP@4
10574- v51 :BasicObject = GetLocal :formatted, l0, EP@3
10571+ v67 :BasicObject = CCallWithFrame v46 , :RubyVM::FrozenCore.lambda@0x1048, block=0x1050
10572+ v49 :BasicObject = GetLocal :a, l0, EP@6
10573+ v50 :BasicObject = GetLocal :_b, l0, EP@5
10574+ v51 :BasicObject = GetLocal :_c, l0, EP@4
10575+ v52 :BasicObject = GetLocal :formatted, l0, EP@3
1057510576 CheckInterrupts
10576- Return v66
10577+ Return v67
1057710578 " ) ;
1057810579 }
1057910580
@@ -11575,8 +11576,9 @@ mod hir_opt_tests {
1157511576 v35:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B]
1157611577 v36:BasicObject = CCallWithFrame v35, :Kernel#proc@0x1038, block=0x1040
1157711578 v18:BasicObject = GetLocal :blk, l0, EP@4
11578- PatchPoint NoEPEscape(foo)
11579- v27:BasicObject = InvokeSuper v10, 0x1048, v36 # SendFallbackReason: super: complex argument passing to `super` call
11579+ SetLocal :other_block, l0, EP@3, v36
11580+ v25:BasicObject = GetLocal :other_block, l0, EP@3
11581+ v27:BasicObject = InvokeSuper v10, 0x1048, v25 # SendFallbackReason: super: complex argument passing to `super` call
1158011582 CheckInterrupts
1158111583 Return v27
1158211584 " ) ;
@@ -11882,4 +11884,97 @@ mod hir_opt_tests {
1188211884 Return v31
1188311885 " ) ;
1188411886 }
11887+
11888+ #[ test]
11889+ fn recompile_after_ep_escape_uses_ep_locals ( ) {
11890+ // When a method creates a lambda, EP escapes to the heap. After
11891+ // invalidation and recompilation, the compiler must use EP-based
11892+ // locals (SetLocal/GetLocal) instead of SSA locals, because the
11893+ // spill target (stack) and the read target (heap EP) diverge.
11894+ eval ( "
11895+ CONST = {}.freeze
11896+ def test_ep_escape(list, sep=nil, iter_method=:each)
11897+ sep ||= lambda { }
11898+ kwsplat = CONST
11899+ list.__send__(iter_method) {|*v| yield(*v) }
11900+ end
11901+
11902+ test_ep_escape({a: 1}, nil, :each_pair) { |k, v|
11903+ test_ep_escape([1], lambda { }) { |x| }
11904+ }
11905+ test_ep_escape({a: 1}, nil, :each_pair) { |k, v|
11906+ test_ep_escape([1], lambda { }) { |x| }
11907+ }
11908+ " ) ;
11909+ assert_snapshot ! ( hir_string( "test_ep_escape" ) , @r"
11910+ fn test_ep_escape@<compiled>:3:
11911+ bb0():
11912+ EntryPoint interpreter
11913+ v1:BasicObject = LoadSelf
11914+ v2:BasicObject = GetLocal :list, l0, SP@7
11915+ v3:BasicObject = GetLocal :sep, l0, SP@6
11916+ v4:BasicObject = GetLocal :iter_method, l0, SP@5
11917+ v5:NilClass = Const Value(nil)
11918+ v6:CPtr = LoadPC
11919+ v7:CPtr[CPtr(0x1000)] = Const CPtr(0x1008)
11920+ v8:CBool = IsBitEqual v6, v7
11921+ IfTrue v8, bb2(v1, v2, v3, v4, v5)
11922+ v10:CPtr[CPtr(0x1000)] = Const CPtr(0x1008)
11923+ v11:CBool = IsBitEqual v6, v10
11924+ IfTrue v11, bb4(v1, v2, v3, v4, v5)
11925+ Jump bb6(v1, v2, v3, v4, v5)
11926+ bb1(v15:BasicObject, v16:BasicObject):
11927+ EntryPoint JIT(0)
11928+ v17:NilClass = Const Value(nil)
11929+ v18:NilClass = Const Value(nil)
11930+ v19:NilClass = Const Value(nil)
11931+ Jump bb2(v15, v16, v17, v18, v19)
11932+ bb2(v35:BasicObject, v36:BasicObject, v37:BasicObject, v38:BasicObject, v39:NilClass):
11933+ v42:NilClass = Const Value(nil)
11934+ SetLocal :sep, l0, EP@5, v42
11935+ Jump bb4(v35, v36, v42, v38, v39)
11936+ bb3(v22:BasicObject, v23:BasicObject, v24:BasicObject):
11937+ EntryPoint JIT(1)
11938+ v25:NilClass = Const Value(nil)
11939+ v26:NilClass = Const Value(nil)
11940+ Jump bb4(v22, v23, v24, v25, v26)
11941+ bb4(v46:BasicObject, v47:BasicObject, v48:BasicObject, v49:BasicObject, v50:NilClass):
11942+ v53:StaticSymbol[:each] = Const Value(VALUE(0x1010))
11943+ SetLocal :iter_method, l0, EP@4, v53
11944+ Jump bb6(v46, v47, v48, v53, v50)
11945+ bb5(v29:BasicObject, v30:BasicObject, v31:BasicObject, v32:BasicObject):
11946+ EntryPoint JIT(2)
11947+ v33:NilClass = Const Value(nil)
11948+ Jump bb6(v29, v30, v31, v32, v33)
11949+ bb6(v57:BasicObject, v58:BasicObject, v59:BasicObject, v60:BasicObject, v61:NilClass):
11950+ CheckInterrupts
11951+ v67:CBool = Test v59
11952+ v68:Truthy = RefineType v59, Truthy
11953+ IfTrue v67, bb7(v57, v58, v68, v60, v61)
11954+ v70:Falsy = RefineType v59, Falsy
11955+ PatchPoint NoSingletonClass(Object@0x1018)
11956+ PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028)
11957+ v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)]
11958+ v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058
11959+ v74:BasicObject = GetLocal :list, l0, EP@6
11960+ v76:BasicObject = GetLocal :iter_method, l0, EP@4
11961+ v77:BasicObject = GetLocal :kwsplat, l0, EP@3
11962+ SetLocal :sep, l0, EP@5, v115
11963+ Jump bb7(v57, v74, v115, v76, v77)
11964+ bb7(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject):
11965+ PatchPoint SingleRactorMode
11966+ PatchPoint StableConstantNames(0x1060, CONST)
11967+ v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068))
11968+ SetLocal :kwsplat, l0, EP@3, v110
11969+ v95:BasicObject = GetLocal :list, l0, EP@6
11970+ v97:BasicObject = GetLocal :iter_method, l0, EP@4
11971+ v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized
11972+ v100:BasicObject = GetLocal :list, l0, EP@6
11973+ v101:BasicObject = GetLocal :sep, l0, EP@5
11974+ v102:BasicObject = GetLocal :iter_method, l0, EP@4
11975+ v103:BasicObject = GetLocal :kwsplat, l0, EP@3
11976+ CheckInterrupts
11977+ Return v99
11978+ " ) ;
11979+ }
1188511980}
0 commit comments