Skip to content

Commit 7797182

Browse files
authored
GH-118093: Improve handling of short and mid-loop traces (GH-122252)
1 parent 78df104 commit 7797182

File tree

2 files changed

+34
-35
lines changed

2 files changed

+34
-35
lines changed

Objects/genobject.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -342,8 +342,9 @@ _PyGen_yf(PyGenObject *gen)
342342
{
343343
if (gen->gi_frame_state == FRAME_SUSPENDED_YIELD_FROM) {
344344
_PyInterpreterFrame *frame = &gen->gi_iframe;
345-
assert(is_resume(frame->instr_ptr));
346-
assert((frame->instr_ptr->op.arg & RESUME_OPARG_LOCATION_MASK) >= RESUME_AFTER_YIELD_FROM);
345+
// GH-122390: These asserts are wrong in the presence of ENTER_EXECUTOR!
346+
// assert(is_resume(frame->instr_ptr));
347+
// assert((frame->instr_ptr->op.arg & RESUME_OPARG_LOCATION_MASK) >= RESUME_AFTER_YIELD_FROM);
347348
return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(frame));
348349
}
349350
return NULL;

Python/optimizer.c

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,7 @@ add_to_trace(
503503
if (trace_stack_depth >= TRACE_STACK_SIZE) { \
504504
DPRINTF(2, "Trace stack overflow\n"); \
505505
OPT_STAT_INC(trace_stack_overflow); \
506-
trace_length = 0; \
507-
goto done; \
506+
return 0; \
508507
} \
509508
assert(func == NULL || func->func_code == (PyObject *)code); \
510509
trace_stack[trace_stack_depth].func = func; \
@@ -550,6 +549,7 @@ translate_bytecode_to_trace(
550549
} trace_stack[TRACE_STACK_SIZE];
551550
int trace_stack_depth = 0;
552551
int confidence = CONFIDENCE_RANGE; // Adjusted by branch instructions
552+
bool jump_seen = false;
553553

554554
#ifdef Py_DEBUG
555555
char *python_lltrace = Py_GETENV("PYTHON_LLTRACE");
@@ -568,7 +568,6 @@ translate_bytecode_to_trace(
568568
ADD_TO_TRACE(_START_EXECUTOR, 0, (uintptr_t)instr, INSTR_IP(instr, code));
569569
uint32_t target = 0;
570570

571-
top: // Jump here after _PUSH_FRAME or likely branches
572571
for (;;) {
573572
target = INSTR_IP(instr, code);
574573
// Need space for _DEOPT
@@ -577,6 +576,13 @@ translate_bytecode_to_trace(
577576
uint32_t opcode = instr->op.code;
578577
uint32_t oparg = instr->op.arg;
579578

579+
if (!progress_needed && instr == initial_instr) {
580+
// We have looped around to the start:
581+
RESERVE(1);
582+
ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0);
583+
goto done;
584+
}
585+
580586
DPRINTF(2, "%d: %s(%d)\n", target, _PyOpcode_OpName[opcode], oparg);
581587

582588
if (opcode == ENTER_EXECUTOR) {
@@ -603,30 +609,21 @@ translate_bytecode_to_trace(
603609
/* Special case the first instruction,
604610
* so that we can guarantee forward progress */
605611
if (progress_needed) {
606-
progress_needed = false;
607-
if (opcode == JUMP_BACKWARD || opcode == JUMP_BACKWARD_NO_INTERRUPT) {
608-
instr += 1 + _PyOpcode_Caches[opcode] - (int32_t)oparg;
609-
initial_instr = instr;
610-
if (opcode == JUMP_BACKWARD) {
611-
ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target);
612-
}
613-
continue;
614-
}
615-
else {
616-
if (OPCODE_HAS_EXIT(opcode) || OPCODE_HAS_DEOPT(opcode)) {
617-
opcode = _PyOpcode_Deopt[opcode];
618-
}
619-
assert(!OPCODE_HAS_EXIT(opcode));
620-
assert(!OPCODE_HAS_DEOPT(opcode));
612+
if (OPCODE_HAS_EXIT(opcode) || OPCODE_HAS_DEOPT(opcode)) {
613+
opcode = _PyOpcode_Deopt[opcode];
621614
}
615+
assert(!OPCODE_HAS_EXIT(opcode));
616+
assert(!OPCODE_HAS_DEOPT(opcode));
622617
}
623618

624619
if (OPCODE_HAS_EXIT(opcode)) {
625-
// Make space for exit code
620+
// Make space for side exit and final _EXIT_TRACE:
621+
RESERVE_RAW(2, "_EXIT_TRACE");
626622
max_length--;
627623
}
628624
if (OPCODE_HAS_ERROR(opcode)) {
629-
// Make space for error code
625+
// Make space for error stub and final _EXIT_TRACE:
626+
RESERVE_RAW(2, "_ERROR_POP_N");
630627
max_length--;
631628
}
632629
switch (opcode) {
@@ -672,19 +669,18 @@ translate_bytecode_to_trace(
672669
}
673670

674671
case JUMP_BACKWARD:
672+
ADD_TO_TRACE(_CHECK_PERIODIC, 0, 0, target);
673+
_Py_FALLTHROUGH;
675674
case JUMP_BACKWARD_NO_INTERRUPT:
676675
{
677-
_Py_CODEUNIT *target = instr + 1 + _PyOpcode_Caches[opcode] - (int)oparg;
678-
if (target == initial_instr) {
679-
/* We have looped round to the start */
680-
RESERVE(1);
681-
ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0);
682-
}
683-
else {
676+
instr += 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] - (int)oparg;
677+
if (jump_seen) {
684678
OPT_STAT_INC(inner_loop);
685679
DPRINTF(2, "JUMP_BACKWARD not to top ends trace\n");
680+
goto done;
686681
}
687-
goto done;
682+
jump_seen = true;
683+
goto top;
688684
}
689685

690686
case JUMP_FORWARD:
@@ -904,23 +900,25 @@ translate_bytecode_to_trace(
904900
assert(instr->op.code == POP_TOP);
905901
instr++;
906902
}
903+
top:
904+
// Jump here after _PUSH_FRAME or likely branches.
905+
progress_needed = false;
907906
} // End for (;;)
908907

909908
done:
910909
while (trace_stack_depth > 0) {
911910
TRACE_STACK_POP();
912911
}
913912
assert(code == initial_code);
914-
// Skip short traces like _SET_IP, LOAD_FAST, _SET_IP, _EXIT_TRACE
915-
if (progress_needed || trace_length < 5) {
913+
// Skip short traces where we can't even translate a single instruction:
914+
if (progress_needed) {
916915
OPT_STAT_INC(trace_too_short);
917916
DPRINTF(2,
918-
"No trace for %s (%s:%d) at byte offset %d (%s)\n",
917+
"No trace for %s (%s:%d) at byte offset %d (no progress)\n",
919918
PyUnicode_AsUTF8(code->co_qualname),
920919
PyUnicode_AsUTF8(code->co_filename),
921920
code->co_firstlineno,
922-
2 * INSTR_IP(initial_instr, code),
923-
progress_needed ? "no progress" : "too short");
921+
2 * INSTR_IP(initial_instr, code));
924922
return 0;
925923
}
926924
if (trace[trace_length-1].opcode != _JUMP_TO_TOP) {

0 commit comments

Comments
 (0)