Skip to content

Commit 49712b7

Browse files
authored
Merge branch 'main' into lazy
2 parents 366ebd2 + 8b64dd8 commit 49712b7

File tree

6 files changed

+33
-4
lines changed

6 files changed

+33
-4
lines changed

Lib/test/test_generators.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ def gen():
134134
self.assertEqual(len(resurrected), 1)
135135
self.assertIsInstance(resurrected[0].gi_code, types.CodeType)
136136

137+
def test_exhausted_generator_frame_cycle(self):
138+
def g():
139+
yield
140+
141+
generator = g()
142+
frame = generator.gi_frame
143+
self.assertIsNone(frame.f_back)
144+
next(generator)
145+
self.assertIsNone(frame.f_back)
146+
next(generator, None)
147+
self.assertIsNone(frame.f_back)
148+
137149

138150
class GeneratorTest(unittest.TestCase):
139151

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix reference cycle in exhausted generator frames. Patch by Savannah Ostrowski.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix building JIT stencils on free-threaded builds.

Python/ceval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2310,9 +2310,9 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
23102310
tstate->exc_info = gen->gi_exc_state.previous_item;
23112311
gen->gi_exc_state.previous_item = NULL;
23122312
assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
2313+
frame->previous = NULL;
23132314
_PyFrame_ClearExceptCode(frame);
23142315
_PyErr_ClearExcState(&gen->gi_exc_state);
2315-
frame->previous = NULL;
23162316
}
23172317

23182318
void

Tools/jit/_optimizers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class InstructionKind(enum.Enum):
9595
JUMP = enum.auto()
9696
LONG_BRANCH = enum.auto()
9797
SHORT_BRANCH = enum.auto()
98+
CALL = enum.auto()
9899
RETURN = enum.auto()
99100
SMALL_CONST_1 = enum.auto()
100101
SMALL_CONST_2 = enum.auto()
@@ -182,6 +183,8 @@ class Optimizer:
182183
# Two groups (instruction and target):
183184
_re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
184185
# One group (target):
186+
_re_call: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
187+
# One group (target):
185188
_re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
186189
# No groups:
187190
_re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
@@ -225,6 +228,11 @@ def __post_init__(self) -> None:
225228
assert inst.target is not None
226229
block.target = self._lookup_label(inst.target)
227230
assert block.fallthrough
231+
elif inst.kind == InstructionKind.CALL:
232+
# A block ending in a call has a target and return point after call:
233+
assert inst.target is not None
234+
block.target = self._lookup_label(inst.target)
235+
assert block.fallthrough
228236
elif inst.kind == InstructionKind.JUMP:
229237
# A block ending in a jump has a target and no fallthrough:
230238
assert inst.target is not None
@@ -256,6 +264,10 @@ def _parse_instruction(self, line: str) -> Instruction:
256264
target = match["target"]
257265
name = line[: -len(target)].strip()
258266
kind = InstructionKind.JUMP
267+
elif match := self._re_call.match(line):
268+
target = match["target"]
269+
name = line[: -len(target)].strip()
270+
kind = InstructionKind.CALL
259271
elif match := self._re_return.match(line):
260272
name = line
261273
kind = InstructionKind.RETURN
@@ -463,6 +475,8 @@ def _fixup_external_labels(self) -> None:
463475
for index, block in enumerate(self._blocks()):
464476
if block.target and block.fallthrough:
465477
branch = block.instructions[-1]
478+
if branch.kind == InstructionKind.CALL:
479+
continue
466480
assert branch.is_branch()
467481
target = branch.target
468482
assert target is not None
@@ -566,6 +580,8 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
566580
rf"\s*(?P<instruction>{'|'.join(_branch_patterns)})\s+(.+,\s+)*(?P<target>[\w.]+)"
567581
)
568582

583+
# https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate-
584+
_re_call = re.compile(r"\s*blx?\s+(?P<target>[\w.]+)")
569585
# https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch-
570586
_re_jump = re.compile(r"\s*b\s+(?P<target>[\w.]+)")
571587
# https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine-
@@ -628,6 +644,8 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods
628644
_re_branch = re.compile(
629645
rf"\s*(?P<instruction>{'|'.join(_X86_BRANCHES)})\s+(?P<target>[\w.]+)"
630646
)
647+
# https://www.felixcloutier.com/x86/call
648+
_re_call = re.compile(r"\s*callq?\s+(?P<target>[\w.]+)")
631649
# https://www.felixcloutier.com/x86/jmp
632650
_re_jump = re.compile(r"\s*jmp\s+(?P<target>[\w.]+)")
633651
# https://www.felixcloutier.com/x86/ret

Tools/tsan/suppressions_free_threading.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ race_top:write_thread_id
2323
# gh-129068: race on shared range iterators (test_free_threading.test_zip.ZipThreading.test_threading)
2424
race_top:rangeiter_next
2525

26-
# gh-129748: test.test_free_threading.test_slots.TestSlots.test_object
27-
race_top:mi_block_set_nextx
28-
2926
# https://gist.github.com/mpage/6962e8870606cfc960e159b407a0cb40
3027
thread:pthread_create
3128

0 commit comments

Comments
 (0)