Skip to content

Commit e269f69

Browse files
committed
forward progress_needed
1 parent 1467ce7 commit e269f69

4 files changed

Lines changed: 62 additions & 6 deletions

File tree

Include/internal/pycore_optimizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);
311311
int _Py_uop_analyze_and_optimize(
312312
_PyThreadStateImpl *tstate,
313313
_PyUOpInstruction *input, int trace_len, int curr_stackentries,
314-
_PyUOpInstruction *output, _PyBloomFilter *dependencies);
314+
_PyUOpInstruction *output, _PyBloomFilter *dependencies,
315+
bool progress_needed);
315316

316317
extern PyTypeObject _PyUOpExecutor_Type;
317318

Lib/test/test_capi/test_opt.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import contextlib
22
import itertools
3+
import subprocess
34
import sys
45
import textwrap
56
import unittest
@@ -9,7 +10,7 @@
910

1011
import _opcode
1112

12-
from test.support import (script_helper, requires_specialization,
13+
from test.support import (SHORT_TIMEOUT, script_helper, requires_specialization,
1314
import_helper, Py_GIL_DISABLED, requires_jit_enabled,
1415
reset_code)
1516

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

5998+
def test_side_exit_to_executor_makes_progress(self):
5999+
script = textwrap.dedent("""
6000+
classes = []
6001+
6002+
def make_iter():
6003+
class It:
6004+
def __init__(self):
6005+
self.i = 0
6006+
6007+
def __iter__(self):
6008+
return self
6009+
6010+
def __next__(self):
6011+
self.i += 1
6012+
if self.i > 1000:
6013+
raise StopIteration
6014+
return self.i
6015+
6016+
classes.append(It)
6017+
return It()
6018+
6019+
def f(n):
6020+
for outer in range(n):
6021+
for x in make_iter():
6022+
pass
6023+
6024+
f(200)
6025+
""")
6026+
env = os.environ.copy()
6027+
env.update({
6028+
"PYTHON_JIT": "1",
6029+
"PYTHON_JIT_SIDE_EXIT_INITIAL_VALUE": "1",
6030+
})
6031+
try:
6032+
result = subprocess.run(
6033+
[sys.executable, "-X", "faulthandler", "-c", script],
6034+
stdout=subprocess.PIPE,
6035+
stderr=subprocess.PIPE,
6036+
env=env,
6037+
timeout=SHORT_TIMEOUT,
6038+
)
6039+
except subprocess.TimeoutExpired as exc:
6040+
self.fail(f"subprocess timed out: {exc}")
6041+
self.assertEqual(result.returncode, 0, result)
6042+
59976043
def test_for_iter_gen_cleared_frame_does_not_crash(self):
59986044
# See: https://github.com/python/cpython/issues/145197
59996045
result = script_helper.run_python_until_end('-c', textwrap.dedent("""

Python/optimizer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1704,7 +1704,7 @@ uop_optimize(
17041704
_PyUOpInstruction *output = &_tstate->jit_tracer_state->uop_array[UOP_MAX_TRACE_LENGTH];
17051705
length = _Py_uop_analyze_and_optimize(
17061706
_tstate, buffer, length, curr_stackentries,
1707-
output, &dependencies);
1707+
output, &dependencies, progress_needed);
17081708

17091709
if (length <= 0) {
17101710
return length;

Python/optimizer_analysis.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,8 @@ optimize_uops(
566566
int trace_len,
567567
int curr_stacklen,
568568
_PyUOpInstruction *output,
569-
_PyBloomFilter *dependencies
569+
_PyBloomFilter *dependencies,
570+
bool progress_needed
570571
)
571572
{
572573
assert(!PyErr_Occurred());
@@ -597,9 +598,13 @@ optimize_uops(
597598

598599
_PyUOpInstruction *this_instr = NULL;
599600
JitOptRef *stack_pointer = ctx->frame->stack_pointer;
601+
int bytecode_count = 0;
600602

601603
for (int i = 0; i < trace_len; i++) {
602604
this_instr = &trace[i];
605+
if (this_instr->opcode == _CHECK_VALIDITY) {
606+
bytecode_count++;
607+
}
603608
if (ctx->done) {
604609
// Don't do any more optimization, but
605610
// we still need to reach a terminator for corrctness.
@@ -629,6 +634,9 @@ optimize_uops(
629634
DPRINTF(1, "\nUnknown opcode in abstract interpreter\n");
630635
Py_UNREACHABLE();
631636
}
637+
if (progress_needed && bytecode_count < 2) {
638+
ctx->out_buffer.next = out_ptr;
639+
}
632640
// If no ADD_OP was called during this iteration, copy the original instruction
633641
if (ctx->out_buffer.next == out_ptr) {
634642
*(ctx->out_buffer.next++) = *this_instr;
@@ -797,13 +805,14 @@ _Py_uop_analyze_and_optimize(
797805
int length,
798806
int curr_stacklen,
799807
_PyUOpInstruction *output,
800-
_PyBloomFilter *dependencies
808+
_PyBloomFilter *dependencies,
809+
bool progress_needed
801810
)
802811
{
803812
OPT_STAT_INC(optimizer_attempts);
804813

805814
length = optimize_uops(
806-
tstate, buffer, length, curr_stacklen, output, dependencies);
815+
tstate, buffer, length, curr_stacklen, output, dependencies, progress_needed);
807816

808817
if (length == 0) {
809818
return length;

0 commit comments

Comments
 (0)