Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,11 @@ extern PyObject* _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);

extern PyTypeObject _PyBufferWrapper_Type;

PyAPI_FUNC(PyObject*) _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj,
PyObject *name, int *meth_found);
PyAPI_FUNC(PyObject*) _PySuper_LookupAttr(PyTypeObject *su_type, PyObject *su_obj,
PyObject *name);

PyAPI_FUNC(PyObject*) _PySuper_LookupMethod(PyTypeObject *su_type, PyObject *su_obj,
PyObject *name, int *method_found);

extern PyObject* _PyType_GetFullyQualifiedName(PyTypeObject *type, char sep);

Expand Down
41 changes: 41 additions & 0 deletions Lib/test/test_opcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -1272,6 +1272,47 @@ def g():
self.assert_specialized(g, "CONTAINS_OP_SET")
self.assert_no_opcode(g, "CONTAINS_OP")

@cpython_only
@requires_specialization_ft
def test_load_super_attr(self):
"""Ensure that LOAD_SUPER_ATTR is specialized as expected."""

class A:
def __init__(self):
meth = super().__init__
super().__init__()

for _ in range(100):
A()

self.assert_specialized(A.__init__, "LOAD_SUPER_ATTR_ATTR")
self.assert_specialized(A.__init__, "LOAD_SUPER_ATTR_METHOD")
self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR")

# Temporarily replace super() with something else.
real_super = super

def fake_super():
def init(self):
pass

return init

# Force unspecialize
globals()['super'] = fake_super
try:
# Should be unspecialized after enough calls.
for _ in range(100):
A()
finally:
globals()['super'] = real_super

# Ensure the specialized instructions are not present
self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR_ATTR")
self.assert_no_opcode(A.__init__, "LOAD_SUPER_ATTR_METHOD")

if 0:
dis.dis(A.__init__, adaptive=True)


if __name__ == "__main__":
Expand Down
26 changes: 24 additions & 2 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -11600,8 +11600,8 @@ supercheck(PyTypeObject *type, PyObject *obj)
return NULL;
}

PyObject *
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *method)
static PyObject *
super_lookup_lock_held(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *method)
{
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
if (su_obj_type == NULL) {
Expand All @@ -11612,6 +11612,28 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
return res;
}

PyObject *
_PySuper_LookupAttr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
{
PyObject *res;
BEGIN_TYPE_LOCK();
res = super_lookup_lock_held(su_type, su_obj, name, NULL);
END_TYPE_LOCK();
return res;
}

PyObject *
_PySuper_LookupMethod(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *method_found)
{
PyObject *res;
BEGIN_TYPE_LOCK();
*method_found = 0;
res = super_lookup_lock_held(su_type, su_obj, name,
Py_TYPE(su_obj)->tp_getattro == PyObject_GenericGetAttr ? method_found : NULL);
END_TYPE_LOCK()
return res;
}

static PyObject *
super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
Expand Down
11 changes: 5 additions & 6 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "pycore_typeobject.h" // _PySuper_Lookup()
#include "pycore_typeobject.h" // _PySuper_LookupAttr() _PySuper_LookupMethod()

#include "pycore_dict.h"
#include "dictobject.h"
Expand Down Expand Up @@ -1932,7 +1932,7 @@ dummy_func(
};

specializing op(_SPECIALIZE_LOAD_SUPER_ATTR, (counter/1, global_super_st, class_st, unused -- global_super_st, class_st, unused)) {
#if ENABLE_SPECIALIZATION
#if ENABLE_SPECIALIZATION_FT
int load_method = oparg & 1;
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
next_instr = this_instr;
Expand All @@ -1941,7 +1941,7 @@ dummy_func(
}
OPCODE_DEFERRED_INC(LOAD_SUPER_ATTR);
ADVANCE_ADAPTIVE_COUNTER(this_instr[1].counter);
#endif /* ENABLE_SPECIALIZATION */
#endif /* ENABLE_SPECIALIZATION_FT */
}

tier1 op(_LOAD_SUPER_ATTR, (global_super_st, class_st, self_st -- attr, null if (oparg & 1))) {
Expand Down Expand Up @@ -2001,7 +2001,7 @@ dummy_func(
DEOPT_IF(!PyType_Check(class));
STAT_INC(LOAD_SUPER_ATTR, hit);
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
PyObject *attr = _PySuper_Lookup((PyTypeObject *)class, self, name, NULL);
PyObject *attr = _PySuper_LookupAttr((PyTypeObject *)class, self, name);
DECREF_INPUTS();
ERROR_IF(attr == NULL, error);
attr_st = PyStackRef_FromPyObjectSteal(attr);
Expand All @@ -2019,8 +2019,7 @@ dummy_func(
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 2);
PyTypeObject *cls = (PyTypeObject *)class;
int method_found = 0;
PyObject *attr_o = _PySuper_Lookup(cls, self, name,
Py_TYPE(self)->tp_getattro == PyObject_GenericGetAttr ? &method_found : NULL);
PyObject *attr_o = _PySuper_LookupMethod(cls, self, name, &method_found);
PyStackRef_CLOSE(global_super_st);
PyStackRef_CLOSE(class_st);
if (attr_o == NULL) {
Expand Down
1 change: 0 additions & 1 deletion Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include "pycore_setobject.h" // _PySet_Update()
#include "pycore_sliceobject.h" // _PyBuildSlice_ConsumeRefs
#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "pycore_typeobject.h" // _PySuper_Lookup()
#include "pycore_uop_ids.h" // Uops
#include "pycore_pyerrors.h"

Expand Down
5 changes: 2 additions & 3 deletions Python/executor_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions Python/generated_cases.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 5 additions & 14 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,8 @@ _Py_Specialize_LoadSuperAttr(_PyStackRef global_super_st, _PyStackRef cls_st, _P
PyObject *global_super = PyStackRef_AsPyObjectBorrow(global_super_st);
PyObject *cls = PyStackRef_AsPyObjectBorrow(cls_st);

assert(ENABLE_SPECIALIZATION);
assert(ENABLE_SPECIALIZATION_FT);
assert(_PyOpcode_Caches[LOAD_SUPER_ATTR] == INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR);
_PySuperAttrCache *cache = (_PySuperAttrCache *)(instr + 1);
if (global_super != (PyObject *)&PySuper_Type) {
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_SHADOWED);
goto fail;
Expand All @@ -802,19 +801,11 @@ _Py_Specialize_LoadSuperAttr(_PyStackRef global_super_st, _PyStackRef cls_st, _P
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_BAD_CLASS);
goto fail;
}
instr->op.code = load_method ? LOAD_SUPER_ATTR_METHOD : LOAD_SUPER_ATTR_ATTR;
goto success;

fail:
STAT_INC(LOAD_SUPER_ATTR, failure);
assert(!PyErr_Occurred());
instr->op.code = LOAD_SUPER_ATTR;
cache->counter = adaptive_counter_backoff(cache->counter);
uint8_t load_code = load_method ? LOAD_SUPER_ATTR_METHOD : LOAD_SUPER_ATTR_ATTR;
specialize(instr, load_code);
return;
success:
STAT_INC(LOAD_SUPER_ATTR, success);
assert(!PyErr_Occurred());
cache->counter = adaptive_counter_cooldown();
fail:
unspecialize(instr);
}

typedef enum {
Expand Down