_Py_CODEUNIT *instr, PyObject *name);
PyAPI_FUNC(void) _Py_Specialize_StoreSubscr(_PyStackRef container, _PyStackRef sub,
_Py_CODEUNIT *instr);
-PyAPI_FUNC(void) _Py_Specialize_Call(_PyStackRef callable, _Py_CODEUNIT *instr,
- int nargs);
+PyAPI_FUNC(void) _Py_Specialize_Call(_PyStackRef callable, _PyStackRef self_or_null,
+ _Py_CODEUNIT *instr, int nargs);
PyAPI_FUNC(void) _Py_Specialize_CallKw(_PyStackRef callable, _Py_CODEUNIT *instr,
int nargs);
PyAPI_FUNC(void) _Py_Specialize_BinaryOp(_PyStackRef lhs, _PyStackRef rhs, _Py_CODEUNIT *instr,
self.assert_specialized(for_iter_generator, "FOR_ITER_GEN")
self.assert_no_opcode(for_iter_generator, "FOR_ITER")
+ @cpython_only
+ @requires_specialization_ft
+ def test_call_list_append(self):
+ # gh-141367: only exact lists should use
+ # CALL_LIST_APPEND instruction after specialization.
+
+ r = range(_testinternalcapi.SPECIALIZATION_THRESHOLD)
+
+ def list_append(l):
+ for _ in r:
+ l.append(1)
+
+ list_append([])
+ self.assert_specialized(list_append, "CALL_LIST_APPEND")
+ self.assert_no_opcode(list_append, "CALL_METHOD_DESCRIPTOR_O")
+ self.assert_no_opcode(list_append, "CALL")
+
+ def my_list_append(l):
+ for _ in r:
+ l.append(1)
+
+ class MyList(list): pass
+ my_list_append(MyList())
+ self.assert_specialized(my_list_append, "CALL_METHOD_DESCRIPTOR_O")
+ self.assert_no_opcode(my_list_append, "CALL_LIST_APPEND")
+ self.assert_no_opcode(my_list_append, "CALL")
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+Specialize ``CALL_LIST_APPEND`` instruction only for lists, not for list
+subclasses, to avoid unnecessary deopt. Patch by Mikhail Efimov.
#if ENABLE_SPECIALIZATION_FT
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
next_instr = this_instr;
- _Py_Specialize_Call(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
+ _Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
DISPATCH_SAME_OPARG();
}
OPCODE_DEFERRED_INC(CALL);
assert(oparg == 1);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- DEOPT_IF(!PyList_CheckExact(self_o));
DEOPT_IF(!LOCK_OBJECT(self_o));
STAT_INC(CALL, hit);
int err = _PyList_AppendTakeRef((PyListObject *)self_o, PyStackRef_AsPyObjectSteal(arg));
callable = stack_pointer[-3];
assert(oparg == 1);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- if (!PyList_CheckExact(self_o)) {
- UOP_STAT_INC(uopcode, miss);
- JUMP_TO_JUMP_TARGET();
- }
if (!LOCK_OBJECT(self_o)) {
UOP_STAT_INC(uopcode, miss);
JUMP_TO_JUMP_TARGET();
if (ADAPTIVE_COUNTER_TRIGGERS(counter)) {
next_instr = this_instr;
_PyFrame_SetStackPointer(frame, stack_pointer);
- _Py_Specialize_Call(callable, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
+ _Py_Specialize_Call(callable, self_or_null, next_instr, oparg + !PyStackRef_IsNull(self_or_null));
stack_pointer = _PyFrame_GetStackPointer(frame);
DISPATCH_SAME_OPARG();
}
self = nos;
assert(oparg == 1);
PyObject *self_o = PyStackRef_AsPyObjectBorrow(self);
- if (!PyList_CheckExact(self_o)) {
- UPDATE_MISS_STATS(CALL);
- assert(_PyOpcode_Deopt[opcode] == (CALL));
- JUMP_TO_PREDICTED(CALL);
- }
if (!LOCK_OBJECT(self_o)) {
UPDATE_MISS_STATS(CALL);
assert(_PyOpcode_Deopt[opcode] == (CALL));
}
static int
-specialize_method_descriptor(PyMethodDescrObject *descr, _Py_CODEUNIT *instr,
- int nargs)
+specialize_method_descriptor(PyMethodDescrObject *descr, PyObject *self_or_null,
+ _Py_CODEUNIT *instr, int nargs)
{
switch (descr->d_method->ml_flags &
(METH_VARARGS | METH_FASTCALL | METH_NOARGS | METH_O |
bool pop = (next.op.code == POP_TOP);
int oparg = instr->op.arg;
if ((PyObject *)descr == list_append && oparg == 1 && pop) {
- specialize(instr, CALL_LIST_APPEND);
- return 0;
+ assert(self_or_null != NULL);
+ if (PyList_CheckExact(self_or_null)) {
+ specialize(instr, CALL_LIST_APPEND);
+ return 0;
+ }
}
specialize(instr, CALL_METHOD_DESCRIPTOR_O);
return 0;
}
Py_NO_INLINE void
-_Py_Specialize_Call(_PyStackRef callable_st, _Py_CODEUNIT *instr, int nargs)
+_Py_Specialize_Call(_PyStackRef callable_st, _PyStackRef self_or_null_st, _Py_CODEUNIT *instr, int nargs)
{
PyObject *callable = PyStackRef_AsPyObjectBorrow(callable_st);
fail = specialize_class_call(callable, instr, nargs);
}
else if (Py_IS_TYPE(callable, &PyMethodDescr_Type)) {
- fail = specialize_method_descriptor((PyMethodDescrObject *)callable, instr, nargs);
+ PyObject *self_or_null = PyStackRef_AsPyObjectBorrow(self_or_null_st);
+ fail = specialize_method_descriptor((PyMethodDescrObject *)callable,
+ self_or_null, instr, nargs);
}
else if (PyMethod_Check(callable)) {
PyObject *func = ((PyMethodObject *)callable)->im_func;