Skip to content

Commit b28effa

Browse files
committed
Hacky way to make FOR_ITER a viable uop
1 parent b838435 commit b28effa

File tree

8 files changed

+137
-44
lines changed

8 files changed

+137
-44
lines changed

Include/internal/pycore_opcode_metadata.h

+47-39
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/abstract_interp_cases.c.h

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/bytecodes.c

+27-2
Original file line numberDiff line numberDiff line change
@@ -2368,7 +2368,7 @@ dummy_func(
23682368
goto enter_tier_one;
23692369
}
23702370

2371-
replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) {
2371+
replaced op(_POP_JUMP_IF_FALSE, (unused/1, cond -- )) {
23722372
assert(PyBool_Check(cond));
23732373
int flag = Py_IsFalse(cond);
23742374
#if ENABLE_SPECIALIZATION
@@ -2512,7 +2512,7 @@ dummy_func(
25122512
#endif /* ENABLE_SPECIALIZATION */
25132513
}
25142514

2515-
op(_FOR_ITER, (iter -- iter, next)) {
2515+
replaced op(_FOR_ITER, (iter -- iter, next)) {
25162516
/* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
25172517
next = (*Py_TYPE(iter)->tp_iternext)(iter);
25182518
if (next == NULL) {
@@ -2535,6 +2535,31 @@ dummy_func(
25352535
// Common case: no jump, leave it to the code generator
25362536
}
25372537

2538+
op(_FOR_ITER_TIER_TWO, (iter -- iter, next)) {
2539+
/* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
2540+
next = (*Py_TYPE(iter)->tp_iternext)(iter);
2541+
if (next == NULL) {
2542+
if (_PyErr_Occurred(tstate)) {
2543+
if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
2544+
GOTO_ERROR(error);
2545+
}
2546+
_PyErr_Clear(tstate);
2547+
}
2548+
/* iterator ended normally */
2549+
Py_DECREF(iter);
2550+
STACK_SHRINK(1);
2551+
/* HACK: Emulate DEOPT_IF to jump over END_FOR */
2552+
_PyFrame_SetStackPointer(frame, stack_pointer);
2553+
frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_FOR_ITER + oparg + 1;
2554+
assert(frame->instr_ptr[-1].op.code == END_FOR ||
2555+
frame->instr_ptr[-1].op.code == INSTRUMENTED_END_FOR);
2556+
Py_DECREF(current_executor);
2557+
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
2558+
goto enter_tier_one;
2559+
}
2560+
// Common case: no jump, leave it to the code generator
2561+
}
2562+
25382563
macro(FOR_ITER) = _SPECIALIZE_FOR_ITER + _FOR_ITER;
25392564

25402565
inst(INSTRUMENTED_FOR_ITER, (unused/1 -- )) {

Python/executor_cases.c.h

+49
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

+1
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ _PyUop_Replacements[OPCODE_METADATA_SIZE] = {
392392
[_ITER_JUMP_RANGE] = _GUARD_NOT_EXHAUSTED_RANGE,
393393
[_ITER_JUMP_LIST] = _GUARD_NOT_EXHAUSTED_LIST,
394394
[_ITER_JUMP_TUPLE] = _GUARD_NOT_EXHAUSTED_TUPLE,
395+
[_FOR_ITER] = _FOR_ITER_TIER_TWO,
395396
};
396397

397398
static const uint16_t

Tools/cases_generator/flags.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def variable_used_unspecialized(node: parsing.Node, name: str) -> bool:
175175
tokens: list[lx.Token] = []
176176
skipping = False
177177
for i, token in enumerate(node.tokens):
178-
if token.kind == "MACRO":
178+
if token.kind == "CMACRO":
179179
text = "".join(token.text.split())
180180
# TODO: Handle nested #if
181181
if text == "#if":

Tools/cases_generator/generate_cases.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ def write_macro_expansions(
658658
if not part.instr.is_viable_uop() and "replaced" not in part.instr.annotations:
659659
# This note just reminds us about macros that cannot
660660
# be expanded to Tier 2 uops. It is not an error.
661-
# It is sometimes emitted for macros that have a
661+
# Suppress it using 'replaced op(...)' for macros having
662662
# manual translation in translate_bytecode_to_trace()
663663
# in Python/optimizer.c.
664664
if len(parts) > 1 or part.instr.name != name:

Tools/cases_generator/instructions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def __init__(self, inst: parsing.InstDef):
115115
def is_viable_uop(self) -> bool:
116116
"""Whether this instruction is viable as a uop."""
117117
dprint: typing.Callable[..., None] = lambda *args, **kwargs: None
118-
if "FRAME" in self.name:
118+
if self.name == "_FOR_ITER_TIER_TWO":
119119
dprint = print
120120

121121
if self.name == "_EXIT_TRACE":

0 commit comments

Comments
 (0)