Skip to content

Commit 1e0f842

Browse files
committed
Restore _PyObject_GetMethod optimization to LOAD_ATTR
1 parent 584015a commit 1e0f842

File tree

7 files changed

+135
-33
lines changed

7 files changed

+135
-33
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 5 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_monitoring.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,8 @@ def func2():
11381138
('call', 'func2', sys.monitoring.MISSING),
11391139
('line', 'func2', 1),
11401140
('line', 'func2', 2),
1141-
('call', 'append', 2),
1142-
('C return', 'append', 2),
1141+
('call', 'append', [2]),
1142+
('C return', 'append', [2]),
11431143
('line', 'func2', 3),
11441144
('line', 'get_events', 11),
11451145
('call', 'set_events', 2)])
@@ -1434,8 +1434,8 @@ def func2():
14341434
self.check_events(func2, recorders = LOCAL_RECORDERS, expected = [
14351435
('line', 'func2', 1),
14361436
('line', 'func2', 2),
1437-
('call', 'append', 2),
1438-
('C return', 'append', 2),
1437+
('call', 'append', [2]),
1438+
('C return', 'append', [2]),
14391439
('line', 'func2', 3)])
14401440

14411441
def test_try_except(self):
@@ -1913,6 +1913,7 @@ def f():
19131913
""")
19141914

19151915
def get_expected(name, call_method, ns):
1916+
repr_arg = 0 if name == "int" else sys.monitoring.MISSING
19161917
return [
19171918
('line', 'get_events', 10),
19181919
('call', 'f', sys.monitoring.MISSING),
@@ -1923,8 +1924,8 @@ def get_expected(name, call_method, ns):
19231924
('C return', name, sys.monitoring.MISSING),
19241925
*(
19251926
[
1926-
('call', '__repr__', sys.monitoring.MISSING),
1927-
('C return', '__repr__', sys.monitoring.MISSING),
1927+
('call', '__repr__', repr_arg),
1928+
('C return', '__repr__', repr_arg),
19281929
] if call_method else []
19291930
),
19301931
('line', 'get_events', 11),

Python/bytecodes.c

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,19 +2155,48 @@ dummy_func(
21552155
#endif /* ENABLE_SPECIALIZATION_FT */
21562156
}
21572157

2158-
op(_LOAD_ATTR, (owner -- attr)) {
2158+
op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) {
21592159
PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
2160-
PyObject *attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
2161-
DECREF_INPUTS();
2162-
ERROR_IF(attr_o == NULL, error);
2160+
PyObject *attr_o;
2161+
if (oparg & 1) {
2162+
/* Designed to work in tandem with CALL, pushes two values. */
2163+
attr_o = NULL;
2164+
int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
2165+
if (is_meth) {
2166+
/* We can bypass temporary bound method object.
2167+
meth is unbound method and obj is self.
2168+
meth | self | arg1 | ... | argN
2169+
*/
2170+
assert(attr_o != NULL); // No errors on this branch
2171+
self_or_null[0] = owner; // Transfer ownership
2172+
DEAD(owner);
2173+
}
2174+
else {
2175+
/* meth is not an unbound method (but a regular attr, or
2176+
something was returned by a descriptor protocol). Set
2177+
the second element of the stack to NULL, to signal
2178+
CALL that it's not a method call.
2179+
meth | NULL | arg1 | ... | argN
2180+
*/
2181+
DECREF_INPUTS();
2182+
ERROR_IF(attr_o == NULL, error);
2183+
self_or_null[0] = PyStackRef_NULL;
2184+
}
2185+
}
2186+
else {
2187+
/* Classic, pushes one value. */
2188+
attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
2189+
DECREF_INPUTS();
2190+
ERROR_IF(attr_o == NULL, error);
2191+
}
21632192
attr = PyStackRef_FromPyObjectSteal(attr_o);
21642193
}
21652194

2195+
21662196
macro(LOAD_ATTR) =
21672197
_SPECIALIZE_LOAD_ATTR +
21682198
unused/8 +
2169-
_LOAD_ATTR +
2170-
_PUSH_NULL_CONDITIONAL;
2199+
_LOAD_ATTR;
21712200

21722201
op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) {
21732202
PyTypeObject *tp = Py_TYPE(PyStackRef_AsPyObjectBorrow(owner));

Python/executor_cases.c.h

Lines changed: 39 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 38 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer_bytecodes.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,12 @@ dummy_func(void) {
548548
null = sym_new_null(ctx);
549549
}
550550

551-
op(_LOAD_ATTR, (owner -- attr)) {
551+
op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) {
552552
(void)owner;
553553
attr = sym_new_not_null(ctx);
554+
if (oparg &1) {
555+
self_or_null[0] = sym_new_unknown(ctx);
556+
}
554557
}
555558

556559
op(_LOAD_ATTR_MODULE_FROM_KEYS, (index/1, owner, mod_keys -- attr)) {

Python/optimizer_cases.c.h

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)