From d3e197b3e05e5e1c03fc67245e0c42d2a4f5eb62 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 6 Apr 2022 10:15:46 +0100 Subject: [PATCH 1/5] add IS_VIRTUAL_JUMP_OPCODE macro --- Python/compile.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Python/compile.c b/Python/compile.c index f04ba9ec50f6fd..e8cf547f163a53 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -87,6 +87,10 @@ #define IS_VIRTUAL_OPCODE(opcode) ((opcode) < 0) +#define IS_VIRTUAL_JUMP_OPCODE(opcode) \ + ((opcode) == JUMP || \ + (opcode) == JUMP_NO_INTERRUPT) + /* opcodes which are not emitted in codegen stage, only by the assembler */ #define IS_ASSEMBLER_OPCODE(opcode) \ ((opcode) == JUMP_FORWARD || \ @@ -156,7 +160,7 @@ is_block_push(struct instr *instr) static inline int is_jump(struct instr *i) { - return i->i_opcode == JUMP || + return IS_VIRTUAL_JUMP_OPCODE(i->i_opcode) || is_bit_set_in_table(_PyOpcode_Jump, i->i_opcode); } From cf18a6d1aed8adb1c67b0f687ed68aff3a1120f5 Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Wed, 6 Apr 2022 11:44:05 +0100 Subject: [PATCH 2/5] bpo-47120: make POP_JUMP_IF_NONE/NOT_NONE relative --- Include/opcode.h | 24 ++++++----- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 6 ++- Lib/test/test_dis.py | 6 ++- Python/ceval.c | 20 +++++++-- Python/compile.c | 61 ++++++++++++++++++++-------- Python/opcode_targets.h | 8 ++-- 7 files changed, 89 insertions(+), 39 deletions(-) diff --git a/Include/opcode.h b/Include/opcode.h index ff3ffddda2147f..7a9382635a2c3b 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -81,8 +81,8 @@ extern "C" { #define LOAD_FAST 124 #define STORE_FAST 125 #define DELETE_FAST 126 -#define POP_JUMP_IF_NOT_NONE 128 -#define POP_JUMP_IF_NONE 129 +#define POP_JUMP_FORWARD_IF_NOT_NONE 128 +#define POP_JUMP_FORWARD_IF_NONE 129 #define RAISE_VARARGS 130 #define GET_AWAITABLE 131 #define MAKE_FUNCTION 132 @@ -114,6 +114,8 @@ extern "C" { #define PRECALL 166 #define CALL 171 #define KW_NAMES 172 +#define POP_JUMP_BACKWARD_IF_NOT_NONE 173 +#define POP_JUMP_BACKWARD_IF_NONE 174 #define BINARY_OP_ADAPTIVE 3 #define BINARY_OP_ADD_FLOAT 4 #define BINARY_OP_ADD_INT 5 @@ -181,9 +183,9 @@ extern "C" { #define STORE_SUBSCR_DICT 168 #define STORE_SUBSCR_LIST_INT 169 #define UNPACK_SEQUENCE_ADAPTIVE 170 -#define UNPACK_SEQUENCE_LIST 173 -#define UNPACK_SEQUENCE_TUPLE 174 -#define UNPACK_SEQUENCE_TWO_TUPLE 175 +#define UNPACK_SEQUENCE_LIST 175 +#define UNPACK_SEQUENCE_TUPLE 176 +#define UNPACK_SEQUENCE_TWO_TUPLE 177 #define DO_TRACING 255 extern const uint8_t _PyOpcode_Caches[256]; @@ -196,8 +198,8 @@ static const uint32_t _PyOpcode_RelativeJump[8] = { 0U, 536870912U, 134234112U, - 4160U, - 0U, + 4163U, + 24576U, 0U, 0U, }; @@ -207,7 +209,7 @@ static const uint32_t _PyOpcode_Jump[8] = { 536870912U, 135118848U, 4163U, - 0U, + 24576U, 0U, 0U, }; @@ -338,9 +340,11 @@ const uint8_t _PyOpcode_Deopt[256] = { [MATCH_SEQUENCE] = MATCH_SEQUENCE, [NOP] = NOP, [POP_EXCEPT] = POP_EXCEPT, + [POP_JUMP_BACKWARD_IF_NONE] = POP_JUMP_BACKWARD_IF_NONE, + [POP_JUMP_BACKWARD_IF_NOT_NONE] = POP_JUMP_BACKWARD_IF_NOT_NONE, + [POP_JUMP_FORWARD_IF_NONE] = POP_JUMP_FORWARD_IF_NONE, + [POP_JUMP_FORWARD_IF_NOT_NONE] = POP_JUMP_FORWARD_IF_NOT_NONE, [POP_JUMP_IF_FALSE] = POP_JUMP_IF_FALSE, - [POP_JUMP_IF_NONE] = POP_JUMP_IF_NONE, - [POP_JUMP_IF_NOT_NONE] = POP_JUMP_IF_NOT_NONE, [POP_JUMP_IF_TRUE] = POP_JUMP_IF_TRUE, [POP_TOP] = POP_TOP, [PRECALL] = PRECALL, diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 45be177df76a92..072a81ff961641 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -400,6 +400,7 @@ def _write_atomic(path, data, mode=0o666): # 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, # add JUMP_BACKWARD_NO_INTERRUPT, make JUMP_NO_INTERRUPT virtual) +# Python 3.11a7 3492 (make POP_JUMP_IF_NONE/NOT_NONE relative) # Python 3.12 will start with magic number 3500 @@ -414,7 +415,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3491).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3492).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _PYCACHE = '__pycache__' diff --git a/Lib/opcode.py b/Lib/opcode.py index 97b580532cb94b..615b71a66dd8aa 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -148,8 +148,8 @@ def jabs_op(name, op, entries=0): haslocal.append(125) def_op('DELETE_FAST', 126) # Local variable number haslocal.append(126) -jabs_op('POP_JUMP_IF_NOT_NONE', 128) -jabs_op('POP_JUMP_IF_NONE', 129) +jrel_op('POP_JUMP_FORWARD_IF_NOT_NONE', 128) +jrel_op('POP_JUMP_FORWARD_IF_NONE', 129) def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('GET_AWAITABLE', 131) def_op('MAKE_FUNCTION', 132) # Flags @@ -197,6 +197,8 @@ def jabs_op(name, op, entries=0): def_op('KW_NAMES', 172) hasconst.append(172) +jrel_op('POP_JUMP_BACKWARD_IF_NOT_NONE', 173) +jrel_op('POP_JUMP_BACKWARD_IF_NONE', 174) del def_op, name_op, jrel_op, jabs_op diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 2f78d42cc724a6..f13b440a5bc322 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -685,7 +685,11 @@ def test_widths(self): for opcode, opname in enumerate(dis.opname): if opname in ('BUILD_MAP_UNPACK_WITH_CALL', 'BUILD_TUPLE_UNPACK_WITH_CALL', - 'JUMP_BACKWARD_NO_INTERRUPT'): + 'JUMP_BACKWARD_NO_INTERRUPT', + 'POP_JUMP_FORWARD_IF_NONE', + 'POP_JUMP_BACKWARD_IF_NONE', + 'POP_JUMP_FORWARD_IF_NOT_NONE', + 'POP_JUMP_BACKWARD_IF_NOT_NONE'): continue with self.subTest(opname=opname): width = dis._OPNAME_WIDTH diff --git a/Python/ceval.c b/Python/ceval.c index 487e09bc664173..3ac8325e2df168 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3972,11 +3972,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DISPATCH(); } - TARGET(POP_JUMP_IF_NOT_NONE) { + TARGET(POP_JUMP_BACKWARD_IF_NOT_NONE) { + oparg = -oparg; + JUMP_TO_INSTRUCTION(POP_JUMP_FORWARD_IF_NOT_NONE); + } + + TARGET(POP_JUMP_FORWARD_IF_NOT_NONE) { + PREDICTED(POP_JUMP_FORWARD_IF_NOT_NONE); PyObject *value = POP(); if (!Py_IsNone(value)) { Py_DECREF(value); - JUMPTO(oparg); + JUMPBY(oparg); CHECK_EVAL_BREAKER(); DISPATCH(); } @@ -3984,11 +3990,17 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DISPATCH(); } - TARGET(POP_JUMP_IF_NONE) { + TARGET(POP_JUMP_BACKWARD_IF_NONE) { + oparg = -oparg; + JUMP_TO_INSTRUCTION(POP_JUMP_FORWARD_IF_NONE); + } + + TARGET(POP_JUMP_FORWARD_IF_NONE) { + PREDICTED(POP_JUMP_FORWARD_IF_NONE); PyObject *value = POP(); if (Py_IsNone(value)) { Py_DECREF(value); - JUMPTO(oparg); + JUMPBY(oparg); CHECK_EVAL_BREAKER(); DISPATCH(); } diff --git a/Python/compile.c b/Python/compile.c index e8cf547f163a53..a2bf0fbe42370f 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -78,8 +78,10 @@ #define POP_BLOCK -4 #define JUMP -5 #define JUMP_NO_INTERRUPT -6 +#define POP_JUMP_IF_NONE -7 +#define POP_JUMP_IF_NOT_NONE -8 -#define MIN_VIRTUAL_OPCODE -6 +#define MIN_VIRTUAL_OPCODE -8 #define MAX_ALLOWED_OPCODE 254 #define IS_WITHIN_OPCODE_RANGE(opcode) \ @@ -89,13 +91,25 @@ #define IS_VIRTUAL_JUMP_OPCODE(opcode) \ ((opcode) == JUMP || \ - (opcode) == JUMP_NO_INTERRUPT) + (opcode) == JUMP_NO_INTERRUPT || \ + (opcode) == POP_JUMP_IF_NONE || \ + (opcode) == POP_JUMP_IF_NOT_NONE) /* 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) + (opcode) == JUMP_BACKWARD_NO_INTERRUPT || \ + (opcode) == POP_JUMP_FORWARD_IF_NONE || \ + (opcode) == POP_JUMP_BACKWARD_IF_NONE || \ + (opcode) == POP_JUMP_FORWARD_IF_NOT_NONE || \ + (opcode) == POP_JUMP_BACKWARD_IF_NOT_NONE) + +#define IS_BACKWARDS_JUMP_OPCODE(opcode) \ + ((opcode) == JUMP_BACKWARD || \ + (opcode) == JUMP_BACKWARD_NO_INTERRUPT || \ + (opcode) == POP_JUMP_BACKWARD_IF_NONE || \ + (opcode) == POP_JUMP_BACKWARD_IF_NOT_NONE) #define IS_TOP_LEVEL_AWAIT(c) ( \ @@ -1031,10 +1045,14 @@ stack_effect(int opcode, int oparg, int jump) case JUMP_IF_FALSE_OR_POP: return jump ? 0 : -1; - case POP_JUMP_IF_FALSE: - case POP_JUMP_IF_TRUE: + case POP_JUMP_BACKWARD_IF_NONE: + case POP_JUMP_FORWARD_IF_NONE: case POP_JUMP_IF_NONE: + case POP_JUMP_BACKWARD_IF_NOT_NONE: + case POP_JUMP_FORWARD_IF_NOT_NONE: case POP_JUMP_IF_NOT_NONE: + case POP_JUMP_IF_FALSE: + case POP_JUMP_IF_TRUE: return -1; case LOAD_GLOBAL: @@ -7613,14 +7631,25 @@ normalize_jumps(struct assembler *a) } struct instr *last = &b->b_instr[b->b_iused-1]; assert(!IS_ASSEMBLER_OPCODE(last->i_opcode)); - if (last->i_opcode == JUMP) { - 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) { + if (is_jump(last)) { bool is_forward = last->i_target->b_visited == 0; - last->i_opcode = is_forward ? - JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT; + switch(last->i_opcode) { + case JUMP: + last->i_opcode = is_forward ? JUMP_FORWARD : JUMP_BACKWARD; + break; + case JUMP_NO_INTERRUPT: + last->i_opcode = is_forward ? + JUMP_FORWARD : JUMP_BACKWARD_NO_INTERRUPT; + break; + case POP_JUMP_IF_NOT_NONE: + last->i_opcode = is_forward ? + POP_JUMP_FORWARD_IF_NOT_NONE : POP_JUMP_BACKWARD_IF_NOT_NONE; + break; + case POP_JUMP_IF_NONE: + last->i_opcode = is_forward ? + POP_JUMP_FORWARD_IF_NONE : POP_JUMP_BACKWARD_IF_NONE; + break; + } } } } @@ -7656,13 +7685,11 @@ 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 || - instr->i_opcode == JUMP_BACKWARD_NO_INTERRUPT); + assert(IS_BACKWARDS_JUMP_OPCODE(instr->i_opcode)); instr->i_oparg = bsize - instr->i_oparg; } else { - assert(instr->i_opcode != JUMP_BACKWARD); - assert(instr->i_opcode != JUMP_BACKWARD_NO_INTERRUPT); + assert(!IS_BACKWARDS_JUMP_OPCODE(instr->i_opcode)); instr->i_oparg -= bsize; } } @@ -8648,7 +8675,7 @@ apply_static_swaps(basicblock *block, int i) static bool jump_thread(struct instr *inst, struct instr *target, int opcode) { - assert(!IS_VIRTUAL_OPCODE(opcode) || opcode == JUMP); + assert(!IS_VIRTUAL_OPCODE(opcode) || IS_VIRTUAL_JUMP_OPCODE(opcode)); assert(is_jump(inst)); assert(is_jump(target)); // bpo-45773: If inst->i_target == target->i_target, then nothing actually diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 064aa060c84284..a0ce7601a2cc3f 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -127,8 +127,8 @@ static void *opcode_targets[256] = { &&TARGET_STORE_FAST, &&TARGET_DELETE_FAST, &&TARGET_PRECALL_NO_KW_TYPE_1, - &&TARGET_POP_JUMP_IF_NOT_NONE, - &&TARGET_POP_JUMP_IF_NONE, + &&TARGET_POP_JUMP_FORWARD_IF_NOT_NONE, + &&TARGET_POP_JUMP_FORWARD_IF_NONE, &&TARGET_RAISE_VARARGS, &&TARGET_GET_AWAITABLE, &&TARGET_MAKE_FUNCTION, @@ -172,6 +172,8 @@ static void *opcode_targets[256] = { &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_CALL, &&TARGET_KW_NAMES, + &&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE, + &&TARGET_POP_JUMP_BACKWARD_IF_NONE, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, @@ -252,7 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; From 5a29371c16977f6e3f49e735a744ebd16a919704 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 6 Apr 2022 22:50:33 +0000 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst new file mode 100644 index 00000000000000..29aa5197f36925 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst @@ -0,0 +1 @@ +Make :opcode:`POP_JUMP_IF_TRUE` and :opcode:`POP_JUMP_IF_FALSE` virtual, mapping to new relative jump opcodes. From 31c24ed584c97e8673fe7753e0ac919b112c088d Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Wed, 6 Apr 2022 23:52:20 +0100 Subject: [PATCH 4/5] fix news --- .../Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst b/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst index 29aa5197f36925..54c9217451033e 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-04-06-22-50-31.bpo-47120.mbfHs5.rst @@ -1 +1 @@ -Make :opcode:`POP_JUMP_IF_TRUE` and :opcode:`POP_JUMP_IF_FALSE` virtual, mapping to new relative jump opcodes. +Make :opcode:`POP_JUMP_IF_NONE` and :opcode:`POP_JUMP_IF_NOT_NONE` virtual, mapping to new relative jump opcodes. From 226f14bd035f00820bcac5660b3f1e3e1841b06b Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Thu, 7 Apr 2022 13:02:31 +0100 Subject: [PATCH 5/5] separate forward/backward jump implementations --- Python/ceval.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 3ac8325e2df168..c00be2f2d4d7f2 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3973,36 +3973,42 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(POP_JUMP_BACKWARD_IF_NOT_NONE) { - oparg = -oparg; - JUMP_TO_INSTRUCTION(POP_JUMP_FORWARD_IF_NOT_NONE); + PyObject *value = POP(); + if (!Py_IsNone(value)) { + Py_DECREF(value); + JUMPBY(-oparg); + CHECK_EVAL_BREAKER(); + DISPATCH(); + } + Py_DECREF(value); + DISPATCH(); } TARGET(POP_JUMP_FORWARD_IF_NOT_NONE) { - PREDICTED(POP_JUMP_FORWARD_IF_NOT_NONE); PyObject *value = POP(); if (!Py_IsNone(value)) { - Py_DECREF(value); JUMPBY(oparg); - CHECK_EVAL_BREAKER(); - DISPATCH(); } Py_DECREF(value); DISPATCH(); } TARGET(POP_JUMP_BACKWARD_IF_NONE) { - oparg = -oparg; - JUMP_TO_INSTRUCTION(POP_JUMP_FORWARD_IF_NONE); + PyObject *value = POP(); + if (Py_IsNone(value)) { + Py_DECREF(value); + JUMPBY(-oparg); + CHECK_EVAL_BREAKER(); + DISPATCH(); + } + Py_DECREF(value); + DISPATCH(); } TARGET(POP_JUMP_FORWARD_IF_NONE) { - PREDICTED(POP_JUMP_FORWARD_IF_NONE); PyObject *value = POP(); if (Py_IsNone(value)) { - Py_DECREF(value); JUMPBY(oparg); - CHECK_EVAL_BREAKER(); - DISPATCH(); } Py_DECREF(value); DISPATCH();