Skip to content

Commit cabd6e8

Browse files
authored
gh-106529: Support JUMP_BACKWARD in Tier 2 (uops) (#106543)
During superblock generation, a JUMP_BACKWARD instruction is translated to either a JUMP_TO_TOP micro-op (when the target of the jump is exactly the beginning of the superblock, closing the loop), or a SAVE_IP + EXIT_TRACE pair, when the jump goes elsewhere. The new JUMP_TO_TOP instruction includes a CHECK_EVAL_BREAKER() call, so a closed loop can still be interrupted.
1 parent 292ac4b commit cabd6e8

File tree

5 files changed

+63
-30
lines changed

5 files changed

+63
-30
lines changed

Lib/test/test_capi/test_misc.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,6 @@ def testfunc(n):
25252525
i += 1
25262526

25272527
opt = _testinternalcapi.get_uop_optimizer()
2528-
25292528
with temporary_optimizer(opt):
25302529
testfunc(10)
25312530

@@ -2541,7 +2540,6 @@ def testfunc(n):
25412540
i += 1
25422541

25432542
opt = _testinternalcapi.get_uop_optimizer()
2544-
25452543
with temporary_optimizer(opt):
25462544
testfunc(10)
25472545

@@ -2550,6 +2548,20 @@ def testfunc(n):
25502548
uops = {opname for opname, _ in ex}
25512549
self.assertIn("_POP_JUMP_IF_TRUE", uops)
25522550

2551+
def test_jump_backward(self):
2552+
def testfunc(n):
2553+
i = 0
2554+
while i < n:
2555+
i += 1
2556+
opt = _testinternalcapi.get_uop_optimizer()
2557+
with temporary_optimizer(opt):
2558+
testfunc(10)
2559+
2560+
ex = get_first_executor(testfunc)
2561+
self.assertIsNotNone(ex)
2562+
uops = {opname for opname, _ in ex}
2563+
self.assertIn("JUMP_TO_TOP", uops)
2564+
25532565

25542566
if __name__ == "__main__":
25552567
unittest.main()

Python/ceval.c

+7
Original file line numberDiff line numberDiff line change
@@ -2783,6 +2783,13 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject
27832783
break;
27842784
}
27852785

2786+
case JUMP_TO_TOP:
2787+
{
2788+
pc = 0;
2789+
CHECK_EVAL_BREAKER();
2790+
break;
2791+
}
2792+
27862793
case SAVE_IP:
27872794
{
27882795
frame->prev_instr = ip_offset + oparg;

Python/opcode_metadata.h

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

Python/optimizer.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,7 @@ translate_bytecode_to_trace(
372372
_PyUOpInstruction *trace,
373373
int buffer_size)
374374
{
375-
#ifdef Py_DEBUG
376375
_Py_CODEUNIT *initial_instr = instr;
377-
#endif
378376
int trace_length = 0;
379377
int max_length = buffer_size;
380378

@@ -456,6 +454,19 @@ translate_bytecode_to_trace(
456454
break;
457455
}
458456

457+
case JUMP_BACKWARD:
458+
{
459+
if (instr + 2 - oparg == initial_instr
460+
&& trace_length + 3 <= max_length)
461+
{
462+
ADD_TO_TRACE(JUMP_TO_TOP, 0);
463+
}
464+
else {
465+
DPRINTF(2, "JUMP_BACKWARD not to top ends trace\n");
466+
}
467+
goto done;
468+
}
469+
459470
default:
460471
{
461472
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];

Tools/cases_generator/generate_cases.py

+1
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,7 @@ def add(name: str) -> None:
13461346
add("SAVE_IP")
13471347
add("_POP_JUMP_IF_FALSE")
13481348
add("_POP_JUMP_IF_TRUE")
1349+
add("JUMP_TO_TOP")
13491350

13501351
for instr in self.instrs.values():
13511352
if instr.kind == "op" and instr.is_viable_uop():

0 commit comments

Comments
 (0)