From 2ca26bb68e4645136168e85d77e38935f30d8f8e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 17:43:21 -0700 Subject: [PATCH 1/9] Print non-viable uops --- Tools/cases_generator/analysis.py | 32 +++++++++++++++++++++++++ Tools/cases_generator/generate_cases.py | 10 ++++++++ 2 files changed, 42 insertions(+) diff --git a/Tools/cases_generator/analysis.py b/Tools/cases_generator/analysis.py index b920c0aa8c1c8a..2a933d2253e051 100644 --- a/Tools/cases_generator/analysis.py +++ b/Tools/cases_generator/analysis.py @@ -414,3 +414,35 @@ def check_macro_components( case _: assert_never(uop) return components + + def report_non_viable_uops(self): + print("The following ops are not viable uops:") + skips = { + "CACHE", + "RESERVED", + "ENTER_EXECUTOR", + "LOAD_FAST_LOAD_FAST", + "LOAD_FAST_LOAD_CONST", + "LOAD_CONST_LOAD_FAST", + "_BINARY_OP_INPLACE_ADD_UNICODE", + "POP_JUMP_IF_TRUE", + "POP_JUMP_IF_FALSE", + "_ITER_JUMP_LIST", + "_ITER_JUMP_TUPLE", + "_ITER_JUMP_SET", + } + non_viable = [ + instr + for instr in self.instrs.values() + if not instr.is_viable_uop() + and instr.name not in skips + and not instr.name.startswith("INSTRUMENTED_") + ] + non_viable.sort(key=lambda instr: instr.name) + for instr in non_viable: + print(f" {instr.name:<35}", end="") + if instr.name in self.families: + print(" (unspecialized)", end="") + elif instr.family is not None: + print(f" (specialization of {instr.family.name})", end="") + print() diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 898736248a98f9..ea017966c8756a 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -92,6 +92,13 @@ description="Generate the code for the interpreter switch.", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) + +arg_parser.add_argument( + "-v", + "--verbose", + help="Print list of non-viable uops and exit", + action="store_true", +) arg_parser.add_argument( "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT ) @@ -865,6 +872,9 @@ def main() -> None: a.analyze() # Prints messages and sets a.errors on failure if a.errors: sys.exit(f"Found {a.errors} errors") + if args.verbose: + a.report_non_viable_uops() + return # These raise OSError if output can't be written a.write_instructions(args.output, args.emit_line_directives) From e19517c7f6cbd7d588fb3aea8654bca07ec7f440 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 18:41:58 -0700 Subject: [PATCH 2/9] Sort non-viable opcodes by execution count --- Tools/cases_generator/analysis.py | 42 ++++++++++++++++++++----- Tools/cases_generator/generate_cases.py | 3 +- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Tools/cases_generator/analysis.py b/Tools/cases_generator/analysis.py index 2a933d2253e051..91dcba8ceee13d 100644 --- a/Tools/cases_generator/analysis.py +++ b/Tools/cases_generator/analysis.py @@ -415,34 +415,60 @@ def check_macro_components( assert_never(uop) return components - def report_non_viable_uops(self): + def report_non_viable_uops(self, jsonfile: str) -> None: print("The following ops are not viable uops:") skips = { "CACHE", "RESERVED", - "ENTER_EXECUTOR", + "INTERPRETER_EXIT", + "JUMP_BACKWARD", "LOAD_FAST_LOAD_FAST", - "LOAD_FAST_LOAD_CONST", "LOAD_CONST_LOAD_FAST", + "STORE_FAST_STORE_FAST", "_BINARY_OP_INPLACE_ADD_UNICODE", "POP_JUMP_IF_TRUE", "POP_JUMP_IF_FALSE", "_ITER_JUMP_LIST", "_ITER_JUMP_TUPLE", - "_ITER_JUMP_SET", + "_ITER_JUMP_RANGE", } + try: + # Secret feature: if bmraw.json exists, print and sort by execution count + counts = load_execution_counts(jsonfile) + except FileNotFoundError as err: + counts = {} non_viable = [ instr for instr in self.instrs.values() - if not instr.is_viable_uop() - and instr.name not in skips + if instr.name not in skips and not instr.name.startswith("INSTRUMENTED_") + and not instr.is_viable_uop() ] - non_viable.sort(key=lambda instr: instr.name) + non_viable.sort(key=lambda instr: (-counts.get(instr.name, 0), instr.name)) for instr in non_viable: - print(f" {instr.name:<35}", end="") + if instr.name in counts: + scount = f"{counts[instr.name]:,}" + else: + scount = "" + print(f" {scount:>15} {instr.name:<35}", end="") if instr.name in self.families: print(" (unspecialized)", end="") elif instr.family is not None: print(f" (specialization of {instr.family.name})", end="") print() + + +def load_execution_counts(jsonfile: str) -> dict[str, int]: + import json + + with open(jsonfile) as f: + jsondata = json.load(f) + + # Look for keys like "opcode[LOAD_FAST].execution_count" + prefix = "opcode[" + suffix = "].execution_count" + res: dict[str, int] = {} + for key, value in jsondata.items(): + if key.startswith(prefix) and key.endswith(suffix): + res[key[len(prefix) : -len(suffix)]] = value + return res diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index ea017966c8756a..9192d1038ab7d6 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -873,7 +873,8 @@ def main() -> None: if a.errors: sys.exit(f"Found {a.errors} errors") if args.verbose: - a.report_non_viable_uops() + # Load execution counts from bmraw.json, if it exists + a.report_non_viable_uops("bmraw.json") return # These raise OSError if output can't be written From 8d1a856b6ec7f86a82f1f6515e3aaa72a2e24ad3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 18:03:00 -0700 Subject: [PATCH 3/9] Double _Py_UOP_MAX_TRACE_LENGTH to 128 --- Include/internal/pycore_uops.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_uops.h b/Include/internal/pycore_uops.h index 249f5c010e0092..d8a7d978f1304e 100644 --- a/Include/internal/pycore_uops.h +++ b/Include/internal/pycore_uops.h @@ -10,7 +10,7 @@ extern "C" { #include "pycore_frame.h" // _PyInterpreterFrame -#define _Py_UOP_MAX_TRACE_LENGTH 64 +#define _Py_UOP_MAX_TRACE_LENGTH 128 typedef struct { uint32_t opcode; From 3796e89b7159e0bddb175e3554c14c61d63c3ae2 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 17:44:19 -0700 Subject: [PATCH 4/9] Split LOAD_ATTR_METHOD_WITH_VALUES --- Include/internal/pycore_opcode_metadata.h | 39 +++++++++++++------ Python/abstract_interp_cases.c.h | 11 ++++++ Python/bytecodes.c | 22 ++++++----- Python/executor_cases.c.h | 30 +++++++++++++++ Python/generated_cases.c.h | 47 +++++++++++++---------- 5 files changed, 106 insertions(+), 43 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index bb37e9a1d1b6b6..6c418225bce034 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -59,18 +59,20 @@ #define _ITER_JUMP_RANGE 331 #define _IS_ITER_EXHAUSTED_RANGE 332 #define _ITER_NEXT_RANGE 333 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 334 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 335 -#define _CHECK_PEP_523 336 -#define _CHECK_FUNCTION_EXACT_ARGS 337 -#define _CHECK_STACK_SPACE 338 -#define _INIT_CALL_PY_EXACT_ARGS 339 -#define _PUSH_FRAME 340 -#define _POP_JUMP_IF_FALSE 341 -#define _POP_JUMP_IF_TRUE 342 -#define _JUMP_TO_TOP 343 -#define _SAVE_CURRENT_IP 344 -#define _INSERT 345 +#define _GUARD_KEYS_VERSION 334 +#define _LOAD_ATTR_METHOD_WITH_VALUES 335 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 336 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 337 +#define _CHECK_PEP_523 338 +#define _CHECK_FUNCTION_EXACT_ARGS 339 +#define _CHECK_STACK_SPACE 340 +#define _INIT_CALL_PY_EXACT_ARGS 341 +#define _PUSH_FRAME 342 +#define _POP_JUMP_IF_FALSE 343 +#define _POP_JUMP_IF_TRUE 344 +#define _JUMP_TO_TOP 345 +#define _SAVE_CURRENT_IP 346 +#define _INSERT 347 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -478,6 +480,10 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 0; case PUSH_EXC_INFO: return 1; + case _GUARD_KEYS_VERSION: + return 1; + case _LOAD_ATTR_METHOD_WITH_VALUES: + return 1; case LOAD_ATTR_METHOD_WITH_VALUES: return 1; case LOAD_ATTR_METHOD_NO_DICT: @@ -1018,6 +1024,10 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case PUSH_EXC_INFO: return 2; + case _GUARD_KEYS_VERSION: + return 1; + case _LOAD_ATTR_METHOD_WITH_VALUES: + return 2; case LOAD_ATTR_METHOD_WITH_VALUES: return 2; case LOAD_ATTR_METHOD_NO_DICT: @@ -1420,6 +1430,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [SETUP_WITH] = { true, INSTR_FMT_IX, 0 }, [POP_BLOCK] = { true, INSTR_FMT_IX, 0 }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, + [_GUARD_KEYS_VERSION] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, + [_LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG }, [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1600,6 +1612,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { WITH_EXCEPT_START, 0, 0 } } }, [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { PUSH_EXC_INFO, 0, 0 } } }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 7, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_TYPE_1] = { .nuops = 1, .uops = { { CALL_TYPE_1, 0, 0 } } }, @@ -1665,6 +1678,8 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_ITER_JUMP_RANGE] = "_ITER_JUMP_RANGE", [_IS_ITER_EXHAUSTED_RANGE] = "_IS_ITER_EXHAUSTED_RANGE", [_ITER_NEXT_RANGE] = "_ITER_NEXT_RANGE", + [_GUARD_KEYS_VERSION] = "_GUARD_KEYS_VERSION", + [_LOAD_ATTR_METHOD_WITH_VALUES] = "_LOAD_ATTR_METHOD_WITH_VALUES", [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = "_CHECK_CALL_BOUND_METHOD_EXACT_ARGS", [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS", [_CHECK_PEP_523] = "_CHECK_PEP_523", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index 5a3848cd726245..4547d929e44527 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -627,6 +627,17 @@ break; } + case _GUARD_KEYS_VERSION: { + break; + } + + case _LOAD_ATTR_METHOD_WITH_VALUES: { + STACK_GROW(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-2)), true); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 402b27101dbdb6..e24306f461d3ec 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2769,20 +2769,16 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } - inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, owner -- attr, self if (1))) { - assert(oparg & 1); - /* Cached method object */ + op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { PyTypeObject *owner_cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); - assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && - !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), - LOAD_ATTR); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, LOAD_ATTR); + } + + op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self if (1))) { + assert(oparg & 1); + /* Cached method object */ STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = Py_NewRef(descr); @@ -2790,6 +2786,12 @@ dummy_func( self = owner; } + macro(LOAD_ATTR_METHOD_WITH_VALUES) = + unused/1 + + _GUARD_TYPE_VERSION + + _GUARD_KEYS_VERSION + + _LOAD_ATTR_METHOD_WITH_VALUES; + inst(LOAD_ATTR_METHOD_NO_DICT, (unused/1, type_version/2, unused/2, descr/4, owner -- attr, self if (1))) { assert(oparg & 1); PyTypeObject *owner_cls = Py_TYPE(owner); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index befb972f1e90f5..a50d2fe5d4a823 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2219,6 +2219,36 @@ break; } + case _GUARD_KEYS_VERSION: { + PyObject *owner; + owner = stack_pointer[-1]; + uint32_t keys_version = (uint32_t)operand; + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != + keys_version, LOAD_ATTR); + break; + } + + case _LOAD_ATTR_METHOD_WITH_VALUES: { + PyObject *owner; + PyObject *attr; + PyObject *self; + owner = stack_pointer[-1]; + PyObject *descr = (PyObject *)operand; + assert(oparg & 1); + /* Cached method object */ + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + self = owner; + STACK_GROW(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self; + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { PyObject *null; PyObject *callable; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ebb87a86de432e..03e495512fc6c0 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3557,28 +3557,33 @@ PyObject *owner; PyObject *attr; PyObject *self; + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&next_instr[1].cache); - uint32_t keys_version = read_u32(&next_instr[3].cache); - PyObject *descr = read_obj(&next_instr[5].cache); - assert(oparg & 1); - /* Cached method object */ - PyTypeObject *owner_cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); - assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && - !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), - LOAD_ATTR); - PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; - DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != - keys_version, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - attr = Py_NewRef(descr); - assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - self = owner; + { + uint32_t type_version = read_u32(&next_instr[1].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _GUARD_KEYS_VERSION + { + uint32_t keys_version = read_u32(&next_instr[3].cache); + PyTypeObject *owner_cls = Py_TYPE(owner); + PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; + DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != + keys_version, LOAD_ATTR); + } + // _LOAD_ATTR_METHOD_WITH_VALUES + { + PyObject *descr = read_obj(&next_instr[5].cache); + assert(oparg & 1); + /* Cached method object */ + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + attr = Py_NewRef(descr); + assert(_PyType_HasFeature(Py_TYPE(attr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + self = owner; + } STACK_GROW(1); stack_pointer[-2] = attr; stack_pointer[-1] = self; From 6d816ed491ff8d1234a4d27f4f9005d95c6c3dfb Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 17:52:25 -0700 Subject: [PATCH 5/9] Split LOAD_ATTR_METHOD_NO_DICT --- Include/internal/pycore_opcode_metadata.h | 32 ++++++++++++++--------- Python/abstract_interp_cases.c.h | 7 +++++ Python/bytecodes.c | 9 +++++-- Python/executor_cases.c.h | 20 ++++++++++++++ Python/generated_cases.c.h | 30 +++++++++++++-------- 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 6c418225bce034..dc5ce7e80c3dda 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -61,18 +61,19 @@ #define _ITER_NEXT_RANGE 333 #define _GUARD_KEYS_VERSION 334 #define _LOAD_ATTR_METHOD_WITH_VALUES 335 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 336 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 337 -#define _CHECK_PEP_523 338 -#define _CHECK_FUNCTION_EXACT_ARGS 339 -#define _CHECK_STACK_SPACE 340 -#define _INIT_CALL_PY_EXACT_ARGS 341 -#define _PUSH_FRAME 342 -#define _POP_JUMP_IF_FALSE 343 -#define _POP_JUMP_IF_TRUE 344 -#define _JUMP_TO_TOP 345 -#define _SAVE_CURRENT_IP 346 -#define _INSERT 347 +#define _LOAD_ATTR_METHOD_NO_DICT 336 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 337 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 338 +#define _CHECK_PEP_523 339 +#define _CHECK_FUNCTION_EXACT_ARGS 340 +#define _CHECK_STACK_SPACE 341 +#define _INIT_CALL_PY_EXACT_ARGS 342 +#define _PUSH_FRAME 343 +#define _POP_JUMP_IF_FALSE 344 +#define _POP_JUMP_IF_TRUE 345 +#define _JUMP_TO_TOP 346 +#define _SAVE_CURRENT_IP 347 +#define _INSERT 348 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -486,6 +487,8 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_METHOD_WITH_VALUES: return 1; + case _LOAD_ATTR_METHOD_NO_DICT: + return 1; case LOAD_ATTR_METHOD_NO_DICT: return 1; case LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: @@ -1030,6 +1033,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 2; case LOAD_ATTR_METHOD_WITH_VALUES: return 2; + case _LOAD_ATTR_METHOD_NO_DICT: + return 2; case LOAD_ATTR_METHOD_NO_DICT: return 2; case LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES: @@ -1433,6 +1438,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [_GUARD_KEYS_VERSION] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, [_LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG }, [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [_LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG }, [LOAD_ATTR_METHOD_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1613,6 +1619,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { WITH_EXCEPT_START, 0, 0 } } }, [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { PUSH_EXC_INFO, 0, 0 } } }, [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 7, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_TYPE_1] = { .nuops = 1, .uops = { { CALL_TYPE_1, 0, 0 } } }, @@ -1680,6 +1687,7 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_ITER_NEXT_RANGE] = "_ITER_NEXT_RANGE", [_GUARD_KEYS_VERSION] = "_GUARD_KEYS_VERSION", [_LOAD_ATTR_METHOD_WITH_VALUES] = "_LOAD_ATTR_METHOD_WITH_VALUES", + [_LOAD_ATTR_METHOD_NO_DICT] = "_LOAD_ATTR_METHOD_NO_DICT", [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = "_CHECK_CALL_BOUND_METHOD_EXACT_ARGS", [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS", [_CHECK_PEP_523] = "_CHECK_PEP_523", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index 4547d929e44527..afa8bc96cec966 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -638,6 +638,13 @@ break; } + case _LOAD_ATTR_METHOD_NO_DICT: { + STACK_GROW(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-2)), true); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e24306f461d3ec..3afb577b5cf0e8 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2792,10 +2792,9 @@ dummy_func( _GUARD_KEYS_VERSION + _LOAD_ATTR_METHOD_WITH_VALUES; - inst(LOAD_ATTR_METHOD_NO_DICT, (unused/1, type_version/2, unused/2, descr/4, owner -- attr, self if (1))) { + op(_LOAD_ATTR_METHOD_NO_DICT, (descr/4, owner -- attr, self if (1))) { assert(oparg & 1); PyTypeObject *owner_cls = Py_TYPE(owner); - DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); assert(owner_cls->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2804,6 +2803,12 @@ dummy_func( self = owner; } + macro(LOAD_ATTR_METHOD_NO_DICT) = + unused/1 + + _GUARD_TYPE_VERSION + + unused/2 + + _LOAD_ATTR_METHOD_NO_DICT; + inst(LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, owner -- attr, unused if (0))) { assert((oparg & 1) == 0); PyTypeObject *owner_cls = Py_TYPE(owner); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a50d2fe5d4a823..b35f16fb99fe2d 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2249,6 +2249,26 @@ break; } + case _LOAD_ATTR_METHOD_NO_DICT: { + PyObject *owner; + PyObject *attr; + PyObject *self; + owner = stack_pointer[-1]; + PyObject *descr = (PyObject *)operand; + assert(oparg & 1); + PyTypeObject *owner_cls = Py_TYPE(owner); + assert(owner_cls->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; + STACK_GROW(1); + stack_pointer[-2] = attr; + stack_pointer[-1] = self; + break; + } + case _CHECK_CALL_BOUND_METHOD_EXACT_ARGS: { PyObject *null; PyObject *callable; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 03e495512fc6c0..d5491509a85240 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3595,18 +3595,26 @@ PyObject *owner; PyObject *attr; PyObject *self; + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&next_instr[1].cache); - PyObject *descr = read_obj(&next_instr[5].cache); - assert(oparg & 1); - PyTypeObject *owner_cls = Py_TYPE(owner); - DEOPT_IF(owner_cls->tp_version_tag != type_version, LOAD_ATTR); - assert(owner_cls->tp_dictoffset == 0); - STAT_INC(LOAD_ATTR, hit); - assert(descr != NULL); - assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); - attr = Py_NewRef(descr); - self = owner; + { + uint32_t type_version = read_u32(&next_instr[1].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _LOAD_ATTR_METHOD_NO_DICT + { + PyObject *descr = read_obj(&next_instr[5].cache); + assert(oparg & 1); + PyTypeObject *owner_cls = Py_TYPE(owner); + assert(owner_cls->tp_dictoffset == 0); + STAT_INC(LOAD_ATTR, hit); + assert(descr != NULL); + assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)); + attr = Py_NewRef(descr); + self = owner; + } STACK_GROW(1); stack_pointer[-2] = attr; stack_pointer[-1] = self; From 5ba03d1d8a2daecd5e6d9243e561ac176ccfffc9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 17:59:22 -0700 Subject: [PATCH 6/9] Split LOAD_ATTR_SLOT (weird: action may deopt) --- Include/internal/pycore_opcode_metadata.h | 66 +++++++++++++---------- Python/abstract_interp_cases.c.h | 7 +++ Python/bytecodes.c | 11 ++-- Python/executor_cases.c.h | 19 +++++++ Python/generated_cases.c.h | 30 ++++++----- 5 files changed, 88 insertions(+), 45 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index dc5ce7e80c3dda..d2ea6c8bcecaed 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -46,34 +46,35 @@ #define _GUARD_TYPE_VERSION 318 #define _CHECK_MANAGED_OBJECT_HAS_VALUES 319 #define _LOAD_ATTR_INSTANCE_VALUE 320 -#define _IS_NONE 321 -#define _ITER_CHECK_LIST 322 -#define _ITER_JUMP_LIST 323 -#define _IS_ITER_EXHAUSTED_LIST 324 -#define _ITER_NEXT_LIST 325 -#define _ITER_CHECK_TUPLE 326 -#define _ITER_JUMP_TUPLE 327 -#define _IS_ITER_EXHAUSTED_TUPLE 328 -#define _ITER_NEXT_TUPLE 329 -#define _ITER_CHECK_RANGE 330 -#define _ITER_JUMP_RANGE 331 -#define _IS_ITER_EXHAUSTED_RANGE 332 -#define _ITER_NEXT_RANGE 333 -#define _GUARD_KEYS_VERSION 334 -#define _LOAD_ATTR_METHOD_WITH_VALUES 335 -#define _LOAD_ATTR_METHOD_NO_DICT 336 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 337 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 338 -#define _CHECK_PEP_523 339 -#define _CHECK_FUNCTION_EXACT_ARGS 340 -#define _CHECK_STACK_SPACE 341 -#define _INIT_CALL_PY_EXACT_ARGS 342 -#define _PUSH_FRAME 343 -#define _POP_JUMP_IF_FALSE 344 -#define _POP_JUMP_IF_TRUE 345 -#define _JUMP_TO_TOP 346 -#define _SAVE_CURRENT_IP 347 -#define _INSERT 348 +#define _LOAD_ATTR_SLOT 321 +#define _IS_NONE 322 +#define _ITER_CHECK_LIST 323 +#define _ITER_JUMP_LIST 324 +#define _IS_ITER_EXHAUSTED_LIST 325 +#define _ITER_NEXT_LIST 326 +#define _ITER_CHECK_TUPLE 327 +#define _ITER_JUMP_TUPLE 328 +#define _IS_ITER_EXHAUSTED_TUPLE 329 +#define _ITER_NEXT_TUPLE 330 +#define _ITER_CHECK_RANGE 331 +#define _ITER_JUMP_RANGE 332 +#define _IS_ITER_EXHAUSTED_RANGE 333 +#define _ITER_NEXT_RANGE 334 +#define _GUARD_KEYS_VERSION 335 +#define _LOAD_ATTR_METHOD_WITH_VALUES 336 +#define _LOAD_ATTR_METHOD_NO_DICT 337 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 338 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 339 +#define _CHECK_PEP_523 340 +#define _CHECK_FUNCTION_EXACT_ARGS 341 +#define _CHECK_STACK_SPACE 342 +#define _INIT_CALL_PY_EXACT_ARGS 343 +#define _PUSH_FRAME 344 +#define _POP_JUMP_IF_FALSE 345 +#define _POP_JUMP_IF_TRUE 346 +#define _JUMP_TO_TOP 347 +#define _SAVE_CURRENT_IP 348 +#define _INSERT 349 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -359,6 +360,8 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_WITH_HINT: return 1; + case _LOAD_ATTR_SLOT: + return 1; case LOAD_ATTR_SLOT: return 1; case LOAD_ATTR_CLASS: @@ -905,8 +908,10 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_WITH_HINT: return ((oparg & 1) ? 1 : 0) + 1; - case LOAD_ATTR_SLOT: + case _LOAD_ATTR_SLOT: return ((oparg & 1) ? 1 : 0) + 1; + case LOAD_ATTR_SLOT: + return (oparg & 1 ? 1 : 0) + 1; case LOAD_ATTR_CLASS: return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_PROPERTY: @@ -1374,6 +1379,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, + [_LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1601,6 +1607,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [LOAD_SUPER_ATTR_METHOD] = { .nuops = 1, .uops = { { LOAD_SUPER_ATTR_METHOD, 0, 0 } } }, [LOAD_ATTR] = { .nuops = 1, .uops = { { LOAD_ATTR, 0, 0 } } }, [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } }, + [LOAD_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 } } }, [COMPARE_OP] = { .nuops = 1, .uops = { { COMPARE_OP, 0, 0 } } }, [COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 0, 0 } } }, [COMPARE_OP_INT] = { .nuops = 1, .uops = { { COMPARE_OP_INT, 0, 0 } } }, @@ -1672,6 +1679,7 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION", [_CHECK_MANAGED_OBJECT_HAS_VALUES] = "_CHECK_MANAGED_OBJECT_HAS_VALUES", [_LOAD_ATTR_INSTANCE_VALUE] = "_LOAD_ATTR_INSTANCE_VALUE", + [_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT", [_IS_NONE] = "_IS_NONE", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", [_ITER_JUMP_LIST] = "_ITER_JUMP_LIST", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index afa8bc96cec966..47118c84e2d85f 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -474,6 +474,13 @@ break; } + case _LOAD_ATTR_SLOT: { + STACK_GROW(((oparg & 1) ? 1 : 0)); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1 - (oparg & 1 ? 1 : 0))), true); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-(oparg & 1 ? 1 : 0))), true); + break; + } + case COMPARE_OP: { STACK_SHRINK(1); PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3afb577b5cf0e8..6b6e595cb656e9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1939,10 +1939,7 @@ dummy_func( DECREF_INPUTS(); } - inst(LOAD_ATTR_SLOT, (unused/1, type_version/2, index/1, unused/5, owner -- attr, null if (oparg & 1))) { - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + op(_LOAD_ATTR_SLOT, (index/1, owner -- attr, null if (oparg & 1))) { char *addr = (char *)owner + index; attr = *(PyObject **)addr; DEOPT_IF(attr == NULL, LOAD_ATTR); @@ -1952,6 +1949,12 @@ dummy_func( DECREF_INPUTS(); } + macro(LOAD_ATTR_SLOT) = + unused/1 + + _GUARD_TYPE_VERSION + + _LOAD_ATTR_SLOT + // NOTE: This action may also deopt + unused/5; + inst(LOAD_ATTR_CLASS, (unused/1, type_version/2, unused/2, descr/4, owner -- attr, null if (oparg & 1))) { DEOPT_IF(!PyType_Check(owner), LOAD_ATTR); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index b35f16fb99fe2d..bd56a6d2cab803 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1714,6 +1714,25 @@ break; } + case _LOAD_ATTR_SLOT: { + PyObject *owner; + PyObject *attr; + PyObject *null = NULL; + owner = stack_pointer[-1]; + uint16_t index = (uint16_t)operand; + char *addr = (char *)owner + index; + attr = *(PyObject **)addr; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); + STACK_GROW(((oparg & 1) ? 1 : 0)); + stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; + if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } + break; + } + case COMPARE_OP: { PyObject *right; PyObject *left; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index d5491509a85240..826ada8933b1a4 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2504,19 +2504,25 @@ PyObject *owner; PyObject *attr; PyObject *null = NULL; + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&next_instr[1].cache); - uint16_t index = read_u16(&next_instr[3].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); - char *addr = (char *)owner + index; - attr = *(PyObject **)addr; - DEOPT_IF(attr == NULL, LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(attr); - null = NULL; - Py_DECREF(owner); + { + uint32_t type_version = read_u32(&next_instr[1].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _LOAD_ATTR_SLOT + { + uint16_t index = read_u16(&next_instr[3].cache); + char *addr = (char *)owner + index; + attr = *(PyObject **)addr; + DEOPT_IF(attr == NULL, LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(attr); + null = NULL; + Py_DECREF(owner); + } STACK_GROW(((oparg & 1) ? 1 : 0)); stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; if (oparg & 1) { stack_pointer[-(oparg & 1 ? 1 : 0)] = null; } From 5d0fa92929342e25f2ef81b8079b1ec25c8ee96c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 18:49:10 -0700 Subject: [PATCH 7/9] Split STORE_ATTR_SLOT --- Include/internal/pycore_opcode_metadata.h | 71 ++++++++++++++--------- Python/abstract_interp_cases.c.h | 9 +++ Python/bytecodes.c | 10 +++- Python/executor_cases.c.h | 26 +++++++++ Python/generated_cases.c.h | 28 +++++---- 5 files changed, 104 insertions(+), 40 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index d2ea6c8bcecaed..a2edc95f300634 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -47,34 +47,36 @@ #define _CHECK_MANAGED_OBJECT_HAS_VALUES 319 #define _LOAD_ATTR_INSTANCE_VALUE 320 #define _LOAD_ATTR_SLOT 321 -#define _IS_NONE 322 -#define _ITER_CHECK_LIST 323 -#define _ITER_JUMP_LIST 324 -#define _IS_ITER_EXHAUSTED_LIST 325 -#define _ITER_NEXT_LIST 326 -#define _ITER_CHECK_TUPLE 327 -#define _ITER_JUMP_TUPLE 328 -#define _IS_ITER_EXHAUSTED_TUPLE 329 -#define _ITER_NEXT_TUPLE 330 -#define _ITER_CHECK_RANGE 331 -#define _ITER_JUMP_RANGE 332 -#define _IS_ITER_EXHAUSTED_RANGE 333 -#define _ITER_NEXT_RANGE 334 -#define _GUARD_KEYS_VERSION 335 -#define _LOAD_ATTR_METHOD_WITH_VALUES 336 -#define _LOAD_ATTR_METHOD_NO_DICT 337 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 338 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 339 -#define _CHECK_PEP_523 340 -#define _CHECK_FUNCTION_EXACT_ARGS 341 -#define _CHECK_STACK_SPACE 342 -#define _INIT_CALL_PY_EXACT_ARGS 343 -#define _PUSH_FRAME 344 -#define _POP_JUMP_IF_FALSE 345 -#define _POP_JUMP_IF_TRUE 346 -#define _JUMP_TO_TOP 347 -#define _SAVE_CURRENT_IP 348 -#define _INSERT 349 +#define _GUARD_TYPE_VERSION_STORE 322 +#define _STORE_ATTR_SLOT 323 +#define _IS_NONE 324 +#define _ITER_CHECK_LIST 325 +#define _ITER_JUMP_LIST 326 +#define _IS_ITER_EXHAUSTED_LIST 327 +#define _ITER_NEXT_LIST 328 +#define _ITER_CHECK_TUPLE 329 +#define _ITER_JUMP_TUPLE 330 +#define _IS_ITER_EXHAUSTED_TUPLE 331 +#define _ITER_NEXT_TUPLE 332 +#define _ITER_CHECK_RANGE 333 +#define _ITER_JUMP_RANGE 334 +#define _IS_ITER_EXHAUSTED_RANGE 335 +#define _ITER_NEXT_RANGE 336 +#define _GUARD_KEYS_VERSION 337 +#define _LOAD_ATTR_METHOD_WITH_VALUES 338 +#define _LOAD_ATTR_METHOD_NO_DICT 339 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 340 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 341 +#define _CHECK_PEP_523 342 +#define _CHECK_FUNCTION_EXACT_ARGS 343 +#define _CHECK_STACK_SPACE 344 +#define _INIT_CALL_PY_EXACT_ARGS 345 +#define _PUSH_FRAME 346 +#define _POP_JUMP_IF_FALSE 347 +#define _POP_JUMP_IF_TRUE 348 +#define _JUMP_TO_TOP 349 +#define _SAVE_CURRENT_IP 350 +#define _INSERT 351 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -374,6 +376,10 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 2; case STORE_ATTR_WITH_HINT: return 2; + case _GUARD_TYPE_VERSION_STORE: + return 1; + case _STORE_ATTR_SLOT: + return 2; case STORE_ATTR_SLOT: return 2; case COMPARE_OP: @@ -922,6 +928,10 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case STORE_ATTR_WITH_HINT: return 0; + case _GUARD_TYPE_VERSION_STORE: + return 1; + case _STORE_ATTR_SLOT: + return 0; case STORE_ATTR_SLOT: return 0; case COMPARE_OP: @@ -1386,6 +1396,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG }, [STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, + [_GUARD_TYPE_VERSION_STORE] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, + [_STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC, 0 }, [STORE_ATTR_SLOT] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG }, [COMPARE_OP] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG }, [COMPARE_OP_FLOAT] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1608,6 +1620,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [LOAD_ATTR] = { .nuops = 1, .uops = { { LOAD_ATTR, 0, 0 } } }, [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } }, [LOAD_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 } } }, + [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION_STORE, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [COMPARE_OP] = { .nuops = 1, .uops = { { COMPARE_OP, 0, 0 } } }, [COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 0, 0 } } }, [COMPARE_OP_INT] = { .nuops = 1, .uops = { { COMPARE_OP_INT, 0, 0 } } }, @@ -1680,6 +1693,8 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_CHECK_MANAGED_OBJECT_HAS_VALUES] = "_CHECK_MANAGED_OBJECT_HAS_VALUES", [_LOAD_ATTR_INSTANCE_VALUE] = "_LOAD_ATTR_INSTANCE_VALUE", [_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT", + [_GUARD_TYPE_VERSION_STORE] = "_GUARD_TYPE_VERSION_STORE", + [_STORE_ATTR_SLOT] = "_STORE_ATTR_SLOT", [_IS_NONE] = "_IS_NONE", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", [_ITER_JUMP_LIST] = "_ITER_JUMP_LIST", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index 47118c84e2d85f..ef1cc5edeea240 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -481,6 +481,15 @@ break; } + case _GUARD_TYPE_VERSION_STORE: { + break; + } + + case _STORE_ATTR_SLOT: { + STACK_SHRINK(2); + break; + } + case COMPARE_OP: { STACK_SHRINK(1); PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6b6e595cb656e9..afc095c4e72647 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2083,10 +2083,13 @@ dummy_func( Py_DECREF(owner); } - inst(STORE_ATTR_SLOT, (unused/1, type_version/2, index/1, value, owner --)) { + op(_GUARD_TYPE_VERSION_STORE, (type_version/2, owner -- owner)) { PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + } + + op(_STORE_ATTR_SLOT, (index/1, value, owner --)) { char *addr = (char *)owner + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; @@ -2095,6 +2098,11 @@ dummy_func( Py_DECREF(owner); } + macro(STORE_ATTR_SLOT) = + unused/1 + + _GUARD_TYPE_VERSION_STORE + + _STORE_ATTR_SLOT; + family(COMPARE_OP, INLINE_CACHE_ENTRIES_COMPARE_OP) = { COMPARE_OP_FLOAT, COMPARE_OP_INT, diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index bd56a6d2cab803..d7eb215557d022 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1733,6 +1733,32 @@ break; } + case _GUARD_TYPE_VERSION_STORE: { + PyObject *owner; + owner = stack_pointer[-1]; + uint32_t type_version = (uint32_t)operand; + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + break; + } + + case _STORE_ATTR_SLOT: { + PyObject *owner; + PyObject *value; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t index = (uint16_t)operand; + char *addr = (char *)owner + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + *(PyObject **)addr = value; + Py_XDECREF(old_value); + Py_DECREF(owner); + STACK_SHRINK(2); + break; + } + case COMPARE_OP: { PyObject *right; PyObject *left; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 826ada8933b1a4..9b8d6234515dce 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2700,19 +2700,25 @@ TARGET(STORE_ATTR_SLOT) { PyObject *owner; PyObject *value; + // _GUARD_TYPE_VERSION_STORE owner = stack_pointer[-1]; + { + uint32_t type_version = read_u32(&next_instr[1].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + } + // _STORE_ATTR_SLOT value = stack_pointer[-2]; - uint32_t type_version = read_u32(&next_instr[1].cache); - uint16_t index = read_u16(&next_instr[3].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - char *addr = (char *)owner + index; - STAT_INC(STORE_ATTR, hit); - PyObject *old_value = *(PyObject **)addr; - *(PyObject **)addr = value; - Py_XDECREF(old_value); - Py_DECREF(owner); + { + uint16_t index = read_u16(&next_instr[3].cache); + char *addr = (char *)owner + index; + STAT_INC(STORE_ATTR, hit); + PyObject *old_value = *(PyObject **)addr; + *(PyObject **)addr = value; + Py_XDECREF(old_value); + Py_DECREF(owner); + } STACK_SHRINK(2); next_instr += 4; DISPATCH(); From b54eee3d757832ae5c5a3093e487600f1019ced4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 26 Sep 2023 20:57:32 -0700 Subject: [PATCH 8/9] Split STORE_ATTR_INSTANCE_VALUE --- Include/internal/pycore_opcode_metadata.h | 75 ++++++++++++++--------- Python/abstract_interp_cases.c.h | 9 +++ Python/bytecodes.c | 14 ++++- Python/executor_cases.c.h | 32 ++++++++++ Python/generated_cases.c.h | 47 ++++++++------ 5 files changed, 126 insertions(+), 51 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index a2edc95f300634..e661f968920080 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -47,36 +47,38 @@ #define _CHECK_MANAGED_OBJECT_HAS_VALUES 319 #define _LOAD_ATTR_INSTANCE_VALUE 320 #define _LOAD_ATTR_SLOT 321 -#define _GUARD_TYPE_VERSION_STORE 322 -#define _STORE_ATTR_SLOT 323 -#define _IS_NONE 324 -#define _ITER_CHECK_LIST 325 -#define _ITER_JUMP_LIST 326 -#define _IS_ITER_EXHAUSTED_LIST 327 -#define _ITER_NEXT_LIST 328 -#define _ITER_CHECK_TUPLE 329 -#define _ITER_JUMP_TUPLE 330 -#define _IS_ITER_EXHAUSTED_TUPLE 331 -#define _ITER_NEXT_TUPLE 332 -#define _ITER_CHECK_RANGE 333 -#define _ITER_JUMP_RANGE 334 -#define _IS_ITER_EXHAUSTED_RANGE 335 -#define _ITER_NEXT_RANGE 336 -#define _GUARD_KEYS_VERSION 337 -#define _LOAD_ATTR_METHOD_WITH_VALUES 338 -#define _LOAD_ATTR_METHOD_NO_DICT 339 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 340 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 341 -#define _CHECK_PEP_523 342 -#define _CHECK_FUNCTION_EXACT_ARGS 343 -#define _CHECK_STACK_SPACE 344 -#define _INIT_CALL_PY_EXACT_ARGS 345 -#define _PUSH_FRAME 346 -#define _POP_JUMP_IF_FALSE 347 -#define _POP_JUMP_IF_TRUE 348 -#define _JUMP_TO_TOP 349 -#define _SAVE_CURRENT_IP 350 -#define _INSERT 351 +#define _GUARD_DORV_VALUES 322 +#define _STORE_ATTR_INSTANCE_VALUE 323 +#define _GUARD_TYPE_VERSION_STORE 324 +#define _STORE_ATTR_SLOT 325 +#define _IS_NONE 326 +#define _ITER_CHECK_LIST 327 +#define _ITER_JUMP_LIST 328 +#define _IS_ITER_EXHAUSTED_LIST 329 +#define _ITER_NEXT_LIST 330 +#define _ITER_CHECK_TUPLE 331 +#define _ITER_JUMP_TUPLE 332 +#define _IS_ITER_EXHAUSTED_TUPLE 333 +#define _ITER_NEXT_TUPLE 334 +#define _ITER_CHECK_RANGE 335 +#define _ITER_JUMP_RANGE 336 +#define _IS_ITER_EXHAUSTED_RANGE 337 +#define _ITER_NEXT_RANGE 338 +#define _GUARD_KEYS_VERSION 339 +#define _LOAD_ATTR_METHOD_WITH_VALUES 340 +#define _LOAD_ATTR_METHOD_NO_DICT 341 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 342 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 343 +#define _CHECK_PEP_523 344 +#define _CHECK_FUNCTION_EXACT_ARGS 345 +#define _CHECK_STACK_SPACE 346 +#define _INIT_CALL_PY_EXACT_ARGS 347 +#define _PUSH_FRAME 348 +#define _POP_JUMP_IF_FALSE 349 +#define _POP_JUMP_IF_TRUE 350 +#define _JUMP_TO_TOP 351 +#define _SAVE_CURRENT_IP 352 +#define _INSERT 353 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -372,6 +374,10 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: return 1; + case _GUARD_DORV_VALUES: + return 1; + case _STORE_ATTR_INSTANCE_VALUE: + return 2; case STORE_ATTR_INSTANCE_VALUE: return 2; case STORE_ATTR_WITH_HINT: @@ -924,6 +930,10 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: return 1; + case _GUARD_DORV_VALUES: + return 1; + case _STORE_ATTR_INSTANCE_VALUE: + return 0; case STORE_ATTR_INSTANCE_VALUE: return 0; case STORE_ATTR_WITH_HINT: @@ -1394,6 +1404,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, + [_GUARD_DORV_VALUES] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, + [_STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC, 0 }, [STORE_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IXC000, HAS_DEOPT_FLAG }, [STORE_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG }, [_GUARD_TYPE_VERSION_STORE] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, @@ -1620,6 +1632,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [LOAD_ATTR] = { .nuops = 1, .uops = { { LOAD_ATTR, 0, 0 } } }, [LOAD_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_MANAGED_OBJECT_HAS_VALUES, 0, 0 }, { _LOAD_ATTR_INSTANCE_VALUE, 1, 3 } } }, [LOAD_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 } } }, + [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION_STORE, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION_STORE, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [COMPARE_OP] = { .nuops = 1, .uops = { { COMPARE_OP, 0, 0 } } }, [COMPARE_OP_FLOAT] = { .nuops = 1, .uops = { { COMPARE_OP_FLOAT, 0, 0 } } }, @@ -1693,6 +1706,8 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_CHECK_MANAGED_OBJECT_HAS_VALUES] = "_CHECK_MANAGED_OBJECT_HAS_VALUES", [_LOAD_ATTR_INSTANCE_VALUE] = "_LOAD_ATTR_INSTANCE_VALUE", [_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT", + [_GUARD_DORV_VALUES] = "_GUARD_DORV_VALUES", + [_STORE_ATTR_INSTANCE_VALUE] = "_STORE_ATTR_INSTANCE_VALUE", [_GUARD_TYPE_VERSION_STORE] = "_GUARD_TYPE_VERSION_STORE", [_STORE_ATTR_SLOT] = "_STORE_ATTR_SLOT", [_IS_NONE] = "_IS_NONE", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index ef1cc5edeea240..a810881a756c63 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -481,6 +481,15 @@ break; } + case _GUARD_DORV_VALUES: { + break; + } + + case _STORE_ATTR_INSTANCE_VALUE: { + STACK_SHRINK(2); + break; + } + case _GUARD_TYPE_VERSION_STORE: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index afc095c4e72647..36d81fefc9bfe0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2022,13 +2022,15 @@ dummy_func( DISPATCH_INLINED(new_frame); } - inst(STORE_ATTR_INSTANCE_VALUE, (unused/1, type_version/2, index/1, value, owner --)) { + op(_GUARD_DORV_VALUES, (owner -- owner)) { PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + } + + op(_STORE_ATTR_INSTANCE_VALUE, (index/1, value, owner --)) { + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); PyDictValues *values = _PyDictOrValues_GetValues(dorv); PyObject *old_value = values->values[index]; @@ -2042,6 +2044,12 @@ dummy_func( Py_DECREF(owner); } + macro(STORE_ATTR_INSTANCE_VALUE) = + unused/1 + + _GUARD_TYPE_VERSION_STORE + + _GUARD_DORV_VALUES + + _STORE_ATTR_INSTANCE_VALUE; + inst(STORE_ATTR_WITH_HINT, (unused/1, type_version/2, hint/1, value, owner --)) { PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index d7eb215557d022..78eb70877b53d3 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1733,6 +1733,38 @@ break; } + case _GUARD_DORV_VALUES: { + PyObject *owner; + owner = stack_pointer[-1]; + PyTypeObject *tp = Py_TYPE(owner); + assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + break; + } + + case _STORE_ATTR_INSTANCE_VALUE: { + PyObject *owner; + PyObject *value; + owner = stack_pointer[-1]; + value = stack_pointer[-2]; + uint16_t index = (uint16_t)operand; + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + STAT_INC(STORE_ATTR, hit); + PyDictValues *values = _PyDictOrValues_GetValues(dorv); + PyObject *old_value = values->values[index]; + values->values[index] = value; + if (old_value == NULL) { + _PyDictValues_AddToInsertionOrder(values, index); + } + else { + Py_DECREF(old_value); + } + Py_DECREF(owner); + STACK_SHRINK(2); + break; + } + case _GUARD_TYPE_VERSION_STORE: { PyObject *owner; owner = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9b8d6234515dce..7b73b97b514987 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2621,27 +2621,38 @@ TARGET(STORE_ATTR_INSTANCE_VALUE) { PyObject *owner; PyObject *value; + // _GUARD_TYPE_VERSION_STORE owner = stack_pointer[-1]; - value = stack_pointer[-2]; - uint32_t type_version = read_u32(&next_instr[1].cache); - uint16_t index = read_u16(&next_instr[3].cache); - PyTypeObject *tp = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); - assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); - DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); - STAT_INC(STORE_ATTR, hit); - PyDictValues *values = _PyDictOrValues_GetValues(dorv); - PyObject *old_value = values->values[index]; - values->values[index] = value; - if (old_value == NULL) { - _PyDictValues_AddToInsertionOrder(values, index); + { + uint32_t type_version = read_u32(&next_instr[1].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); } - else { - Py_DECREF(old_value); + // _GUARD_DORV_VALUES + { + PyTypeObject *tp = Py_TYPE(owner); + assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR); + } + // _STORE_ATTR_INSTANCE_VALUE + value = stack_pointer[-2]; + { + uint16_t index = read_u16(&next_instr[3].cache); + PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); + STAT_INC(STORE_ATTR, hit); + PyDictValues *values = _PyDictOrValues_GetValues(dorv); + PyObject *old_value = values->values[index]; + values->values[index] = value; + if (old_value == NULL) { + _PyDictValues_AddToInsertionOrder(values, index); + } + else { + Py_DECREF(old_value); + } + Py_DECREF(owner); } - Py_DECREF(owner); STACK_SHRINK(2); next_instr += 4; DISPATCH(); From 85fb1e72177d21e22214793561c1c922cdfa5258 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 27 Sep 2023 13:58:04 -0700 Subject: [PATCH 9/9] Fix LOAD_ATTR_METHOD_WITH_VALUES I had dropped one of the DEOPT_IF() calls! :-( --- Include/internal/pycore_opcode_metadata.h | 39 +++++++++++++---------- Python/abstract_interp_cases.c.h | 4 +++ Python/bytecodes.c | 10 ++++++ Python/executor_cases.c.h | 12 +++++++ Python/generated_cases.c.h | 9 ++++++ 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index e661f968920080..16c1637e496033 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -64,21 +64,22 @@ #define _ITER_JUMP_RANGE 336 #define _IS_ITER_EXHAUSTED_RANGE 337 #define _ITER_NEXT_RANGE 338 -#define _GUARD_KEYS_VERSION 339 -#define _LOAD_ATTR_METHOD_WITH_VALUES 340 -#define _LOAD_ATTR_METHOD_NO_DICT 341 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 342 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 343 -#define _CHECK_PEP_523 344 -#define _CHECK_FUNCTION_EXACT_ARGS 345 -#define _CHECK_STACK_SPACE 346 -#define _INIT_CALL_PY_EXACT_ARGS 347 -#define _PUSH_FRAME 348 -#define _POP_JUMP_IF_FALSE 349 -#define _POP_JUMP_IF_TRUE 350 -#define _JUMP_TO_TOP 351 -#define _SAVE_CURRENT_IP 352 -#define _INSERT 353 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 339 +#define _GUARD_KEYS_VERSION 340 +#define _LOAD_ATTR_METHOD_WITH_VALUES 341 +#define _LOAD_ATTR_METHOD_NO_DICT 342 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 343 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 344 +#define _CHECK_PEP_523 345 +#define _CHECK_FUNCTION_EXACT_ARGS 346 +#define _CHECK_STACK_SPACE 347 +#define _INIT_CALL_PY_EXACT_ARGS 348 +#define _PUSH_FRAME 349 +#define _POP_JUMP_IF_FALSE 350 +#define _POP_JUMP_IF_TRUE 351 +#define _JUMP_TO_TOP 352 +#define _SAVE_CURRENT_IP 353 +#define _INSERT 354 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -496,6 +497,8 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 0; case PUSH_EXC_INFO: return 1; + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: + return 1; case _GUARD_KEYS_VERSION: return 1; case _LOAD_ATTR_METHOD_WITH_VALUES: @@ -1052,6 +1055,8 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return 0; case PUSH_EXC_INFO: return 2; + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: + return 1; case _GUARD_KEYS_VERSION: return 1; case _LOAD_ATTR_METHOD_WITH_VALUES: @@ -1465,6 +1470,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [SETUP_WITH] = { true, INSTR_FMT_IX, 0 }, [POP_BLOCK] = { true, INSTR_FMT_IX, 0 }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, + [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, [_GUARD_KEYS_VERSION] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, [_LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG }, [LOAD_ATTR_METHOD_WITH_VALUES] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1651,7 +1657,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [GET_YIELD_FROM_ITER] = { .nuops = 1, .uops = { { GET_YIELD_FROM_ITER, 0, 0 } } }, [WITH_EXCEPT_START] = { .nuops = 1, .uops = { { WITH_EXCEPT_START, 0, 0 } } }, [PUSH_EXC_INFO] = { .nuops = 1, .uops = { { PUSH_EXC_INFO, 0, 0 } } }, - [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, + [LOAD_ATTR_METHOD_WITH_VALUES] = { .nuops = 4, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, 0, 0 }, { _GUARD_KEYS_VERSION, 2, 3 }, { _LOAD_ATTR_METHOD_WITH_VALUES, 4, 5 } } }, [LOAD_ATTR_METHOD_NO_DICT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_METHOD_NO_DICT, 4, 5 } } }, [CALL_BOUND_METHOD_EXACT_ARGS] = { .nuops = 9, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _INIT_CALL_BOUND_METHOD_EXACT_ARGS, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 7, .uops = { { _CHECK_PEP_523, 0, 0 }, { _CHECK_FUNCTION_EXACT_ARGS, 2, 1 }, { _CHECK_STACK_SPACE, 0, 0 }, { _INIT_CALL_PY_EXACT_ARGS, 0, 0 }, { _SET_IP, 7, 3 }, { _SAVE_CURRENT_IP, 0, 0 }, { _PUSH_FRAME, 0, 0 } } }, @@ -1723,6 +1729,7 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_ITER_JUMP_RANGE] = "_ITER_JUMP_RANGE", [_IS_ITER_EXHAUSTED_RANGE] = "_IS_ITER_EXHAUSTED_RANGE", [_ITER_NEXT_RANGE] = "_ITER_NEXT_RANGE", + [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = "_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT", [_GUARD_KEYS_VERSION] = "_GUARD_KEYS_VERSION", [_LOAD_ATTR_METHOD_WITH_VALUES] = "_LOAD_ATTR_METHOD_WITH_VALUES", [_LOAD_ATTR_METHOD_NO_DICT] = "_LOAD_ATTR_METHOD_NO_DICT", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index a810881a756c63..61b1db9e5a1543 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -652,6 +652,10 @@ break; } + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: { + break; + } + case _GUARD_KEYS_VERSION: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 36d81fefc9bfe0..0f89779fb9245f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2788,6 +2788,15 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } + op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner)) { + PyTypeObject *owner_cls = Py_TYPE(owner); + assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), + LOAD_ATTR); + } + op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; @@ -2808,6 +2817,7 @@ dummy_func( macro(LOAD_ATTR_METHOD_WITH_VALUES) = unused/1 + _GUARD_TYPE_VERSION + + _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + _GUARD_KEYS_VERSION + _LOAD_ATTR_METHOD_WITH_VALUES; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 78eb70877b53d3..981db6973f281a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2296,6 +2296,18 @@ break; } + case _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT: { + PyObject *owner; + owner = stack_pointer[-1]; + PyTypeObject *owner_cls = Py_TYPE(owner); + assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), + LOAD_ATTR); + break; + } + case _GUARD_KEYS_VERSION: { PyObject *owner; owner = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 7b73b97b514987..17df44019a6581 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3588,6 +3588,15 @@ assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); } + // _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT + { + PyTypeObject *owner_cls = Py_TYPE(owner); + assert(owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); + PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); + DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && + !_PyObject_MakeInstanceAttributesFromDict(owner, dorv), + LOAD_ATTR); + } // _GUARD_KEYS_VERSION { uint32_t keys_version = read_u32(&next_instr[3].cache);