From 6e9d5771cc1d4533d2ab9b8410b300c0e2e75cbd Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 20:26:51 +0000 Subject: [PATCH 01/10] Treat call as jump in JIT assembly backend optimizer on x86-64 --- Tools/jit/_optimizers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 08d4e700652deb..afeaea953add25 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -629,6 +629,8 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods rf"\s*(?P{'|'.join(_X86_BRANCHES)})\s+(?P[\w.]+)" ) # https://www.felixcloutier.com/x86/jmp - _re_jump = re.compile(r"\s*jmp\s+(?P[\w.]+)") + # https://www.felixcloutier.com/x86/call + # Calls are also logically jumps to labels. + _re_jump = re.compile(r"\s*((?:jmp)|(?:callq?))\s+(?P[\w.]+)") # https://www.felixcloutier.com/x86/ret _re_return = re.compile(r"\s*ret\b") From 0abce6dac5ead46fa093f307b4725cc4c21dedcf Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 20:31:17 +0000 Subject: [PATCH 02/10] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst new file mode 100644 index 00000000000000..3c476d3eaea574 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-17-20-31-09.gh-issue-139757.6DWxeQ.rst @@ -0,0 +1 @@ +Fix building JIT stencils on free-threaded builds. From 969c8fd6ee4241a6433550b5cfd3fcbf7b4750a1 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:09:18 +0000 Subject: [PATCH 03/10] a proper fix by special casing calls --- Tools/jit/_optimizers.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index afeaea953add25..2ed451c30cbedb 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -95,6 +95,7 @@ class InstructionKind(enum.Enum): JUMP = enum.auto() LONG_BRANCH = enum.auto() SHORT_BRANCH = enum.auto() + CALL = enum.auto() RETURN = enum.auto() SMALL_CONST_1 = enum.auto() SMALL_CONST_2 = enum.auto() @@ -182,6 +183,8 @@ class Optimizer: # Two groups (instruction and target): _re_branch: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # One group (target): + _re_call: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH + # One group (target): _re_jump: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH # No groups: _re_return: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH @@ -220,7 +223,7 @@ def __post_init__(self) -> None: block.link = block = _Block() inst = self._parse_instruction(line) block.instructions.append(inst) - if inst.is_branch(): + if inst.is_branch() or inst.kind == InstructionKind.CALL: # A block ending in a branch has a target and fallthrough: assert inst.target is not None block.target = self._lookup_label(inst.target) @@ -256,6 +259,10 @@ def _parse_instruction(self, line: str) -> Instruction: target = match["target"] name = line[: -len(target)].strip() kind = InstructionKind.JUMP + elif match := self._re_call.match(line): + target = match["target"] + name = line[: -len(target)].strip() + kind = InstructionKind.JUMP elif match := self._re_return.match(line): name = line kind = InstructionKind.RETURN @@ -566,6 +573,8 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods rf"\s*(?P{'|'.join(_branch_patterns)})\s+(.+,\s+)*(?P[\w.]+)" ) + # https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate- + _re_call = re.compile(r"\s*blx??\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- _re_jump = re.compile(r"\s*b\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine- @@ -628,9 +637,9 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods _re_branch = re.compile( rf"\s*(?P{'|'.join(_X86_BRANCHES)})\s+(?P[\w.]+)" ) - # https://www.felixcloutier.com/x86/jmp # https://www.felixcloutier.com/x86/call - # Calls are also logically jumps to labels. - _re_jump = re.compile(r"\s*((?:jmp)|(?:callq?))\s+(?P[\w.]+)") + _re_call = re.compile(r"\s*callq?\s+(?P[\w.]+)") + # https://www.felixcloutier.com/x86/jmp + _re_jump = re.compile(r"\s*jmp\s+(?P[\w.]+)") # https://www.felixcloutier.com/x86/ret _re_return = re.compile(r"\s*ret\b") From ddf85c675ccbf20626e2bb880f7bcd585121cd7e Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:10:23 +0000 Subject: [PATCH 04/10] update comment --- Tools/jit/_optimizers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 2ed451c30cbedb..6a5043b469d768 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -224,7 +224,7 @@ def __post_init__(self) -> None: inst = self._parse_instruction(line) block.instructions.append(inst) if inst.is_branch() or inst.kind == InstructionKind.CALL: - # A block ending in a branch has a target and fallthrough: + # A block ending in a branch/call has a target and fallthrough: assert inst.target is not None block.target = self._lookup_label(inst.target) assert block.fallthrough From bfe5eb969e2bc02657bbdd8b82e970fe8d278e48 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:14:14 +0000 Subject: [PATCH 05/10] address review --- Tools/jit/_optimizers.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 6a5043b469d768..8c800ffc70a74c 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -223,8 +223,13 @@ def __post_init__(self) -> None: block.link = block = _Block() inst = self._parse_instruction(line) block.instructions.append(inst) - if inst.is_branch() or inst.kind == InstructionKind.CALL: - # A block ending in a branch/call has a target and fallthrough: + if inst.is_branch(): + # A block ending in a branch has a target and fallthrough: + assert inst.target is not None + block.target = self._lookup_label(inst.target) + assert block.fallthrough + elif inst.kind == InstructionKind.CALL: + # A block ending in a call has a target and return point after call: assert inst.target is not None block.target = self._lookup_label(inst.target) assert block.fallthrough @@ -262,7 +267,7 @@ def _parse_instruction(self, line: str) -> Instruction: elif match := self._re_call.match(line): target = match["target"] name = line[: -len(target)].strip() - kind = InstructionKind.JUMP + kind = InstructionKind.CALL elif match := self._re_return.match(line): name = line kind = InstructionKind.RETURN From cddf406f0b796ecd3d165e2a29e39b8abd4f9331 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:20:06 +0000 Subject: [PATCH 06/10] Address review Co-authored-by: Savannah Ostrowski --- Tools/jit/_optimizers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 8c800ffc70a74c..c260723bb3b106 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -579,7 +579,7 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods ) # https://developer.arm.com/documentation/ddi0406/b/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/BL--BLX--immediate- - _re_call = re.compile(r"\s*blx??\s+(?P[\w.]+)") + _re_call = re.compile(r"\s*blx?\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-03/Base-Instructions/B--Branch- _re_jump = re.compile(r"\s*b\s+(?P[\w.]+)") # https://developer.arm.com/documentation/ddi0602/2025-09/Base-Instructions/RET--Return-from-subroutine- From be801793e05673bacef52636f8925ada3d11ab1c Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:38:05 +0000 Subject: [PATCH 07/10] try fix macos --- Tools/jit/_optimizers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index 8c800ffc70a74c..7b12841c27610b 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -475,7 +475,7 @@ def _fixup_external_labels(self) -> None: for index, block in enumerate(self._blocks()): if block.target and block.fallthrough: branch = block.instructions[-1] - assert branch.is_branch() + assert branch.is_branch() or branch.kind == InstructionKind.CALL target = branch.target assert target is not None reloc = self._branches[branch.name][1] From 7fad18f0cd6464e0a03abb41500b63939944cfe3 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:43:57 +0000 Subject: [PATCH 08/10] trigger macos CI --- .github/workflows/jit.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 62325250bd368e..796e67038ebd52 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -50,7 +50,7 @@ jobs: ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 jit: name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }}) - needs: interpreter +# needs: interpreter runs-on: ${{ matrix.runner }} timeout-minutes: 90 strategy: @@ -131,7 +131,7 @@ jobs: jit-with-disabled-gil: name: Free-Threaded (Debug) - needs: interpreter +# needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: @@ -159,7 +159,7 @@ jobs: no-opt-jit: name: JIT without optimizations (Debug) - needs: interpreter +# needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: @@ -186,7 +186,7 @@ jobs: tail-call-jit: name: JIT with tail calling interpreter - needs: interpreter +# needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: From f603077fb83e82c7708516265dc1a7439a3aa56c Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:48:13 +0000 Subject: [PATCH 09/10] fix macos again --- Tools/jit/_optimizers.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/jit/_optimizers.py b/Tools/jit/_optimizers.py index bf84f81f9503a5..83c878d8fe205b 100644 --- a/Tools/jit/_optimizers.py +++ b/Tools/jit/_optimizers.py @@ -475,7 +475,9 @@ def _fixup_external_labels(self) -> None: for index, block in enumerate(self._blocks()): if block.target and block.fallthrough: branch = block.instructions[-1] - assert branch.is_branch() or branch.kind == InstructionKind.CALL + if branch.kind == InstructionKind.CALL: + continue + assert branch.is_branch() target = branch.target assert target is not None reloc = self._branches[branch.name][1] From e4d37d0a0f673966989f6f0018df9b09f7195731 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 17 Dec 2025 21:53:34 +0000 Subject: [PATCH 10/10] revert CI changes --- .github/workflows/jit.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 796e67038ebd52..62325250bd368e 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -50,7 +50,7 @@ jobs: ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3 jit: name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }}) -# needs: interpreter + needs: interpreter runs-on: ${{ matrix.runner }} timeout-minutes: 90 strategy: @@ -131,7 +131,7 @@ jobs: jit-with-disabled-gil: name: Free-Threaded (Debug) -# needs: interpreter + needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: @@ -159,7 +159,7 @@ jobs: no-opt-jit: name: JIT without optimizations (Debug) -# needs: interpreter + needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: @@ -186,7 +186,7 @@ jobs: tail-call-jit: name: JIT with tail calling interpreter -# needs: interpreter + needs: interpreter runs-on: ubuntu-24.04 timeout-minutes: 90 strategy: