https://github.com/python/cpython/commit/78adb63ee198c94c6ce2a1634aa7ea1d47c011ad
commit: 78adb63ee198c94c6ce2a1634aa7ea1d47c011ad
branch: main
author: mpage <[email protected]>
committer: mpage <[email protected]>
date: 2025-05-04T21:00:11-07:00
summary:
gh-133371: Don't optimize `LOAD_FAST` instructions whose local is killed by
`DELETE_FAST` (#133383)
In certain cases it's possible for locals loaded by `LOAD_FAST` instructions
to be on the stack when the local is killed by `DEL_FAST`. These `LOAD_FAST`
instructions should not be optimized into `LOAD_FAST_BORROW` as the strong
reference in the frame is killed while there is still a reference on the stack.
files:
M Include/internal/pycore_magic_number.h
M Lib/test/test_peepholer.py
M Python/flowgraph.c
diff --git a/Include/internal/pycore_magic_number.h
b/Include/internal/pycore_magic_number.h
index a96cb6236f78b7..1cc897c5a69469 100644
--- a/Include/internal/pycore_magic_number.h
+++ b/Include/internal/pycore_magic_number.h
@@ -276,6 +276,7 @@ Known values:
Python 3.14a7 3621 (Optimize LOAD_FAST opcodes into LOAD_FAST_BORROW)
Python 3.14a7 3622 (Store annotations in different class dict keys)
Python 3.14a7 3623 (Add BUILD_INTERPOLATION & BUILD_TEMPLATE opcodes)
+ Python 3.14b1 3624 (Don't optimize LOAD_FAST when local is killed by
DELETE_FAST)
Python 3.15 will start with 3650
@@ -288,7 +289,7 @@ PC/launcher.c must also be updated.
*/
-#define PYC_MAGIC_NUMBER 3623
+#define PYC_MAGIC_NUMBER 3624
/* This is equivalent to converting PYC_MAGIC_NUMBER to 2 bytes
(little-endian) and then appending b'\r\n'. */
#define PYC_MAGIC_NUMBER_TOKEN \
diff --git a/Lib/test/test_peepholer.py b/Lib/test/test_peepholer.py
index 565e42b04a68d0..47f51f1979faab 100644
--- a/Lib/test/test_peepholer.py
+++ b/Lib/test/test_peepholer.py
@@ -1,4 +1,5 @@
import dis
+import gc
from itertools import combinations, product
import opcode
import sys
@@ -2472,6 +2473,13 @@ def test_unoptimized_if_support_killed(self):
]
self.check(insts, insts)
+ insts = [
+ ("LOAD_FAST", 0, 1),
+ ("DELETE_FAST", 0, 2),
+ ("POP_TOP", None, 3),
+ ]
+ self.check(insts, insts)
+
def test_unoptimized_if_aliased(self):
insts = [
("LOAD_FAST", 0, 1),
@@ -2606,6 +2614,22 @@ def test_send(self):
]
self.cfg_optimization_test(insts, expected, consts=[None])
+ def test_del_in_finally(self):
+ # This loads `obj` onto the stack, executes `del obj`, then returns the
+ # `obj` from the stack. See gh-133371 for more details.
+ def create_obj():
+ obj = [42]
+ try:
+ return obj
+ finally:
+ del obj
+
+ obj = create_obj()
+ # The crash in the linked issue happens while running GC during
+ # interpreter finalization, so run it here manually.
+ gc.collect()
+ self.assertEqual(obj, [42])
+
if __name__ == "__main__":
diff --git a/Python/flowgraph.c b/Python/flowgraph.c
index e0bb5615db3e0b..78ef02a911a72b 100644
--- a/Python/flowgraph.c
+++ b/Python/flowgraph.c
@@ -2795,6 +2795,11 @@ optimize_load_fast(cfg_builder *g)
assert(opcode != EXTENDED_ARG);
switch (opcode) {
// Opcodes that load and store locals
+ case DELETE_FAST: {
+ kill_local(instr_flags, &refs, oparg);
+ break;
+ }
+
case LOAD_FAST: {
PUSH_REF(i, oparg);
break;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]