10000 gh-100239: spcialize dict subclasses with no getitem override · iritkatriel/cpython@bf2d1dd · GitHub
[go: up one dir, main page]

Skip to content

Commit bf2d1dd

Browse files
committed
pythongh-100239: spcialize dict subclasses with no getitem override
1 parent f0dcb29 commit bf2d1dd

File tree

6 files changed

+85
-36
lines changed

6 files changed

+85
-36
lines changed

Lib/test/test_opcache.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import collections
12
import copy
23
import pickle
34
import dis
@@ -1729,9 +1730,43 @@ def binary_subscr_dict():
17291730
self.assertEqual(a[1], 2)
17301731
self.assertEqual(a[2], 3)
17311732

1732-
binary_subscr_dict()
1733-
self.assert_specialized(binary_subscr_dict, "BINARY_OP_SUBSCR_DICT")
1734-
self.assert_no_opcode(binary_subscr_dict, "BINARY_OP")
1733+
def binary_subscr_dict_subclass_defaultdict():
1734+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1735+
a = collections.defaultdict(lambda : 42, {1: 2, 2: 3})
1736+
self.assertEqual(a[1], 2)
1737+
self.assertEqual(a[2], 3)
1738+
self.assertEqual(a[7], 42)
1739+
1740+
def binary_subscr_dict_subclass_Counter():
1741+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1742+
a = collections.Counter('abcdeabcdabcaba')
1743+
self.assertEqual(a['a'], 5)
1744+
self.assertEqual(a['b'], 4)
1745+
self.assertEqual(a['m'], 0)
1746+
1747+
for f in [binary_subscr_dict,
1748+
binary_subscr_dict_subclass_defaultdict,
1749+
binary_subscr_dict_subclass_Counter]:
1750+
1751+
with self.subTest(f=f):
1752+
f()
1753+
self.assert_specialized(f, "BINARY_OP_SUBSCR_DICT")
1754+
self.assert_no_opcode(f, "BINARY_OP")
1755+
1756+
def binary_subscr_dict_subclass 10000 _with_subscript_override():
1757+
class MyDict(dict):
1758+
def __getitem__(self, key):
1759+
return 42
1760+
1761+
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
1762+
a = MyDict()
1763+
self.assertEqual(a['a'], 42)
1764+
self.assertEqual(a['b'], 42)
1765+
1766+
binary_subscr_dict_subclass_with_subscript_override()
1767+
self.assert_no_opcode(
1768+
binary_subscr_dict_subclass_with_subscript_override,
1769+
"BINARY_OP_SUBSCR_DICT")
17351770

17361771
def binary_subscr_str_int():
17371772
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):

Objects/typeobject.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11184,7 +11184,14 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
1118411184
}
1118511185
else {
1118611186
use_generic = 1;
11187-
generic = p->function;
11187+
if (generic == NULL && Py_IS_TYPE(descr, &PyMethodDescr_Type) &&
11188+
*ptr == ((PyMethodDescrObject *)descr)->d_method->ml_meth)
11189+
{
11190+
generic = *ptr;
11191+
}
11192+
else {
11193+
generic = p->function;
11194+
}
1118811195
if (p->function == slot_tp_call) {
1118911196
/* A generic __call__ is incompatible with vectorcall */
1119011197
type_clear_flags(type, Py_TPFLAGS_HAVE_VECTORCALL);

Python/bytecodes.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -939,15 +939,14 @@ dummy_func(
939939
PyObject *sub = PyStackRef_AsPyObjectBorrow(sub_st);
940940
PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
941941

942-
DEOPT_IF(!PyDict_CheckExact(dict));
942+
DEOPT_IF(!PyDict_Check(dict));
943+
DEOPT_IF(!Py_TYPE(dict)->tp_as_mapping);
944+
DEOPT_IF(Py_TYPE(dict)->tp_as_mapping->mp_subscript !=
945+
PyDict_Type.tp_as_mapping->mp_subscript);
943946
STAT_INC(BINARY_OP, hit);
944-
PyObject *res_o;
945-
int rc = PyDict_GetItemRef(dict, sub, &res_o);
946-
if (rc == 0) {
947-
_PyErr_SetKeyError(sub);
948-
}
947+
PyObject *res_o = PyDict_Type.tp_as_mapping->mp_subscript(dict, sub);
949948
DECREF_INPUTS();
950-
ERROR_IF(rc <= 0, error); // not found or error
949+
ERROR_IF(res_o == NULL, error); // not found or error
951950
res = PyStackRef_FromPyObjectSteal(res_o);
952951
}
953952

Python/executor_cases.c.h

Lines changed: 12 additions & 11 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: 14 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2618,9 +2618,13 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in
26182618
return;
26192619
}
26202620
}
2621-
if (PyDict_CheckExact(lhs)) {
2622-
specialize(instr, BINARY_OP_SUBSCR_DICT);
2623-
return;
2621+
if (PyDict_Check(lhs)) {
2622+
if ((Py_TYPE(lhs)->tp_as_mapping != NULL) &&
2623+
(Py_TYPE(lhs)->tp_as_mapping->mp_subscript == PyDict_Type.tp_as_mapping->mp_subscript))
2624+
{
2625+
specialize(instr, BINARY_OP_SUBSCR_DICT);
2626+
return;
2627+
}
26242628
}
26252629
unsigned int tp_version;
26262630
PyTypeObject *container_type = Py_TYPE(lhs);

0 commit comments

Comments
 (0)
0