@@ -40,6 +40,17 @@ def get_first_executor(func):
4040 pass
4141 return None
4242
43+ def get_all_executors (func ):
44+ code = func .__code__
45+ co_code = code .co_code
46+ executors = []
47+ for i in range (0 , len (co_code ), 2 ):
48+ try :
49+ executors .append (_opcode .get_executor (code , i ))
50+ except ValueError :
51+ pass
52+ return executors
53+
4354
4455def iter_opnames (ex ):
4556 for item in ex :
@@ -2629,6 +2640,31 @@ def gen():
26292640 next(g)
26302641 """ % _testinternalcapi .SPECIALIZATION_THRESHOLD ))
26312642
2643+ def test_executor_side_exits_create_another_executor (self ):
2644+ def f ():
2645+ for x in range (TIER2_THRESHOLD + 3 ):
2646+ for y in range (TIER2_THRESHOLD + 3 ):
2647+ z = x + y
2648+
2649+ f ()
2650+ all_executors = get_all_executors (f )
2651+ # Inner loop warms up first.
2652+ # Outer loop warms up later, linking to the inner one.
2653+ # Therefore, we have at least two executors.
2654+ self .assertGreaterEqual (len (all_executors ), 2 )
2655+ for executor in all_executors :
2656+ opnames = list (get_opnames (executor ))
2657+ # Assert all executors first terminator ends in
2658+ # _EXIT_TRACE or _JUMP_TO_TOP, not _DEOPT
2659+ for idx , op in enumerate (opnames ):
2660+ if op == "_EXIT_TRACE" or op == "_JUMP_TO_TOP" :
2661+ break
2662+ elif op == "_DEOPT" :
2663+ self .fail (f"_DEOPT encountered first at executor"
2664+ f" { executor } at offset { idx } rather"
2665+ f" than expected _EXIT_TRACE" )
2666+
2667+
26322668
26332669def global_identity (x ):
26342670 return x
0 commit comments