]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-47120: make JUMP_NO_INTERRUPT relative (GH-32221)
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Tue, 5 Apr 2022 11:49:08 +0000 (12:49 +0100)
committerGitHub <noreply@github.com>
Tue, 5 Apr 2022 11:49:08 +0000 (12:49 +0100)
Doc/library/dis.rst
Doc/whatsnew/3.11.rst
Include/opcode.h
Lib/importlib/_bootstrap_external.py
Lib/opcode.py
Lib/test/test_dis.py
Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst [new file with mode: 0644]
Objects/frameobject.c
Python/ceval.c
Python/compile.c
Python/opcode_targets.h

index 63d846715b165b1b7583408d06ca29ddd1ac392a..fa0e23a6c96939f8bd33ff4b7b921a7b7c51ed05 100644 (file)
@@ -925,7 +925,14 @@ iterations of the loop.
 
 .. opcode:: JUMP_BACKWARD (delta)
 
-   Decrements bytecode counter by *delta*.
+   Decrements bytecode counter by *delta*. Checks for interrupts.
+
+   .. versionadded:: 3.11
+
+
+.. opcode:: JUMP_BACKWARD_NO_INTERRUPT (delta)
+
+   Decrements bytecode counter by *delta*. Does not check for interrupts.
 
    .. versionadded:: 3.11
 
@@ -974,13 +981,6 @@ iterations of the loop.
    .. versionadded:: 3.1
 
 
-.. opcode:: JUMP_NO_INTERRUPT (target)
-
-   Set bytecode counter to *target*. Do not check for interrupts.
-
-   .. versionadded:: 3.11
-
-
 .. opcode:: FOR_ITER (delta)
 
    TOS is an :term:`iterator`.  Call its :meth:`~iterator.__next__` method.  If
index fae85d867f91b37b30e3e9cca17cf3c4ace83e8c..d0c10a91009970df0c529f2d33c21a65a89fef4a 100644 (file)
@@ -528,6 +528,8 @@ CPython bytecode changes
 
 * Replaced :opcode:`JUMP_ABSOLUTE` by the relative :opcode:`JUMP_BACKWARD`.
 
+* Added :opcode:`JUMP_BACKWARD_NO_INTERRUPT`, which is used in certain loops where it is undesirable to handle interrupts.
+
 Deprecated
 ==========
 
