Skip to content

Commit 0aa8d5c

Browse files
authored
bpo-47120: make JUMP_NO_INTERRUPT relative (GH-32221)
1 parent 32091df commit 0aa8d5c

File tree

11 files changed

+56
-46
lines changed

11 files changed

+56
-46
lines changed

Doc/library/dis.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,14 @@ iterations of the loop.
925925

926926
.. opcode:: JUMP_BACKWARD (delta)
927927

928-
Decrements bytecode counter by *delta*.
928+
Decrements bytecode counter by *delta*. Checks for interrupts.
929+
930+
.. versionadded:: 3.11
931+
932+
933+
.. opcode:: JUMP_BACKWARD_NO_INTERRUPT (delta)
934+
935+
Decrements bytecode counter by *delta*. Does not check for interrupts.
929936

930937
.. versionadded:: 3.11
931938

@@ -974,13 +981,6 @@ iterations of the loop.
974981
.. versionadded:: 3.1
975982

976983

977-
.. opcode:: JUMP_NO_INTERRUPT (target)
978-
979-
Set bytecode counter to *target*. Do not check for interrupts.
980-
981-
.. versionadded:: 3.11
982-
983-
984984
.. opcode:: FOR_ITER (delta)
985985

986986
TOS is an :term:`iterator`. Call its :meth:`~iterator.__next__` method. If

Doc/whatsnew/3.11.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ CPython bytecode changes
528528

529529
* Replaced :opcode:`JUMP_ABSOLUTE` by the relative :opcode:`JUMP_BACKWARD`.
530530

531+
* Added :opcode:`JUMP_BACKWARD_NO_INTERRUPT`, which is used in certain loops where it is undesirable to handle interrupts.
532+
531533
Deprecated
532534
==========
533535

Include/opcode.h

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ def _write_atomic(path, data, mode=0o666):
398398
# Python 3.11a6 3488 (LOAD_GLOBAL can push additional NULL)
399399
# Python 3.11a6 3489 (Add JUMP_BACKWARD, remove JUMP_ABSOLUTE)
400400
# Python 3.11a6 3490 (remove JUMP_IF_NOT_EXC_MATCH, add CHECK_EXC_MATCH)
401-
# Python 3.11a6 3491 (remove JUMP_IF_NOT_EG_MATCH, add CHECK_EG_MATCH)
401+
# Python 3.11a6 3491 (remove JUMP_IF_NOT_EG_MATCH, add CHECK_EG_MATCH,
402+
# add JUMP_BACKWARD_NO_INTERRUPT, make JUMP_NO_INTERRUPT virtual)
402403

403404
# Python 3.12 will start with magic number 3500
404405

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def jabs_op(name, op, entries=0):
154154
def_op('GET_AWAITABLE', 131)
155155
def_op('MAKE_FUNCTION', 132) # Flags
156156
def_op('BUILD_SLICE', 133) # Number of items
157-
jabs_op('JUMP_NO_INTERRUPT', 134) # Target byte offset from beginning of code
157+
jrel_op('JUMP_BACKWARD_NO_INTERRUPT', 134) # Number of words to skip (backwards)
158158
def_op('MAKE_CELL', 135)
159159
hasfree.append(135)
160160
def_op('LOAD_CLOSURE', 136)

Lib/test/test_dis.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,8 @@ def test_boundaries(self):
684684
def test_widths(self):
685685
for opcode, opname in enumerate(dis.opname):
686686
if opname in ('BUILD_MAP_UNPACK_WITH_CALL',
687-
'BUILD_TUPLE_UNPACK_WITH_CALL'):
687+
'BUILD_TUPLE_UNPACK_WITH_CALL',
688+
'JUMP_BACKWARD_NO_INTERRUPT'):
688689
continue
689690
with self.subTest(opname=opname):
690691
width = dis._OPNAME_WIDTH
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Replace the absolute jump opcode :opcode:`JUMP_NO_INTERRUPT` by the relative :opcode:`JUMP_BACKWARD_NO_INTERRUPT`.

Objects/frameobject.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,15 +229,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
229229
stacks[i+1] = next_stack;
230230
break;
231231
}
232-
case JUMP_NO_INTERRUPT:
233-
j = get_arg(code, i);
234-
assert(j < len);
235-
if (stacks[j] == UNINITIALIZED && j < i) {
236-
todo = 1;
237-
}
238-
assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
239-
stacks[j] = next_stack;
240-
break;
241232
case POP_EXCEPT:
242233
next_stack = pop_value(pop_value(pop_value(next_stack)));
243234
stacks[i+1] = next_stack;
@@ -256,8 +247,13 @@ mark_stacks(PyCodeObject *code_obj, int len)
256247
stacks[j] = next_stack;
257248
break;
258249
case JUMP_BACKWARD:
250+
case JUMP_BACKWARD_NO_INTERRUPT:
259251
j = i + 1 - get_arg(code, i);
260252
assert(j >= 0);
253+
assert(j < len);
254+
if (stacks[j] == UNINITIALIZED && j < i) {
255+
todo = 1;
256+
}
261257
assert(stacks[j] == UNINITIALIZED || stacks[j] == next_stack);
262258
stacks[j] = next_stack;
263259
break;

