Skip to content

Commit 1384f02

Browse files
gh-126910: Verify that JIT stencils preserve frame pointer (GH-146524)
1 parent b60b926 commit 1384f02

File tree

6 files changed

+52
-32
lines changed

6 files changed

+52
-32
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,7 @@ static inline void _Py_LeaveRecursiveCallTstate(PyThreadState *tstate) {
249249

250250
PyAPI_FUNC(void) _Py_InitializeRecursionLimits(PyThreadState *tstate);
251251

252-
static inline int _Py_ReachedRecursionLimit(PyThreadState *tstate) {
253-
uintptr_t here_addr = _Py_get_machine_stack_pointer();
254-
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
255-
assert(_tstate->c_stack_hard_limit != 0);
256-
#if _Py_STACK_GROWS_DOWN
257-
return here_addr <= _tstate->c_stack_soft_limit;
258-
#else
259-
return here_addr >= _tstate->c_stack_soft_limit;
260-
#endif
261-
}
252+
PyAPI_FUNC(int) _Py_ReachedRecursionLimit(PyThreadState *tstate);
262253

263254
// Export for test_peg_generator
264255
PyAPI_FUNC(int) _Py_ReachedRecursionLimitWithMargin(

Include/internal/pycore_pystate.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,18 @@ static uintptr_t return_pointer_as_int(char* p) {
312312
}
313313
#endif
314314

315-
PyAPI_DATA(uintptr_t) _Py_get_machine_stack_pointer(void);
315+
static inline uintptr_t
316+
_Py_get_machine_stack_pointer(void) {
317+
#if _Py__has_builtin(__builtin_frame_address) || defined(__GNUC__)
318+
return (uintptr_t)__builtin_frame_address(0);
319+
#elif defined(_MSC_VER)
320+
return (uintptr_t)_AddressOfReturnAddress();
321+
#else
322+
char here;
323+
/* Avoid compiler warning about returning stack address */
324+
return return_pointer_as_int(&here);
325+
#endif
326+
}
316327

317328
static inline intptr_t
318329
_Py_RecursionLimit_GetMargin(PyThreadState *tstate)

Python/ceval.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,19 @@ _PyEval_GetIter(_PyStackRef iterable, _PyStackRef *index_or_null, int yield_from
12011201
return PyStackRef_FromPyObjectSteal(iter_o);
12021202
}
12031203

1204+
Py_NO_INLINE int
1205+
_Py_ReachedRecursionLimit(PyThreadState *tstate) {
1206+
uintptr_t here_addr = _Py_get_machine_stack_pointer();
1207+
_PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate;
1208+
assert(_tstate->c_stack_hard_limit != 0);
1209+
#if _Py_STACK_GROWS_DOWN
1210+
return here_addr <= _tstate->c_stack_soft_limit;
1211+
#else
1212+
return here_addr >= _tstate->c_stack_soft_limit;
1213+
#endif
1214+
}
1215+
1216+
12041217
#if (defined(__GNUC__) && __GNUC__ >= 10 && !defined(__clang__)) && defined(__x86_64__)
12051218
/*
12061219
* gh-129987: The SLP autovectorizer can cause poor code generation for

Python/pystate.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3286,16 +3286,3 @@ _Py_GetMainConfig(void)
32863286
}
32873287
return _PyInterpreterState_GetConfig(interp);
32883288
}
3289-
3290-
uintptr_t
3291-
_Py_get_machine_stack_pointer(void) {
3292-
#if _Py__has_builtin(__builtin_frame_address) || defined(__GNUC__)
3293-
return (uintptr_t)__builtin_frame_address(0);
3294-
#elif defined(_MSC_VER)
3295-
return (uintptr_t)_AddressOfReturnAddress();
3296-
#else
3297-
char here;
3298-
/* Avoid compiler warning about returning stack address */
3299-
return return_pointer_as_int(&here);
3300-
#endif
3301-
}

Tools/jit/_optimizers.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class Optimizer:
162162
label_prefix: str
163163
symbol_prefix: str
164164
re_global: re.Pattern[str]
165+
frame_pointers: bool
165166
# The first block in the linked list:
166167
_root: _Block = dataclasses.field(init=False, default_factory=_Block)
167168
_labels: dict[str, _Block] = dataclasses.field(init=False, default_factory=dict)
@@ -193,6 +194,7 @@ class Optimizer:
193194
_re_small_const_1 = _RE_NEVER_MATCH
194195
_re_small_const_2 = _RE_NEVER_MATCH
195196
const_reloc = "<Not supported>"
197+
_frame_pointer_modify: typing.ClassVar[re.Pattern[str]] = _RE_NEVER_MATCH
196198

197199
def __post_init__(self) -> None:
198200
# Split the code into a linked list of basic blocks. A basic block is an
@@ -553,6 +555,16 @@ def _small_const_2(self, inst: Instruction) -> tuple[str, Instruction | None]:
553555
def _small_consts_match(self, inst1: Instruction, inst2: Instruction) -> bool:
554556
raise NotImplementedError()
555557

558+
def _validate(self) -> None:
559+
for block in self._blocks():
560+
if not block.instructions:
561+
continue
562+
for inst in block.instructions:
563+
if self.frame_pointers:
564+
assert (
565+
self._frame_pointer_modify.match(inst.text) is None
566+
), "Frame pointer should not be modified"
567+
556568
def run(self) -> None:
557569
"""Run this optimizer."""
558570
self._insert_continue_label()
@@ -565,6 +577,7 @@ def run(self) -> None:
565577
self._remove_unreachable()
566578
self._fixup_external_labels()
567579
self._fixup_constants()
580+
self._validate()
568581
self.path.write_text(self._body())
569582

570583

@@ -595,6 +608,7 @@ class OptimizerAArch64(Optimizer): # pylint: disable = too-few-public-methods
595608
r"\s*(?P<instruction>ldr)\s+.*(?P<value>_JIT_OP(ARG|ERAND(0|1))_(16|32)).*"
596609
)
597610
const_reloc = "CUSTOM_AARCH64_CONST"
611+
_frame_pointer_modify = re.compile(r"\s*stp\s+x29.*")
598612

