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
3 changes: 2 additions & 1 deletion Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,10 @@ _PyEval_SpecialMethodCanSuggest(PyObject *self, int oparg);
#define _PY_EVAL_PLEASE_STOP_BIT (1U << 5)
#define _PY_EVAL_EXPLICIT_MERGE_BIT (1U << 6)
#define _PY_EVAL_JIT_INVALIDATE_COLD_BIT (1U << 7)
#define _PY_EVAL_JIT_FORCE_TIER1_BIT (1U << 8)

/* Reserve a few bits for future use */
#define _PY_EVAL_EVENTS_BITS 8
#define _PY_EVAL_EVENTS_BITS 9
#define _PY_EVAL_EVENTS_MASK ((1 << _PY_EVAL_EVENTS_BITS)-1)

static inline void
Expand Down
48 changes: 47 additions & 1 deletion Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import contextlib
import itertools
import subprocess
import sys
import textwrap
import unittest
Expand All @@ -9,7 +10,7 @@

import _opcode

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

Expand Down Expand Up @@ -5994,6 +5995,51 @@ def __init__(self, name):
PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE="1")
self.assertEqual(result[0].rc, 0, result)

def test_side_exit_to_executor_makes_progress(self):
script = textwrap.dedent("""
classes = []

def make_iter():
class It:
def __init__(self):
self.i = 0

def __iter__(self):
return self

def __next__(self):
self.i += 1
if self.i > 1000:
raise StopIteration
return self.i

classes.append(It)
return It()

def f(n):
for outer in range(n):
for x in make_iter():
pass

f(200)
""")
env = os.environ.copy()
env.update({
"PYTHON_JIT": "1",
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE": "1",
})
try:
result = subprocess.run(
[sys.executable, "-X", "faulthandler", "-c", script],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env,
timeout=SHORT_TIMEOUT,
)
except subprocess.TimeoutExpired as exc:
self.fail(f"subprocess timed out: {exc}")
self.assertEqual(result.returncode, 0, result)

def test_for_iter_gen_cleared_frame_does_not_crash(self):
# See: https://github.com/python/cpython/issues/145197
result = script_helper.run_python_until_end('-c', textwrap.dedent("""
Expand Down
10 changes: 10 additions & 0 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -6297,6 +6297,16 @@ dummy_func(
if (target->op.code == ENTER_EXECUTOR) {
PyCodeObject *code = _PyFrame_GetCode(frame);
executor = code->co_executors->executors[target->op.arg];
_PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit);
if (!exit->is_control_flow) {
int chain_depth = previous_executor->vm_data.chain_depth + 1;
if (executor == previous_executor ||
chain_depth % MAX_CHAIN_DEPTH == 0) {
/* Avoid tier-2 self-link/wrap that bypasses the eval breaker. */
_Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_FORCE_TIER1_BIT);
GOTO_TIER_ONE(target);
}
}
Py_INCREF(executor);
assert(tstate->jit_exit == exit);
exit->executor = executor;
Expand Down
4 changes: 4 additions & 0 deletions Python/ceval_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,10 @@ _Py_HandlePending(PyThreadState *tstate)
}

#ifdef _Py_TIER2
if ((breaker & _PY_EVAL_JIT_FORCE_TIER1_BIT) != 0) {
_Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_FORCE_TIER1_BIT);
}

if ((breaker & _PY_EVAL_JIT_INVALIDATE_COLD_BIT) != 0) {
_Py_unset_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
_Py_Executors_InvalidateCold(tstate->interp);
Expand Down
10 changes: 10 additions & 0 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading