summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>2022-04-05 11:49:08 (GMT)
committerGitHub <noreply@github.com>2022-04-05 11:49:08 (GMT)
commit0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70 (patch)
treeb28edee28c5dd4cf022135e1204421c9795b3e38
parent32091df41ce6e3a71df2cf37dc74b728c0d885f2 (diff)
downloadcpython-0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70.zip
cpython-0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70.tar.gz
cpython-0aa8d5cbd89cf3b61d7e8626f3a7b9c4881dfd70.tar.bz2
bpo-47120: make JUMP_NO_INTERRUPT relative (GH-32221)
-rw-r--r--Doc/library/dis.rst16
-rw-r--r--Doc/whatsnew/3.11.rst2
-rw-r--r--Include/opcode.h6
-rw-r--r--Lib/importlib/_bootstrap_external.py3
-rw-r--r--Lib/opcode.py2
-rw-r--r--Lib/test/test_dis.py3
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-03-31-21-43-57.bpo-47120.NgxQbA.rst1
-rw-r--r--Objects/frameobject.c14
-rw-r--r--Python/ceval.c4
-rw-r--r--Python/compile.c49
-rw-r--r--Python/opcode_targets.h2
11 files changed, 56 insertions, 46 deletions
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 63d8467..fa0e23a 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 fae85d8..d0c10a9 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 fd49dfe..ff3ffdd 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 189547c..45be177 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -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
diff --git a/Lib/opcode.py b/Lib/opcode.py
index 23d98df..97b5805 100644
--- a/Lib/opcode.py
+++ b/Lib/opcode.py
@@ -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)
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 544e135..2f78d42 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -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
index 0000000..236ad94
--- /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 c257c0a..6842e62 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 f7e08c6..9b7c42c 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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();
}
diff --git a/Python/compile.c b/Python/compile.c
index c92b267..f04ba9e 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 e71b3e2..064aa06 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,