599613
def _get_reg(self, inst: Instruction) -> str:
600614
_, rest = inst.text.split(inst.name)
@@ -649,4 +663,5 @@ class OptimizerX86(Optimizer): # pylint: disable = too-few-public-methods
649663
# https://www.felixcloutier.com/x86/jmp
650664
_re_jump = re.compile(r"\s*jmp\s+(?P<target>[\w.]+)")
651665
# https://www.felixcloutier.com/x86/ret
652-
_re_return = re.compile(r"\s*ret\b")
666+
_re_return = re.compile(r"\s*retq?\b")
667+
_frame_pointer_modify = re.compile(r"\s*movq?\s+%(\w+),\s+%rbp.*")

Tools/jit/_targets.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,21 +176,24 @@ async def _compile(
176176
f"{s}",
177177
f"{c}",
178178
]
179+
is_shim = opname == "shim"
179180
if self.frame_pointers:
180-
frame_pointer = "all" if opname == "shim" else "reserved"
181+
frame_pointer = "all" if is_shim else "reserved"
181182
args_s += ["-Xclang", f"-mframe-pointer={frame_pointer}"]
182183
args_s += self.args
183184
# Allow user-provided CFLAGS to override any defaults
184185
args_s += shlex.split(self.cflags)
185186
await _llvm.run(
186187
"clang", args_s, echo=self.verbose, llvm_version=self.llvm_version
187188
)
188-
self.optimizer(
189-
s,
190-
label_prefix=self.label_prefix,
191-
symbol_prefix=self.symbol_prefix,
192-
re_global=self.re_global,
193-
).run()
189+
if not is_shim:
190+
self.optimizer(
191+
s,
192+
label_prefix=self.label_prefix,
193+
symbol_prefix=self.symbol_prefix,
194+
re_global=self.re_global,
195+
frame_pointers=self.frame_pointers,
196+
).run()
194197
args_o = [f"--target={self.triple}", "-c", "-o", f"{o}", f"{s}"]
195198
await _llvm.run(
196199
"clang", args_o, echo=self.verbose, llvm_version=self.llvm_version

0 commit comments

Comments
 (0)