Skip to content

Commit be2f32e

Browse files
authored
[3.14] gh-134889: Fix handling of a few opcodes when optimizing LOAD_FAST (#134958) (#135187)
We were incorrectly handling a few opcodes that leave their operands on the stack. Treat all of these conservatively; assume that they always leave operands on the stack. (cherry picked from commit 6b77af2)
1 parent 945af60 commit be2f32e

File tree

5 files changed

+117
-2
lines changed

5 files changed

+117
-2
lines changed

Include/internal/pycore_magic_number.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ Known values:
277277
Python 3.14a7 3622 (Store annotations in different class dict keys)
278278
Python 3.14a7 3623 (Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes)
279279
Python 3.14b1 3624 (Don't optimize LOAD_FAST when local is killed by DELETE_FAST)
280+
Python 3.14b3 3625 (Fix handling of opcodes that may leave operands on the stack when optimizing LOAD_FAST)
280281
281282
Python 3.15 will start with 3650
282283
@@ -289,7 +290,7 @@ PC/launcher.c must also be updated.
289290
290291
*/
291292

292-
#define PYC_MAGIC_NUMBER 3624
293+
#define PYC_MAGIC_NUMBER 3625
293294
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
294295
(little-endian) and then appending b'\r\n'. */
295296
#define PYC_MAGIC_NUMBER_TOKEN \

Lib/test/test_dis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ async def _asyncwith(c):
606606
POP_TOP
607607
L1: RESUME 0
608608
609-
%4d LOAD_FAST_BORROW 0 (c)
609+
%4d LOAD_FAST 0 (c)
610610
COPY 1
611611
LOAD_SPECIAL 3 (__aexit__)
612612
SWAP 2

Lib/test/test_peepholer.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2614,6 +2614,90 @@ def test_send(self):
26142614
]
26152615
self.cfg_optimization_test(insts, expected, consts=[None])
26162616

2617+
def test_format_simple(self):
2618+
# FORMAT_SIMPLE will leave its operand on the stack if it's a unicode
2619+
# object. We treat it conservatively and assume that it always leaves
2620+
# its operand on the stack.
2621+
insts = [
2622+
("LOAD_FAST", 0, 1),
2623+
("FORMAT_SIMPLE", None, 2),
2624+
("STORE_FAST", 1, 3),
2625+
]
2626+
self.check(insts, insts)
2627+
2628+
insts = [
2629+
("LOAD_FAST", 0, 1),
2630+
("FORMAT_SIMPLE", None, 2),
2631+
("POP_TOP", None, 3),
2632+
]
2633+
expected = [
2634+
("LOAD_FAST_BORROW", 0, 1),
2635+
("FORMAT_SIMPLE", None, 2),
2636+
("POP_TOP", None, 3),
2637+
]
2638+
self.check(insts, expected)
2639+
2640+
def test_set_function_attribute(self):
2641+
# SET_FUNCTION_ATTRIBUTE leaves the function on the stack
2642+
insts = [
2643+
("LOAD_CONST", 0, 1),
2644+
("LOAD_FAST", 0, 2),
2645+
("SET_FUNCTION_ATTRIBUTE", 2, 3),
2646+
("STORE_FAST", 1, 4),
2647+
("LOAD_CONST", 0, 5),
2648+
("RETURN_VALUE", None, 6)
2649+
]
2650+
self.cfg_optimization_test(insts, insts, consts=[None])
2651+
2652+
insts = [
2653+
("LOAD_CONST", 0, 1),
2654+
("LOAD_FAST", 0, 2),
2655+
("SET_FUNCTION_ATTRIBUTE", 2, 3),
2656+
("RETURN_VALUE", None, 4)
2657+
]
2658+
expected = [
2659+
("LOAD_CONST", 0, 1),
2660+
("LOAD_FAST_BORROW", 0, 2),
2661+
("SET_FUNCTION_ATTRIBUTE", 2, 3),
2662+
("RETURN_VALUE", None, 4)
2663+
]
2664+
self.cfg_optimization_test(insts, expected, consts=[None])
2665+
2666+
def test_get_yield_from_iter(self):
2667+
# GET_YIELD_FROM_ITER may leave its operand on the stack
2668+
insts = [
2669+
("LOAD_FAST", 0, 1),
2670+
("GET_YIELD_FROM_ITER", None, 2),
2671+
("LOAD_CONST", 0, 3),
2672+
send := self.Label(),
2673+
("SEND", end := self.Label(), 5),
2674+
("YIELD_VALUE", 1, 6),
2675+
("RESUME", 2, 7),
2676+
("JUMP", send, 8),
2677+
end,
2678+
("END_SEND", None, 9),
2679+
("LOAD_CONST", 0, 10),
2680+
("RETURN_VALUE", None, 11),
2681+
]
2682+
self.cfg_optimization_test(insts, insts, consts=[None])
2683+
2684+
def test_push_exc_info(self):
2685+
insts = [
2686+
("LOAD_FAST", 0, 1),
2687+
("PUSH_EXC_INFO", None, 2),
2688+
]
2689+
self.check(insts, insts)
2690+
2691+
def test_load_special(self):
2692+
# LOAD_SPECIAL may leave self on the stack
2693+
insts = [
2694+
("LOAD_FAST", 0, 1),
2695+
("LOAD_SPECIAL", 0, 2),
2696+
("STORE_FAST", 1, 3),
2697+
]
2698+
self.check(insts, insts)
2699+
2700+
26172701
def test_del_in_finally(self):
26182702
# This loads `obj` onto the stack, executes `del obj`, then returns the
26192703
# `obj` from the stack. See gh-133371 for more details.
@@ -2630,6 +2714,14 @@ def create_obj():
26302714
gc.collect()
26312715
self.assertEqual(obj, [42])
26322716

2717+
def test_format_simple_unicode(self):
2718+
# Repro from gh-134889
2719+
def f():
2720+
var = f"{1}"
2721+
var = f"{var}"
2722+
return var
2723+
self.assertEqual(f(), "1")
2724+
26332725

26342726

26352727
if __name__ == "__main__":
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix handling of a few opcodes that leave operands on the stack when
2+
optimizing ``LOAD_FAST``.

Python/flowgraph.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,8 +2862,10 @@ optimize_load_fast(cfg_builder *g)
28622862
// how many inputs should be left on the stack.
28632863

28642864
// Opcodes that consume no inputs
2865+
case FORMAT_SIMPLE:
28652866
case GET_ANEXT:
28662867
case GET_LEN:
2868+
case GET_YIELD_FROM_ITER:
28672869
case IMPORT_FROM:
28682870
case MATCH_KEYS:
28692871
case MATCH_MAPPING:
@@ -2898,6 +2900,16 @@ optimize_load_fast(cfg_builder *g)
28982900
break;
28992901
}
29002902

2903+
case END_SEND:
2904+
case SET_FUNCTION_ATTRIBUTE: {
2905+
assert(_PyOpcode_num_popped(opcode, oparg) == 2);
2906+
assert(_PyOpcode_num_pushed(opcode, oparg) == 1);
2907+
ref tos = ref_stack_pop(&refs);
2908+
ref_stack_pop(&refs);
2909+
PUSH_REF(tos.instr, tos.local);
2910+
break;
2911+
}
2912+
29012913
// Opcodes that consume some inputs and push new values
29022914
case CHECK_EXC_MATCH: {
29032915
ref_stack_pop(&refs);
@@ -2927,6 +2939,14 @@ optimize_load_fast(cfg_builder *g)
29272939
break;
29282940
}
29292941

2942+
case LOAD_SPECIAL:
2943+
case PUSH_EXC_INFO: {
2944+
ref tos = ref_stack_pop(&refs);
2945+
PUSH_REF(i, NOT_LOCAL);
2946+
PUSH_REF(tos.instr, tos.local);
2947+
break;
2948+
}
2949+
29302950
case SEND: {
29312951
load_fast_push_block(&sp, instr->i_target, refs.size);
29322952
ref_stack_pop(&refs);

0 commit comments

Comments
 (0)