Skip to content

Commit 1281be1

Browse files
pythongh-141367: Use CALL_LIST_APPEND instruction only for lists, not for list subclasses (pythonGH-141398)
Co-authored-by: Ken Jin <[email protected]>
1 parent f26ed45 commit 1281be1

File tree

7 files changed

+44
-20
lines changed

7 files changed

+44
-20
lines changed

Include/internal/pycore_code.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ PyAPI_FUNC(void) _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins
311311
_Py_CODEUNIT *instr, PyObject *name);
312312
PyAPI_FUNC(void) _Py_Specialize_StoreSubscr(_PyStackRef container, _PyStackRef sub,
313313
_Py_CODEUNIT *instr);
314-
PyAPI_FUNC(void) _Py_Specialize_Call(_PyStackRef callable, _Py_CODEUNIT *instr,
315-
int nargs);
314+
PyAPI_FUNC(void) _Py_Specialize_Call(_PyStackRef callable, _PyStackRef self_or_null,
315+
_Py_CODEUNIT *instr, int nargs);
316316
PyAPI_FUNC(void) _Py_Specialize_CallKw(_PyStackRef callable, _Py_CODEUNIT *instr,
317317
int nargs);
318318
PyAPI_FUNC(void) _Py_Specialize_BinaryOp(_PyStackRef lhs, _PyStackRef rhs, _Py_CODEUNIT *instr,

Lib/test/test_opcache.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,33 @@ def for_iter_generator():
18721872
self.assert_specialized(for_iter_generator, "FOR_ITER_GEN")
18731873
self.assert_no_opcode(for_iter_generator, "FOR_ITER")
18741874

1875+
@cpython_only
1876+
@requires_specialization_ft
1877+
def test_call_list_append(self):
1878+
# gh-141367: only exact lists should use
1879+
# CALL_LIST_APPEND instruction after specialization.
1880+
1881+
r = range(_testinternalcapi.SPECIALIZATION_THRESHOLD)
1882+
1883+
def list_append(l):
1884+
for _ in r:
1885+
l.append(1)
1886+
1887+
list_append([])
1888+
self.assert_specialized(list_append, "CALL_LIST_APPEND")
1889+
self.assert_no_opcode(list_append, "CALL_METHOD_DESCRIPTOR_O")
1890+
self.assert_no_opcode(list_append, "CALL")
1891+
1892+
def my_list_append(l):
1893+
for _ in r:
1894+
l.append(1)
1895+
1896+
class MyList(list): pass
1897+
my_list_append(MyList())
1898+
self.assert_specialized(my_list_append, "CALL_METHOD_DESCRIPTOR_O")
1899+
self.assert_no_opcode(my_list_append, "CALL_LIST_APPEND")
1900+
self.assert_no_opcode(my_list_append, "CALL")
1901+
18751902

18761903
if __name__ == "__main__":
18771904
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Specialize ``CALL_LIST_APPEND`` instruction only for lists, not for list
2+
subclasses, to avoid unnecessary deopt. Patch by Mikhail Efimov.

Python/bytecodes.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,7 +3689,7 @@ dummy_func(
36893689
#if ENABLE_SPECIALIZATION_FT
36903690
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
36913691
next_instr = this_instr;
3692-
_Py_Specialize_Call(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
3692+
_Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
36933693
DISPATCH_SAME_OPARG();
36943694
}
36953695
OPCODE_DEFERRED_INC(CALL);
@@ -4395,7 +4395,6 @@ dummy_func(
43954395
assert(oparg == 1);
43964396
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
43974397

4398-
DEOPT_IF(!PyList_CheckExact(self_o));
43994398
DEOPT_IF(!LOCK_OBJECT(self_o));
44004399
STAT_INC(CALL, hit);
44014400
int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));

Python/executor_cases.c.h

Lines changed: 0 additions & 4 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: 1 addition & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,8 +1602,8 @@ specialize_class_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs)
16021602
}
16031603

16041604
static int
1605-
specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr,
1606-
int nargs)
1605+
specialize_method_descriptor(PyMethodDescrObject *descr, PyObject *self_or_null,
1606+
_Py_CODEUNIT *instr, int nargs)
16071607
{
16081608
switch (descr->d_method->ml_flags &
16091609
(METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O |
@@ -1627,8 +1627,11 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr,
16271627
bool pop = (next.op.code == POP_TOP);
16281628
int oparg = instr->op.arg;
16291629
if ((PyObject *)descr == list_append && oparg == 1 && pop) {
1630-
specialize(instr, CALL_LIST_APPEND);
1631-
return 0;
1630+
assert(self_or_null != NULL);
1631+
if (PyList_CheckExact(self_or_null)) {
1632+
specialize(instr, CALL_LIST_APPEND);
1633+
return 0;
1634+
}
16321635
}
16331636
specialize(instr, CALL_METHOD_DESCRIPTOR_O);
16341637
return 0;
@@ -1766,7 +1769,7 @@ specialize_c_call(PyObject *callable, _Py_CODEUNIT *instr, int nargs)
17661769
}
17671770

17681771
Py_NO_INLINE void
1769-
_Py_Specialize_Call(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs)
1772+
_Py_Specialize_Call(_PyStackRef callable_st, _PyStackRef self_or_null_st, _Py_CODEUNIT *instr, int nargs)
17701773
{
17711774
PyObject *callable = PyStackRef_AsPyObjectBorrow(callable_st);
17721775

@@ -1784,7 +1787,9 @@ _Py_Specialize_Call(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs)
17841787
fail = specialize_class_call(callable, instr, nargs);
17851788
}
17861789
else if (Py_IS_TYPE(callable, &PyMethodDescr_Type)) {
1787-
fail = specialize_method_descriptor((PyMethodDescrObject *)callable, instr, nargs);
1790+
PyObject *self_or_null = PyStackRef_AsPyObjectBorrow(self_or_null_st);
1791+
fail = specialize_method_descriptor((PyMethodDescrObject *)callable,
1792+
self_or_null, instr, nargs);
17881793
}
17891794
else if (PyMethod_Check(callable)) {
17901795
PyObject *func = ((PyMethodObject *)callable)->im_func;

0 commit comments

Comments
 (0)