index fd49dfed27ecd2fefc41939ed2c3f23b949589a5..ff3ffddda2147f7274b3eb68bd29928efde052d4 100644 (file)
@@ -87,7 +87,7 @@ extern "C" {
 #define GET_AWAITABLE                          131
 #define MAKE_FUNCTION                          132
 #define BUILD_SLICE                            133
-#define JUMP_NO_INTERRUPT                      134
+#define JUMP_BACKWARD_NO_INTERRUPT             134
 #define MAKE_CELL                              135
 #define LOAD_CLOSURE                           136
 #define LOAD_DEREF                             137
@@ -196,7 +196,7 @@ static const uint32_t _PyOpcode_RelativeJump[8] = {
     0U,
     536870912U,
     134234112U,
-    4096U,
+    4160U,
     0U,
     0U,
     0U,
@@ -292,11 +292,11 @@ const uint8_t _PyOpcode_Deopt[256] = {
     [IMPORT_STAR] = IMPORT_STAR,
     [IS_OP] = IS_OP,
     [JUMP_BACKWARD] = JUMP_BACKWARD,
+    [JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT,
     [JUMP_BACKWARD_QUICK] = JUMP_BACKWARD,
     [JUMP_FORWARD] = JUMP_FORWARD,
     [JUMP_IF_FALSE_OR_POP] = JUMP_IF_FALSE_OR_POP,
     [JUMP_IF_TRUE_OR_POP] = JUMP_IF_TRUE_OR_POP,
-    [JUMP_NO_INTERRUPT] = JUMP_NO_INTERRUPT,
     [KW_NAMES] = KW_NAMES,
     [LIST_APPEND] = LIST_APPEND,
     [LIST_EXTEND] = LIST_EXTEND,
index 189547cc16a8c776fda2668ff140071a602964eb..45be177df76a92f0301ba80db7d4af2238c73026 100644 (file)
@@ -398,7 +398,8 @@ _code_type = type(_write_atomic.__code__)
 #     Python 3.11a6 3488 (LOAD_GLOBAL can push additional NULL)
 #     Python 3.11a6 3489 (Add JUMP_BACKWARD, remove JUMP_ABSOLUTE)
 #     Python 3.11a6 3490 (remove JUMP_IF_NOT_EXC_MATCH, add CHECK_EXC_MATCH)
-#     Python 3.11a6 3491 (remove JUMP_IF_NOT_EG_MATCH, add CHECK_EG_MATCH)
+#     Python 3.11a6 3491 (remove JUMP_IF_NOT_EG_MATCH, add CHECK_EG_MATCH,
+#                         add JUMP_BACKWARD_NO_INTERRUPT, make JUMP_NO_INTERRUPT virtual)
 
 #     Python 3.12 will start with magic number 3500
 
index 23d98df5061f430b9eda4cab3a210151e7e04829..97b580532cb94bb0b1736043226cbcd1c7d415d8 100644 (file)
@@ -154,7 +154,7 @@ def_op('RAISE_VARARGS', 130)    # Number of raise arguments (1, 2, or 3)
 def_op('GET_AWAITABLE', 131)
 def_op('MAKE_FUNCTION', 132)    # Flags
 def_op('BUILD_SLICE', 133)      # Number of items
-jabs_op('JUMP_NO_INTERRUPT', 134) # Target byte offset from beginning of code
+jrel_op('JUMP_BACKWARD_NO_INTERRUPT', 134) # Number of words to skip (backwards)
 def_op('MAKE_CELL', 135)
 hasfree.append(135)
 def_op('LOAD_CLOSURE', 136)
index 544e1350cb7f9e902413810bf4f961769c4bf0bd..2f78d42cc724a69a86c657ffa57ffece5b89b894 100644 (file)
@@ -684,7 +684,8 @@ class DisTests(DisTestBase):
     def test_widths(self):
         for opcode, opname in enumerate(dis.opname):
             if opname in ('BUILD_MAP_UNPACK_WITH_CALL',
-                          'BUILD_TUPLE_UNPACK_WITH_CALL'):
+                          'BUILD_TUPLE_UNPACK_WITH_CALL',
+                          'JUMP_BACKWARD_NO_INTERRUPT'):
                 continue
             with self.subTest(opname=opname):
                 width = dis._OPNAME_WIDTH
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst
new file mode 100644 (file)
index 0000000..236ad94
--- /dev/null
@@ -0,0 +1 @@
+Replace the absolute jump opcode :opcode:`JUMP_NO_INTERRUPT` by the relative :opcode:`JUMP_BACKWARD_NO_INTERRUPT`.
index c257c0a3b43f27f189ecfa4a86b0f95522731724..6842e62839fd1491caef25eb13a2e7c381b23cf5 100644 (file)
@@ -229,15 +229,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
                     stacks[i+1] = next_stack;
                     break;
                 }
-                case JUMP_NO_INTERRUPT:
-                    j = get_arg(code, i);
-                    assert(j < len);
-                    if (stacks[j] == UNINITIALIZED && j < i) {
-                        todo = 1;
-                    }
-                    assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
-                    stacks[j] = next_stack;
-                    break;
                 case POP_EXCEPT:
                     next_stack = pop_value(pop_value(pop_value(next_stack)));
                     stacks[i+1] = next_stack;
@@ -256,8 +247,13 @@ mark_stacks(PyCodeObject *code_obj, int len)
                     stacks[j] = next_stack;
                     break;
                 case JUMP_BACKWARD:
+                case JUMP_BACKWARD_NO_INTERRUPT:
                     j = i + 1 - get_arg(code, i);
                     assert(j >= 0);
+                    assert(j < len);
+                    if (stacks[j] == UNINITIALIZED && j < i) {
+                        todo = 1;
+                    }
                     assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
                     stacks[j] = next_stack;
                     break;
index f7e08c6ee813a648a7ddc06000d6428c2aa65fab..9b7c42cbe4b114811b2cad9976ee6c1eca8f3df5 100644 (file)
@@ -4045,13 +4045,13 @@ handle_eval_breaker:
             DISPATCH();
         }
 
-        TARGET(JUMP_NO_INTERRUPT) {
+        TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
             /* This bytecode is used in the `yield from` or `await` loop.
              * If there is an interrupt, we want it handled in the innermost
              * generator or coroutine, so we deliberately do not check it here.
              * (see bpo-30039).
              */
-            JUMPTO(oparg);
+            JUMPBY(-oparg);
             DISPATCH();
         }
 
index c92b267dd03a15c2566f189ba08828831caf3d43..f04ba9ec50f6fdf373684d9e247345cb1e480a0b 100644 (file)
@@ -77,8 +77,9 @@
 #define SETUP_WITH -3
 #define POP_BLOCK -4
 #define JUMP -5
+#define JUMP_NO_INTERRUPT -6
 
-#define MIN_VIRTUAL_OPCODE -5
+#define MIN_VIRTUAL_OPCODE -6
 #define MAX_ALLOWED_OPCODE 254
 
 #define IS_WITHIN_OPCODE_RANGE(opcode) \
 
 #define IS_VIRTUAL_OPCODE(opcode) ((opcode) < 0)
 
+/* opcodes which are not emitted in codegen stage, only by the assembler */
+#define IS_ASSEMBLER_OPCODE(opcode) \
+        ((opcode) == JUMP_FORWARD || \
+         (opcode) == JUMP_BACKWARD || \
+         (opcode) == JUMP_BACKWARD_NO_INTERRUPT)
+
+
 #define IS_TOP_LEVEL_AWAIT(c) ( \
         (c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
         && (c->u->u_ste->ste_type == ModuleBlock))
@@ -1011,6 +1019,7 @@ stack_effect(int opcode, int oparg, int jump)
         case JUMP_FORWARD:
         case JUMP_BACKWARD:
         case JUMP:
+        case JUMP_BACKWARD_NO_INTERRUPT:
         case JUMP_NO_INTERRUPT:
             return 0;
 
@@ -1199,6 +1208,7 @@ compiler_addop_line(struct compiler *c, int opcode, int line,
                     int end_line, int col_offset, int end_col_offset)
 {
     assert(IS_WITHIN_OPCODE_RANGE(opcode));
+    assert(!IS_ASSEMBLER_OPCODE(opcode));
     assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode));
 
     if (compiler_use_new_implicit_block_if_needed(c) < 0) {
@@ -1442,6 +1452,7 @@ compiler_addop_i_line(struct compiler *c, int opcode, Py_ssize_t oparg,
        EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */
 
     assert(IS_WITHIN_OPCODE_RANGE(opcode));
+    assert(!IS_ASSEMBLER_OPCODE(opcode));
     assert(HAS_ARG(opcode));
     assert(0 <= oparg && oparg <= 2147483647);
 
@@ -1486,6 +1497,7 @@ static int add_jump_to_block(struct compiler *c, int opcode,
                              basicblock *target)
 {
     assert(IS_WITHIN_OPCODE_RANGE(opcode));
+    assert(!IS_ASSEMBLER_OPCODE(opcode));
     assert(HAS_ARG(opcode) || IS_VIRTUAL_OPCODE(opcode));
     assert(target != NULL);
 
@@ -7089,8 +7101,7 @@ stackdepth(struct compiler *c)
                 stackdepth_push(&sp, instr->i_target, target_depth);
             }
             depth = new_depth;
-            assert(instr->i_opcode != JUMP_FORWARD);
-            assert(instr->i_opcode != JUMP_BACKWARD);
+            assert(!IS_ASSEMBLER_OPCODE(instr->i_opcode));
             if (instr->i_opcode == JUMP_NO_INTERRUPT ||
                 instr->i_opcode == JUMP ||
                 instr->i_opcode == RETURN_VALUE ||
@@ -7597,15 +7608,15 @@ normalize_jumps(struct assembler *a)
             continue;
         }
         struct instr *last = &b->b_instr[b->b_iused-1];
-        assert(last->i_opcode != JUMP_FORWARD);
-        assert(last->i_opcode != JUMP_BACKWARD);
+        assert(!IS_ASSEMBLER_OPCODE(last->i_opcode));
         if (last->i_opcode == JUMP) {
-            if (last->i_target->b_visited == 0) {
-                last->i_opcode = JUMP_FORWARD;
-            }
-            else {
-                last->i_opcode = JUMP_BACKWARD;
-            }
+            bool is_forward = last->i_target->b_visited == 0;
+            last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD;
+        }
+        if (last->i_opcode == JUMP_NO_INTERRUPT) {
+            bool is_forward = last->i_target->b_visited == 0;
+            last->i_opcode = is_forward ?
+                             JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT;
         }
     }
 }