Python/ceval.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4045,13 +4045,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
40454045
DISPATCH();
40464046
}
40474047

4048-
TARGET(JUMP_NO_INTERRUPT) {
4048+
TARGET(JUMP_BACKWARD_NO_INTERRUPT) {
40494049
/* This bytecode is used in the `yield from` or `await` loop.
40504050
* If there is an interrupt, we want it handled in the innermost
40514051
* generator or coroutine, so we deliberately do not check it here.
40524052
* (see bpo-30039).
40534053
*/
4054-
JUMPTO(oparg);
4054+
JUMPBY(-oparg);
40554055
DISPATCH();
40564056
}
40574057

Python/compile.c

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,23 @@
7777
#define SETUP_WITH -3
7878
#define POP_BLOCK -4
7979
#define JUMP -5
80+
#define JUMP_NO_INTERRUPT -6
8081

81-
#define MIN_VIRTUAL_OPCODE -5
82+
#define MIN_VIRTUAL_OPCODE -6
8283
#define MAX_ALLOWED_OPCODE 254
8384

8485
#define IS_WITHIN_OPCODE_RANGE(opcode) \
8586
((opcode) >= MIN_VIRTUAL_OPCODE && (opcode) <= MAX_ALLOWED_OPCODE)
8687

8788
#define IS_VIRTUAL_OPCODE(opcode) ((opcode) < 0)
8889

90+
/* opcodes which are not emitted in codegen stage, only by the assembler */
91+
#define IS_ASSEMBLER_OPCODE(opcode) \
92+
((opcode) == JUMP_FORWARD || \
93+
(opcode) == JUMP_BACKWARD || \
94+
(opcode) == JUMP_BACKWARD_NO_INTERRUPT)
95+
96+
8997
#define IS_TOP_LEVEL_AWAIT(c) ( \
9098
(c->c_flags->cf_flags & PyCF_ALLOW_TOP_LEVEL_AWAIT) \
9199
&& (c->u->u_ste->ste_type == ModuleBlock))
@@ -1011,6 +1019,7 @@ stack_effect(int opcode, int oparg, int jump)
10111019
case JUMP_FORWARD:
10121020
case JUMP_BACKWARD:
10131021
case JUMP:
1022+
case JUMP_BACKWARD_NO_INTERRUPT:
10141023
case JUMP_NO_INTERRUPT:
10151024
return 0;
10161025

