Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@

from test.support import (script_helper, requires_specialization,
import_helper, Py_GIL_DISABLED, requires_jit_enabled,
reset_code)
reset_code, infinite_recursion)

_testinternalcapi = import_helper.import_module("_testinternalcapi")

from _testinternalcapi import TIER2_THRESHOLD
from _testcapi import pyobject_vectorcall


@contextlib.contextmanager
Expand Down Expand Up @@ -2502,6 +2503,37 @@ def testfunc(n):
self.assertIn("_BINARY_OP", uops)


@requires_jit_enabled
class TestStackUse(unittest.TestCase):

def test_jit_at_depth(self):

def run_at_depth(n, func):
if n:
pyobject_vectorcall(run_at_depth, (n-1, func), None)
else:
func()

def find_stack_limit():
nonlocal depth
depth += 1
pyobject_vectorcall(find_stack_limit, (), None)

with infinite_recursion():
depth = 0
try:
find_stack_limit()
except RecursionError:
pass

def loop_func():
r = 0
for i in range(TIER2_THRESHOLD):
r += i
return r
run_at_depth(depth-1, loop_func)


def global_identity(x):
return x

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Guard against C stack overflow when JIT compiling.
6 changes: 6 additions & 0 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ _PyOptimizer_Optimize(
_PyInterpreterFrame *frame, _Py_CODEUNIT *start,
_PyExecutorObject **executor_ptr, int chain_depth)
{
/* Make sure we have enough C stack space for the buffer */
int margin = 1 + sizeof(_PyUOpInstruction) * UOP_MAX_TRACE_LENGTH / _PyOS_STACK_MARGIN_BYTES;
if (_Py_ReachedRecursionLimitWithMargin(_PyThreadState_GET(), margin)) {
return 0;
}

_PyStackRef *stack_pointer = frame->stackpointer;
assert(_PyInterpreterState_GET()->jit);
// The first executor in a chain and the MAX_CHAIN_DEPTH'th executor *must*
Expand Down
5 changes: 5 additions & 0 deletions Python/optimizer_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,11 @@ _Py_uop_analyze_and_optimize(
)
{
OPT_STAT_INC(optimizer_attempts);
/* Make sure we have enough C stack space for the optimizer */
int margin = 1 + sizeof(JitOptContext)/_PyOS_STACK_MARGIN_BYTES;
if (_Py_ReachedRecursionLimitWithMargin(_PyThreadState_GET(), margin)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To work properly, the optimize_uops function or this function must be marked Py_NO_INLINE as well.

Otherwise, the compiler can just inline the functions and hoist the alloca above the check, making the check useless.

return 0;
}

int err = remove_globals(frame, buffer, length, dependencies);
if (err <= 0) {
Expand Down
Loading