@@ -7641,11 +7652,13 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
                     instr->i_oparg = instr->i_target->b_offset;
                     if (is_relative_jump(instr)) {
                         if (instr->i_oparg < bsize) {
-                            assert(instr->i_opcode == JUMP_BACKWARD);
+                            assert(instr->i_opcode == JUMP_BACKWARD ||
+                                   instr->i_opcode == JUMP_BACKWARD_NO_INTERRUPT);
                             instr->i_oparg = bsize - instr->i_oparg;
                         }
                         else {
                             assert(instr->i_opcode != JUMP_BACKWARD);
+                            assert(instr->i_opcode != JUMP_BACKWARD_NO_INTERRUPT);
                             instr->i_oparg -= bsize;
                         }
                     }
@@ -8667,14 +8680,12 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
                 inst->i_target = inst->i_target->b_next;
             }
             target = &inst->i_target->b_instr[0];
-            assert(target->i_opcode != JUMP_FORWARD);
-            assert(target->i_opcode != JUMP_BACKWARD);
+            assert(!IS_ASSEMBLER_OPCODE(target->i_opcode));
         }
         else {
             target = &nop;
         }
-        assert(inst->i_opcode != JUMP_FORWARD);
-        assert(inst->i_opcode != JUMP_BACKWARD);
+        assert(!IS_ASSEMBLER_OPCODE(inst->i_opcode));
         switch (inst->i_opcode) {
             /* Remove LOAD_CONST const; conditional jump */
             case LOAD_CONST:
@@ -8975,8 +8986,7 @@ normalize_basic_block(basicblock *bb) {
     /* Mark blocks as exit and/or nofallthrough.
      Raise SystemError if CFG is malformed. */
     for (int i = 0; i < bb->b_iused; i++) {
-        assert(bb->b_instr[i].i_opcode != JUMP_FORWARD);
-        assert(bb->b_instr[i].i_opcode != JUMP_BACKWARD);
+        assert(!IS_ASSEMBLER_OPCODE(bb->b_instr[i].i_opcode));
         switch(bb->b_instr[i].i_opcode) {
             case RETURN_VALUE:
             case RAISE_VARARGS:
@@ -9163,8 +9173,7 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
     for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
         if (b->b_iused > 0) {
             struct instr *b_last_instr = &b->b_instr[b->b_iused - 1];
-            assert(b_last_instr->i_opcode != JUMP_FORWARD);
-            assert(b_last_instr->i_opcode != JUMP_BACKWARD);
+            assert(!IS_ASSEMBLER_OPCODE(b_last_instr->i_opcode));
             if (b_last_instr->i_opcode == JUMP ||
                 b_last_instr->i_opcode == JUMP_NO_INTERRUPT) {
                 if (b_last_instr->i_target == b->b_next) {
index e71b3e2120f7a01f687a4ad46bf50ad9ea910b3a..064aa060c84284ddcd647628d3036a48840b9de9 100644 (file)
@@ -133,7 +133,7 @@ static void *opcode_targets[256] = {
     &&TARGET_GET_AWAITABLE,
     &&TARGET_MAKE_FUNCTION,
     &&TARGET_BUILD_SLICE,
-    &&TARGET_JUMP_NO_INTERRUPT,
+    &&TARGET_JUMP_BACKWARD_NO_INTERRUPT,
     &&TARGET_MAKE_CELL,
     &&TARGET_LOAD_CLOSURE,
     &&TARGET_LOAD_DEREF,