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.
This commit is contained in:
Guido van Rossum 2023-07-11 11:08:10 -07:00 committed by GitHub
parent 292ac4bfe9
commit cabd6e8a10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 30 deletions

View file

@ -2525,7 +2525,6 @@ class TestUops(unittest.TestCase):
i += 1 i += 1
opt = _testinternalcapi.get_uop_optimizer() opt = _testinternalcapi.get_uop_optimizer()
with temporary_optimizer(opt): with temporary_optimizer(opt):
testfunc(10) testfunc(10)
@ -2541,7 +2540,6 @@ class TestUops(unittest.TestCase):
i += 1 i += 1
opt = _testinternalcapi.get_uop_optimizer() opt = _testinternalcapi.get_uop_optimizer()
with temporary_optimizer(opt): with temporary_optimizer(opt):
testfunc(10) testfunc(10)
@ -2550,6 +2548,20 @@ class TestUops(unittest.TestCase):
uops = {opname for opname, _ in ex} uops = {opname for opname, _ in ex}
self.assertIn("_POP_JUMP_IF_TRUE", uops) self.assertIn("_POP_JUMP_IF_TRUE", uops)
def test_jump_backward(self):
def testfunc(n):
i = 0
while i < n:
i += 1
opt = _testinternalcapi.get_uop_optimizer()
with temporary_optimizer(opt):
testfunc(10)
ex = get_first_executor(testfunc)
self.assertIsNotNone(ex)
uops = {opname for opname, _ in ex}
self.assertIn("JUMP_TO_TOP", uops)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -2783,6 +2783,13 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject
break; break;
} }
case JUMP_TO_TOP:
{
pc = 0;
CHECK_EVAL_BREAKER();
break;
}
case SAVE_IP: case SAVE_IP:
{ {
frame->prev_instr = ip_offset + oparg; frame->prev_instr = ip_offset + oparg;

View file

@ -23,19 +23,20 @@
#define SAVE_IP 301 #define SAVE_IP 301
#define _POP_JUMP_IF_FALSE 302 #define _POP_JUMP_IF_FALSE 302
#define _POP_JUMP_IF_TRUE 303 #define _POP_JUMP_IF_TRUE 303
#define _GUARD_BOTH_INT 304 #define JUMP_TO_TOP 304
#define _BINARY_OP_MULTIPLY_INT 305 #define _GUARD_BOTH_INT 305
#define _BINARY_OP_ADD_INT 306 #define _BINARY_OP_MULTIPLY_INT 306
#define _BINARY_OP_SUBTRACT_INT 307 #define _BINARY_OP_ADD_INT 307
#define _GUARD_BOTH_FLOAT 308 #define _BINARY_OP_SUBTRACT_INT 308
#define _BINARY_OP_MULTIPLY_FLOAT 309 #define _GUARD_BOTH_FLOAT 309
#define _BINARY_OP_ADD_FLOAT 310 #define _BINARY_OP_MULTIPLY_FLOAT 310
#define _BINARY_OP_SUBTRACT_FLOAT 311 #define _BINARY_OP_ADD_FLOAT 311
#define _GUARD_BOTH_UNICODE 312 #define _BINARY_OP_SUBTRACT_FLOAT 312
#define _BINARY_OP_ADD_UNICODE 313 #define _GUARD_BOTH_UNICODE 313
#define _LOAD_LOCALS 314 #define _BINARY_OP_ADD_UNICODE 314
#define _LOAD_FROM_DICT_OR_GLOBALS 315 #define _LOAD_LOCALS 315
#define IS_NONE 316 #define _LOAD_FROM_DICT_OR_GLOBALS 316
#define IS_NONE 317
#ifndef NEED_OPCODE_METADATA #ifndef NEED_OPCODE_METADATA
extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump);
@ -1298,18 +1299,19 @@ const char * const _PyOpcode_uop_name[512] = {
[301] = "SAVE_IP", [301] = "SAVE_IP",
[302] = "_POP_JUMP_IF_FALSE", [302] = "_POP_JUMP_IF_FALSE",
[303] = "_POP_JUMP_IF_TRUE", [303] = "_POP_JUMP_IF_TRUE",
[304] = "_GUARD_BOTH_INT", [304] = "JUMP_TO_TOP",
[305] = "_BINARY_OP_MULTIPLY_INT", [305] = "_GUARD_BOTH_INT",
[306] = "_BINARY_OP_ADD_INT", [306] = "_BINARY_OP_MULTIPLY_INT",
[307] = "_BINARY_OP_SUBTRACT_INT", [307] = "_BINARY_OP_ADD_INT",
[308] = "_GUARD_BOTH_FLOAT", [308] = "_BINARY_OP_SUBTRACT_INT",
[309] = "_BINARY_OP_MULTIPLY_FLOAT", [309] = "_GUARD_BOTH_FLOAT",
[310] = "_BINARY_OP_ADD_FLOAT", [310] = "_BINARY_OP_MULTIPLY_FLOAT",
[311] = "_BINARY_OP_SUBTRACT_FLOAT", [311] = "_BINARY_OP_ADD_FLOAT",
[312] = "_GUARD_BOTH_UNICODE", [312] = "_BINARY_OP_SUBTRACT_FLOAT",
[313] = "_BINARY_OP_ADD_UNICODE", [313] = "_GUARD_BOTH_UNICODE",
[314] = "_LOAD_LOCALS", [314] = "_BINARY_OP_ADD_UNICODE",
[315] = "_LOAD_FROM_DICT_OR_GLOBALS", [315] = "_LOAD_LOCALS",
[316] = "IS_NONE", [316] = "_LOAD_FROM_DICT_OR_GLOBALS",
[317] = "IS_NONE",
}; };
#endif // NEED_OPCODE_METADATA #endif // NEED_OPCODE_METADATA

View file

@ -372,9 +372,7 @@ translate_bytecode_to_trace(
_PyUOpInstruction *trace, _PyUOpInstruction *trace,
int buffer_size) int buffer_size)
{ {
#ifdef Py_DEBUG
_Py_CODEUNIT *initial_instr = instr; _Py_CODEUNIT *initial_instr = instr;
#endif
int trace_length = 0; int trace_length = 0;
int max_length = buffer_size; int max_length = buffer_size;
@ -456,6 +454,19 @@ translate_bytecode_to_trace(
break; break;
} }
case JUMP_BACKWARD:
{
if (instr + 2 - oparg == initial_instr
&& trace_length + 3 <= max_length)
{
ADD_TO_TRACE(JUMP_TO_TOP, 0);
}
else {
DPRINTF(2, "JUMP_BACKWARD not to top ends trace\n");
}
goto done;
}
default: default:
{ {
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode]; const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];

View file

@ -1346,6 +1346,7 @@ class Analyzer:
add("SAVE_IP") add("SAVE_IP")
add("_POP_JUMP_IF_FALSE") add("_POP_JUMP_IF_FALSE")
add("_POP_JUMP_IF_TRUE") add("_POP_JUMP_IF_TRUE")
add("JUMP_TO_TOP")
for instr in self.instrs.values(): for instr in self.instrs.values():
if instr.kind == "op" and instr.is_viable_uop(): if instr.kind == "op" and instr.is_viable_uop():