Skip to content

Commit c496f80

Browse files
committed
[mypyc] feat: use exact dict getitem op for module dict lookup
1 parent 4171da0 commit c496f80

File tree

5 files changed

+36
-14
lines changed

5 files changed

+36
-14
lines changed

mypyc/irbuild/builder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
)
127127
from mypyc.irbuild.util import bytes_from_str, is_constant
128128
from mypyc.options import CompilerOptions
129-
from mypyc.primitives.dict_ops import dict_get_item_op, dict_set_item_op
129+
from mypyc.primitives.dict_ops import dict_set_item_op, exact_dict_get_item_op
130130
from mypyc.primitives.generic_ops import iter_op, next_op, py_setattr_op
131131
from mypyc.primitives.list_ops import list_get_item_unsafe_op, list_pop_last, to_list
132132
from mypyc.primitives.misc_ops import check_unpack_count_op, get_module_dict_op, import_op
@@ -472,7 +472,7 @@ def get_module(self, module: str, line: int) -> Value:
472472
# Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :(
473473
mod_dict = self.call_c(get_module_dict_op, [], line)
474474
# Get module object from modules dict.
475-
return self.primitive_op(dict_get_item_op, [mod_dict, self.load_str(module)], line)
475+
return self.primitive_op(exact_dict_get_item_op, [mod_dict, self.load_str(module)], line)
476476

477477
def get_module_attr(self, module: str, attr: str, line: int) -> Value:
478478
"""Look up an attribute of a module without storing it in the local namespace.
@@ -1410,7 +1410,7 @@ def load_global(self, expr: NameExpr) -> Value:
14101410
def load_global_str(self, name: str, line: int) -> Value:
14111411
_globals = self.load_globals_dict()
14121412
reg = self.load_str(name)
1413-
return self.primitive_op(dict_get_item_op, [_globals, reg], line)
1413+
return self.primitive_op(exact_dict_get_item_op, [_globals, reg], line)
14141414

14151415
def load_globals_dict(self) -> Value:
14161416
return self.add(LoadStatic(dict_rprimitive, "globals", self.module_name))

mypyc/irbuild/expression.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
translate_object_setattr,
105105
)
106106
from mypyc.primitives.bytes_ops import bytes_slice_op
107-
from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op
107+
from mypyc.primitives.dict_ops import dict_new_op, exact_dict_get_item_op, exact_dict_set_item_op
108108
from mypyc.primitives.generic_ops import iter_op, name_op
109109
from mypyc.primitives.list_ops import list_append_op, list_extend_op, list_slice_op
110110
from mypyc.primitives.misc_ops import ellipsis_op, get_module_dict_op, new_slice_op, type_op
@@ -190,7 +190,7 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
190190
# instead load the module separately on each access.
191191
mod_dict = builder.call_c(get_module_dict_op, [], expr.line)
192192
obj = builder.primitive_op(
193-
dict_get_item_op, [mod_dict, builder.load_str(expr.node.fullname)], expr.line
193+
exact_dict_get_item_op, [mod_dict, builder.load_str(expr.node.fullname)], expr.line
194194
)
195195
return obj
196196
else:

mypyc/lib-rt/CPy.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,9 @@ tuple_T3CIO CPyDict_NextValue(PyObject *dict_or_iter, CPyTagged offset);
711711
tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset);
712712
int CPyMapping_Check(PyObject *obj);
713713

714+
// Unsafe dict operations (assume PyDict_CheckExact(dict) is always true)
715+
PyObject *CPyDict_GetItemUnsafe(PyObject *dict, PyObject *key);
716+
714717
// Check that dictionary didn't change size during iteration.
715718
static inline char CPyDict_CheckSize(PyObject *dict, Py_ssize_t size) {
716719
if (!PyDict_CheckExact(dict)) {

mypyc/lib-rt/dict_ops.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,7 @@
1515
// some indirections.
1616
PyObject *CPyDict_GetItem(PyObject *dict, PyObject *key) {
1717
if (PyDict_CheckExact(dict)) {
18-
PyObject *res = PyDict_GetItemWithError(dict, key);
19-
if (!res) {
20-
if (!PyErr_Occurred()) {
21-
PyErr_SetObject(PyExc_KeyError, key);
22-
}
23-
} else {
24-
Py_INCREF(res);
25-
}
26-
return res;
18+
return CPyDict_GetItemUnsafe(dict, key);
2719
} else {
2820
return PyObject_GetItem(dict, key);
2921
}
@@ -489,3 +481,22 @@ tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset) {
489481
int CPyMapping_Check(PyObject *obj) {
490482
return Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MAPPING;
491483
}
484+
485+
// =======================
486+
// Unsafe dict operations
487+
// =======================
488+
489+
// Unsafe: assumes dict is a true dict (PyDict_CheckExact(dict) is always true)
490+
491+
PyObject *CPyDict_GetItemUnsafe(PyObject *dict, PyObject *key) {
492+
// No type check, direct call
493+
PyObject *res = PyDict_GetItemWithError(dict, key);
494+
if (!res) {
495+
if (!PyErr_Occurred()) {
496+
PyErr_SetObject(PyExc_KeyError, key);
497+
}
498+
} else {
499+
Py_INCREF(res);
500+
}
501+
return res;
502+
}

mypyc/primitives/dict_ops.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@
8181
)
8282

8383
# dict[key]
84+
exact_dict_get_item_op = custom_op(
85+
arg_types=[dict_rprimitive, object_rprimitive],
86+
return_type=object_rprimitive,
87+
c_function_name="CPyDict_GetItemUnsafe",
88+
error_kind=ERR_MAGIC,
89+
)
90+
91+
# dictsubclass[key]
8492
dict_get_item_op = method_op(
8593
name="__getitem__",
8694
arg_types=[dict_rprimitive, object_rprimitive],

0 commit comments

Comments
 (0)