Skip to content

Commit f3b579b

Browse files
Add fix, test, NEWS
1 parent 92741c5 commit f3b579b

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

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+
Use ``CALL_LIST_APPEND`` instruction only for lists, not for list
2+
subclasses, to avoid unnecessary deopt. Patch by Mikhail Efimov.

Python/specialize.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "pycore_pylifecycle.h" // _PyOS_URandomNonblock()
2020
#include "pycore_runtime.h" // _Py_ID()
2121
#include "pycore_unicodeobject.h" // _PyUnicodeASCIIIter_Type
22+
#include "pycore_pystate.h" // _PyThreadState_GET()
2223

2324
#include <stdlib.h> // rand()
2425

@@ -1627,8 +1628,13 @@ specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr,
16271628
bool pop = (next.op.code == POP_TOP);
16281629
int oparg = instr->op.arg;
16291630
if ((PyObject *)descr == list_append && oparg == 1 && pop) {
1630-
specialize(instr, CALL_LIST_APPEND);
1631-
return 0;
1631+
PyThreadState *tstate = _PyThreadState_GET();
1632+
_PyStackRef *stack_pointer = tstate->current_frame->stackpointer;
1633+
PyObject *self = PyStackRef_AsPyObjectBorrow(stack_pointer[-2]);
1634+
if (PyList_CheckExact(self)) {
1635+
specialize(instr, CALL_LIST_APPEND);
1636+
return 0;
1637+
}
16321638
}
16331639
specialize(instr, CALL_METHOD_DESCRIPTOR_O);
16341640
return 0;

0 commit comments

Comments
 (0)