mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
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.
This commit is contained in:
parent
2bbcaedb75
commit
78adb63ee1
3 changed files with 31 additions and 1 deletions
|
@ -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 \
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import dis
|
||||
import gc
|
||||
from itertools import combinations, product
|
||||
import opcode
|
||||
import sys
|
||||
|
@ -2472,6 +2473,13 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
|||
]
|
||||
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 @@ class OptimizeLoadFastTestCase(DirectCfgOptimizerTests):
|
|||
]
|
||||
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__":
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue