From c0453a40faaadb43d2e69767af6c9680f8689063 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 27 Jun 2022 12:24:23 +0100 Subject: [PATCH] GH-94163: Add BINARY_SLICE and STORE_SLICE instructions. (GH-94168) --- Doc/library/dis.rst | 26 +++- Include/internal/pycore_opcode.h | 58 ++++----- Include/internal/pycore_sliceobject.h | 2 + Include/opcode.h | 112 +++++++++--------- Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 2 + Lib/test/test_compile.py | 36 ++++++ ...2-06-23-12-10-39.gh-issue-94163.SqAfQq.rst | 5 + Objects/sliceobject.c | 59 +++++---- Python/ceval.c | 41 +++++++ Python/compile.c | 88 +++++++++++--- Python/opcode_targets.h | 52 ++++---- Python/specialize.c | 3 +- Tools/scripts/summarize_stats.py | 13 +- 14 files changed, 339 insertions(+), 161 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-06-23-12-10-39.gh-issue-94163.SqAfQq.rst diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst index 84712bce7fd..4ec2fb0de98 100644 --- a/Doc/library/dis.rst +++ b/Doc/library/dis.rst @@ -446,12 +446,13 @@ result back on the stack. **Binary and in-place operations** -Binary operations remove the top of the stack (TOS) and the second top-most -stack item (TOS1) from the stack. They perform the operation, and put the -result back on the stack. +In the following, TOS is the top-of-stack. +TOS1, TOS2, TOS3 are the second, thrid and fourth items on the stack, respectively. -In-place operations are like binary operations, in that they remove TOS and -TOS1, and push the result back on the stack, but the operation is done in-place +Binary operations remove the top two items from the stack (TOS and TOS1). +They perform the operation, then put the result back on the stack. + +In-place operations are like binary operations, but the operation is done in-place when TOS1 supports it, and the resulting TOS may be (but does not have to be) the original TOS1. @@ -460,6 +461,7 @@ the original TOS1. Implements the binary and in-place operators (depending on the value of *op*). + ``TOS = TOS1 op TOS``. .. versionadded:: 3.11 @@ -479,6 +481,20 @@ the original TOS1. Implements ``del TOS1[TOS]``. +.. opcode:: BINARY_SLICE + + Implements ``TOS = TOS2[TOS1:TOS]``. + + .. versionadded:: 3.12 + + +.. opcode:: STORE_SLICE + + Implements ``TOS2[TOS1:TOS] = TOS3``. + + .. versionadded:: 3.12 + + **Coroutine opcodes** .. opcode:: GET_AWAITABLE (where) diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index b9195f5c329..7e3c6420b03 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -67,6 +67,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [BINARY_OP_MULTIPLY_INT] = BINARY_OP, [BINARY_OP_SUBTRACT_FLOAT] = BINARY_OP, [BINARY_OP_SUBTRACT_INT] = BINARY_OP, + [BINARY_SLICE] = BINARY_SLICE, [BINARY_SUBSCR] = BINARY_SUBSCR, [BINARY_SUBSCR_ADAPTIVE] = BINARY_SUBSCR, [BINARY_SUBSCR_DICT] = BINARY_SUBSCR, @@ -218,6 +219,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [STORE_FAST__STORE_FAST] = STORE_FAST, [STORE_GLOBAL] = STORE_GLOBAL, [STORE_NAME] = STORE_NAME, + [STORE_SLICE] = STORE_SLICE, [STORE_SUBSCR] = STORE_SUBSCR, [STORE_SUBSCR_ADAPTIVE] = STORE_SUBSCR, [STORE_SUBSCR_DICT] = STORE_SUBSCR, @@ -251,6 +253,7 @@ const uint8_t _PyOpcode_Original[256] = { [BINARY_OP_MULTIPLY_INT] = BINARY_OP, [BINARY_OP_SUBTRACT_FLOAT] = BINARY_OP, [BINARY_OP_SUBTRACT_INT] = BINARY_OP, + [BINARY_SLICE] = BINARY_SLICE, [BINARY_SUBSCR] = BINARY_SUBSCR, [BINARY_SUBSCR_ADAPTIVE] = BINARY_SUBSCR, [BINARY_SUBSCR_DICT] = BINARY_SUBSCR, @@ -402,6 +405,7 @@ const uint8_t _PyOpcode_Original[256] = { [STORE_FAST__STORE_FAST] = STORE_FAST, [STORE_GLOBAL] = STORE_GLOBAL, [STORE_NAME] = STORE_NAME, + [STORE_SLICE] = STORE_SLICE, [STORE_SUBSCR] = STORE_SUBSCR, [STORE_SUBSCR_ADAPTIVE] = STORE_SUBSCR, [STORE_SUBSCR_DICT] = STORE_SUBSCR, @@ -450,18 +454,20 @@ static const char *const _PyOpcode_OpName[256] = { [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS", [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS", [BINARY_SUBSCR] = "BINARY_SUBSCR", + [BINARY_SLICE] = "BINARY_SLICE", + [STORE_SLICE] = "STORE_SLICE", [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS", [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", - [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", [GET_LEN] = "GET_LEN", [MATCH_MAPPING] = "MATCH_MAPPING", [MATCH_SEQUENCE] = "MATCH_SEQUENCE", [MATCH_KEYS] = "MATCH_KEYS", - [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST", + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", [PUSH_EXC_INFO] = "PUSH_EXC_INFO", [CHECK_EXC_MATCH] = "CHECK_EXC_MATCH", [CHECK_EG_MATCH] = "CHECK_EG_MATCH", + [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", + [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST", [CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O", [CALL_NO_KW_ISINSTANCE] = "CALL_NO_KW_ISINSTANCE", [CALL_NO_KW_LEN] = "CALL_NO_KW_LEN", @@ -471,46 +477,44 @@ static const char *const _PyOpcode_OpName[256] = { [CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O", [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1", [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1", - [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1", - [COMPARE_OP_ADAPTIVE] = "COMPARE_OP_ADAPTIVE", [WITH_EXCEPT_START] = "WITH_EXCEPT_START", [GET_AITER] = "GET_AITER", [GET_ANEXT] = "GET_ANEXT", [BEFORE_ASYNC_WITH] = "BEFORE_ASYNC_WITH", [BEFORE_WITH] = "BEFORE_WITH", [END_ASYNC_FOR] = "END_ASYNC_FOR", + [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1", + [COMPARE_OP_ADAPTIVE] = "COMPARE_OP_ADAPTIVE", [COMPARE_OP_FLOAT_JUMP] = "COMPARE_OP_FLOAT_JUMP", [COMPARE_OP_INT_JUMP] = "COMPARE_OP_INT_JUMP", [COMPARE_OP_STR_JUMP] = "COMPARE_OP_STR_JUMP", - [EXTENDED_ARG_QUICK] = "EXTENDED_ARG_QUICK", - [FOR_ITER_ADAPTIVE] = "FOR_ITER_ADAPTIVE", [STORE_SUBSCR] = "STORE_SUBSCR", [DELETE_SUBSCR] = "DELETE_SUBSCR", + [EXTENDED_ARG_QUICK] = "EXTENDED_ARG_QUICK", + [FOR_ITER_ADAPTIVE] = "FOR_ITER_ADAPTIVE", [FOR_ITER_LIST] = "FOR_ITER_LIST", [FOR_ITER_RANGE] = "FOR_ITER_RANGE", [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK", [LOAD_ATTR_ADAPTIVE] = "LOAD_ATTR_ADAPTIVE", - [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", - [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [GET_ITER] = "GET_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", [PRINT_EXPR] = "PRINT_EXPR", [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", - [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", - [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY", + [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", + [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", [RETURN_GENERATOR] = "RETURN_GENERATOR", + [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", + [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", - [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", - [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [LIST_TO_TUPLE] = "LIST_TO_TUPLE", [RETURN_VALUE] = "RETURN_VALUE", [IMPORT_STAR] = "IMPORT_STAR", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", - [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", @@ -537,7 +541,7 @@ static const char *const _PyOpcode_OpName[256] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", + [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [POP_JUMP_FORWARD_IF_FALSE] = "POP_JUMP_FORWARD_IF_FALSE", [POP_JUMP_FORWARD_IF_TRUE] = "POP_JUMP_FORWARD_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -545,7 +549,7 @@ static const char *const _PyOpcode_OpName[256] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", + [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -565,9 +569,9 @@ static const char *const _PyOpcode_OpName[256] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [LOAD_GLOBAL_ADAPTIVE] = "LOAD_GLOBAL_ADAPTIVE", + [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", + [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -577,36 +581,36 @@ static const char *const _PyOpcode_OpName[256] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", - [RESUME_QUICK] = "RESUME_QUICK", + [LOAD_GLOBAL_ADAPTIVE] = "LOAD_GLOBAL_ADAPTIVE", + [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", + [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", + [RESUME_QUICK] = "RESUME_QUICK", [STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE", [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", - [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", + [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", + [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_ADAPTIVE] = "STORE_SUBSCR_ADAPTIVE", - [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", - [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [POP_JUMP_BACKWARD_IF_NOT_NONE] = "POP_JUMP_BACKWARD_IF_NOT_NONE", [POP_JUMP_BACKWARD_IF_NONE] = "POP_JUMP_BACKWARD_IF_NONE", [POP_JUMP_BACKWARD_IF_FALSE] = "POP_JUMP_BACKWARD_IF_FALSE", [POP_JUMP_BACKWARD_IF_TRUE] = "POP_JUMP_BACKWARD_IF_TRUE", + [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", + [STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT", [UNPACK_SEQUENCE_ADAPTIVE] = "UNPACK_SEQUENCE_ADAPTIVE", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", - [181] = "<181>", - [182] = "<182>", [183] = "<183>", [184] = "<184>", [185] = "<185>", @@ -684,8 +688,6 @@ static const char *const _PyOpcode_OpName[256] = { #endif #define EXTRA_CASES \ - case 181: \ - case 182: \ case 183: \ case 184: \ case 185: \ diff --git a/Include/internal/pycore_sliceobject.h b/Include/internal/pycore_sliceobject.h index e81834c041e..98665c3859d 100644 --- a/Include/internal/pycore_sliceobject.h +++ b/Include/internal/pycore_sliceobject.h @@ -13,6 +13,8 @@ extern "C" { extern void _PySlice_Fini(PyInterpreterState *); +extern PyObject * +_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop); #ifdef __cplusplus } diff --git a/Include/opcode.h b/Include/opcode.h index 7a22d5257a6..66e8c9e8cf8 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -17,6 +17,8 @@ extern "C" { #define UNARY_NOT 12 #define UNARY_INVERT 15 #define BINARY_SUBSCR 25 +#define BINARY_SLICE 26 +#define STORE_SLICE 27 #define GET_LEN 30 #define MATCH_MAPPING 31 #define MATCH_SEQUENCE 32 @@ -135,61 +137,61 @@ extern "C" { #define CALL_ADAPTIVE 22 #define CALL_PY_EXACT_ARGS 23 #define CALL_PY_WITH_DEFAULTS 24 -#define CALL_BOUND_METHOD_EXACT_ARGS 26 -#define CALL_BUILTIN_CLASS 27 -#define CALL_BUILTIN_FAST_WITH_KEYWORDS 28 -#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 29 -#define CALL_NO_KW_BUILTIN_FAST 34 -#define CALL_NO_KW_BUILTIN_O 38 -#define CALL_NO_KW_ISINSTANCE 39 -#define CALL_NO_KW_LEN 40 -#define CALL_NO_KW_LIST_APPEND 41 -#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 42 -#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 43 -#define CALL_NO_KW_METHOD_DESCRIPTOR_O 44 -#define CALL_NO_KW_STR_1 45 -#define CALL_NO_KW_TUPLE_1 46 -#define CALL_NO_KW_TYPE_1 47 -#define COMPARE_OP_ADAPTIVE 48 -#define COMPARE_OP_FLOAT_JUMP 55 -#define COMPARE_OP_INT_JUMP 56 -#define COMPARE_OP_STR_JUMP 57 -#define EXTENDED_ARG_QUICK 58 -#define FOR_ITER_ADAPTIVE 59 -#define FOR_ITER_LIST 62 -#define FOR_ITER_RANGE 63 -#define JUMP_BACKWARD_QUICK 64 -#define LOAD_ATTR_ADAPTIVE 65 -#define LOAD_ATTR_CLASS 66 -#define LOAD_ATTR_INSTANCE_VALUE 67 -#define LOAD_ATTR_MODULE 72 -#define LOAD_ATTR_PROPERTY 73 -#define LOAD_ATTR_SLOT 76 -#define LOAD_ATTR_WITH_HINT 77 -#define LOAD_ATTR_METHOD_LAZY_DICT 78 -#define LOAD_ATTR_METHOD_NO_DICT 79 -#define LOAD_ATTR_METHOD_WITH_DICT 80 -#define LOAD_ATTR_METHOD_WITH_VALUES 81 -#define LOAD_CONST__LOAD_FAST 86 -#define LOAD_FAST__LOAD_CONST 113 -#define LOAD_FAST__LOAD_FAST 121 -#define LOAD_GLOBAL_ADAPTIVE 141 -#define LOAD_GLOBAL_BUILTIN 143 -#define LOAD_GLOBAL_MODULE 153 -#define RESUME_QUICK 154 -#define STORE_ATTR_ADAPTIVE 158 -#define STORE_ATTR_INSTANCE_VALUE 159 -#define STORE_ATTR_SLOT 160 -#define STORE_ATTR_WITH_HINT 161 -#define STORE_FAST__LOAD_FAST 166 -#define STORE_FAST__STORE_FAST 167 -#define STORE_SUBSCR_ADAPTIVE 168 -#define STORE_SUBSCR_DICT 169 -#define STORE_SUBSCR_LIST_INT 170 -#define UNPACK_SEQUENCE_ADAPTIVE 177 -#define UNPACK_SEQUENCE_LIST 178 -#define UNPACK_SEQUENCE_TUPLE 179 -#define UNPACK_SEQUENCE_TWO_TUPLE 180 +#define CALL_BOUND_METHOD_EXACT_ARGS 28 +#define CALL_BUILTIN_CLASS 29 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34 +#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38 +#define CALL_NO_KW_BUILTIN_FAST 39 +#define CALL_NO_KW_BUILTIN_O 40 +#define CALL_NO_KW_ISINSTANCE 41 +#define CALL_NO_KW_LEN 42 +#define CALL_NO_KW_LIST_APPEND 43 +#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44 +#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45 +#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46 +#define CALL_NO_KW_STR_1 47 +#define CALL_NO_KW_TUPLE_1 48 +#define CALL_NO_KW_TYPE_1 55 +#define COMPARE_OP_ADAPTIVE 56 +#define COMPARE_OP_FLOAT_JUMP 57 +#define COMPARE_OP_INT_JUMP 58 +#define COMPARE_OP_STR_JUMP 59 +#define EXTENDED_ARG_QUICK 62 +#define FOR_ITER_ADAPTIVE 63 +#define FOR_ITER_LIST 64 +#define FOR_ITER_RANGE 65 +#define JUMP_BACKWARD_QUICK 66 +#define LOAD_ATTR_ADAPTIVE 67 +#define LOAD_ATTR_CLASS 72 +#define LOAD_ATTR_INSTANCE_VALUE 73 +#define LOAD_ATTR_MODULE 76 +#define LOAD_ATTR_PROPERTY 77 +#define LOAD_ATTR_SLOT 78 +#define LOAD_ATTR_WITH_HINT 79 +#define LOAD_ATTR_METHOD_LAZY_DICT 80 +#define LOAD_ATTR_METHOD_NO_DICT 81 +#define LOAD_ATTR_METHOD_WITH_DICT 86 +#define LOAD_ATTR_METHOD_WITH_VALUES 113 +#define LOAD_CONST__LOAD_FAST 121 +#define LOAD_FAST__LOAD_CONST 141 +#define LOAD_FAST__LOAD_FAST 143 +#define LOAD_GLOBAL_ADAPTIVE 153 +#define LOAD_GLOBAL_BUILTIN 154 +#define LOAD_GLOBAL_MODULE 158 +#define RESUME_QUICK 159 +#define STORE_ATTR_ADAPTIVE 160 +#define STORE_ATTR_INSTANCE_VALUE 161 +#define STORE_ATTR_SLOT 166 +#define STORE_ATTR_WITH_HINT 167 +#define STORE_FAST__LOAD_FAST 168 +#define STORE_FAST__STORE_FAST 169 +#define STORE_SUBSCR_ADAPTIVE 170 +#define STORE_SUBSCR_DICT 177 +#define STORE_SUBSCR_LIST_INT 178 +#define UNPACK_SEQUENCE_ADAPTIVE 179 +#define UNPACK_SEQUENCE_LIST 180 +#define UNPACK_SEQUENCE_TUPLE 181 +#define UNPACK_SEQUENCE_TWO_TUPLE 182 #define DO_TRACING 255 #define HAS_CONST(op) (false\ diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 5c72049ffd4..6cd3538624c 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -409,6 +409,7 @@ _code_type = type(_write_atomic.__code__) # Python 3.12a1 3503 (Shrink LOAD_METHOD cache) # Python 3.12a1 3504 (Merge LOAD_METHOD back into LOAD_ATTR) # Python 3.12a1 3505 (Specialization/Cache for FOR_ITER) +# Python 3.12a1 3506 (Add BINARY_SLICE and STORE_SLICE instructions) # Python 3.13 will start with 3550 @@ -422,7 +423,7 @@ _code_type = type(_write_atomic.__code__) # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3505).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3506).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index f515aaa9fc8..c7bc12a1fba 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -66,6 +66,8 @@ def_op('UNARY_NOT', 12) def_op('UNARY_INVERT', 15) def_op('BINARY_SUBSCR', 25) +def_op('BINARY_SLICE', 26) +def_op('STORE_SLICE', 27) def_op('GET_LEN', 30) def_op('MATCH_MAPPING', 31) diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index d4357243dda..85b2d825184 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -1026,6 +1026,42 @@ if 1: for instr in dis.Bytecode(while_not_chained): self.assertNotEqual(instr.opname, "EXTENDED_ARG") + @support.cpython_only + def test_uses_slice_instructions(self): + + def check_op_count(func, op, expected): + actual = 0 + for instr in dis.Bytecode(func): + if instr.opname == op: + actual += 1 + self.assertEqual(actual, expected) + + def load(): + return x[a:b] + x [a:] + x[:b] + x[:] + + def store(): + x[a:b] = y + x [a:] = y + x[:b] = y + x[:] = y + + def long_slice(): + return x[a:b:c] + + def aug(): + x[a:b] += y + + check_op_count(load, "BINARY_SLICE", 4) + check_op_count(load, "BUILD_SLICE", 0) + check_op_count(store, "STORE_SLICE", 4) + check_op_count(store, "BUILD_SLICE", 0) + check_op_count(long_slice, "BUILD_SLICE", 1) + check_op_count(long_slice, "BINARY_SLICE", 0) + check_op_count(aug, "BINARY_SLICE", 1) + check_op_count(aug, "STORE_SLICE", 1) + check_op_count(aug, "BUILD_SLICE", 0) + + @requires_debug_ranges() class TestSourcePositions(unittest.TestCase): # Ensure that compiled code snippets have correct line and column numbers diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-23-12-10-39.gh-issue-94163.SqAfQq.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-23-12-10-39.gh-issue-94163.SqAfQq.rst new file mode 100644 index 00000000000..3f82943cbfb --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-23-12-10-39.gh-issue-94163.SqAfQq.rst @@ -0,0 +1,5 @@ +Add :opcode:`BINARY_SLICE` and :opcode:`STORE_SLICE` instructions for more efficient handling +and better specialization of slicing operations, where the slice is explicit +in the source code. + + diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 713829da574..e37623f38ba 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -110,6 +110,37 @@ void _PySlice_Fini(PyInterpreterState *interp) index is present. */ +static PySliceObject * +_PyBuildSlice_Consume2(PyObject *start, PyObject *stop, PyObject *step) +{ + assert(start != NULL && stop != NULL && step != NULL); + + PyInterpreterState *interp = _PyInterpreterState_GET(); + PySliceObject *obj; + if (interp->slice_cache != NULL) { + obj = interp->slice_cache; + interp->slice_cache = NULL; + _Py_NewReference((PyObject *)obj); + } + else { + obj = PyObject_GC_New(PySliceObject, &PySlice_Type); + if (obj == NULL) { + goto error; + } + } + + obj->start = start; + obj->stop = stop; + obj->step = Py_NewRef(step); + + _PyObject_GC_TRACK(obj); + return obj; +error: + Py_DECREF(start); + Py_DECREF(stop); + return NULL; +} + PyObject * PySlice_New(PyObject *start, PyObject *stop, PyObject *step) { @@ -122,30 +153,16 @@ PySlice_New(PyObject *start, PyObject *stop, PyObject *step) if (stop == NULL) { stop = Py_None; } - - PyInterpreterState *interp = _PyInterpreterState_GET(); - PySliceObject *obj; - if (interp->slice_cache != NULL) { - obj = interp->slice_cache; - interp->slice_cache = NULL; - _Py_NewReference((PyObject *)obj); - } - else { - obj = PyObject_GC_New(PySliceObject, &PySlice_Type); - if (obj == NULL) { - return NULL; - } - } - - Py_INCREF(step); - obj->step = step; Py_INCREF(start); - obj->start = start; Py_INCREF(stop); - obj->stop = stop; + return (PyObject *) _PyBuildSlice_Consume2(start, stop, step); +} - _PyObject_GC_TRACK(obj); - return (PyObject *) obj; +PyObject * +_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop) +{ + assert(start != NULL && stop != NULL); + return (PyObject *)_PyBuildSlice_Consume2(start, stop, Py_None); } PyObject * diff --git a/Python/ceval.c b/Python/ceval.c index 47dd1008c35..946e997fc83 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -23,6 +23,7 @@ #include "pycore_pymem.h" // _PyMem_IsPtrFreed() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_range.h" // _PyRangeIterObject +#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs #include "pycore_sysmodule.h" // _PySys_Audit() #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS @@ -2139,6 +2140,46 @@ handle_eval_breaker: DISPATCH(); } + TARGET(BINARY_SLICE) { + PyObject *stop = POP(); + PyObject *start = POP(); + PyObject *container = TOP(); + + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + if (slice == NULL) { + goto error; + } + PyObject *res = PyObject_GetItem(container, slice); + Py_DECREF(slice); + if (res == NULL) { + goto error; + } + SET_TOP(res); + Py_DECREF(container); + DISPATCH(); + } + + TARGET(STORE_SLICE) { + PyObject *stop = POP(); + PyObject *start = POP(); + PyObject *container = TOP(); + PyObject *v = SECOND(); + + PyObject *slice = _PyBuildSlice_ConsumeRefs(start, stop); + if (slice == NULL) { + goto error; + } + int err = PyObject_SetItem(container, slice, v); + Py_DECREF(slice); + if (err) { + goto error; + } + STACK_SHRINK(2); + Py_DECREF(v); + Py_DECREF(container); + DISPATCH(); + } + TARGET(BINARY_SUBSCR_ADAPTIVE) { _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { diff --git a/Python/compile.c b/Python/compile.c index 3946aac8c99..74c21fd69f8 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1030,8 +1030,12 @@ stack_effect(int opcode, int oparg, int jump) case BINARY_SUBSCR: return -1; + case BINARY_SLICE: + return -2; case STORE_SUBSCR: return -3; + case STORE_SLICE: + return -4; case DELETE_SUBSCR: return -2; @@ -5864,7 +5868,14 @@ compiler_visit_expr1(struct compiler *c, expr_ty e) } break; case Slice_kind: - return compiler_slice(c, e); + { + int n = compiler_slice(c, e); + if (n == 0) { + return 0; + } + ADDOP_I(c, BUILD_SLICE, n); + break; + } case Name_kind: return compiler_nameop(c, e->v.Name.id, e->v.Name.ctx); /* child nodes of List and Tuple will have expr_context set */ @@ -5886,6 +5897,13 @@ compiler_visit_expr(struct compiler *c, expr_ty e) return res; } +static bool +is_two_element_slice(expr_ty s) +{ + return s->kind == Slice_kind && + s->v.Slice.step == NULL; +} + static int compiler_augassign(struct compiler *c, stmt_ty s) { @@ -5906,10 +5924,21 @@ compiler_augassign(struct compiler *c, stmt_ty s) break; case Subscript_kind: VISIT(c, expr, e->v.Subscript.value); - VISIT(c, expr, e->v.Subscript.slice); - ADDOP_I(c, COPY, 2); - ADDOP_I(c, COPY, 2); - ADDOP(c, BINARY_SUBSCR); + if (is_two_element_slice(e->v.Subscript.slice)) { + if (!compiler_slice(c, e->v.Subscript.slice)) { + return 0; + } + ADDOP_I(c, COPY, 3); + ADDOP_I(c, COPY, 3); + ADDOP_I(c, COPY, 3); + ADDOP(c, BINARY_SLICE); + } + else { + VISIT(c, expr, e->v.Subscript.slice); + ADDOP_I(c, COPY, 2); + ADDOP_I(c, COPY, 2); + ADDOP(c, BINARY_SUBSCR); + } break; case Name_kind: if (!compiler_nameop(c, e->v.Name.id, Load)) @@ -5936,9 +5965,17 @@ compiler_augassign(struct compiler *c, stmt_ty s) ADDOP_NAME(c, STORE_ATTR, e->v.Attribute.attr, names); break; case Subscript_kind: - ADDOP_I(c, SWAP, 3); - ADDOP_I(c, SWAP, 2); - ADDOP(c, STORE_SUBSCR); + if (is_two_element_slice(e->v.Subscript.slice)) { + ADDOP_I(c, SWAP, 4); + ADDOP_I(c, SWAP, 3); + ADDOP_I(c, SWAP, 2); + ADDOP(c, STORE_SLICE); + } + else { + ADDOP_I(c, SWAP, 3); + ADDOP_I(c, SWAP, 2); + ADDOP(c, STORE_SUBSCR); + } break; case Name_kind: return compiler_nameop(c, e->v.Name.id, Store); @@ -6146,18 +6183,34 @@ compiler_subscript(struct compiler *c, expr_ty e) } } - switch (ctx) { - case Load: op = BINARY_SUBSCR; break; - case Store: op = STORE_SUBSCR; break; - case Del: op = DELETE_SUBSCR; break; - } - assert(op); VISIT(c, expr, e->v.Subscript.value); - VISIT(c, expr, e->v.Subscript.slice); - ADDOP(c, op); + if (is_two_element_slice(e->v.Subscript.slice) && ctx != Del) { + if (!compiler_slice(c, e->v.Subscript.slice)) { + return 0; + } + if (ctx == Load) { + ADDOP(c, BINARY_SLICE); + } + else { + assert(ctx == Store); + ADDOP(c, STORE_SLICE); + } + } + else { + VISIT(c, expr, e->v.Subscript.slice); + switch (ctx) { + case Load: op = BINARY_SUBSCR; break; + case Store: op = STORE_SUBSCR; break; + case Del: op = DELETE_SUBSCR; break; + } + assert(op); + ADDOP(c, op); + } return 1; } +/* Returns the number of the values emitted, + * thus are needed to build the slice, or 0 if there is an error. */ static int compiler_slice(struct compiler *c, expr_ty s) { @@ -6183,8 +6236,7 @@ compiler_slice(struct compiler *c, expr_ty s) n++; VISIT(c, expr, s->v.Slice.step); } - ADDOP_I(c, BUILD_SLICE, n); - return 1; + return n; } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 8a6f1cdbbbb..c9f65edfad1 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -25,18 +25,20 @@ static void *opcode_targets[256] = { &&TARGET_CALL_PY_EXACT_ARGS, &&TARGET_CALL_PY_WITH_DEFAULTS, &&TARGET_BINARY_SUBSCR, + &&TARGET_BINARY_SLICE, + &&TARGET_STORE_SLICE, &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, &&TARGET_CALL_BUILTIN_CLASS, - &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, - &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_GET_LEN, &&TARGET_MATCH_MAPPING, &&TARGET_MATCH_SEQUENCE, &&TARGET_MATCH_KEYS, - &&TARGET_CALL_NO_KW_BUILTIN_FAST, + &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, &&TARGET_PUSH_EXC_INFO, &&TARGET_CHECK_EXC_MATCH, &&TARGET_CHECK_EG_MATCH, + &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, + &&TARGET_CALL_NO_KW_BUILTIN_FAST, &&TARGET_CALL_NO_KW_BUILTIN_O, &&TARGET_CALL_NO_KW_ISINSTANCE, &&TARGET_CALL_NO_KW_LEN, @@ -46,46 +48,44 @@ static void *opcode_targets[256] = { &&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O, &&TARGET_CALL_NO_KW_STR_1, &&TARGET_CALL_NO_KW_TUPLE_1, - &&TARGET_CALL_NO_KW_TYPE_1, - &&TARGET_COMPARE_OP_ADAPTIVE, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, &&TARGET_BEFORE_ASYNC_WITH, &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, + &&TARGET_CALL_NO_KW_TYPE_1, + &&TARGET_COMPARE_OP_ADAPTIVE, &&TARGET_COMPARE_OP_FLOAT_JUMP, &&TARGET_COMPARE_OP_INT_JUMP, &&TARGET_COMPARE_OP_STR_JUMP, - &&TARGET_EXTENDED_ARG_QUICK, - &&TARGET_FOR_ITER_ADAPTIVE, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, + &&TARGET_EXTENDED_ARG_QUICK, + &&TARGET_FOR_ITER_ADAPTIVE, &&TARGET_FOR_ITER_LIST, &&TARGET_FOR_ITER_RANGE, &&TARGET_JUMP_BACKWARD_QUICK, &&TARGET_LOAD_ATTR_ADAPTIVE, - &&TARGET_LOAD_ATTR_CLASS, - &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, - &&TARGET_LOAD_ATTR_MODULE, - &&TARGET_LOAD_ATTR_PROPERTY, + &&TARGET_LOAD_ATTR_CLASS, + &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, + &&TARGET_LOAD_ATTR_MODULE, + &&TARGET_LOAD_ATTR_PROPERTY, &&TARGET_LOAD_ATTR_SLOT, &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_LOAD_ATTR_METHOD_NO_DICT, - &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, - &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, - &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, &&TARGET_ASYNC_GEN_WRAP, &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_LOAD_FAST__LOAD_CONST, + &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_POP_JUMP_FORWARD_IF_FALSE, &&TARGET_POP_JUMP_FORWARD_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_LOAD_FAST__LOAD_FAST, + &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_LOAD_GLOBAL_ADAPTIVE, + &&TARGET_LOAD_FAST__LOAD_CONST, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_LOAD_GLOBAL_BUILTIN, + &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,30 +152,32 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_LOAD_GLOBAL_MODULE, - &&TARGET_RESUME_QUICK, + &&TARGET_LOAD_GLOBAL_ADAPTIVE, + &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_LOAD_GLOBAL_MODULE, + &&TARGET_RESUME_QUICK, &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_STORE_ATTR_INSTANCE_VALUE, - &&TARGET_STORE_ATTR_SLOT, - &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, + &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_ADAPTIVE, - &&TARGET_STORE_SUBSCR_DICT, - &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_FALSE, &&TARGET_POP_JUMP_BACKWARD_IF_TRUE, + &&TARGET_STORE_SUBSCR_DICT, + &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_TUPLE, @@ -252,7 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; diff --git a/Python/specialize.c b/Python/specialize.c index 948fb328695..66cae44fa8f 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -140,7 +140,8 @@ print_spec_stats(FILE *out, OpcodeStats *stats) { /* Mark some opcodes as specializable for stats, * even though we don't specialize them yet. */ - fprintf(out, "opcode[%d].specializable : 1\n", FOR_ITER); + fprintf(out, "opcode[%d].specializable : 1\n", BINARY_SLICE); + fprintf(out, "opcode[%d].specializable : 1\n", STORE_SLICE); for (int i = 0; i < 256; i++) { if (_PyOpcode_Adaptive[i]) { fprintf(out, "opcode[%d].specializable : 1\n", i); diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py index 26e9c4ecb16..2e8261a4755 100644 --- a/Tools/scripts/summarize_stats.py +++ b/Tools/scripts/summarize_stats.py @@ -60,12 +60,13 @@ def print_specialization_stats(name, family_stats, defines): for key in ("specialization.success", "specialization.failure"): total_attempts += family_stats.get(key, 0) rows = [] - for key in ("specialization.success", "specialization.failure"): - label = key[len("specialization."):] - label = label[0].upper() + label[1:] - val = family_stats.get(key, 0) - rows.append((label, val, f"{100*val/total_attempts:0.1f}%")) - emit_table(("", "Count:", "Ratio:"), rows) + if total_attempts: + for key in ("specialization.success", "specialization.failure"): + label = key[len("specialization."):] + label = label[0].upper() + label[1:] + val = family_stats.get(key, 0) + rows.append((label, val, f"{100*val/total_attempts:0.1f}%")) + emit_table(("", "Count:", "Ratio:"), rows) total_failures = family_stats.get("specialization.failure", 0) failure_kinds = [ 0 ] * 30 for key in family_stats: