From b083450f8896bb4a29ac522e4474d91c056b9f32 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 14 Jun 2022 18:36:22 +0800 Subject: [PATCH] GH-93429: Merge `LOAD_METHOD` back into `LOAD_ATTR` (GH-93430) --- Include/internal/pycore_code.h | 12 +- Include/internal/pycore_opcode.h | 80 +++--- Include/opcode.h | 61 +++-- Lib/dis.py | 5 + Lib/importlib/_bootstrap_external.py | 3 +- Lib/opcode.py | 27 +- Lib/test/test_dis.py | 6 +- ...2-06-02-08-28-55.gh-issue-93429.DZTWHx.rst | 1 + Programs/test_frozenmain.h | 69 ++--- Python/ceval.c | 240 ++++++++---------- Python/compile.c | 13 +- Python/opcode_targets.h | 48 ++-- Python/specialize.c | 136 ++++------ 13 files changed, 321 insertions(+), 380 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-06-02-08-28-55.gh-issue-93429.DZTWHx.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index c181543722f..805c82483ee 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -58,10 +58,6 @@ typedef struct { _Py_CODEUNIT index; } _PyAttrCache; -#define INLINE_CACHE_ENTRIES_LOAD_ATTR CACHE_ENTRIES(_PyAttrCache) - -#define INLINE_CACHE_ENTRIES_STORE_ATTR CACHE_ENTRIES(_PyAttrCache) - typedef struct { _Py_CODEUNIT counter; _Py_CODEUNIT type_version[2]; @@ -69,7 +65,11 @@ typedef struct { _Py_CODEUNIT descr[4]; } _PyLoadMethodCache; -#define INLINE_CACHE_ENTRIES_LOAD_METHOD CACHE_ENTRIES(_PyLoadMethodCache) + +// MUST be the max(_PyAttrCache, _PyLoadMethodCache) +#define INLINE_CACHE_ENTRIES_LOAD_ATTR CACHE_ENTRIES(_PyLoadMethodCache) + +#define INLINE_CACHE_ENTRIES_STORE_ATTR CACHE_ENTRIES(_PyAttrCache) typedef struct { _Py_CODEUNIT counter; @@ -233,8 +233,6 @@ extern int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, extern int _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name); extern int _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, _Py_CODEUNIT *instr, PyObject *name); -extern int _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, - PyObject *name); extern int _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, _Py_CODEUNIT *instr); extern int _Py_Specialize_StoreSubscr(PyObject *container, PyObject *sub, _Py_CODEUNIT *instr); extern int _Py_Specialize_Call(PyObject *callable, _Py_CODEUNIT *instr, diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index e254537da76..62db22f1952 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -45,11 +45,10 @@ const uint8_t _PyOpcode_Caches[256] = { [STORE_SUBSCR] = 1, [UNPACK_SEQUENCE] = 1, [STORE_ATTR] = 4, - [LOAD_ATTR] = 4, + [LOAD_ATTR] = 9, [COMPARE_OP] = 2, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, - [LOAD_METHOD] = 9, [CALL] = 4, }; @@ -147,7 +146,12 @@ const uint8_t _PyOpcode_Deopt[256] = { [LOAD_ASSERTION_ERROR] = LOAD_ASSERTION_ERROR, [LOAD_ATTR] = LOAD_ATTR, [LOAD_ATTR_ADAPTIVE] = LOAD_ATTR, + [LOAD_ATTR_CLASS] = LOAD_ATTR, [LOAD_ATTR_INSTANCE_VALUE] = LOAD_ATTR, + [LOAD_ATTR_METHOD_LAZY_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_NO_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_WITH_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_WITH_VALUES] = LOAD_ATTR, [LOAD_ATTR_MODULE] = LOAD_ATTR, [LOAD_ATTR_SLOT] = LOAD_ATTR, [LOAD_ATTR_WITH_HINT] = LOAD_ATTR, @@ -165,14 +169,6 @@ const uint8_t _PyOpcode_Deopt[256] = { [LOAD_GLOBAL_ADAPTIVE] = LOAD_GLOBAL, [LOAD_GLOBAL_BUILTIN] = LOAD_GLOBAL, [LOAD_GLOBAL_MODULE] = LOAD_GLOBAL, - [LOAD_METHOD] = LOAD_METHOD, - [LOAD_METHOD_ADAPTIVE] = LOAD_METHOD, - [LOAD_METHOD_CLASS] = LOAD_METHOD, - [LOAD_METHOD_LAZY_DICT] = LOAD_METHOD, - [LOAD_METHOD_MODULE] = LOAD_METHOD, - [LOAD_METHOD_NO_DICT] = LOAD_METHOD, - [LOAD_METHOD_WITH_DICT] = LOAD_METHOD, - [LOAD_METHOD_WITH_VALUES] = LOAD_METHOD, [LOAD_NAME] = LOAD_NAME, [MAKE_CELL] = MAKE_CELL, [MAKE_FUNCTION] = MAKE_FUNCTION, @@ -330,7 +326,12 @@ const uint8_t _PyOpcode_Original[256] = { [LOAD_ASSERTION_ERROR] = LOAD_ASSERTION_ERROR, [LOAD_ATTR] = LOAD_ATTR, [LOAD_ATTR_ADAPTIVE] = LOAD_ATTR, + [LOAD_ATTR_CLASS] = LOAD_ATTR, [LOAD_ATTR_INSTANCE_VALUE] = LOAD_ATTR, + [LOAD_ATTR_METHOD_LAZY_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_NO_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_WITH_DICT] = LOAD_ATTR, + [LOAD_ATTR_METHOD_WITH_VALUES] = LOAD_ATTR, [LOAD_ATTR_MODULE] = LOAD_ATTR, [LOAD_ATTR_SLOT] = LOAD_ATTR, [LOAD_ATTR_WITH_HINT] = LOAD_ATTR, @@ -348,14 +349,6 @@ const uint8_t _PyOpcode_Original[256] = { [LOAD_GLOBAL_ADAPTIVE] = LOAD_GLOBAL, [LOAD_GLOBAL_BUILTIN] = LOAD_GLOBAL, [LOAD_GLOBAL_MODULE] = LOAD_GLOBAL, - [LOAD_METHOD] = LOAD_METHOD, - [LOAD_METHOD_ADAPTIVE] = LOAD_METHOD, - [LOAD_METHOD_CLASS] = LOAD_METHOD, - [LOAD_METHOD_LAZY_DICT] = LOAD_METHOD, - [LOAD_METHOD_MODULE] = LOAD_METHOD, - [LOAD_METHOD_NO_DICT] = LOAD_METHOD, - [LOAD_METHOD_WITH_DICT] = LOAD_METHOD, - [LOAD_METHOD_WITH_VALUES] = LOAD_METHOD, [LOAD_NAME] = LOAD_NAME, [MAKE_CELL] = MAKE_CELL, [MAKE_FUNCTION] = MAKE_FUNCTION, @@ -485,30 +478,30 @@ static const char *const _PyOpcode_OpName[256] = { [STORE_SUBSCR] = "STORE_SUBSCR", [DELETE_SUBSCR] = "DELETE_SUBSCR", [LOAD_ATTR_ADAPTIVE] = "LOAD_ATTR_ADAPTIVE", + [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", - [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [GET_ITER] = "GET_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", [PRINT_EXPR] = "PRINT_EXPR", [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", - [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", - [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", + [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", + [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", [RETURN_GENERATOR] = "RETURN_GENERATOR", + [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", + [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", + [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", + [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [LOAD_GLOBAL_ADAPTIVE] = "LOAD_GLOBAL_ADAPTIVE", - [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", - [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", - [LOAD_METHOD_ADAPTIVE] = "LOAD_METHOD_ADAPTIVE", - [LOAD_METHOD_CLASS] = "LOAD_METHOD_CLASS", - [LOAD_METHOD_LAZY_DICT] = "LOAD_METHOD_LAZY_DICT", [LIST_TO_TUPLE] = "LIST_TO_TUPLE", [RETURN_VALUE] = "RETURN_VALUE", [IMPORT_STAR] = "IMPORT_STAR", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", - [LOAD_METHOD_MODULE] = "LOAD_METHOD_MODULE", + [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", @@ -535,7 +528,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_METHOD_NO_DICT] = "LOAD_METHOD_NO_DICT", + [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", [POP_JUMP_FORWARD_IF_FALSE] = "POP_JUMP_FORWARD_IF_FALSE", [POP_JUMP_FORWARD_IF_TRUE] = "POP_JUMP_FORWARD_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -543,7 +536,7 @@ static const char *const _PyOpcode_OpName[256] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [LOAD_METHOD_WITH_DICT] = "LOAD_METHOD_WITH_DICT", + [RESUME_QUICK] = "RESUME_QUICK", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -563,9 +556,9 @@ static const char *const _PyOpcode_OpName[256] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [LOAD_METHOD_WITH_VALUES] = "LOAD_METHOD_WITH_VALUES", + [STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [RESUME_QUICK] = "RESUME_QUICK", + [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -575,33 +568,33 @@ static const char *const _PyOpcode_OpName[256] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", - [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", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", - [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", - [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", - [LOAD_METHOD] = "LOAD_METHOD", [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", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", - [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", [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", [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", - [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", - [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", - [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", + [177] = "<177>", + [178] = "<178>", + [179] = "<179>", [180] = "<180>", [181] = "<181>", [182] = "<182>", @@ -682,6 +675,9 @@ static const char *const _PyOpcode_OpName[256] = { #endif #define EXTRA_CASES \ + case 177: \ + case 178: \ + case 179: \ case 180: \ case 181: \ case 182: \ diff --git a/Include/opcode.h b/Include/opcode.h index e7713013cd3..04e5bc8310f 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -108,7 +108,6 @@ extern "C" { #define FORMAT_VALUE 155 #define BUILD_CONST_KEY_MAP 156 #define BUILD_STRING 157 -#define LOAD_METHOD 160 #define LIST_EXTEND 162 #define SET_UPDATE 163 #define DICT_MERGE 164 @@ -158,37 +157,35 @@ extern "C" { #define EXTENDED_ARG_QUICK 58 #define JUMP_BACKWARD_QUICK 59 #define LOAD_ATTR_ADAPTIVE 62 -#define LOAD_ATTR_INSTANCE_VALUE 63 -#define LOAD_ATTR_MODULE 64 -#define LOAD_ATTR_SLOT 65 -#define LOAD_ATTR_WITH_HINT 66 -#define LOAD_CONST__LOAD_FAST 67 -#define LOAD_FAST__LOAD_CONST 72 -#define LOAD_FAST__LOAD_FAST 73 -#define LOAD_GLOBAL_ADAPTIVE 76 -#define LOAD_GLOBAL_BUILTIN 77 -#define LOAD_GLOBAL_MODULE 78 -#define LOAD_METHOD_ADAPTIVE 79 -#define LOAD_METHOD_CLASS 80 -#define LOAD_METHOD_LAZY_DICT 81 -#define LOAD_METHOD_MODULE 86 -#define LOAD_METHOD_NO_DICT 113 -#define LOAD_METHOD_WITH_DICT 121 -#define LOAD_METHOD_WITH_VALUES 141 -#define RESUME_QUICK 143 -#define STORE_ATTR_ADAPTIVE 153 -#define STORE_ATTR_INSTANCE_VALUE 154 -#define STORE_ATTR_SLOT 158 -#define STORE_ATTR_WITH_HINT 159 -#define STORE_FAST__LOAD_FAST 161 -#define STORE_FAST__STORE_FAST 166 -#define STORE_SUBSCR_ADAPTIVE 167 -#define STORE_SUBSCR_DICT 168 -#define STORE_SUBSCR_LIST_INT 169 -#define UNPACK_SEQUENCE_ADAPTIVE 170 -#define UNPACK_SEQUENCE_LIST 177 -#define UNPACK_SEQUENCE_TUPLE 178 -#define UNPACK_SEQUENCE_TWO_TUPLE 179 +#define LOAD_ATTR_CLASS 63 +#define LOAD_ATTR_INSTANCE_VALUE 64 +#define LOAD_ATTR_MODULE 65 +#define LOAD_ATTR_SLOT 66 +#define LOAD_ATTR_WITH_HINT 67 +#define LOAD_ATTR_METHOD_LAZY_DICT 72 +#define LOAD_ATTR_METHOD_NO_DICT 73 +#define LOAD_ATTR_METHOD_WITH_DICT 76 +#define LOAD_ATTR_METHOD_WITH_VALUES 77 +#define LOAD_CONST__LOAD_FAST 78 +#define LOAD_FAST__LOAD_CONST 79 +#define LOAD_FAST__LOAD_FAST 80 +#define LOAD_GLOBAL_ADAPTIVE 81 +#define LOAD_GLOBAL_BUILTIN 86 +#define LOAD_GLOBAL_MODULE 113 +#define RESUME_QUICK 121 +#define STORE_ATTR_ADAPTIVE 141 +#define STORE_ATTR_INSTANCE_VALUE 143 +#define STORE_ATTR_SLOT 153 +#define STORE_ATTR_WITH_HINT 154 +#define STORE_FAST__LOAD_FAST 158 +#define STORE_FAST__STORE_FAST 159 +#define STORE_SUBSCR_ADAPTIVE 160 +#define STORE_SUBSCR_DICT 161 +#define STORE_SUBSCR_LIST_INT 166 +#define UNPACK_SEQUENCE_ADAPTIVE 167 +#define UNPACK_SEQUENCE_LIST 168 +#define UNPACK_SEQUENCE_TUPLE 169 +#define UNPACK_SEQUENCE_TWO_TUPLE 170 #define DO_TRACING 255 #define HAS_CONST(op) (false\ diff --git a/Lib/dis.py b/Lib/dis.py index 5a5ee8d848d..4d30fd745dd 100644 --- a/Lib/dis.py +++ b/Lib/dis.py @@ -37,6 +37,7 @@ LOAD_CONST = opmap['LOAD_CONST'] LOAD_GLOBAL = opmap['LOAD_GLOBAL'] BINARY_OP = opmap['BINARY_OP'] JUMP_BACKWARD = opmap['JUMP_BACKWARD'] +LOAD_ATTR = opmap['LOAD_ATTR'] CACHE = opmap["CACHE"] @@ -463,6 +464,10 @@ def _get_instructions_bytes(code, varname_from_oparg=None, argval, argrepr = _get_name_info(arg//2, get_name) if (arg & 1) and argrepr: argrepr = "NULL + " + argrepr + elif deop == LOAD_ATTR: + argval, argrepr = _get_name_info(arg//2, get_name) + if (arg & 1) and argrepr: + argrepr = "NULL|self + " + argrepr else: argval, argrepr = _get_name_info(arg, get_name) elif deop in hasjabs: diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 8605c2a037d..252d0a5cac7 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -408,6 +408,7 @@ _code_type = type(_write_atomic.__code__) # Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth) # Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST) # Python 3.12a1 3503 (Shrink LOAD_METHOD cache) +# Python 3.12a1 3504 (Merge LOAD_METHOD back into LOAD_ATTR) # Python 3.13 will start with 3550 @@ -421,7 +422,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 = (3503).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3504).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 8ae997e4b6b..912b280c49b 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -184,8 +184,6 @@ def_op('FORMAT_VALUE', 155) def_op('BUILD_CONST_KEY_MAP', 156) def_op('BUILD_STRING', 157) -name_op('LOAD_METHOD', 160) - def_op('LIST_EXTEND', 162) def_op('SET_UPDATE', 163) def_op('DICT_MERGE', 164) @@ -285,10 +283,17 @@ _specializations = { ], "LOAD_ATTR": [ "LOAD_ATTR_ADAPTIVE", + # These potentially push [NULL, bound method] onto the stack. + "LOAD_ATTR_CLASS", "LOAD_ATTR_INSTANCE_VALUE", "LOAD_ATTR_MODULE", "LOAD_ATTR_SLOT", "LOAD_ATTR_WITH_HINT", + # These will always push [unbound method, self] onto the stack. + "LOAD_ATTR_METHOD_LAZY_DICT", + "LOAD_ATTR_METHOD_NO_DICT", + "LOAD_ATTR_METHOD_WITH_DICT", + "LOAD_ATTR_METHOD_WITH_VALUES", ], "LOAD_CONST": [ "LOAD_CONST__LOAD_FAST", @@ -302,15 +307,6 @@ _specializations = { "LOAD_GLOBAL_BUILTIN", "LOAD_GLOBAL_MODULE", ], - "LOAD_METHOD": [ - "LOAD_METHOD_ADAPTIVE", - "LOAD_METHOD_CLASS", - "LOAD_METHOD_LAZY_DICT", - "LOAD_METHOD_MODULE", - "LOAD_METHOD_NO_DICT", - "LOAD_METHOD_WITH_DICT", - "LOAD_METHOD_WITH_VALUES", - ], "RESUME": [ "RESUME_QUICK", ], @@ -373,19 +369,14 @@ _cache_format = { "LOAD_ATTR": { "counter": 1, "version": 2, - "index": 1, + "keys_version": 2, + "descr": 4, }, "STORE_ATTR": { "counter": 1, "version": 2, "index": 1, }, - "LOAD_METHOD": { - "counter": 1, - "type_version": 2, - "keys_version": 2, - "descr": 4, - }, "CALL": { "counter": 1, "func_version": 2, diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 656d801107f..f616406111b 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -366,11 +366,11 @@ dis_traceback = """\ %3d LOAD_GLOBAL 0 (Exception) CHECK_EXC_MATCH - POP_JUMP_FORWARD_IF_FALSE 18 (to 72) + POP_JUMP_FORWARD_IF_FALSE 23 (to 82) STORE_FAST 0 (e) %3d LOAD_FAST 0 (e) - LOAD_ATTR 1 (__traceback__) + LOAD_ATTR 2 (__traceback__) STORE_FAST 1 (tb) POP_EXCEPT LOAD_CONST 0 (None) @@ -1134,7 +1134,7 @@ class DisTests(DisTestBase): 1 2 LOAD_CONST 0 ('a') 4 LOAD_ATTR_SLOT 0 (__class__) - 14 RETURN_VALUE + 24 RETURN_VALUE """ co = compile("'a'.__class__", "", "eval") self.code_quicken(lambda: exec(co, {}, {})) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-02-08-28-55.gh-issue-93429.DZTWHx.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-02-08-28-55.gh-issue-93429.DZTWHx.rst new file mode 100644 index 00000000000..02efedaa964 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-02-08-28-55.gh-issue-93429.DZTWHx.rst @@ -0,0 +1 @@ +``LOAD_METHOD`` instruction has been removed. It was merged back into ``LOAD_ATTR``. diff --git a/Programs/test_frozenmain.h b/Programs/test_frozenmain.h index 8d4ba94bd76..5570ad01ce8 100644 --- a/Programs/test_frozenmain.h +++ b/Programs/test_frozenmain.h @@ -1,40 +1,41 @@ // Auto-generated by Programs/freeze_test_frozenmain.py unsigned char M_test_frozenmain[] = { 227,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0, - 0,0,0,0,0,243,160,0,0,0,151,0,100,0,100,1, + 0,0,0,0,0,243,180,0,0,0,151,0,100,0,100,1, 108,0,90,0,100,0,100,1,108,1,90,1,2,0,101,2, 100,2,171,1,0,0,0,0,0,0,0,0,1,0,2,0, - 101,2,100,3,101,0,106,3,0,0,0,0,0,0,0,0, - 171,2,0,0,0,0,0,0,0,0,1,0,2,0,101,1, - 106,4,0,0,0,0,0,0,0,0,171,0,0,0,0,0, - 0,0,0,0,100,4,25,0,0,0,0,0,0,0,0,0, - 90,5,100,5,68,0,93,23,90,6,2,0,101,2,100,6, - 101,6,155,0,100,7,101,5,101,6,25,0,0,0,0,0, - 0,0,0,0,155,0,157,4,171,1,0,0,0,0,0,0, - 0,0,1,0,140,24,100,1,83,0,41,8,233,0,0,0, - 0,78,122,18,70,114,111,122,101,110,32,72,101,108,108,111, - 32,87,111,114,108,100,122,8,115,121,115,46,97,114,103,118, - 218,6,99,111,110,102,105,103,41,5,218,12,112,114,111,103, - 114,97,109,95,110,97,109,101,218,10,101,120,101,99,117,116, - 97,98,108,101,218,15,117,115,101,95,101,110,118,105,114,111, - 110,109,101,110,116,218,17,99,111,110,102,105,103,117,114,101, - 95,99,95,115,116,100,105,111,218,14,98,117,102,102,101,114, - 101,100,95,115,116,100,105,111,122,7,99,111,110,102,105,103, - 32,122,2,58,32,41,7,218,3,115,121,115,218,17,95,116, - 101,115,116,105,110,116,101,114,110,97,108,99,97,112,105,218, - 5,112,114,105,110,116,218,4,97,114,103,118,218,11,103,101, - 116,95,99,111,110,102,105,103,115,114,3,0,0,0,218,3, - 107,101,121,169,0,243,0,0,0,0,250,18,116,101,115,116, - 95,102,114,111,122,101,110,109,97,105,110,46,112,121,250,8, - 60,109,111,100,117,108,101,62,114,18,0,0,0,1,0,0, - 0,115,140,0,0,0,248,240,6,0,1,11,128,10,128,10, - 128,10,216,0,24,208,0,24,208,0,24,208,0,24,224,0, - 5,128,5,208,6,26,212,0,27,208,0,27,216,0,5,128, - 5,128,106,144,35,148,40,212,0,27,208,0,27,216,9,38, - 208,9,26,212,9,38,212,9,40,168,24,212,9,50,128,6, - 240,2,6,12,2,240,0,7,1,42,240,0,7,1,42,128, - 67,240,14,0,5,10,128,69,208,10,40,144,67,208,10,40, - 208,10,40,152,54,160,35,156,59,208,10,40,208,10,40,212, - 4,41,208,4,41,208,4,41,240,15,7,1,42,240,0,7, - 1,42,114,16,0,0,0, + 101,2,100,3,101,0,106,6,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,171,2,0,0,0,0, + 0,0,0,0,1,0,2,0,101,1,106,8,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,171,0, + 0,0,0,0,0,0,0,0,100,4,25,0,0,0,0,0, + 0,0,0,0,90,5,100,5,68,0,93,23,90,6,2,0, + 101,2,100,6,101,6,155,0,100,7,101,5,101,6,25,0, + 0,0,0,0,0,0,0,0,155,0,157,4,171,1,0,0, + 0,0,0,0,0,0,1,0,140,24,100,1,83,0,41,8, + 233,0,0,0,0,78,122,18,70,114,111,122,101,110,32,72, + 101,108,108,111,32,87,111,114,108,100,122,8,115,121,115,46, + 97,114,103,118,218,6,99,111,110,102,105,103,41,5,218,12, + 112,114,111,103,114,97,109,95,110,97,109,101,218,10,101,120, + 101,99,117,116,97,98,108,101,218,15,117,115,101,95,101,110, + 118,105,114,111,110,109,101,110,116,218,17,99,111,110,102,105, + 103,117,114,101,95,99,95,115,116,100,105,111,218,14,98,117, + 102,102,101,114,101,100,95,115,116,100,105,111,122,7,99,111, + 110,102,105,103,32,122,2,58,32,41,7,218,3,115,121,115, + 218,17,95,116,101,115,116,105,110,116,101,114,110,97,108,99, + 97,112,105,218,5,112,114,105,110,116,218,4,97,114,103,118, + 218,11,103,101,116,95,99,111,110,102,105,103,115,114,3,0, + 0,0,218,3,107,101,121,169,0,243,0,0,0,0,250,18, + 116,101,115,116,95,102,114,111,122,101,110,109,97,105,110,46, + 112,121,250,8,60,109,111,100,117,108,101,62,114,18,0,0, + 0,1,0,0,0,115,145,0,0,0,248,240,6,0,1,11, + 128,10,128,10,128,10,216,0,24,208,0,24,208,0,24,208, + 0,24,224,0,5,128,5,208,6,26,212,0,27,208,0,27, + 216,0,5,128,5,128,106,144,35,151,40,145,40,212,0,27, + 208,0,27,216,9,38,208,9,26,215,9,38,209,9,38,212, + 9,40,168,24,212,9,50,128,6,240,2,6,12,2,240,0, + 7,1,42,240,0,7,1,42,128,67,240,14,0,5,10,128, + 69,208,10,40,144,67,208,10,40,208,10,40,152,54,160,35, + 156,59,208,10,40,208,10,40,212,4,41,208,4,41,208,4, + 41,240,15,7,1,42,240,0,7,1,42,114,16,0,0,0, }; diff --git a/Python/ceval.c b/Python/ceval.c index 341d1d23ad9..f9ec640ef17 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1502,22 +1502,6 @@ eval_frame_handle_pending(PyThreadState *tstate) /* Shared opcode macros */ -// shared by LOAD_ATTR_MODULE and LOAD_METHOD_MODULE -#define LOAD_MODULE_ATTR_OR_METHOD(attr_or_method) \ - _PyAttrCache *cache = (_PyAttrCache *)next_instr; \ - DEOPT_IF(!PyModule_CheckExact(owner), LOAD_##attr_or_method); \ - PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; \ - assert(dict != NULL); \ - DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version), \ - LOAD_##attr_or_method); \ - assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); \ - assert(cache->index < dict->ma_keys->dk_nentries); \ - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index; \ - res = ep->me_value; \ - DEOPT_IF(res == NULL, LOAD_##attr_or_method); \ - STAT_INC(LOAD_##attr_or_method, hit); \ - Py_INCREF(res); - #define TRACE_FUNCTION_EXIT() \ if (cframe.use_tracing) { \ if (trace_function_exit(tstate, frame, retval)) { \ @@ -3467,8 +3451,43 @@ handle_eval_breaker: TARGET(LOAD_ATTR) { PREDICTED(LOAD_ATTR); - PyObject *name = GETITEM(names, oparg); + PyObject *name = GETITEM(names, oparg >> 1); PyObject *owner = TOP(); + if (oparg & 1) { + /* Designed to work in tandem with CALL. */ + PyObject* meth = NULL; + + int meth_found = _PyObject_GetMethod(owner, name, &meth); + + if (meth == NULL) { + /* Most likely attribute wasn't found. */ + goto error; + } + + if (meth_found) { + /* We can bypass temporary bound method object. + meth is unbound method and obj is self. + + meth | self | arg1 | ... | argN + */ + SET_TOP(meth); + PUSH(owner); // self + } + else { + /* meth is not an unbound method (but a regular attr, or + something was returned by a descriptor protocol). Set + the second element of the stack to NULL, to signal + CALL that it's not a method call. + + NULL | meth | arg1 | ... | argN + */ + SET_TOP(NULL); + Py_DECREF(owner); + PUSH(meth); + } + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + DISPATCH(); + } PyObject *res = PyObject_GetAttr(owner, name); if (res == NULL) { goto error; @@ -3484,7 +3503,7 @@ handle_eval_breaker: _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *owner = TOP(); - PyObject *name = GETITEM(names, oparg); + PyObject *name = GETITEM(names, oparg>>1); next_instr--; if (_Py_Specialize_LoadAttr(owner, next_instr, name) < 0) { goto error; @@ -3515,6 +3534,8 @@ handle_eval_breaker: DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); + SET_TOP(NULL); + STACK_GROW((oparg & 1)); SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); @@ -3523,10 +3544,23 @@ handle_eval_breaker: TARGET(LOAD_ATTR_MODULE) { assert(cframe.use_tracing == 0); - // shared with LOAD_METHOD_MODULE PyObject *owner = TOP(); PyObject *res; - LOAD_MODULE_ATTR_OR_METHOD(ATTR); + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR); + PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; + assert(dict != NULL); + DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version), + LOAD_ATTR); + assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); + assert(cache->index < dict->ma_keys->dk_nentries); + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index; + res = ep->me_value; + DEOPT_IF(res == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(res); + SET_TOP(NULL); + STACK_GROW((oparg & 1)); SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); @@ -3546,7 +3580,7 @@ handle_eval_breaker: PyDictObject *dict = *(PyDictObject **)_PyObject_ManagedDictPointer(owner); DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(names, oparg); + PyObject *name = GETITEM(names, oparg>>1); uint16_t hint = cache->index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); if (DK_IS_UNICODE(dict->ma_keys)) { @@ -3562,6 +3596,8 @@ handle_eval_breaker: DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); + SET_TOP(NULL); + STACK_GROW((oparg & 1)); SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); @@ -3582,12 +3618,38 @@ handle_eval_breaker: DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); + SET_TOP(NULL); + STACK_GROW((oparg & 1)); SET_TOP(res); Py_DECREF(owner); JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } + TARGET(LOAD_ATTR_CLASS) { + /* LOAD_METHOD, for class methods */ + assert(cframe.use_tracing == 0); + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; + + PyObject *cls = TOP(); + DEOPT_IF(!PyType_Check(cls), LOAD_ATTR); + uint32_t type_version = read_u32(cache->type_version); + DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version, + LOAD_ATTR); + assert(type_version != 0); + + STAT_INC(LOAD_ATTR, hit); + PyObject *res = read_obj(cache->descr); + assert(res != NULL); + Py_INCREF(res); + SET_TOP(NULL); + STACK_GROW((oparg & 1)); + SET_TOP(res); + Py_DECREF(cls); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); + NOTRACE_DISPATCH(); + } + TARGET(STORE_ATTR_ADAPTIVE) { assert(cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -4486,65 +4548,7 @@ handle_eval_breaker: DISPATCH(); } - TARGET(LOAD_METHOD) { - PREDICTED(LOAD_METHOD); - /* Designed to work in tandem with CALL. */ - PyObject *name = GETITEM(names, oparg); - PyObject *obj = TOP(); - PyObject *meth = NULL; - - int meth_found = _PyObject_GetMethod(obj, name, &meth); - - if (meth == NULL) { - /* Most likely attribute wasn't found. */ - goto error; - } - - if (meth_found) { - /* We can bypass temporary bound method object. - meth is unbound method and obj is self. - - meth | self | arg1 | ... | argN - */ - SET_TOP(meth); - PUSH(obj); // self - } - else { - /* meth is not an unbound method (but a regular attr, or - something was returned by a descriptor protocol). Set - the second element of the stack to NULL, to signal - CALL that it's not a method call. - - NULL | meth | arg1 | ... | argN - */ - SET_TOP(NULL); - Py_DECREF(obj); - PUSH(meth); - } - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - DISPATCH(); - } - - TARGET(LOAD_METHOD_ADAPTIVE) { - assert(cframe.use_tracing == 0); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { - PyObject *owner = TOP(); - PyObject *name = GETITEM(names, oparg); - next_instr--; - if (_Py_Specialize_LoadMethod(owner, next_instr, name) < 0) { - goto error; - } - NOTRACE_DISPATCH_SAME_OPARG(); - } - else { - STAT_INC(LOAD_METHOD, deferred); - DECREMENT_ADAPTIVE_COUNTER(cache); - JUMP_TO_INSTRUCTION(LOAD_METHOD); - } - } - - TARGET(LOAD_METHOD_WITH_VALUES) { + TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { /* LOAD_METHOD, with cached method object */ assert(cframe.use_tracing == 0); PyObject *self = TOP(); @@ -4552,25 +4556,25 @@ handle_eval_breaker: _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; uint32_t type_version = read_u32(cache->type_version); assert(type_version != 0); - DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD); + DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = *(PyDictObject**)_PyObject_ManagedDictPointer(self); - DEOPT_IF(dict != NULL, LOAD_METHOD); + DEOPT_IF(dict != NULL, LOAD_ATTR); PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls; DEOPT_IF(self_heap_type->ht_cached_keys->dk_version != - read_u32(cache->keys_version), LOAD_METHOD); - STAT_INC(LOAD_METHOD, hit); + read_u32(cache->keys_version), LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); PyObject *res = read_obj(cache->descr); assert(res != NULL); assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); Py_INCREF(res); SET_TOP(res); PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } - TARGET(LOAD_METHOD_WITH_DICT) { + TARGET(LOAD_ATTR_METHOD_WITH_DICT) { /* LOAD_METHOD, with a dict Can be either a managed dict, or a tp_dictoffset offset.*/ assert(cframe.use_tracing == 0); @@ -4579,101 +4583,65 @@ handle_eval_breaker: _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; DEOPT_IF(self_cls->tp_version_tag != read_u32(cache->type_version), - LOAD_METHOD); + LOAD_ATTR); /* Treat index as a signed 16 bit value */ Py_ssize_t dictoffset = self_cls->tp_dictoffset; assert(dictoffset > 0); PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset); PyDictObject *dict = *dictptr; - DEOPT_IF(dict == NULL, LOAD_METHOD); + DEOPT_IF(dict == NULL, LOAD_ATTR); DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->keys_version), - LOAD_METHOD); - STAT_INC(LOAD_METHOD, hit); + LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); PyObject *res = read_obj(cache->descr); assert(res != NULL); assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); Py_INCREF(res); SET_TOP(res); PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } - TARGET(LOAD_METHOD_NO_DICT) { + TARGET(LOAD_ATTR_METHOD_NO_DICT) { assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; uint32_t type_version = read_u32(cache->type_version); - DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD); + DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); assert(self_cls->tp_dictoffset == 0); - STAT_INC(LOAD_METHOD, hit); + STAT_INC(LOAD_ATTR, hit); PyObject *res = read_obj(cache->descr); assert(res != NULL); assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); Py_INCREF(res); SET_TOP(res); PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } - TARGET(LOAD_METHOD_LAZY_DICT) { + TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; uint32_t type_version = read_u32(cache->type_version); - DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD); + DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR); Py_ssize_t dictoffset = self_cls->tp_dictoffset; assert(dictoffset > 0); PyObject *dict = *(PyObject **)((char *)self + dictoffset); /* This object has a __dict__, just not yet created */ - DEOPT_IF(dict != NULL, LOAD_METHOD); - STAT_INC(LOAD_METHOD, hit); + DEOPT_IF(dict != NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); PyObject *res = read_obj(cache->descr); assert(res != NULL); assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)); Py_INCREF(res); SET_TOP(res); PUSH(self); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); - } - - TARGET(LOAD_METHOD_MODULE) { - /* LOAD_METHOD, for module methods */ - assert(cframe.use_tracing == 0); - PyObject *owner = TOP(); - PyObject *res; - LOAD_MODULE_ATTR_OR_METHOD(METHOD); - SET_TOP(NULL); - Py_DECREF(owner); - PUSH(res); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); - NOTRACE_DISPATCH(); - } - - TARGET(LOAD_METHOD_CLASS) { - /* LOAD_METHOD, for class methods */ - assert(cframe.use_tracing == 0); - _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - - PyObject *cls = TOP(); - DEOPT_IF(!PyType_Check(cls), LOAD_METHOD); - uint32_t type_version = read_u32(cache->type_version); - DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version, - LOAD_METHOD); - assert(type_version != 0); - - STAT_INC(LOAD_METHOD, hit); - PyObject *res = read_obj(cache->descr); - assert(res != NULL); - Py_INCREF(res); - SET_TOP(NULL); - Py_DECREF(cls); - PUSH(res); - JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } diff --git a/Python/compile.c b/Python/compile.c index 93aafa78f85..f36c4aad565 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -84,8 +84,9 @@ #define POP_JUMP_IF_TRUE -8 #define POP_JUMP_IF_NONE -9 #define POP_JUMP_IF_NOT_NONE -10 +#define LOAD_METHOD -11 -#define MIN_VIRTUAL_OPCODE -10 +#define MIN_VIRTUAL_OPCODE -11 #define MAX_ALLOWED_OPCODE 254 #define IS_WITHIN_OPCODE_RANGE(opcode) \ @@ -1069,7 +1070,7 @@ stack_effect(int opcode, int oparg, int jump) case BUILD_CONST_KEY_MAP: return -oparg; case LOAD_ATTR: - return 0; + return (oparg & 1); case COMPARE_OP: case IS_OP: case CONTAINS_OP: @@ -1493,6 +1494,14 @@ compiler_addop_name(struct compiler *c, int opcode, PyObject *dict, Py_DECREF(mangled); if (arg < 0) return 0; + if (opcode == LOAD_ATTR) { + arg <<= 1; + } + if (opcode == LOAD_METHOD) { + opcode = LOAD_ATTR; + arg <<= 1; + arg |= 1; + } return compiler_addop_i(c, opcode, arg); } diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 1009b3a9345..a6523d4af2b 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -62,30 +62,30 @@ static void *opcode_targets[256] = { &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, &&TARGET_LOAD_ATTR_ADAPTIVE, + &&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_MODULE, &&TARGET_LOAD_ATTR_SLOT, &&TARGET_LOAD_ATTR_WITH_HINT, - &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, - &&TARGET_LOAD_FAST__LOAD_CONST, - &&TARGET_LOAD_FAST__LOAD_FAST, + &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, + &&TARGET_LOAD_ATTR_METHOD_NO_DICT, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, + &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, + &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, + &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_FAST__LOAD_CONST, + &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_LOAD_GLOBAL_ADAPTIVE, - &&TARGET_LOAD_GLOBAL_BUILTIN, - &&TARGET_LOAD_GLOBAL_MODULE, - &&TARGET_LOAD_METHOD_ADAPTIVE, - &&TARGET_LOAD_METHOD_CLASS, - &&TARGET_LOAD_METHOD_LAZY_DICT, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, - &&TARGET_LOAD_METHOD_MODULE, + &&TARGET_LOAD_GLOBAL_BUILTIN, &&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_METHOD_NO_DICT, + &&TARGET_LOAD_GLOBAL_MODULE, &&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_METHOD_WITH_DICT, + &&TARGET_RESUME_QUICK, &&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_METHOD_WITH_VALUES, + &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_RESUME_QUICK, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,33 +152,33 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, - &&TARGET_STORE_ATTR_ADAPTIVE, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, - &&TARGET_STORE_ATTR_SLOT, - &&TARGET_STORE_ATTR_WITH_HINT, - &&TARGET_LOAD_METHOD, &&TARGET_STORE_FAST__LOAD_FAST, + &&TARGET_STORE_FAST__STORE_FAST, + &&TARGET_STORE_SUBSCR_ADAPTIVE, + &&TARGET_STORE_SUBSCR_DICT, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, - &&TARGET_STORE_FAST__STORE_FAST, - &&TARGET_STORE_SUBSCR_ADAPTIVE, - &&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_LIST_INT, &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, + &&TARGET_UNPACK_SEQUENCE_LIST, + &&TARGET_UNPACK_SEQUENCE_TUPLE, + &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&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_UNPACK_SEQUENCE_LIST, - &&TARGET_UNPACK_SEQUENCE_TUPLE, - &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&_unknown_opcode, + &&_unknown_opcode, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index b1877841cdf..11bd838a401 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -20,7 +20,6 @@ uint8_t _PyOpcode_Adaptive[256] = { [LOAD_ATTR] = LOAD_ATTR_ADAPTIVE, [LOAD_GLOBAL] = LOAD_GLOBAL_ADAPTIVE, - [LOAD_METHOD] = LOAD_METHOD_ADAPTIVE, [BINARY_SUBSCR] = BINARY_SUBSCR_ADAPTIVE, [STORE_SUBSCR] = STORE_SUBSCR_ADAPTIVE, [CALL] = CALL_ADAPTIVE, @@ -362,22 +361,14 @@ miss_counter_start(void) { /* Methods */ -#define SPEC_FAIL_LOAD_METHOD_OVERRIDING_DESCRIPTOR 8 -#define SPEC_FAIL_LOAD_METHOD_NON_OVERRIDING_DESCRIPTOR 9 -#define SPEC_FAIL_LOAD_METHOD_NOT_DESCRIPTOR 10 -#define SPEC_FAIL_LOAD_METHOD_METHOD 11 -#define SPEC_FAIL_LOAD_METHOD_MUTABLE_CLASS 12 -#define SPEC_FAIL_LOAD_METHOD_PROPERTY 13 -#define SPEC_FAIL_LOAD_METHOD_NON_OBJECT_SLOT 14 -#define SPEC_FAIL_LOAD_METHOD_IS_ATTR 15 -#define SPEC_FAIL_LOAD_METHOD_DICT_SUBCLASS 16 -#define SPEC_FAIL_LOAD_METHOD_BUILTIN_CLASS_METHOD 17 -#define SPEC_FAIL_LOAD_METHOD_CLASS_METHOD_OBJ 18 -#define SPEC_FAIL_LOAD_METHOD_OBJECT_SLOT 19 -#define SPEC_FAIL_LOAD_METHOD_HAS_DICT 20 -#define SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT 21 -#define SPEC_FAIL_LOAD_METHOD_INSTANCE_ATTRIBUTE 22 -#define SPEC_FAIL_LOAD_METHOD_METACLASS_ATTRIBUTE 23 +#define SPEC_FAIL_LOAD_METHOD_METHOD 20 +#define SPEC_FAIL_LOAD_METHOD_IS_ATTR 21 +#define SPEC_FAIL_LOAD_METHOD_BUILTIN_CLASS_METHOD 22 +#define SPEC_FAIL_LOAD_METHOD_CLASS_METHOD_OBJ 23 +#define SPEC_FAIL_LOAD_METHOD_OBJECT_SLOT 24 +#define SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT 25 +#define SPEC_FAIL_LOAD_METHOD_INSTANCE_ATTRIBUTE 26 +#define SPEC_FAIL_LOAD_METHOD_METACLASS_ATTRIBUTE 27 /* Binary subscr and store subscr */ @@ -660,6 +651,10 @@ specialize_dict_access( return 1; } +static int specialize_attr_loadmethod(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name, + PyObject* descr, DescriptorClassification kind); +static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name); + int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { @@ -673,21 +668,37 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) } goto success; } + if (PyType_Check(owner)) { + int err = specialize_class_load_attr(owner, instr, name); + if (err) { + goto fail; + } + goto success; + } PyTypeObject *type = Py_TYPE(owner); if (type->tp_dict == NULL) { if (PyType_Ready(type) < 0) { return -1; } } - PyObject *descr; + PyObject *descr = NULL; DescriptorClassification kind = analyze_descriptor(type, name, &descr, 0); + assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); switch(kind) { case OVERRIDING: SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR); goto fail; case METHOD: + { + int oparg = _Py_OPARG(*instr); + if (oparg & 1) { + if (specialize_attr_loadmethod(owner, instr, name, descr, kind)) { + goto success; + } + } SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD); goto fail; + } case PROPERTY: SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_PROPERTY); goto fail; @@ -843,23 +854,23 @@ success: #ifdef Py_STATS static int -load_method_fail_kind(DescriptorClassification kind) +load_attr_fail_kind(DescriptorClassification kind) { switch (kind) { case OVERRIDING: - return SPEC_FAIL_LOAD_METHOD_OVERRIDING_DESCRIPTOR; + return SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR; case METHOD: - return SPEC_FAIL_LOAD_METHOD_METHOD; + return SPEC_FAIL_ATTR_METHOD; case PROPERTY: - return SPEC_FAIL_LOAD_METHOD_PROPERTY; + return SPEC_FAIL_ATTR_PROPERTY; case OBJECT_SLOT: return SPEC_FAIL_LOAD_METHOD_OBJECT_SLOT; case OTHER_SLOT: - return SPEC_FAIL_LOAD_METHOD_NON_OBJECT_SLOT; + return SPEC_FAIL_ATTR_NON_OBJECT_SLOT; case DUNDER_CLASS: return SPEC_FAIL_OTHER; case MUTABLE: - return SPEC_FAIL_LOAD_METHOD_MUTABLE_CLASS; + return SPEC_FAIL_ATTR_MUTABLE_CLASS; case GETSET_OVERRIDDEN: return SPEC_FAIL_OVERRIDDEN; case BUILTIN_CLASSMETHOD: @@ -867,9 +878,9 @@ load_method_fail_kind(DescriptorClassification kind) case PYTHON_CLASSMETHOD: return SPEC_FAIL_LOAD_METHOD_CLASS_METHOD_OBJ; case NON_OVERRIDING: - return SPEC_FAIL_LOAD_METHOD_NON_OVERRIDING_DESCRIPTOR; + return SPEC_FAIL_ATTR_NON_OVERRIDING_DESCRIPTOR; case NON_DESCRIPTOR: - return SPEC_FAIL_LOAD_METHOD_NOT_DESCRIPTOR; + return SPEC_FAIL_ATTR_NOT_DESCRIPTOR; case ABSENT: return SPEC_FAIL_LOAD_METHOD_INSTANCE_ATTRIBUTE; } @@ -878,7 +889,7 @@ load_method_fail_kind(DescriptorClassification kind) #endif static int -specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, +specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { _PyLoadMethodCache *cache = (_PyLoadMethodCache *)(instr + 1); @@ -890,20 +901,20 @@ specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, case NON_DESCRIPTOR: write_u32(cache->type_version, ((PyTypeObject *)owner)->tp_version_tag); write_obj(cache->descr, descr); - _Py_SET_OPCODE(*instr, LOAD_METHOD_CLASS); + _Py_SET_OPCODE(*instr, LOAD_ATTR_CLASS); return 0; #ifdef Py_STATS case ABSENT: if (_PyType_Lookup(Py_TYPE(owner), name) != NULL) { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_METACLASS_ATTRIBUTE); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_LOAD_METHOD_METACLASS_ATTRIBUTE); } else { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_EXPECTED_ERROR); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR); } return -1; #endif default: - SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind)); + SPECIALIZATION_FAIL(LOAD_ATTR, load_attr_fail_kind(kind)); return -1; } } @@ -919,44 +930,14 @@ typedef enum { // Please collect stats carefully before and after modifying. A subtle change // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. -int -_Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) +static int +specialize_attr_loadmethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, +PyObject *descr, DescriptorClassification kind) { - assert(_PyOpcode_Caches[LOAD_METHOD] == INLINE_CACHE_ENTRIES_LOAD_METHOD); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)(instr + 1); PyTypeObject *owner_cls = Py_TYPE(owner); - if (PyModule_CheckExact(owner)) { - assert(INLINE_CACHE_ENTRIES_LOAD_ATTR <= - INLINE_CACHE_ENTRIES_LOAD_METHOD); - int err = specialize_module_load_attr(owner, instr, name, LOAD_METHOD, - LOAD_METHOD_MODULE); - if (err) { - goto fail; - } - goto success; - } - if (owner_cls->tp_dict == NULL) { - if (PyType_Ready(owner_cls) < 0) { - return -1; - } - } - if (PyType_Check(owner)) { - int err = specialize_class_load_method(owner, instr, name); - if (err) { - goto fail; - } - goto success; - } - - PyObject *descr = NULL; - DescriptorClassification kind = 0; - kind = analyze_descriptor(owner_cls, name, &descr, 0); - assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); - if (kind != METHOD) { - SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind)); - goto fail; - } + assert(kind == METHOD && descr != NULL); ObjectDictKind dictkind; PyDictKeysObject *keys; if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { @@ -972,7 +953,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) else { Py_ssize_t dictoffset = owner_cls->tp_dictoffset; if (dictoffset < 0 || dictoffset > INT16_MAX) { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_OUT_OF_RANGE); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE); goto fail; } if (dictoffset == 0) { @@ -995,33 +976,33 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) { Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_LOAD_METHOD_IS_ATTR); goto fail; } uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(keys); if (keys_version == 0) { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_OUT_OF_VERSIONS); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS); goto fail; } write_u32(cache->keys_version, keys_version); } switch(dictkind) { case NO_DICT: - _Py_SET_OPCODE(*instr, LOAD_METHOD_NO_DICT); + _Py_SET_OPCODE(*instr, LOAD_ATTR_METHOD_NO_DICT); break; case MANAGED_VALUES: - _Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_VALUES); + _Py_SET_OPCODE(*instr, LOAD_ATTR_METHOD_WITH_VALUES); break; case MANAGED_DICT: - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT); + SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT); goto fail; case OFFSET_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - _Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT); + _Py_SET_OPCODE(*instr, LOAD_ATTR_METHOD_WITH_DICT); break; case LAZY_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - _Py_SET_OPCODE(*instr, LOAD_METHOD_LAZY_DICT); + _Py_SET_OPCODE(*instr, LOAD_ATTR_METHOD_LAZY_DICT); break; } /* `descr` is borrowed. This is safe for methods (even inherited ones from @@ -1041,15 +1022,8 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) write_u32(cache->type_version, owner_cls->tp_version_tag); write_obj(cache->descr, descr); // Fall through. -success: - STAT_INC(LOAD_METHOD, success); - assert(!PyErr_Occurred()); - cache->counter = miss_counter_start(); - return 0; + return 1; fail: - STAT_INC(LOAD_METHOD, failure); - assert(!PyErr_Occurred()); - cache->counter = adaptive_counter_backoff(cache->counter); return 0; }