[Python-checkins] bpo-47120: make JUMP_NO_INTERRUPT relative (GH-32221)

iritkatriel webhook-mailer at python.org
Tue Apr 5 07:49:24 EDT 2022


https://github.com/python/cpython/commit/0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70
commit: 0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: iritkatriel <1055913+iritkatriel at users.noreply.github.com>
date: 2022-04-05T12:49:08+01:00
summary:

bpo-47120: make JUMP_NO_INTERRUPT relative (GH-32221)

files:
A Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst
M Doc/library/dis.rst
M Doc/whatsnew/3.11.rst
M Include/opcode.h
M Lib/importlib/_bootstrap_external.py
M Lib/opcode.py
M Lib/test/test_dis.py
M Objects/frameobject.c
M Python/ceval.c
M Python/compile.c
M Python/opcode_targets.h

diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 63d846715b165..fa0e23a6c9693 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -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
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index fae85d867f91b..d0c10a9100997 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -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
 ==========
 
diff --git a/Include/opcode.h b/Include/opcode.h
index fd49dfed27ecd..ff3ffddda2147 100644
--- a/Include/opcode.h
+++ b/Include/opcode.h
@@ -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,
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index 189547cc16a8c..45be177df76a9 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -398,7 +398,8 @@ def _write_atomic(path, data, mode=0o666):
 #     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
 
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 23d98df5061f4..97b580532cb94 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -154,7 +154,7 @@ def jabs_op(name, op, entries=0):
 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)
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 544e1350cb7f9..2f78d42cc724a 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -684,7 +684,8 @@ def test_boundaries(self):
     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
index 0000000000000..236ad94795056
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst	
@@ -0,0 +1 @@
+Replace the absolute jump opcode :opcode:`JUMP_NO_INTERRUPT` by the relative :opcode:`JUMP_BACKWARD_NO_INTERRUPT`.
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index c257c0a3b43f2..6842e62839fd1 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -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;
diff --git a/Python/ceval.c b/Python/ceval.c
index f7e08c6ee813a..9b7c42cbe4b11 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -4045,13 +4045,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
             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();
         }
 
diff --git a/Python/compile.c b/Python/compile.c
index c92b267dd03a1..f04ba9ec50f6f 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -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) \
@@ -86,6 +87,13 @@
 
 #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) {
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index e71b3e2120f7a..064aa060c8428 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -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,



More information about the Python-checkins mailing list