From d76d264bc68994e539efc2aa638947363886b056 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Thu, 27 Nov 2025 12:34:51 -0800 Subject: [PATCH 1/5] Reserve the frame pointer in JIT code --- Tools/jit/_targets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 39be353ec30858..f2b017753187ae 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -158,6 +158,7 @@ async def _compile( # code than -O3). As a nice benefit, it uses less memory too: "-Os", "-S", + "-Xclang", f"-mframe-pointer={'all' if opname == 'shim' else 'reserved'}", # Shorten full absolute file paths in the generated code (like the # __FILE__ macro and assert failure messages) for reproducibility: f"-ffile-prefix-map={CPYTHON}=.", From c214c070e414dea0686696fe2c86cb46a56429ed Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Fri, 28 Nov 2025 10:51:13 -0800 Subject: [PATCH 2/5] shim -> trampoline --- Tools/jit/_targets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index f2b017753187ae..d0ba5eb0af810b 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -158,7 +158,7 @@ async def _compile( # code than -O3). As a nice benefit, it uses less memory too: "-Os", "-S", - "-Xclang", f"-mframe-pointer={'all' if opname == 'shim' else 'reserved'}", + "-Xclang", f"-mframe-pointer={'all' if opname == 'trampoline' else 'reserved'}", # Shorten full absolute file paths in the generated code (like the # __FILE__ macro and assert failure messages) for reproducibility: f"-ffile-prefix-map={CPYTHON}=.", From 8cfff6f47cb4aaf437c7180a1a8b5c7adcd7b30e Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Jan 2026 18:49:40 -0800 Subject: [PATCH 3/5] trampoline -> shim --- Tools/jit/_targets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index d0ba5eb0af810b..f2b017753187ae 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -158,7 +158,7 @@ async def _compile( # code than -O3). As a nice benefit, it uses less memory too: "-Os", "-S", - "-Xclang", f"-mframe-pointer={'all' if opname == 'trampoline' else 'reserved'}", + "-Xclang", f"-mframe-pointer={'all' if opname == 'shim' else 'reserved'}", # Shorten full absolute file paths in the generated code (like the # __FILE__ macro and assert failure messages) for reproducibility: f"-ffile-prefix-map={CPYTHON}=.", From 766d40435208756a61f282e56afb40120e9d0b43 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Jan 2026 19:05:22 -0800 Subject: [PATCH 4/5] Clean up --- Tools/jit/_targets.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index f2b017753187ae..79a8ead8ff69a3 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -51,6 +51,7 @@ class _Target(typing.Generic[_S, _R]): debug: bool = False verbose: bool = False cflags: str = "" + frame_pointers: bool = False llvm_version: str = _llvm._LLVM_VERSION known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve() @@ -158,7 +159,6 @@ async def _compile( # code than -O3). As a nice benefit, it uses less memory too: "-Os", "-S", - "-Xclang", f"-mframe-pointer={'all' if opname == 'shim' else 'reserved'}", # Shorten full absolute file paths in the generated code (like the # __FILE__ macro and assert failure messages) for reproducibility: f"-ffile-prefix-map={CPYTHON}=.", @@ -175,10 +175,13 @@ async def _compile( "-o", f"{s}", f"{c}", - *self.args, - # Allow user-provided CFLAGS to override any defaults - *shlex.split(self.cflags), ] + if self.frame_pointers: + frame_pointer = "all" if opname == "shim" else "reserved" + args_s += ["-Xclang", f"-mframe-pointer={frame_pointer}"] + args_s += self.args + # Allow user-provided CFLAGS to override any defaults + args_s += shlex.split(self.cflags) await _llvm.run( "clang", args_s, echo=self.verbose, llvm_version=self.llvm_version ) @@ -614,7 +617,9 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: condition = "defined(__x86_64__) && defined(__linux__)" args = ["-fno-pic", "-mcmodel=medium", "-mlarge-data-threshold=0", "-fno-plt"] optimizer = _optimizers.OptimizerX86 - target = _ELF(host, condition, args=args, optimizer=optimizer) + target = _ELF( + host, condition, args=args, optimizer=optimizer, frame_pointers=True + ) else: raise ValueError(host) return target From ae8649fba3a7d5cfde726dc89ddec2e1940fbc57 Mon Sep 17 00:00:00 2001 From: Brandt Bucher Date: Wed, 7 Jan 2026 23:07:57 -0800 Subject: [PATCH 5/5] blurb add --- .../2026-01-07-23-07-17.gh-issue-126910.d8zdm-.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-01-07-23-07-17.gh-issue-126910.d8zdm-.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-07-23-07-17.gh-issue-126910.d8zdm-.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-07-23-07-17.gh-issue-126910.d8zdm-.rst new file mode 100644 index 00000000000000..c86bfdb306f4c9 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-07-23-07-17.gh-issue-126910.d8zdm-.rst @@ -0,0 +1,2 @@ +Set frame pointers in ``x86_64-unknown-linux-gnu`` JIT code, allowing +most native profilers and debuggers to unwind through them.