Skip to content

Commit

Permalink
[mypyc] Use C99 compound literals for undefined tuple values (#15453)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
JukkaL committed Jun 16, 2023
1 parent cfec717 commit 65a715a
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 32 deletions.
35 changes: 13 additions & 22 deletions mypyc/codegen/emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,6 @@ def tuple_c_declaration(self, rtuple: RTuple) -> list[str]:
result.append(f"{self.ctype_spaced(typ)}f{i};")
i += 1
result.append(f"}} {rtuple.struct_name};")
values = self.tuple_undefined_value_helper(rtuple)
result.append(
"static {} {} = {{ {} }};".format(
self.ctype(rtuple), self.tuple_undefined_value(rtuple), "".join(values)
)
)
result.append("#endif")
result.append("")

Expand Down Expand Up @@ -470,23 +464,20 @@ def tuple_undefined_check_cond(
return check

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

def tuple_undefined_value_helper(self, rtuple: RTuple) -> list[str]:
res = []
# see tuple_c_declaration()
if len(rtuple.types) == 0:
return [self.c_undefined_value(int_rprimitive)]
for item in rtuple.types:
if not isinstance(item, RTuple):
res.append(self.c_undefined_value(item))
else:
sub_list = self.tuple_undefined_value_helper(item)
res.append("{ ")
res.extend(sub_list)
res.append(" }")
res.append(", ")
return res[:-1]
def c_initializer_undefined_value(self, rtype: RType) -> str:
"""Undefined value represented in a form suitable for variable initialization."""
if isinstance(rtype, RTuple):
if not rtype.types:
# Empty tuples contain a flag so that they can still indicate
# error values.
return f"{{ {int_rprimitive.c_undefined} }}"
items = ", ".join([self.c_initializer_undefined_value(t) for t in rtype.types])
return f"{{ {items} }}"
else:
return self.c_undefined_value(rtype)

# Higher-level operations

Expand Down
8 changes: 2 additions & 6 deletions mypyc/codegen/emitmodule.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from mypyc.ir.func_ir import FuncIR
from mypyc.ir.module_ir import ModuleIR, ModuleIRs, deserialize_modules
from mypyc.ir.ops import DeserMaps, LoadLiteral
from mypyc.ir.rtypes import RTuple, RType
from mypyc.ir.rtypes import RType
from mypyc.irbuild.main import build_ir
from mypyc.irbuild.mapper import Mapper
from mypyc.irbuild.prepare import load_type_map
Expand Down Expand Up @@ -1052,11 +1052,7 @@ def declare_finals(
def final_definition(self, module: str, name: str, typ: RType, emitter: Emitter) -> str:
static_name = emitter.static_name(name, module)
# Here we rely on the fact that undefined value and error value are always the same
if isinstance(typ, RTuple):
# We need to inline because initializer must be static
undefined = "{{ {} }}".format("".join(emitter.tuple_undefined_value_helper(typ)))
else:
undefined = emitter.c_undefined_value(typ)
undefined = emitter.c_initializer_undefined_value(typ)
return f"{emitter.ctype_spaced(typ)}{static_name} = {undefined};"

def declare_static_pyobject(self, identifier: str, emitter: Emitter) -> None:
Expand Down
3 changes: 0 additions & 3 deletions mypyc/lib-rt/CPy.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ typedef struct tuple_T3OOO {
PyObject *f1;
PyObject *f2;
} tuple_T3OOO;
static tuple_T3OOO tuple_undefined_T3OOO = { NULL, NULL, NULL };
#endif

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

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


Expand Down
20 changes: 19 additions & 1 deletion mypyc/test/test_emit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from mypyc.codegen.emit import Emitter, EmitterContext
from mypyc.ir.ops import BasicBlock, Register, Value
from mypyc.ir.rtypes import int_rprimitive
from mypyc.ir.rtypes import RTuple, bool_rprimitive, int_rprimitive, str_rprimitive
from mypyc.namegen import NameGenerator


Expand Down Expand Up @@ -49,3 +49,21 @@ def test_emit_line(self) -> None:
CPyStatics[1]; /* [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29] */\n"""
)

def test_emit_undefined_value_for_simple_type(self) -> None:
emitter = Emitter(self.context, {})
assert emitter.c_undefined_value(int_rprimitive) == "CPY_INT_TAG"
assert emitter.c_undefined_value(str_rprimitive) == "NULL"
assert emitter.c_undefined_value(bool_rprimitive) == "2"

def test_emit_undefined_value_for_tuple(self) -> None:
emitter = Emitter(self.context, {})
assert (
emitter.c_undefined_value(RTuple([str_rprimitive, int_rprimitive, bool_rprimitive]))
== "(tuple_T3OIC) { NULL, CPY_INT_TAG, 2 }"
)
assert emitter.c_undefined_value(RTuple([str_rprimitive])) == "(tuple_T1O) { NULL }"
assert (
emitter.c_undefined_value(RTuple([RTuple([str_rprimitive]), bool_rprimitive]))
== "(tuple_T2T1OC) { { NULL }, 2 }"
)

0 comments on commit 65a715a

Please sign in to comment.