Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mypyc] Use C99 compound literals for undefined tuple values #15453

Merged
merged 1 commit into from Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 13 additions & 22 deletions mypyc/codegen/emit.py
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
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
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
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 }"
)