]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-106529: Support JUMP_BACKWARD in Tier 2 (uops) (#106543)
authorGuido van Rossum <guido@python.org>
Tue, 11 Jul 2023 18:08:10 +0000 (11:08 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Jul 2023 18:08:10 +0000 (18:08 +0000)
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.

Lib/test/test_capi/test_misc.py
Python/ceval.c
Python/opcode_metadata.h
Python/optimizer.c
Tools/cases_generator/generate_cases.py

index fd27bd06097f6a24ba3787a9a80e7a0d1a62facf..9cbd5506bf6dea5c2a5b51cbbad0a71fea9f9c2e 100644 (file)
@@ -2525,7 +2525,6 @@ class TestUops(unittest.TestCase):
                 i += 1
 
         opt = _testinternalcapi.get_uop_optimizer()
-
         with temporary_optimizer(opt):
             testfunc(10)
 
@@ -2541,7 +2540,6 @@ class TestUops(unittest.TestCase):
                 i += 1
 
         opt = _testinternalcapi.get_uop_optimizer()
-
         with temporary_optimizer(opt):
             testfunc(10)
 
@@ -2550,6 +2548,20 @@ class TestUops(unittest.TestCase):
         uops = {opname for opname, _ in ex}
         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__":
     unittest.main()
index 866acd2dd69c7e4b60749e09ad5cae1e567259dc..57e478c1313f6425eef3f81d96930a32a28d18a7 100644 (file)
@@ -2783,6 +2783,13 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject
                 break;
             }
 
+            case JUMP_TO_TOP:
+            {
+                pc = 0;
+                CHECK_EVAL_BREAKER();
+                break;
+            }
+
             case SAVE_IP:
             {
                 frame->prev_instr = ip_offset + oparg;
index 34ac85d6517a88bebc32f4b42f2c09feac545baa..4a41cd86a4287beb4bd4a7a1471bfa9dcc0b01ad 100644 (file)
 #define SAVE_IP 301
 #define _POP_JUMP_IF_FALSE 302
 #define _POP_JUMP_IF_TRUE 303
-#define _GUARD_BOTH_INT 304
-#define _BINARY_OP_MULTIPLY_INT 305
-#define _BINARY_OP_ADD_INT 306
-#define _BINARY_OP_SUBTRACT_INT 307
-#define _GUARD_BOTH_FLOAT 308
-#define _BINARY_OP_MULTIPLY_FLOAT 309
-#define _BINARY_OP_ADD_FLOAT 310
-#define _BINARY_OP_SUBTRACT_FLOAT 311
-#define _GUARD_BOTH_UNICODE 312
-#define _BINARY_OP_ADD_UNICODE 313
-#define _LOAD_LOCALS 314
-#define _LOAD_FROM_DICT_OR_GLOBALS 315
-#define IS_NONE 316
+#define JUMP_TO_TOP 304
+#define _GUARD_BOTH_INT 305
+#define _BINARY_OP_MULTIPLY_INT 306
+#define _BINARY_OP_ADD_INT 307
+#define _BINARY_OP_SUBTRACT_INT 308
+#define _GUARD_BOTH_FLOAT 309
+#define _BINARY_OP_MULTIPLY_FLOAT 310
+#define _BINARY_OP_ADD_FLOAT 311
+#define _BINARY_OP_SUBTRACT_FLOAT 312
+#define _GUARD_BOTH_UNICODE 313
+#define _BINARY_OP_ADD_UNICODE 314
+#define _LOAD_LOCALS 315
+#define _LOAD_FROM_DICT_OR_GLOBALS 316
+#define IS_NONE 317
 
 #ifndef NEED_OPCODE_METADATA
 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",
     [302] = "_POP_JUMP_IF_FALSE",
     [303] = "_POP_JUMP_IF_TRUE",
-    [304] = "_GUARD_BOTH_INT",
-    [305] = "_BINARY_OP_MULTIPLY_INT",
-    [306] = "_BINARY_OP_ADD_INT",
-    [307] = "_BINARY_OP_SUBTRACT_INT",
-    [308] = "_GUARD_BOTH_FLOAT",
-    [309] = "_BINARY_OP_MULTIPLY_FLOAT",
-    [310] = "_BINARY_OP_ADD_FLOAT",
-    [311] = "_BINARY_OP_SUBTRACT_FLOAT",
-    [312] = "_GUARD_BOTH_UNICODE",
-    [313] = "_BINARY_OP_ADD_UNICODE",
-    [314] = "_LOAD_LOCALS",
-    [315] = "_LOAD_FROM_DICT_OR_GLOBALS",
-    [316] = "IS_NONE",
+    [304] = "JUMP_TO_TOP",
+    [305] = "_GUARD_BOTH_INT",
+    [306] = "_BINARY_OP_MULTIPLY_INT",
+    [307] = "_BINARY_OP_ADD_INT",
+    [308] = "_BINARY_OP_SUBTRACT_INT",
+    [309] = "_GUARD_BOTH_FLOAT",
+    [310] = "_BINARY_OP_MULTIPLY_FLOAT",
+    [311] = "_BINARY_OP_ADD_FLOAT",
+    [312] = "_BINARY_OP_SUBTRACT_FLOAT",
+    [313] = "_GUARD_BOTH_UNICODE",
+    [314] = "_BINARY_OP_ADD_UNICODE",
+    [315] = "_LOAD_LOCALS",
+    [316] = "_LOAD_FROM_DICT_OR_GLOBALS",
+    [317] = "IS_NONE",
 };
 #endif // NEED_OPCODE_METADATA
index 08073193c0228f7bb27b7672e8db0792b26e8177..8d4162b44c2f35c004c111d873cba9fe8715ba8a 100644 (file)
@@ -372,9 +372,7 @@ translate_bytecode_to_trace(
     _PyUOpInstruction *trace,
     int buffer_size)
 {
-#ifdef Py_DEBUG
     _Py_CODEUNIT *initial_instr = instr;
-#endif
     int trace_length = 0;
     int max_length = buffer_size;
 
@@ -456,6 +454,19 @@ translate_bytecode_to_trace(
                 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:
             {
                 const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];
index 641a327b06aa22bc3cdf98c61ce7ef6080ea9eb3..8c77c1f08335dfbcc859bbd96fffc3fa4f75b100 100644 (file)
@@ -1346,6 +1346,7 @@ class Analyzer:
         add("SAVE_IP")
         add("_POP_JUMP_IF_FALSE")
         add("_POP_JUMP_IF_TRUE")
+        add("JUMP_TO_TOP")
 
         for instr in self.instrs.values():
             if instr.kind == "op" and instr.is_viable_uop():