Skip to content

Commit a51742a

Browse files
gh-93911: Specialize LOAD_ATTR_PROPERTY (GH-93912)
1 parent 0ff626f commit a51742a

12 files changed

+172
-71
lines changed

Include/internal/pycore_descrobject.h

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#ifndef Py_INTERNAL_DESCROBJECT_H
2+
#define Py_INTERNAL_DESCROBJECT_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
typedef struct {
12+
PyObject_HEAD
13+
PyObject *prop_get;
14+
PyObject *prop_set;
15+
PyObject *prop_del;
16+
PyObject *prop_doc;
17+
PyObject *prop_name;
18+
int getter_doc;
19+
} propertyobject;
20+
21+
typedef propertyobject _PyPropertyObject;
22+
23+
#ifdef __cplusplus
24+
}
25+
#endif
26+
#endif /* !Py_INTERNAL_DESCROBJECT_H */

Include/internal/pycore_opcode.h

+14-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

+27-26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

+1
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ def jabs_op(name, op):
287287
"LOAD_ATTR_CLASS",
288288
"LOAD_ATTR_INSTANCE_VALUE",
289289
"LOAD_ATTR_MODULE",
290+
"LOAD_ATTR_PROPERTY",
290291
"LOAD_ATTR_SLOT",
291292
"LOAD_ATTR_WITH_HINT",
292293
# These will always push [unbound method, self] onto the stack.

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,7 @@ PYTHON_HEADERS= \
15951595
$(srcdir)/Include/internal/pycore_condvar.h \
15961596
$(srcdir)/Include/internal/pycore_context.h \
15971597
$(srcdir)/Include/internal/pycore_dict.h \
1598+
$(srcdir)/Include/internal/pycore_descrobject.h \
15981599
$(srcdir)/Include/internal/pycore_dtoa.h \
15991600
$(srcdir)/Include/internal/pycore_exceptions.h \
16001601
$(srcdir)/Include/internal/pycore_fileutils.h \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Specialize ``LOAD_ATTR`` for ``property()`` attributes.

Objects/descrobject.c

+1-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "pycore_pystate.h" // _PyThreadState_GET()
77
#include "pycore_tuple.h" // _PyTuple_ITEMS()
88
#include "structmember.h" // PyMemberDef
9+
#include "pycore_descrobject.h"
910

1011
/*[clinic input]
1112
class mappingproxy "mappingproxyobject *" "&PyDictProxy_Type"
@@ -1501,16 +1502,6 @@ class property(object):
15011502
15021503
*/
15031504

1504-
typedef struct {
1505-
PyObject_HEAD
1506-
PyObject *prop_get;
1507-
PyObject *prop_set;
1508-
PyObject *prop_del;
1509-
PyObject *prop_doc;
1510-
PyObject *prop_name;
1511-
int getter_doc;
1512-
} propertyobject;
1513-
15141505
static PyObject * property_copy(PyObject *, PyObject *, PyObject *,
15151506
PyObject *);
15161507

PCbuild/pythoncore.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
<ClInclude Include="..\Include\internal\pycore_compile.h" />
208208
<ClInclude Include="..\Include\internal\pycore_condvar.h" />
209209
<ClInclude Include="..\Include\internal\pycore_context.h" />
210+
<ClInclude Include="..\Include\internal\pycore_descrobject.h" />
210211
<ClInclude Include="..\Include\internal\pycore_dtoa.h" />
211212
<ClInclude Include="..\Include\internal\pycore_exceptions.h" />
212213
<ClInclude Include="..\Include\internal\pycore_fileutils.h" />

PCbuild/pythoncore.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@
528528
<ClInclude Include="..\Include\internal\pycore_context.h">
529529
<Filter>Include\internal</Filter>
530530
</ClInclude>
531+
<ClInclude Include="..\Include\internal\pycore_descrobject.h">
532+
<Filter>Include\internal</Filter>
533+
</ClInclude>
531534
<ClInclude Include="..\Include\internal\pycore_dtoa.h">
532535
<Filter>Include\internal</Filter>
533536
</ClInclude>

Python/ceval.c

+42-4
Original file line numberDiff line numberDiff line change
@@ -3626,7 +3626,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
36263626
}
36273627

36283628
TARGET(LOAD_ATTR_CLASS) {
3629-
/* LOAD_METHOD, for class methods */
36303629
assert(cframe.use_tracing == 0);
36313630
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
36323631

@@ -3649,6 +3648,46 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
36493648
NOTRACE_DISPATCH();
36503649
}
36513650

3651+
TARGET(LOAD_ATTR_PROPERTY) {
3652+
assert(cframe.use_tracing == 0);
3653+
DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
3654+
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
3655+
3656+
PyObject *owner = TOP();
3657+
PyTypeObject *cls = Py_TYPE(owner);
3658+
uint32_t type_version = read_u32(cache->type_version);
3659+
DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
3660+
assert(type_version != 0);
3661+
PyObject *fget = read_obj(cache->descr);
3662+
PyFunctionObject *f = (PyFunctionObject *)fget;
3663+
uint32_t func_version = read_u32(cache->keys_version);
3664+
assert(func_version != 0);
3665+
DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
3666+
PyCodeObject *code = (PyCodeObject *)f->func_code;
3667+
assert(code->co_argcount == 1);
3668+
STAT_INC(LOAD_ATTR, hit);
3669+
3670+
Py_INCREF(fget);
3671+
_PyInterpreterFrame *new_frame = _PyFrame_Push(tstate, f);
3672+
if (new_frame == NULL) {
3673+
goto error;
3674+
}
3675+
SET_TOP(NULL);
3676+
int push_null = !(oparg & 1);
3677+
STACK_SHRINK(push_null);
3678+
new_frame->localsplus[0] = owner;
3679+
for (int i = 1; i < code->co_nlocalsplus; i++) {
3680+
new_frame->localsplus[i] = NULL;
3681+
}
3682+
_PyFrame_SetStackPointer(frame, stack_pointer);
3683+
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
3684+
frame->prev_instr = next_instr - 1;
3685+
new_frame->previous = frame;
3686+
frame = cframe.current_frame = new_frame;
3687+
CALL_STAT_INC(inlined_py_calls);
3688+
goto start_frame;
3689+
}
3690+
36523691
TARGET(STORE_ATTR_ADAPTIVE) {
36533692
assert(cframe.use_tracing == 0);
36543693
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
@@ -4548,7 +4587,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
45484587
}
45494588

45504589
TARGET(LOAD_ATTR_METHOD_WITH_VALUES) {
4551-
/* LOAD_METHOD, with cached method object */
4590+
/* Cached method object */
45524591
assert(cframe.use_tracing == 0);
45534592
PyObject *self = TOP();
45544593
PyTypeObject *self_cls = Py_TYPE(self);
@@ -4574,8 +4613,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
45744613
}
45754614

45764615
TARGET(LOAD_ATTR_METHOD_WITH_DICT) {
4577-
/* LOAD_METHOD, with a dict
4578-
Can be either a managed dict, or a tp_dictoffset offset.*/
4616+
/* Can be either a managed dict, or a tp_dictoffset offset.*/
45794617
assert(cframe.use_tracing == 0);
45804618
PyObject *self = TOP();
45814619
PyTypeObject *self_cls = Py_TYPE(self);

0 commit comments

Comments
 (0)