From 3a0e8ea3410d56ddc1fd6acc4f9137b86193df2e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 9 Oct 2023 10:24:50 +0200 Subject: [PATCH 1/3] Rearrange cache for LOAD_ATTR_PROPERTY --- Include/internal/pycore_code.h | 9 ++++++++- Python/bytecodes.c | 2 +- Python/generated_cases.c.h | 4 ++-- Python/specialize.c | 7 +++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index eaf84a9c94fc9b..ade7e5d4ded10f 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -74,8 +74,15 @@ typedef struct { uint16_t descr[4]; } _PyLoadMethodCache; +typedef struct { + uint16_t counter; + uint16_t type_version[2]; + uint16_t func[4]; + uint16_t func_version[2]; +} _PyLoadPropertyCache; + -// MUST be the max(_PyAttrCache, _PyLoadMethodCache) +// MUST be max(_PyAttrCache, _PyLoadMethodCache, _PyLoadPropertyCache) #define INLINE_CACHE_ENTRIES_LOAD_ATTR CACHE_ENTRIES(_PyLoadMethodCache) #define INLINE_CACHE_ENTRIES_STORE_ATTR CACHE_ENTRIES(_PyAttrCache) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index da98630f53943a..9f9a8a1b28faca 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2021,7 +2021,7 @@ dummy_func( unused/2 + _LOAD_ATTR_CLASS; - inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused, unused if (0))) { + inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, fget/4, func_version/2, owner -- unused, unused if (0))) { assert((oparg & 1) == 0); DEOPT_IF(tstate->interp->eval_frame); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 3b4cc7562da081..1b004694e102a5 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2927,8 +2927,8 @@ PyObject *owner; owner = stack_pointer[-1]; uint32_t type_version = read_u32(&this_instr[2].cache); - uint32_t func_version = read_u32(&this_instr[4].cache); - PyObject *fget = read_obj(&this_instr[6].cache); + PyObject *fget = read_obj(&this_instr[4].cache); + uint32_t func_version = read_u32(&this_instr[8].cache); assert((oparg & 1) == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); diff --git a/Python/specialize.c b/Python/specialize.c index ba704cbbb464d7..fb0debd6e66a7b 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -891,7 +891,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) } case PROPERTY: { - _PyLoadMethodCache *lm_cache = (_PyLoadMethodCache *)(instr + 1); + _PyLoadPropertyCache *lm_cache = (_PyLoadPropertyCache *)(instr + 1); assert(Py_TYPE(descr) == &PyProperty_Type); PyObject *fget = ((_PyPropertyObject *)descr)->prop_get; if (fget == NULL) { @@ -917,11 +917,10 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER); goto fail; } - write_u32(lm_cache->keys_version, version); assert(type->tp_version_tag != 0); write_u32(lm_cache->type_version, type->tp_version_tag); - /* borrowed */ - write_obj(lm_cache->descr, fget); + write_obj(lm_cache->func, fget); // borrowed + write_u32(lm_cache->func_version, version); instr->op.code = LOAD_ATTR_PROPERTY; goto success; } From 5e43670b2bafedc27a4da824d8a535531d972576 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 9 Oct 2023 10:48:59 +0200 Subject: [PATCH 2/3] Split LOAD_ATTR_PROPERTY into uops (but final uop is not yet viable) --- Include/internal/pycore_opcode_metadata.h | 119 +++++++++++++--------- Python/abstract_interp_cases.c.h | 10 ++ Python/bytecodes.c | 32 +++--- Python/executor_cases.c.h | 19 ++++ Python/generated_cases.c.h | 62 ++++++----- 5 files changed, 157 insertions(+), 85 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4d98b23df5d927..ed964c719cd4f1 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -71,55 +71,58 @@ #define _LOAD_ATTR_SLOT 343 #define _CHECK_ATTR_CLASS 344 #define _LOAD_ATTR_CLASS 345 -#define _GUARD_DORV_VALUES 346 -#define _STORE_ATTR_INSTANCE_VALUE 347 -#define _STORE_ATTR_SLOT 348 -#define _SPECIALIZE_COMPARE_OP 349 -#define _COMPARE_OP 350 -#define _POP_JUMP_IF_FALSE 351 -#define _POP_JUMP_IF_TRUE 352 -#define _IS_NONE 353 -#define _SPECIALIZE_FOR_ITER 354 -#define _FOR_ITER 355 -#define _ITER_CHECK_LIST 356 -#define _ITER_JUMP_LIST 357 -#define _GUARD_NOT_EXHAUSTED_LIST 358 -#define _ITER_NEXT_LIST 359 -#define _ITER_CHECK_TUPLE 360 -#define _ITER_JUMP_TUPLE 361 -#define _GUARD_NOT_EXHAUSTED_TUPLE 362 -#define _ITER_NEXT_TUPLE 363 -#define _ITER_CHECK_RANGE 364 -#define _ITER_JUMP_RANGE 365 -#define _GUARD_NOT_EXHAUSTED_RANGE 366 -#define _ITER_NEXT_RANGE 367 -#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 368 -#define _GUARD_KEYS_VERSION 369 -#define _LOAD_ATTR_METHOD_WITH_VALUES 370 -#define _LOAD_ATTR_METHOD_NO_DICT 371 -#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 372 -#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 373 -#define _CHECK_ATTR_METHOD_LAZY_DICT 374 -#define _LOAD_ATTR_METHOD_LAZY_DICT 375 -#define _SPECIALIZE_CALL 376 -#define _CALL 377 -#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 378 -#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 379 -#define _CHECK_PEP_523 380 -#define _CHECK_FUNCTION_EXACT_ARGS 381 -#define _CHECK_STACK_SPACE 382 -#define _INIT_CALL_PY_EXACT_ARGS 383 -#define _PUSH_FRAME 384 -#define _SPECIALIZE_BINARY_OP 385 -#define _BINARY_OP 386 -#define _GUARD_IS_TRUE_POP 387 -#define _GUARD_IS_FALSE_POP 388 -#define _GUARD_IS_NONE_POP 389 -#define _GUARD_IS_NOT_NONE_POP 390 -#define _JUMP_TO_TOP 391 -#define _SAVE_RETURN_OFFSET 392 -#define _INSERT 393 -#define _CHECK_VALIDITY 394 +#define _HELPER_LOAD_FUNC_FROM_CACHE 346 +#define _CHECK_FUNC_VERSION 347 +#define _LOAD_ATTR_PROPERTY 348 +#define _GUARD_DORV_VALUES 349 +#define _STORE_ATTR_INSTANCE_VALUE 350 +#define _STORE_ATTR_SLOT 351 +#define _SPECIALIZE_COMPARE_OP 352 +#define _COMPARE_OP 353 +#define _POP_JUMP_IF_FALSE 354 +#define _POP_JUMP_IF_TRUE 355 +#define _IS_NONE 356 +#define _SPECIALIZE_FOR_ITER 357 +#define _FOR_ITER 358 +#define _ITER_CHECK_LIST 359 +#define _ITER_JUMP_LIST 360 +#define _GUARD_NOT_EXHAUSTED_LIST 361 +#define _ITER_NEXT_LIST 362 +#define _ITER_CHECK_TUPLE 363 +#define _ITER_JUMP_TUPLE 364 +#define _GUARD_NOT_EXHAUSTED_TUPLE 365 +#define _ITER_NEXT_TUPLE 366 +#define _ITER_CHECK_RANGE 367 +#define _ITER_JUMP_RANGE 368 +#define _GUARD_NOT_EXHAUSTED_RANGE 369 +#define _ITER_NEXT_RANGE 370 +#define _GUARD_DORV_VALUES_INST_ATTR_FROM_DICT 371 +#define _GUARD_KEYS_VERSION 372 +#define _LOAD_ATTR_METHOD_WITH_VALUES 373 +#define _LOAD_ATTR_METHOD_NO_DICT 374 +#define _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES 375 +#define _LOAD_ATTR_NONDESCRIPTOR_NO_DICT 376 +#define _CHECK_ATTR_METHOD_LAZY_DICT 377 +#define _LOAD_ATTR_METHOD_LAZY_DICT 378 +#define _SPECIALIZE_CALL 379 +#define _CALL 380 +#define _CHECK_CALL_BOUND_METHOD_EXACT_ARGS 381 +#define _INIT_CALL_BOUND_METHOD_EXACT_ARGS 382 +#define _CHECK_PEP_523 383 +#define _CHECK_FUNCTION_EXACT_ARGS 384 +#define _CHECK_STACK_SPACE 385 +#define _INIT_CALL_PY_EXACT_ARGS 386 +#define _PUSH_FRAME 387 +#define _SPECIALIZE_BINARY_OP 388 +#define _BINARY_OP 389 +#define _GUARD_IS_TRUE_POP 390 +#define _GUARD_IS_FALSE_POP 391 +#define _GUARD_IS_NONE_POP 392 +#define _GUARD_IS_NOT_NONE_POP 393 +#define _JUMP_TO_TOP 394 +#define _SAVE_RETURN_OFFSET 395 +#define _INSERT 396 +#define _CHECK_VALIDITY 397 extern int _PyOpcode_num_popped(int opcode, int oparg, bool jump); #ifdef NEED_OPCODE_METADATA @@ -459,6 +462,12 @@ int _PyOpcode_num_popped(int opcode, int oparg, bool jump) { return 1; case LOAD_ATTR_CLASS: return 1; + case _HELPER_LOAD_FUNC_FROM_CACHE: + return 0; + case _CHECK_FUNC_VERSION: + return 1; + case _LOAD_ATTR_PROPERTY: + return 2; case LOAD_ATTR_PROPERTY: return 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: @@ -1097,6 +1106,12 @@ int _PyOpcode_num_pushed(int opcode, int oparg, bool jump) { return ((oparg & 1) ? 1 : 0) + 1; case LOAD_ATTR_CLASS: return (oparg & 1 ? 1 : 0) + 1; + case _HELPER_LOAD_FUNC_FROM_CACHE: + return 1; + case _CHECK_FUNC_VERSION: + return 1; + case _LOAD_ATTR_PROPERTY: + return 1; case LOAD_ATTR_PROPERTY: return 1; case LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN: @@ -1634,6 +1649,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [_CHECK_ATTR_CLASS] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, [_LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC000, HAS_ARG_FLAG }, [LOAD_ATTR_CLASS] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [_HELPER_LOAD_FUNC_FROM_CACHE] = { true, INSTR_FMT_IXC000, HAS_ESCAPES_FLAG }, + [_CHECK_FUNC_VERSION] = { true, INSTR_FMT_IXC0, HAS_DEOPT_FLAG }, + [_LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_PROPERTY] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [_GUARD_DORV_VALUES] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, @@ -1995,6 +2013,9 @@ const char * const _PyOpcode_uop_name[OPCODE_UOP_NAME_SIZE] = { [_LOAD_ATTR_SLOT] = "_LOAD_ATTR_SLOT", [_CHECK_ATTR_CLASS] = "_CHECK_ATTR_CLASS", [_LOAD_ATTR_CLASS] = "_LOAD_ATTR_CLASS", + [_HELPER_LOAD_FUNC_FROM_CACHE] = "_HELPER_LOAD_FUNC_FROM_CACHE", + [_CHECK_FUNC_VERSION] = "_CHECK_FUNC_VERSION", + [_LOAD_ATTR_PROPERTY] = "_LOAD_ATTR_PROPERTY", [_GUARD_DORV_VALUES] = "_GUARD_DORV_VALUES", [_STORE_ATTR_INSTANCE_VALUE] = "_STORE_ATTR_INSTANCE_VALUE", [_STORE_ATTR_SLOT] = "_STORE_ATTR_SLOT", diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index a2f6aa8def8f69..821baf17896380 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -518,6 +518,16 @@ break; } + case _HELPER_LOAD_FUNC_FROM_CACHE: { + STACK_GROW(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + break; + } + + case _CHECK_FUNC_VERSION: { + break; + } + case _GUARD_DORV_VALUES: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 9f9a8a1b28faca..ab8694dc81d552 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2021,23 +2021,23 @@ dummy_func( unused/2 + _LOAD_ATTR_CLASS; - inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, fget/4, func_version/2, owner -- unused, unused if (0))) { - assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame); - - PyTypeObject *cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version); + op(_HELPER_LOAD_FUNC_FROM_CACHE, (fget/4 -- func: PyFunctionObject *)) { assert(Py_IS_TYPE(fget, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)fget; + func = (PyFunctionObject *)fget; + } + + op(_CHECK_FUNC_VERSION, (func_version/2, func: PyFunctionObject * -- func: PyFunctionObject *)) { assert(func_version != 0); - DEOPT_IF(f->func_version != func_version); - PyCodeObject *code = (PyCodeObject *)f->func_code; + DEOPT_IF(func->func_version != func_version); + } + + op(_LOAD_ATTR_PROPERTY, (owner, func: PyFunctionObject * -- unused, unused if (0))) { + assert((oparg & 1) == 0); + PyCodeObject *code = (PyCodeObject *)func->func_code; assert(code->co_argcount == 1); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); STAT_INC(LOAD_ATTR, hit); - Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, Py_NewRef(func), 1); // Manipulate stack directly because we exit with DISPATCH_INLINED(). STACK_SHRINK(1); new_frame->localsplus[0] = owner; @@ -2045,6 +2045,14 @@ dummy_func( DISPATCH_INLINED(new_frame); } + macro(LOAD_ATTR_PROPERTY) = + unused/1 + + _CHECK_PEP_523 + + _GUARD_TYPE_VERSION + + _HELPER_LOAD_FUNC_FROM_CACHE + + _CHECK_FUNC_VERSION + + _LOAD_ATTR_PROPERTY; + inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused, unused if (0))) { assert((oparg & 1) == 0); DEOPT_IF(tstate->interp->eval_frame); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4e29fb9f0fa93d..c1c8c13c72cb97 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1743,6 +1743,25 @@ break; } + case _HELPER_LOAD_FUNC_FROM_CACHE: { + PyFunctionObject *func; + PyObject *fget = (PyObject *)operand; + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + func = (PyFunctionObject *)fget; + STACK_GROW(1); + stack_pointer[-1] = (PyObject *)func; + break; + } + + case _CHECK_FUNC_VERSION: { + PyFunctionObject *func; + func = (PyFunctionObject *)stack_pointer[-1]; + uint32_t func_version = (uint32_t)operand; + assert(func_version != 0); + DEOPT_IF(func->func_version != func_version, _CHECK_FUNC_VERSION); + break; + } + case _GUARD_DORV_VALUES: { PyObject *owner; owner = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 1b004694e102a5..8ccbcc05ab6111 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2925,31 +2925,45 @@ next_instr += 10; INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); PyObject *owner; + PyFunctionObject *func; + // _CHECK_PEP_523 + { + DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); + } + // _GUARD_TYPE_VERSION owner = stack_pointer[-1]; - uint32_t type_version = read_u32(&this_instr[2].cache); - PyObject *fget = read_obj(&this_instr[4].cache); - uint32_t func_version = read_u32(&this_instr[8].cache); - assert((oparg & 1) == 0); - DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); - - PyTypeObject *cls = Py_TYPE(owner); - assert(type_version != 0); - DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR); - assert(Py_IS_TYPE(fget, &PyFunction_Type)); - PyFunctionObject *f = (PyFunctionObject *)fget; - assert(func_version != 0); - DEOPT_IF(f->func_version != func_version, LOAD_ATTR); - PyCodeObject *code = (PyCodeObject *)f->func_code; - assert(code->co_argcount == 1); - DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); - STAT_INC(LOAD_ATTR, hit); - Py_INCREF(fget); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); - new_frame->localsplus[0] = owner; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + { + uint32_t type_version = read_u32(&this_instr[2].cache); + PyTypeObject *tp = Py_TYPE(owner); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + } + // _HELPER_LOAD_FUNC_FROM_CACHE + { + PyObject *fget = read_obj(&this_instr[4].cache); + assert(Py_IS_TYPE(fget, &PyFunction_Type)); + func = (PyFunctionObject *)fget; + } + // _CHECK_FUNC_VERSION + { + uint32_t func_version = read_u32(&this_instr[8].cache); + assert(func_version != 0); + DEOPT_IF(func->func_version != func_version, LOAD_ATTR); + } + // _LOAD_ATTR_PROPERTY + { + assert((oparg & 1) == 0); + PyCodeObject *code = (PyCodeObject *)func->func_code; + assert(code->co_argcount == 1); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); + STAT_INC(LOAD_ATTR, hit); + _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, Py_NewRef(func), 1); + // Manipulate stack directly because we exit with DISPATCH_INLINED(). + STACK_SHRINK(1); + new_frame->localsplus[0] = owner; + frame->return_offset = (uint16_t)(next_instr - this_instr); + DISPATCH_INLINED(new_frame); + } } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { From 0b71d63562c01f218e1033f4f58b08ae414c209a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 9 Oct 2023 14:54:00 +0200 Subject: [PATCH 3/3] Make _LOAD_ATTR_PROPERTY viable; at the cost of hacks --- Include/internal/pycore_opcode_metadata.h | 1 + Python/abstract_interp_cases.c.h | 7 +++++ Python/bytecodes.c | 18 +++++------ Python/executor_cases.c.h | 20 ++++++++++++ Python/generated_cases.c.h | 38 ++++++++++++++++++++--- Tools/cases_generator/stacking.py | 3 ++ 6 files changed, 74 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index ed964c719cd4f1..035310d935138a 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1903,6 +1903,7 @@ const struct opcode_macro_expansion _PyOpcode_macro_expansion[OPCODE_MACRO_EXPAN [LOAD_ATTR_WITH_HINT] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _CHECK_ATTR_WITH_HINT, 0, 0 }, { _LOAD_ATTR_WITH_HINT, 1, 3 } } }, [LOAD_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _LOAD_ATTR_SLOT, 1, 3 } } }, [LOAD_ATTR_CLASS] = { .nuops = 2, .uops = { { _CHECK_ATTR_CLASS, 2, 1 }, { _LOAD_ATTR_CLASS, 4, 5 } } }, + [LOAD_ATTR_PROPERTY] = { .nuops = 7, .uops = { { _CHECK_PEP_523, 0, 0 }, { _GUARD_TYPE_VERSION, 2, 1 }, { _HELPER_LOAD_FUNC_FROM_CACHE, 4, 3 }, { _CHECK_FUNC_VERSION, 2, 7 }, { _LOAD_ATTR_PROPERTY, 0, 0 }, { _SAVE_RETURN_OFFSET, 7, 9 }, { _PUSH_FRAME, 0, 0 } } }, [STORE_ATTR_INSTANCE_VALUE] = { .nuops = 3, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _GUARD_DORV_VALUES, 0, 0 }, { _STORE_ATTR_INSTANCE_VALUE, 1, 3 } } }, [STORE_ATTR_SLOT] = { .nuops = 2, .uops = { { _GUARD_TYPE_VERSION, 2, 1 }, { _STORE_ATTR_SLOT, 1, 3 } } }, [COMPARE_OP] = { .nuops = 1, .uops = { { _COMPARE_OP, 0, 0 } } }, diff --git a/Python/abstract_interp_cases.c.h b/Python/abstract_interp_cases.c.h index 821baf17896380..bea9d2dbe8df9a 100644 --- a/Python/abstract_interp_cases.c.h +++ b/Python/abstract_interp_cases.c.h @@ -528,6 +528,13 @@ break; } + case _LOAD_ATTR_PROPERTY: { + STACK_SHRINK(1); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(-1)), true); + PARTITIONNODE_OVERWRITE((_Py_PARTITIONNODE_t *)PARTITIONNODE_NULLROOT, PEEK(-(0)), true); + break; + } + case _GUARD_DORV_VALUES: { break; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ab8694dc81d552..6eb739353f7a48 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2021,28 +2021,26 @@ dummy_func( unused/2 + _LOAD_ATTR_CLASS; - op(_HELPER_LOAD_FUNC_FROM_CACHE, (fget/4 -- func: PyFunctionObject *)) { + op(_HELPER_LOAD_FUNC_FROM_CACHE, (fget/4 -- func: PyFunctionObject*)) { assert(Py_IS_TYPE(fget, &PyFunction_Type)); func = (PyFunctionObject *)fget; } - op(_CHECK_FUNC_VERSION, (func_version/2, func: PyFunctionObject * -- func: PyFunctionObject *)) { + op(_CHECK_FUNC_VERSION, (func_version/2, func: PyFunctionObject* -- func: PyFunctionObject*)) { assert(func_version != 0); DEOPT_IF(func->func_version != func_version); } - op(_LOAD_ATTR_PROPERTY, (owner, func: PyFunctionObject * -- unused, unused if (0))) { + op(_LOAD_ATTR_PROPERTY, (owner, func: PyFunctionObject* -- new_frame: _PyInterpreterFrame*, unused if (0))) { assert((oparg & 1) == 0); PyCodeObject *code = (PyCodeObject *)func->func_code; assert(code->co_argcount == 1); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); STAT_INC(LOAD_ATTR, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, Py_NewRef(func), 1); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); + Py_INCREF(func); + new_frame = _PyFrame_PushUnchecked(tstate, func, 1); new_frame->localsplus[0] = owner; - frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + stack_pointer[-1] = (PyObject *)new_frame; // Unfortunately this is needed } macro(LOAD_ATTR_PROPERTY) = @@ -2051,7 +2049,9 @@ dummy_func( _GUARD_TYPE_VERSION + _HELPER_LOAD_FUNC_FROM_CACHE + _CHECK_FUNC_VERSION + - _LOAD_ATTR_PROPERTY; + _LOAD_ATTR_PROPERTY + + _SAVE_RETURN_OFFSET + + _PUSH_FRAME; inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused, unused if (0))) { assert((oparg & 1) == 0); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index c1c8c13c72cb97..1e9f3d8c9197da 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1762,6 +1762,26 @@ break; } + case _LOAD_ATTR_PROPERTY: { + PyFunctionObject *func; + PyObject *owner; + _PyInterpreterFrame *new_frame; + func = (PyFunctionObject *)stack_pointer[-1]; + owner = stack_pointer[-2]; + assert((oparg & 1) == 0); + PyCodeObject *code = (PyCodeObject *)func->func_code; + assert(code->co_argcount == 1); + DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), _LOAD_ATTR_PROPERTY); + STAT_INC(LOAD_ATTR, hit); + Py_INCREF(func); + new_frame = _PyFrame_PushUnchecked(tstate, func, 1); + new_frame->localsplus[0] = owner; + stack_pointer[-1] = (PyObject *)new_frame; // Unfortunately this is needed + STACK_SHRINK(1); + stack_pointer[-1] = (PyObject *)new_frame; + break; + } + case _GUARD_DORV_VALUES: { PyObject *owner; owner = stack_pointer[-1]; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8ccbcc05ab6111..8adaa4f052d558 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2926,6 +2926,7 @@ INSTRUCTION_STATS(LOAD_ATTR_PROPERTY); PyObject *owner; PyFunctionObject *func; + _PyInterpreterFrame *new_frame; // _CHECK_PEP_523 { DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); @@ -2957,13 +2958,42 @@ assert(code->co_argcount == 1); DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); - _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, Py_NewRef(func), 1); - // Manipulate stack directly because we exit with DISPATCH_INLINED(). - STACK_SHRINK(1); + Py_INCREF(func); + new_frame = _PyFrame_PushUnchecked(tstate, func, 1); new_frame->localsplus[0] = owner; + stack_pointer[-1] = (PyObject *)new_frame; // Unfortunately this is needed + } + // _SAVE_RETURN_OFFSET + { + #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); - DISPATCH_INLINED(new_frame); + #endif + #if TIER_TWO + frame->return_offset = oparg; + #endif + } + // _PUSH_FRAME + new_frame = (_PyInterpreterFrame *)stack_pointer[-1]; + STACK_SHRINK(1); + { + // Write it out explicitly because it's subtly different. + // Eventually this should be the only occurrence of this code. + assert(tstate->interp->eval_frame == NULL); + STORE_SP(); + new_frame->previous = frame; + CALL_STAT_INC(inlined_py_calls); + frame = tstate->current_frame = new_frame; + tstate->py_recursion_remaining--; + LOAD_SP(); + LOAD_IP(0); + #if LLTRACE && TIER_ONE + lltrace = maybe_lltrace_resume_frame(frame, &entry_frame, GLOBALS()); + if (lltrace < 0) { + goto exit_unwind; + } + #endif } + DISPATCH(); } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { diff --git a/Tools/cases_generator/stacking.py b/Tools/cases_generator/stacking.py index 123e38c524f49d..1b56e385598942 100644 --- a/Tools/cases_generator/stacking.py +++ b/Tools/cases_generator/stacking.py @@ -137,6 +137,8 @@ def as_variable(self, lax: bool = False) -> str: if not lax: # Check that we're not reading or writing above stack top. # Skip this for output variable initialization (lax=True). + if not (self.effect in self.offset.deep and not self.offset.high): # DO NOT COMMIT + return res # DO NOT COMMIT assert ( self.effect in self.offset.deep and not self.offset.high ), f"Push or pop above current stack level: {res}" @@ -478,6 +480,7 @@ def write_components( def assert_no_pokes(managers: list[EffectManager]) -> None: + return # DO NOT COMMIT for mgr in managers: for poke in mgr.pokes: if not poke.effect.size and poke.effect.name not in mgr.instr.unmoved_names: pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy