mirror of
https://github.com/python/cpython.git
synced 2025-08-30 21:48:47 +00:00
GH-118093: Add tier two support for LOAD_ATTR_PROPERTY (GH-122283)
This commit is contained in:
parent
5e686ff57d
commit
5f6001130f
9 changed files with 168 additions and 99 deletions
|
@ -2243,32 +2243,30 @@ 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))) {
|
||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||
|
||||
op(_LOAD_ATTR_PROPERTY_FRAME, (fget/4, owner -- new_frame: _PyInterpreterFrame *)) {
|
||||
assert((oparg & 1) == 0);
|
||||
DEOPT_IF(tstate->interp->eval_frame);
|
||||
|
||||
PyTypeObject *cls = Py_TYPE(owner_o);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(cls->tp_version_tag != type_version);
|
||||
assert(Py_IS_TYPE(fget, &PyFunction_Type));
|
||||
PyFunctionObject *f = (PyFunctionObject *)fget;
|
||||
assert(func_version != 0);
|
||||
DEOPT_IF(f->func_version != func_version);
|
||||
PyCodeObject *code = (PyCodeObject *)f->func_code;
|
||||
assert(code->co_argcount == 1);
|
||||
DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED);
|
||||
DEOPT_IF(code->co_kwonlyargcount);
|
||||
DEOPT_IF(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);
|
||||
// Manipulate stack directly because we exit with DISPATCH_INLINED().
|
||||
STACK_SHRINK(1);
|
||||
new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
|
||||
new_frame->localsplus[0] = owner;
|
||||
frame->return_offset = (uint16_t)(next_instr - this_instr);
|
||||
DISPATCH_INLINED(new_frame);
|
||||
}
|
||||
|
||||
macro(LOAD_ATTR_PROPERTY) =
|
||||
unused/1 +
|
||||
_CHECK_PEP_523 +
|
||||
_GUARD_TYPE_VERSION +
|
||||
unused/2 +
|
||||
_LOAD_ATTR_PROPERTY_FRAME +
|
||||
_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))) {
|
||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||
|
||||
|
|
34
Python/executor_cases.c.h
generated
34
Python/executor_cases.c.h
generated
|
@ -2507,7 +2507,39 @@
|
|||
|
||||
/* _LOAD_ATTR_CLASS is split on (oparg & 1) */
|
||||
|
||||
/* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
|
||||
case _LOAD_ATTR_PROPERTY_FRAME: {
|
||||
_PyStackRef owner;
|
||||
_PyInterpreterFrame *new_frame;
|
||||
oparg = CURRENT_OPARG();
|
||||
owner = stack_pointer[-1];
|
||||
PyObject *fget = (PyObject *)CURRENT_OPERAND();
|
||||
assert((oparg & 1) == 0);
|
||||
assert(Py_IS_TYPE(fget, &PyFunction_Type));
|
||||
PyFunctionObject *f = (PyFunctionObject *)fget;
|
||||
PyCodeObject *code = (PyCodeObject *)f->func_code;
|
||||
if ((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
if (code->co_kwonlyargcount) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
if (code->co_argcount != 1) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
if (!_PyThreadState_HasStackSpace(tstate, code->co_framesize)) {
|
||||
UOP_STAT_INC(uopcode, miss);
|
||||
JUMP_TO_JUMP_TARGET();
|
||||
}
|
||||
STAT_INC(LOAD_ATTR, hit);
|
||||
Py_INCREF(fget);
|
||||
new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
|
||||
new_frame->localsplus[0] = owner;
|
||||
stack_pointer[-1].bits = (uintptr_t)new_frame;
|
||||
break;
|
||||
}
|
||||
|
||||
/* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 because it uses the 'this_instr' variable */
|
||||
|
||||
|
|
79
Python/generated_cases.c.h
generated
79
Python/generated_cases.c.h
generated
|
@ -4429,32 +4429,63 @@
|
|||
INSTRUCTION_STATS(LOAD_ATTR_PROPERTY);
|
||||
static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size");
|
||||
_PyStackRef owner;
|
||||
_PyInterpreterFrame *new_frame;
|
||||
/* Skip 1 cache entry */
|
||||
// _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);
|
||||
uint32_t func_version = read_u32(&this_instr[4].cache);
|
||||
PyObject *fget = read_obj(&this_instr[6].cache);
|
||||
PyObject *owner_o = PyStackRef_AsPyObjectBorrow(owner);
|
||||
assert((oparg & 1) == 0);
|
||||
DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
|
||||
PyTypeObject *cls = Py_TYPE(owner_o);
|
||||
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(PyStackRef_AsPyObjectBorrow(owner));
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
|
||||
}
|
||||
/* Skip 2 cache entries */
|
||||
// _LOAD_ATTR_PROPERTY_FRAME
|
||||
{
|
||||
PyObject *fget = read_obj(&this_instr[6].cache);
|
||||
assert((oparg & 1) == 0);
|
||||
assert(Py_IS_TYPE(fget, &PyFunction_Type));
|
||||
PyFunctionObject *f = (PyFunctionObject *)fget;
|
||||
PyCodeObject *code = (PyCodeObject *)f->func_code;
|
||||
DEOPT_IF((code->co_flags & (CO_VARKEYWORDS | CO_VARARGS | CO_OPTIMIZED)) != CO_OPTIMIZED, LOAD_ATTR);
|
||||
DEOPT_IF(code->co_kwonlyargcount, LOAD_ATTR);
|
||||
DEOPT_IF(code->co_argcount != 1, LOAD_ATTR);
|
||||
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), LOAD_ATTR);
|
||||
STAT_INC(LOAD_ATTR, hit);
|
||||
Py_INCREF(fget);
|
||||
new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
|
||||
new_frame->localsplus[0] = owner;
|
||||
}
|
||||
// _SAVE_RETURN_OFFSET
|
||||
{
|
||||
#if TIER_ONE
|
||||
frame->return_offset = (uint16_t)(next_instr - this_instr);
|
||||
#endif
|
||||
#if TIER_TWO
|
||||
frame->return_offset = oparg;
|
||||
#endif
|
||||
}
|
||||
// _PUSH_FRAME
|
||||
{
|
||||
// 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);
|
||||
stack_pointer += -1;
|
||||
assert(WITHIN_STACK_BOUNDS());
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
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);
|
||||
LLTRACE_RESUME_FRAME();
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_ATTR_SLOT) {
|
||||
|
|
|
@ -797,7 +797,10 @@ top: // Jump here after _PUSH_FRAME or likely branches
|
|||
|
||||
if (uop == _PUSH_FRAME) {
|
||||
assert(i + 1 == nuops);
|
||||
if (opcode == FOR_ITER_GEN || opcode == SEND_GEN) {
|
||||
if (opcode == FOR_ITER_GEN ||
|
||||
opcode == LOAD_ATTR_PROPERTY ||
|
||||
opcode == SEND_GEN)
|
||||
{
|
||||
DPRINTF(2, "Bailing due to dynamic target\n");
|
||||
ADD_TO_TRACE(uop, oparg, 0, target);
|
||||
ADD_TO_TRACE(_DYNAMIC_EXIT, 0, 0, 0);
|
||||
|
|
7
Python/optimizer_cases.c.h
generated
7
Python/optimizer_cases.c.h
generated
|
@ -1160,7 +1160,12 @@
|
|||
break;
|
||||
}
|
||||
|
||||
/* _LOAD_ATTR_PROPERTY is not a viable micro-op for tier 2 */
|
||||
case _LOAD_ATTR_PROPERTY_FRAME: {
|
||||
_PyInterpreterFrame *new_frame;
|
||||
new_frame = sym_new_not_null(ctx);
|
||||
stack_pointer[-1] = (_Py_UopsSymbol *)new_frame;
|
||||
break;
|
||||
}
|
||||
|
||||
/* _LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN is not a viable micro-op for tier 2 */
|
||||
|
||||
|
|
|
@ -963,15 +963,10 @@ _Py_Specialize_LoadAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *nam
|
|||
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_METHOD);
|
||||
goto fail;
|
||||
}
|
||||
uint32_t version = function_get_version(fget, LOAD_ATTR);
|
||||
if (version == 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (_PyInterpreterState_GET()->eval_frame) {
|
||||
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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue