Skip to content

Commit 65a715a

Browse files
authored
[mypyc] Use C99 compound literals for undefined tuple values (#15453)
This simplifies things a bit. All the C compilers we care about should support this. This will make things easier once we support more types represented as C structs. I expect this to have no measurable impact on performance.
1 parent cfec717 commit 65a715a

File tree

4 files changed

+34
-32
lines changed

4 files changed

+34
-32
lines changed

Diff for: mypyc/codegen/emit.py

+13-22
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,6 @@ def tuple_c_declaration(self, rtuple: RTuple) -> list[str]:
345345
result.append(f"{self.ctype_spaced(typ)}f{i};")
346346
i += 1
347347
result.append(f"}} {rtuple.struct_name};")
348-
values = self.tuple_undefined_value_helper(rtuple)
349-
result.append(
350-
"static {} {} = {{ {} }};".format(
351-
self.ctype(rtuple), self.tuple_undefined_value(rtuple), "".join(values)
352-
)
353-
)
354348
result.append("#endif")
355349
result.append("")
356350

@@ -470,23 +464,20 @@ def tuple_undefined_check_cond(
470464
return check
471465

472466
def tuple_undefined_value(self, rtuple: RTuple) -> str:
473-
return "tuple_undefined_" + rtuple.unique_id
467+
"""Undefined tuple value suitable in an expression."""
468+
return f"({rtuple.struct_name}) {self.c_initializer_undefined_value(rtuple)}"
474469

475-
def tuple_undefined_value_helper(self, rtuple: RTuple) -> list[str]:
476-
res = []
477-
# see tuple_c_declaration()
478-
if len(rtuple.types) == 0:
479-
return [self.c_undefined_value(int_rprimitive)]
480-
for item in rtuple.types:
481-
if not isinstance(item, RTuple):
482-
res.append(self.c_undefined_value(item))
483-
else:
484-
sub_list = self.tuple_undefined_value_helper(item)
485-
res.append("{ ")
486-
res.extend(sub_list)
487-
res.append(" }")
488-
res.append(", ")
489-
return res[:-1]
470+
def c_initializer_undefined_value(self, rtype: RType) -> str:
471+
"""Undefined value represented in a form suitable for variable initialization."""
472+
if isinstance(rtype, RTuple):
473+
if not rtype.types:
474+
# Empty tuples contain a flag so that they can still indicate
475+
# error values.
476+
return f"{{ {int_rprimitive.c_undefined} }}"
477+
items = ", ".join([self.c_initializer_undefined_value(t) for t in rtype.types])
478+
return f"{{ {items} }}"
479+
else:
480+
return self.c_undefined_value(rtype)
490481

491482
# Higher-level operations
492483

Diff for: mypyc/codegen/emitmodule.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
from mypyc.ir.func_ir import FuncIR
5252
from mypyc.ir.module_ir import ModuleIR, ModuleIRs, deserialize_modules
5353
from mypyc.ir.ops import DeserMaps, LoadLiteral
54-
from mypyc.ir.rtypes import RTuple, RType
54+
from mypyc.ir.rtypes import RType
5555
from mypyc.irbuild.main import build_ir
5656
from mypyc.irbuild.mapper import Mapper
5757
from mypyc.irbuild.prepare import load_type_map
@@ -1052,11 +1052,7 @@ def declare_finals(
10521052
def final_definition(self, module: str, name: str, typ: RType, emitter: Emitter) -> str:
10531053
static_name = emitter.static_name(name, module)
10541054
# Here we rely on the fact that undefined value and error value are always the same
1055-
if isinstance(typ, RTuple):
1056-
# We need to inline because initializer must be static
1057-
undefined = "{{ {} }}".format("".join(emitter.tuple_undefined_value_helper(typ)))
1058-
else:
1059-
undefined = emitter.c_undefined_value(typ)
1055+
undefined = emitter.c_initializer_undefined_value(typ)
10601056
return f"{emitter.ctype_spaced(typ)}{static_name} = {undefined};"
10611057

10621058
def declare_static_pyobject(self, identifier: str, emitter: Emitter) -> None:

Diff for: mypyc/lib-rt/CPy.h

-3
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ typedef struct tuple_T3OOO {
4141
PyObject *f1;
4242
PyObject *f2;
4343
} tuple_T3OOO;
44-
static tuple_T3OOO tuple_undefined_T3OOO = { NULL, NULL, NULL };
4544
#endif
4645

4746
// Our return tuple wrapper for dictionary iteration helper.
@@ -52,7 +51,6 @@ typedef struct tuple_T3CIO {
5251
CPyTagged f1; // Last dict offset
5352
PyObject *f2; // Next dictionary key or value
5453
} tuple_T3CIO;
55-
static tuple_T3CIO tuple_undefined_T3CIO = { 2, CPY_INT_TAG, NULL };
5654
#endif
5755

5856
// Same as above but for both key and value.
@@ -64,7 +62,6 @@ typedef struct tuple_T4CIOO {
6462
PyObject *f2; // Next dictionary key
6563
PyObject *f3; // Next dictionary value
6664
} tuple_T4CIOO;
67-
static tuple_T4CIOO tuple_undefined_T4CIOO = { 2, CPY_INT_TAG, NULL, NULL };
6865
#endif
6966

7067

Diff for: mypyc/test/test_emit.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from mypyc.codegen.emit import Emitter, EmitterContext
66
from mypyc.ir.ops import BasicBlock, Register, Value
7-
from mypyc.ir.rtypes import int_rprimitive
7+
from mypyc.ir.rtypes import RTuple, bool_rprimitive, int_rprimitive, str_rprimitive
88
from mypyc.namegen import NameGenerator
99

1010

@@ -49,3 +49,21 @@ def test_emit_line(self) -> None:
4949
CPyStatics[1]; /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
5050
21, 22, 23, 24, 25, 26, 27, 28, 29] */\n"""
5151
)
52+
53+
def test_emit_undefined_value_for_simple_type(self) -> None:
54+
emitter = Emitter(self.context, {})
55+
assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG"
56+
assert emitter.c_undefined_value(str_rprimitive) == "NULL"
57+
assert emitter.c_undefined_value(bool_rprimitive) == "2"
58+
59+
def test_emit_undefined_value_for_tuple(self) -> None:
60+
emitter = Emitter(self.context, {})
61+
assert (
62+
emitter.c_undefined_value(RTuple([str_rprimitive, int_rprimitive, bool_rprimitive]))
63+
== "(tuple_T3OIC) { NULL, CPY_INT_TAG, 2 }"
64+
)
65+
assert emitter.c_undefined_value(RTuple([str_rprimitive])) == "(tuple_T1O) { NULL }"
66+
assert (
67+
emitter.c_undefined_value(RTuple([RTuple([str_rprimitive]), bool_rprimitive]))
68+
== "(tuple_T2T1OC) { { NULL }, 2 }"
69+
)

0 commit comments

Comments
 (0)