@@ -1199,6 +1208,7 @@ compiler_addop_line(struct compiler *c, int opcode, int line,
11991208
int end_line, int col_offset, int end_col_offset)
12001209
{
12011210
assert(IS_WITHIN_OPCODE_RANGE(opcode));
1211+
assert(!IS_ASSEMBLER_OPCODE(opcode));
12021212
assert(!HAS_ARG(opcode) || IS_ARTIFICIAL(opcode));
12031213

12041214
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,
14421452
EXTENDED_ARG is used for 16, 24, and 32-bit arguments. */
14431453

14441454
assert(IS_WITHIN_OPCODE_RANGE(opcode));
1455+
assert(!IS_ASSEMBLER_OPCODE(opcode));
14451456
assert(HAS_ARG(opcode));
14461457
assert(0 <= oparg && oparg <= 2147483647);
14471458

@@ -1486,6 +1497,7 @@ static int add_jump_to_block(struct compiler *c, int opcode,
14861497
basicblock *target)
14871498
{
14881499
assert(IS_WITHIN_OPCODE_RANGE(opcode));
1500+
assert(!IS_ASSEMBLER_OPCODE(opcode));
14891501
assert(HAS_ARG(opcode) || IS_VIRTUAL_OPCODE(opcode));
14901502
assert(target != NULL);
14911503

@@ -7089,8 +7101,7 @@ stackdepth(struct compiler *c)
70897101
stackdepth_push(&sp, instr->i_target, target_depth);
70907102
}
70917103
depth = new_depth;
7092-
assert(instr->i_opcode != JUMP_FORWARD);
7093-
assert(instr->i_opcode != JUMP_BACKWARD);
7104+
assert(!IS_ASSEMBLER_OPCODE(instr->i_opcode));
70947105
if (instr->i_opcode == JUMP_NO_INTERRUPT ||
70957106
instr->i_opcode == JUMP ||
70967107
instr->i_opcode == RETURN_VALUE ||
@@ -7597,15 +7608,15 @@ normalize_jumps(struct assembler *a)
75977608
continue;
75987609
}
75997610
struct instr *last = &b->b_instr[b->b_iused-1];
7600-
assert(last->i_opcode != JUMP_FORWARD);
7601-
assert(last->i_opcode != JUMP_BACKWARD);
7611+
assert(!IS_ASSEMBLER_OPCODE(last->i_opcode));
76027612
if (last->i_opcode == JUMP) {
7603-
if (last->i_target->b_visited == 0) {
7604-
last->i_opcode = JUMP_FORWARD;
7605-
}
7606-
else {
7607-
last->i_opcode = JUMP_BACKWARD;
7608-
}
7613+
bool is_forward = last->i_target->b_visited == 0;
7614+
last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD;
7615+
}
7616+
if (last->i_opcode == JUMP_NO_INTERRUPT) {
7617+
bool is_forward = last->i_target->b_visited == 0;
7618+
last->i_opcode = is_forward ?
7619+
JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT;
76097620
}
76107621
}
76117622
}
@@ -7641,11 +7652,13 @@ assemble_jump_offsets(struct assembler *a, struct compiler *c)
76417652
instr->i_oparg = instr->i_target->b_offset;
76427653
if (is_relative_jump(instr)) {
76437654
if (instr->i_oparg < bsize) {
7644-
assert(instr->i_opcode == JUMP_BACKWARD);
7655+
assert(instr->i_opcode == JUMP_BACKWARD ||
7656+
instr->i_opcode == JUMP_BACKWARD_NO_INTERRUPT);
76457657
instr->i_oparg = bsize - instr->i_oparg;
76467658
}
76477659
else {
76487660
assert(instr->i_opcode != JUMP_BACKWARD);
7661+
assert(instr->i_opcode != JUMP_BACKWARD_NO_INTERRUPT);
76497662
instr->i_oparg -= bsize;
76507663
}
76517664
}
@@ -8667,14 +8680,12 @@ optimize_basic_block(struct compiler *c, basicblock *bb, PyObject *consts)
86678680
inst->i_target = inst->i_target->b_next;
86688681
}
86698682
target = &inst->i_target->b_instr[0];
8670-
assert(target->i_opcode != JUMP_FORWARD);
8671-
assert(target->i_opcode != JUMP_BACKWARD);
8683+
assert(!IS_ASSEMBLER_OPCODE(target->i_opcode));
86728684
}
86738685
else {
86748686
target = &nop;
86758687
}
8676-
assert(inst->i_opcode != JUMP_FORWARD);
8677-
assert(inst->i_opcode != JUMP_BACKWARD);
8688+
assert(!IS_ASSEMBLER_OPCODE(inst->i_opcode));
86788689
switch (inst->i_opcode) {
86798690
/* Remove LOAD_CONST const; conditional jump */
86808691
case LOAD_CONST:
@@ -8975,8 +8986,7 @@ normalize_basic_block(basicblock *bb) {
89758986
/* Mark blocks as exit and/or nofallthrough.
89768987
Raise SystemError if CFG is malformed. */
89778988
for (int i = 0; i < bb->b_iused; i++) {
8978-
assert(bb->b_instr[i].i_opcode != JUMP_FORWARD);
8979-
assert(bb->b_instr[i].i_opcode != JUMP_BACKWARD);
8989+
assert(!IS_ASSEMBLER_OPCODE(bb->b_instr[i].i_opcode));
89808990
switch(bb->b_instr[i].i_opcode) {
89818991
case RETURN_VALUE:
89828992
case RAISE_VARARGS:
@@ -9163,8 +9173,7 @@ optimize_cfg(struct compiler *c, struct assembler *a, PyObject *consts)
91639173
for (basicblock *b = a->a_entry; b != NULL; b = b->b_next) {
91649174
if (b->b_iused > 0) {
91659175
struct instr *b_last_instr = &b->b_instr[b->b_iused - 1];
9166-
assert(b_last_instr->i_opcode != JUMP_FORWARD);
9167-
assert(b_last_instr->i_opcode != JUMP_BACKWARD);
9176+
assert(!IS_ASSEMBLER_OPCODE(b_last_instr->i_opcode));
91689177
if (b_last_instr->i_opcode == JUMP ||
91699178
b_last_instr->i_opcode == JUMP_NO_INTERRUPT) {
91709179
if (b_last_instr->i_target == b->b_next) {

Python/opcode_targets.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)