Skip to content
Draft
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_optimizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,8 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);
int _Py_uop_analyze_and_optimize(
_PyThreadStateImpl *tstate,
_PyUOpInstruction *input, int trace_len, int curr_stackentries,
_PyUOpInstruction *output, _PyBloomFilter *dependencies);
_PyUOpInstruction *output, _PyBloomFilter *dependencies,
bool progress_needed);

extern PyTypeObject _PyUOpExecutor_Type;

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
2 changes: 1 addition & 1 deletion Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ uop_optimize(
_PyUOpInstruction *output = &_tstate->jit_tracer_state->uop_array[UOP_MAX_TRACE_LENGTH];
length = _Py_uop_analyze_and_optimize(
_tstate, buffer, length, curr_stackentries,
output, &dependencies);
output, &dependencies, progress_needed);

if (length <= 0) {
return length;
Expand Down
15 changes: 12 additions & 3 deletions Python/optimizer_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,8 @@ optimize_uops(
int trace_len,
int curr_stacklen,
_PyUOpInstruction *output,
_PyBloomFilter *dependencies
_PyBloomFilter *dependencies,
bool progress_needed
)
{
assert(!PyErr_Occurred());
Expand Down Expand Up @@ -597,9 +598,13 @@ optimize_uops(

_PyUOpInstruction *this_instr = NULL;
JitOptRef *stack_pointer = ctx->frame->stack_pointer;
int bytecode_count = 0;

for (int i = 0; i < trace_len; i++) {
this_instr = &trace[i];
if (this_instr->opcode == _CHECK_VALIDITY) {
bytecode_count++;
}
if (ctx->done) {
// Don't do any more optimization, but
// we still need to reach a terminator for corrctness.
Expand Down Expand Up @@ -629,6 +634,9 @@ optimize_uops(
DPRINTF(1, "\nUnknown opcode in abstract interpreter\n");
Py_UNREACHABLE();
}
if (progress_needed && bytecode_count < 2) {
ctx->out_buffer.next = out_ptr;
}
// If no ADD_OP was called during this iteration, copy the original instruction
if (ctx->out_buffer.next == out_ptr) {
*(ctx->out_buffer.next++) = *this_instr;
Expand Down Expand Up @@ -797,13 +805,14 @@ _Py_uop_analyze_and_optimize(
int length,
int curr_stacklen,
_PyUOpInstruction *output,
_PyBloomFilter *dependencies
_PyBloomFilter *dependencies,
bool progress_needed
)
{
OPT_STAT_INC(optimizer_attempts);

length = optimize_uops(
tstate, buffer, length, curr_stacklen, output, dependencies);
tstate, buffer, length, curr_stacklen, output, dependencies, progress_needed);

if (length == 0) {
return length;
Expand Down
Loading