Skip to content

Commit 6f75dbe

Browse files
committed
Use _DYNAMIC_EXIT to stitch traces ending in unbalanced retuens or yields.
1 parent b034f14 commit 6f75dbe

11 files changed

+104
-42
lines changed

Include/internal/pycore_uop_ids.h

Lines changed: 22 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,8 +2589,7 @@ dummy_func(
25892589
_PyErr_Clear(tstate);
25902590
}
25912591
/* iterator ended normally */
2592-
assert(next_instr[oparg].op.code == END_FOR ||
2593-
next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
2592+
assert(base_opcode(_PyFrame_GetCode(frame), INSTR_OFFSET() + oparg) == END_FOR);
25942593
Py_DECREF(iter);
25952594
STACK_SHRINK(1);
25962595
/* Jump forward oparg, then skip following END_FOR and POP_TOP instruction */
@@ -4351,6 +4350,14 @@ dummy_func(
43514350
assert(tstate->tracing || eval_breaker == FT_ATOMIC_LOAD_UINTPTR_ACQUIRE(_PyFrame_GetCode(frame)->_co_instrumentation_version));
43524351
}
43534352

4353+
tier2 op(_RETURN_OFFSET, (--)) {
4354+
frame->instr_ptr += frame->return_offset;
4355+
}
4356+
4357+
tier2 op(_YIELD_OFFSET, (--)) {
4358+
frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND;
4359+
}
4360+
43544361
// END BYTECODES //
43554362

43564363
}

Python/ceval.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,18 @@ extern const struct _PyCode_DEF(8) _Py_InitCleanup;
655655

656656
#ifdef Py_DEBUG
657657
extern void _PyUOpPrint(const _PyUOpInstruction *uop);
658+
659+
static int
660+
base_opcode(PyCodeObject *code, int offset)
661+
{
662+
int opcode = _Py_GetBaseOpcode(code, offset);
663+
if (opcode == ENTER_EXECUTOR) {
664+
int oparg = _PyCode_CODE(code)[offset].op.arg;
665+
_PyExecutorObject *ex = code->co_executors->executors[oparg];
666+
return ex->vm_data.opcode;
667+
}
668+
return opcode;
669+
}
658670
#endif
659671

660672

Python/executor_cases.c.h

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ _PyOptimizer_Optimize(
211211
_PyInterpreterFrame *frame, _Py_CODEUNIT *start,
212212
PyObject **stack_pointer, _PyExecutorObject **executor_ptr)
213213
{
214+
if (start->op.code == INTERPRETER_EXIT || start->op.code == EXIT_INIT_CHECK) {
215+
return 0;
216+
}
214217
PyCodeObject *code = _PyFrame_GetCode(frame);
215218
assert(PyCode_Check(code));
216219
PyInterpreterState *interp = _PyInterpreterState_GET();
@@ -747,17 +750,6 @@ translate_bytecode_to_trace(
747750
// Reserve space for nuops (+ _SET_IP + _EXIT_TRACE)
748751
int nuops = expansion->nuops;
749752
RESERVE(nuops + 1); /* One extra for exit */
750-
int16_t last_op = expansion->uops[nuops-1].uop;
751-
if (last_op == _POP_FRAME || last_op == _RETURN_GENERATOR || last_op == _YIELD_VALUE) {
752-
// Check for trace stack underflow now:
753-
// We can't bail e.g. in the middle of
754-
// LOAD_CONST + _POP_FRAME.
755-
if (trace_stack_depth == 0) {
756-
DPRINTF(2, "Trace stack underflow\n");
757-
OPT_STAT_INC(trace_stack_underflow);
758-
goto done;
759-
}
760-
}
761753
uint32_t orig_oparg = oparg; // For OPARG_TOP/BOTTOM
762754
for (int i = 0; i < nuops; i++) {
763755
oparg = orig_oparg;
@@ -811,6 +803,19 @@ translate_bytecode_to_trace(
811803
}
812804

813805
if (uop == _POP_FRAME || uop == _RETURN_GENERATOR || uop == _YIELD_VALUE) {
806+
if (trace_stack_depth == 0) {
807+
DPRINTF(2, "Trace stack underflow\n");
808+
OPT_STAT_INC(trace_stack_underflow);
809+
ADD_TO_TRACE(uop, oparg, 0, target);
810+
if (uop == _POP_FRAME || uop == _RETURN_GENERATOR) {
811+
ADD_TO_TRACE(_RETURN_OFFSET, 0, 0, 0);
812+
}
813+
else {
814+
ADD_TO_TRACE(_YIELD_OFFSET, 0, 0, 0);
815+
}
816+
ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0);
817+
goto done;
818+
}
814819
TRACE_STACK_POP();
815820
/* Set the operand to the function or code object returned to,
816821
* to assist optimization passes. (See _PUSH_FRAME below.)

Python/optimizer_bytecodes.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,9 @@ dummy_func(void) {
641641
op(_POP_FRAME, (retval -- res)) {
642642
SYNC_SP();
643643
ctx->frame->stack_pointer = stack_pointer;
644-
frame_pop(ctx);
644+
if (frame_pop(ctx)) {
645+
goto done;
646+
}
645647
stack_pointer = ctx->frame->stack_pointer;
646648
res = retval;
647649

@@ -663,7 +665,9 @@ dummy_func(void) {
663665
op(_RETURN_GENERATOR, ( -- res)) {
664666
SYNC_SP();
665667
ctx->frame->stack_pointer = stack_pointer;
666-
frame_pop(ctx);
668+
if (frame_pop(ctx)) {
669+
goto done;
670+
}
667671
stack_pointer = ctx->frame->stack_pointer;
668672
OUT_OF_SPACE_IF_NULL(res = sym_new_unknown(ctx));
669673

Python/optimizer_cases.c.h

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_symbols.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,9 +384,11 @@ _Py_uop_frame_pop(_Py_UOpsContext *ctx)
384384
_Py_UOpsAbstractFrame *frame = ctx->frame;
385385
ctx->n_consumed = frame->locals;
386386
ctx->curr_frame_depth--;
387-
assert(ctx->curr_frame_depth >= 1);
387+
if (ctx->curr_frame_depth == 0) {
388+
ctx->frame = NULL;
389+
return -1;
390+
}
388391
ctx->frame = &ctx->frames[ctx->curr_frame_depth - 1];
389-
390392
return 0;
391393
}
392394

Python/specialize.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2357,7 +2357,8 @@ _Py_Specialize_ForIter(PyObject *iter, _Py_CODEUNIT *instr, int oparg)
23572357
}
23582358
else if (tp == &PyGen_Type && oparg <= SHRT_MAX) {
23592359
assert(instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == END_FOR ||
2360-
instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR
2360+
instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == INSTRUMENTED_END_FOR ||
2361+
instr[oparg + INLINE_CACHE_ENTRIES_FOR_ITER + 1].op.code == ENTER_EXECUTOR
23612362
);
23622363
if (_PyInterpreterState_GET()->eval_frame) {
23632364
SPECIALIZATION_FAIL(FOR_ITER, SPEC_FAIL_OTHER);

0 commit comments

Comments
 (0)