n_first_v(1029) 与 vcount(1010/1055) 承接 sizet* 元素,原先 64->32 隐式窄化;改用显式 int(...) 保持原截断语义
1182 lines
69 KiB
Plaintext
1182 lines
69 KiB
Plaintext
import lib.core.Gen.GenTypes as GenTypes
|
|
import t, c
|
|
from stdint import *
|
|
import ast
|
|
import mlirlite
|
|
import memhub
|
|
import string
|
|
import viperlib
|
|
import lib.core.VLogger as VLogger
|
|
import lib.core.Gen.Translator as HT
|
|
import lib.core.CompilerFactory as CompilerFactory
|
|
|
|
from . import ClassGenBase
|
|
|
|
|
|
# Local constant copies (originally defined in HandlesEnum/HandlesClassDef; lost when the file was split).
|
|
# Undefined names fold to 0 in constant context, silently skipping enum member collection and CEnum registration.
|
|
define ENUM_MEMBER_MAX_LOCAL = 128
|
|
define RENUM_CLASS_MAX = 64
|
|
define RENUM_VARIANT_PER_CLASS = 32
|
|
define RENUM_ALIAS_MAX = 512
|
|
|
|
# ClassGenEnum - split from ClassGenMixin (stage D): enum/union/renum definitions and variant tables.
|
|
# ============================================================
|
|
novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
|
"""enum/union/renum definitions and variant tables"""
|
|
|
|
def _resolve_enum_member_type(self, pool: memhub.MemBuddy*, ann_node: ast.AST*, imported_modules: str, from_imports: str) -> mlirlite.LLVMType*:
|
|
"""Resolve an enum member's type annotation to an LLVM integer type"""
|
|
if ann_node is None: return mlirlite.Int32(pool)
|
|
|
|
k: int = ann_node.kind()
|
|
|
|
# Attribute node: t.State / int / int8 etc.
|
|
if k == ast.ASTKind.Attribute:
|
|
at: ast.Attribute* = ast.Attribute*(ann_node)
|
|
if at.attr is None:
|
|
# [S4 fail-fast] Attribute missing the member name (corrupt AST); no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, "enum member type annotation Attribute is missing the member name", "TypeError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
# t.State -> default i32
|
|
if string.strcmp(at.attr, "State") == 0: return mlirlite.Int32(pool)
|
|
# [enum bool rule] `t.CBool` and bare `bool` both materialize as 8 bits in enum member annotations,
|
|
# else the same member would split into i1 / i8; the global `t.CBool` stays i1 (only enums differ).
|
|
if string.strcmp(at.attr, "CBool") == 0: return mlirlite.Int8(pool)
|
|
# Other t.CXxx types: unified (CTypedef dynamic table first, fall back to map_t_type)
|
|
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, imported_modules, from_imports)
|
|
if ty is not None: return ty
|
|
# [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: t.{at.attr}", "NameError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
|
|
# Name node: from-import form (State / CInt)
|
|
if k == ast.ASTKind.Name:
|
|
nm: ast.Name* = ast.Name*(ann_node)
|
|
if nm.id is None:
|
|
# [S4 fail-fast] Name missing the identifier (corrupt AST); no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, "enum member type annotation Name is missing the identifier", "TypeError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
if string.strcmp(nm.id, "State") == 0: return mlirlite.Int32(pool)
|
|
# Builtin bare integer types (same rule as resolve_annotation_type's Name branch): enum members are
|
|
# integer constants, so `int` / `char` / `bool` pass through; other bare names fail fast on a typedef miss.
|
|
if string.strcmp(nm.id, "int") == 0: return mlirlite.Int32(pool)
|
|
if string.strcmp(nm.id, "char") == 0: return mlirlite.Int8(pool)
|
|
if string.strcmp(nm.id, "bool") == 0:
|
|
# [enum bool rule] enum storage must be byte-addressable, and i1 cannot be an enum width,
|
|
# so an enum member's bool is 8 bits. This does not change the global bool = i1.
|
|
return mlirlite.Int8(pool)
|
|
ty2: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, nm.id, imported_modules, from_imports)
|
|
if ty2 is not None: return ty2
|
|
# [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {nm.id}", "NameError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
|
|
# BinOp node: t.State | int8 (recurse to find the concrete type)
|
|
if k == ast.ASTKind.BinOp:
|
|
bop: ast.BinOp* = ast.BinOp*(ann_node)
|
|
if bop.op == ast.OpKind.BitOr:
|
|
left_ty: mlirlite.LLVMType* = self._resolve_enum_member_type(pool, bop.left, imported_modules, from_imports)
|
|
# Skip t.State (the i32 default); prefer a concrete type
|
|
left_bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(left_ty)
|
|
if left_bits != 0 and not self._is_state_annotation(bop.left): return left_ty
|
|
right_ty: mlirlite.LLVMType* = self._resolve_enum_member_type(pool, bop.right, imported_modules, from_imports)
|
|
if right_ty is not None and not self._is_state_annotation(bop.right): return right_ty
|
|
# Both sides are State or undecidable; return the wider one
|
|
right_bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(right_ty)
|
|
if left_bits >= right_bits: return left_ty
|
|
return right_ty
|
|
# fail-fast: BinOp but not BitOr (unknown union op); no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {self.Gen.TypeGenResolve.annotation_to_str(ann_node)}", "TypeError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
|
|
# fail-fast: unknown annotation node kind; no silent i32 fallback
|
|
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {self.Gen.TypeGenResolve.annotation_to_str(ann_node)}", "TypeError")
|
|
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
|
def _is_state_annotation(self, node: ast.AST*) -> int:
|
|
"""Check whether an annotation node is t.State, 1=yes / 0=no"""
|
|
if node is None: return 0
|
|
k: int = node.kind()
|
|
if k == ast.ASTKind.Attribute:
|
|
at: ast.Attribute* = ast.Attribute*(node)
|
|
if at.attr is not None and string.strcmp(at.attr, "State") == 0: return 1
|
|
return 0
|
|
if k == ast.ASTKind.Name:
|
|
nm: ast.Name* = ast.Name*(node)
|
|
if nm.id is not None and string.strcmp(nm.id, "State") == 0: return 1
|
|
return 0
|
|
return 0
|
|
def _enum_annotation_name_is_ptr(self, node: ast.AST*) -> int:
|
|
"""Whether the annotation's top name (Name/Attribute/Subscript) is a pointer form, 1=yes / 0=no"""
|
|
if node is None: return 0
|
|
k: int = node.kind()
|
|
if k == ast.ASTKind.Attribute:
|
|
at: ast.Attribute* = ast.Attribute*(node)
|
|
if at.attr is not None:
|
|
if string.strcmp(at.attr, "CPtr") == 0: return 1
|
|
if string.strcmp(at.attr, "CRef") == 0: return 1
|
|
if string.strcmp(at.attr, "CTempRef") == 0: return 1
|
|
if string.strcmp(at.attr, "CAutoPtr") == 0: return 1
|
|
if string.strcmp(at.attr, "rawptr") == 0: return 1
|
|
if string.strcmp(at.attr, "str") == 0: return 1
|
|
if string.strcmp(at.attr, "bytes") == 0: return 1
|
|
return 0
|
|
if k == ast.ASTKind.Name:
|
|
nm: ast.Name* = ast.Name*(node)
|
|
if nm.id is not None:
|
|
if string.strcmp(nm.id, "CPtr") == 0: return 1
|
|
if string.strcmp(nm.id, "CRef") == 0: return 1
|
|
if string.strcmp(nm.id, "CTempRef") == 0: return 1
|
|
if string.strcmp(nm.id, "CAutoPtr") == 0: return 1
|
|
if string.strcmp(nm.id, "rawptr") == 0: return 1
|
|
if string.strcmp(nm.id, "str") == 0: return 1
|
|
if string.strcmp(nm.id, "bytes") == 0: return 1
|
|
return 0
|
|
if k == ast.ASTKind.Subscript:
|
|
sub: ast.Subscript* = ast.Subscript*(node)
|
|
if sub.value is not None: return self._enum_annotation_name_is_ptr(sub.value)
|
|
return 0
|
|
return 0
|
|
def _enum_member_annotation_is_ptr(self, ann_node: ast.AST*) -> int:
|
|
"""Whether an enum member annotation (checking each union side) contains a pointer type, 1=yes / 0=no"""
|
|
if ann_node is None: return 0
|
|
if ann_node.kind() == ast.ASTKind.BinOp:
|
|
bop: ast.BinOp* = ast.BinOp*(ann_node)
|
|
if bop.op != ast.OpKind.BitOr: return 0
|
|
if self._enum_member_annotation_is_ptr(bop.left) == 1: return 1
|
|
return self._enum_member_annotation_is_ptr(bop.right)
|
|
return self._enum_annotation_name_is_ptr(ann_node)
|
|
def _extract_int_value(self, node: ast.AST*) -> int64:
|
|
"""Extract an integer value from an AST node, 0 on failure"""
|
|
if node is None: return 0
|
|
k: int = node.kind()
|
|
if k == ast.ASTKind.Constant:
|
|
cn: ast.Constant* = ast.Constant*(node)
|
|
if cn.const_kind == ast.CONST_INT: return cn.int_val
|
|
return 0
|
|
if k == ast.ASTKind.UnaryOp:
|
|
uo: ast.UnaryOp* = ast.UnaryOp*(node)
|
|
if uo.op == ast.OpKind.USub:
|
|
inner: int64 = self._extract_int_value(uo.operand)
|
|
return -inner
|
|
return 0
|
|
return 0
|
|
|
|
# "Visible name vs registered name" fallback check for enum members (same rule source as TPC).
|
|
# Any unrecognized member form makes the visible-name count exceed the registered count -> fail-fast.
|
|
def _enum_elts_visible_names(self, elts: list[ast.AST*]*) -> int:
|
|
"""Count visible names in an element table (shared by Tuple/List)."""
|
|
if elts is None: return 0
|
|
n: int = 0
|
|
for i in range(len(elts)): n += self._enum_target_visible_names(elts.get(i))
|
|
return n
|
|
def _enum_target_visible_names(self, node: ast.AST*) -> int:
|
|
"""Count visible names at a member-name position (Name / Tuple / List items, Starred recursion)."""
|
|
if node is None: return 0
|
|
k: int = node.kind()
|
|
if k == ast.ASTKind.Name: return 1
|
|
if k == ast.ASTKind.Tuple:
|
|
tup: ast.Tuple* = ast.Tuple*(node)
|
|
return self._enum_elts_visible_names(tup.elts)
|
|
if k == ast.ASTKind.List:
|
|
lst: ast.List* = ast.List*(node)
|
|
return self._enum_elts_visible_names(lst.elts)
|
|
if k == ast.ASTKind.Starred:
|
|
st: ast.Starred* = ast.Starred*(node)
|
|
return self._enum_target_visible_names(st.value)
|
|
return 0
|
|
def _enum_body_visible_names(self, children: list[ast.AST*]*) -> int:
|
|
"""Total source-visible member names in an enum class body (including forms the registrar rejects)."""
|
|
if children is None: return 0
|
|
n: int = 0
|
|
for ci in range(len(children)):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk: int = stmt.kind()
|
|
if sk == ast.ASTKind.Assign:
|
|
ag: ast.Assign* = ast.Assign*(stmt)
|
|
if ag.targets is not None:
|
|
for ti in range(len(ag.targets)): n += self._enum_target_visible_names(ag.targets.get(ti))
|
|
elif sk == ast.ASTKind.AnnAssign:
|
|
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
|
n += self._enum_target_visible_names(aa.target)
|
|
elif sk == ast.ASTKind.Expr:
|
|
ex: ast.Expr* = ast.Expr*(stmt)
|
|
n += self._enum_target_visible_names(ex.value)
|
|
return n
|
|
def _enum_first_unregistered_node(self, children: list[ast.AST*]*) -> ast.AST*:
|
|
"""First statement with visible member names the registrar rejects, for fail-fast line location; None if none."""
|
|
if children is None: return None
|
|
for ci in range(len(children)):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk: int = stmt.kind()
|
|
if sk == ast.ASTKind.Expr:
|
|
ex: ast.Expr* = ast.Expr*(stmt)
|
|
if self._enum_target_visible_names(ex.value) > 0: return stmt
|
|
elif sk == ast.ASTKind.AnnAssign:
|
|
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
|
if aa.target is not None and aa.target.kind() != ast.ASTKind.Name and self._enum_target_visible_names(aa.target) > 0: return stmt
|
|
elif sk == ast.ASTKind.Assign:
|
|
ag: ast.Assign* = ast.Assign*(stmt)
|
|
if ag.targets is not None:
|
|
for ti in range(len(ag.targets)):
|
|
tgt: ast.AST* = ag.targets.get(ti)
|
|
if tgt is not None and tgt.kind() != ast.ASTKind.Name and self._enum_target_visible_names(tgt) > 0: return stmt
|
|
return None
|
|
def _translate_enum_def(self, trans: HT.Translator*, cd: ast.ClassDef*) -> int:
|
|
"""Translate an enum class definition, return 0"""
|
|
if cd is None or cd.name is None or trans is None: return 0
|
|
|
|
pool: memhub.MemBuddy* = trans.Pool
|
|
class_name: str = cd.name
|
|
|
|
# Do not skip by global same-name: cross-module enums may share a name (e.g. FRESULT), and
|
|
# skipping would drop the later module's members. register_enum/add_enum_member are idempotent.
|
|
|
|
children: list[ast.AST*]* = cd.children
|
|
if children is None:
|
|
VLogger.warning(f"{class_name} has no body", "ENUM")
|
|
return 0
|
|
|
|
cn: sizet = len(children)
|
|
|
|
# Temporary arrays holding member info
|
|
member_names_buf: sizet* = pool.alloc(8 * ENUM_MEMBER_MAX_LOCAL)
|
|
member_types_buf: sizet* = pool.alloc(8 * ENUM_MEMBER_MAX_LOCAL)
|
|
member_values_buf: int64* = pool.alloc(8 * ENUM_MEMBER_MAX_LOCAL)
|
|
if member_names_buf is None or member_types_buf is None or member_values_buf is None: return 0
|
|
string.memset(member_names_buf, 0, 8 * ENUM_MEMBER_MAX_LOCAL)
|
|
string.memset(member_types_buf, 0, 8 * ENUM_MEMBER_MAX_LOCAL)
|
|
string.memset(member_values_buf, 0, 8 * ENUM_MEMBER_MAX_LOCAL)
|
|
|
|
member_count: int = 0
|
|
next_val: int64 = 0
|
|
|
|
for ci in range(cn):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk: int = stmt.kind()
|
|
|
|
if sk == ast.ASTKind.AnnAssign:
|
|
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
|
if aa is None or aa.target is None: continue
|
|
|
|
# Get the member name
|
|
if aa.target.kind() != ast.ASTKind.Name: continue
|
|
tgt: ast.Name* = ast.Name*(aa.target)
|
|
if tgt.id is None: continue
|
|
|
|
# [enum spec fail-fast] Members may not be pointer types: t.CPtr / CRef /
|
|
# CTempRef / CAutoPtr / rawptr[...] / str / bytes and unions carrying a pointer. Checked before type resolution.
|
|
if self._enum_member_annotation_is_ptr(aa.annotation) == 1:
|
|
self.Gen.TypeGenErr.fatal_error(aa, f"enum member '{tgt.id}' cannot be a pointer type (enum values only support integer member types)", "TypeError")
|
|
return 0 # unreachable (fatal_error already exited)
|
|
|
|
# Resolve the member type
|
|
member_ty: mlirlite.LLVMType* = self._resolve_enum_member_type(pool, aa.annotation, trans._imported_modules, trans._from_imports)
|
|
|
|
# [enum spec fail-fast] Member types must be integers: float/void/struct/array have bit
|
|
# width 0 and cannot drive "enum width = widest member"; no silent downgrade.
|
|
member_bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(member_ty)
|
|
if member_bits <= 0:
|
|
self.Gen.TypeGenErr.fatal_error(aa, f"the type of enum member '{tgt.id}' must be an integer type (pointers/floats/void/structs/arrays are not allowed), cannot infer enum width", "TypeError")
|
|
return 0 # unreachable (fatal_error already exited)
|
|
|
|
# Resolve the member value
|
|
has_value: int = 0
|
|
cur_val: int64 = 0
|
|
if aa.value is not None:
|
|
cur_val = self._extract_int_value(aa.value)
|
|
has_value = 1
|
|
else: cur_val = next_val
|
|
|
|
# Next automatic value
|
|
next_val = cur_val + 1
|
|
|
|
# Store
|
|
if member_count < ENUM_MEMBER_MAX_LOCAL:
|
|
member_names_buf[member_count] = sizet(tgt.id)
|
|
member_types_buf[member_count] = sizet(member_ty)
|
|
member_values_buf[member_count] = cur_val
|
|
member_count += 1
|
|
|
|
elif sk == ast.ASTKind.Assign:
|
|
# Handle Assign-form enum members (DEBUG = 0, INFO = 1, ...). Each Name target is a
|
|
# member (A = B = 1 registers both), matching TPC's per-target rule.
|
|
ag: ast.Assign* = ast.Assign*(stmt)
|
|
if ag is None or ag.targets is None: continue
|
|
tgts: list[ast.AST*]* = ag.targets
|
|
tn: sizet = len(tgts)
|
|
if tn == 0: continue
|
|
|
|
# Assign has no annotation; default to i32
|
|
member_ty_a: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
|
|
|
# Resolve the member value (all targets in the statement share it; next_val advances once)
|
|
cur_val_a: int64 = 0
|
|
if ag.value is not None: cur_val_a = self._extract_int_value(ag.value)
|
|
else: cur_val_a = next_val
|
|
next_val = cur_val_a + 1
|
|
|
|
# Store per target (non-Name targets are caught by the registration-side fallback check)
|
|
for ti in range(tn):
|
|
t0: ast.AST* = tgts.get(ti)
|
|
if t0 is None or t0.kind() != ast.ASTKind.Name: continue
|
|
tgt_a: ast.Name* = ast.Name*(t0)
|
|
if tgt_a.id is None: continue
|
|
if member_count < ENUM_MEMBER_MAX_LOCAL:
|
|
member_names_buf[member_count] = sizet(tgt_a.id)
|
|
member_types_buf[member_count] = sizet(member_ty_a)
|
|
member_values_buf[member_count] = cur_val_a
|
|
member_count += 1
|
|
|
|
# [fallback check fail-fast] "Visible member names" vs "actually registered members". Any form the
|
|
# registrar does not recognize makes visible > registered, so fail-fast instead of silently dropping.
|
|
visible_count: int = self._enum_body_visible_names(children)
|
|
if visible_count != member_count:
|
|
diff_count: int = visible_count - member_count
|
|
bad: ast.AST* = self._enum_first_unregistered_node(children)
|
|
bad_desc: str = "(cannot locate the specific statement, check the enum body)"
|
|
if bad is not None: bad_desc = bad.type_name()
|
|
self.Gen.TypeGenErr.fatal_error(bad if bad is not None else cd, f"enum '{class_name}' member registration count mismatch: {visible_count} member names visible in source, " f"but only {member_count} registered ({diff_count} difference would be silently dropped). " f"first unrecognized member form: a statement of shape {bad_desc} -- e.g. comma group with value `A, B = 1`, " f"comma group with annotation `A, B: T`, single name with trailing comma `A,`. " f"expected forms: one `Name` / `Name: T` / `Name = value` per line, " f"or multiple simple identifiers separated by commas (`A, B, C`, trailing comma allowed). " f"fix: rewrite that line as one member per line, or as a plain identifier comma group (remove `= value` / `: T`). " f"(this check compares the number of visible names vs registered names at the registrar, so any unrecognized form hits it, " f"no more members are silently dropped)", "TypeError")
|
|
return 0
|
|
|
|
if member_count == 0:
|
|
VLogger.warning(f"{class_name} has no members", "ENUM")
|
|
return 0
|
|
|
|
# Determine the base type: the widest member type in bits
|
|
base_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
|
max_bits: int = 0
|
|
for mi in range(member_count):
|
|
ty_addr: sizet = member_types_buf[mi]
|
|
if ty_addr == 0: continue
|
|
mty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_addr)
|
|
bits: int = self.Gen.TypeGenErr.get_llvm_type_bits(mty)
|
|
if bits > max_bits:
|
|
max_bits = bits
|
|
base_ty = mty
|
|
|
|
# Register the enum
|
|
entry: GenTypes.EnumEntry* = self.Gen.StmtGenDecl.register_enum(pool, class_name, base_ty)
|
|
if entry is None:
|
|
VLogger.error("failed to register {class_name}", "ENUM")
|
|
return 0
|
|
|
|
# Add members
|
|
for mi in range(member_count):
|
|
fname_addr: sizet = member_names_buf[mi]
|
|
if fname_addr == 0: continue
|
|
mname: str = rawptr(fname_addr)
|
|
fty_addr2: sizet = member_types_buf[mi]
|
|
if fty_addr2 == 0: continue
|
|
mty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr2)
|
|
mval: int64 = member_values_buf[mi]
|
|
if mname is not None and mty2 is not None: self.Gen.StmtGenDecl.add_enum_member(pool, entry, mname, mval, mty2)
|
|
|
|
VLogger.info(f"registered {class_name} with {member_count} members, base_bits={max_bits}", "ENUM")
|
|
return 0
|
|
|
|
def _get_type_size(self, ty: mlirlite.LLVMType*) -> int:
|
|
"""Compute the byte size of an LLVM type including alignment padding"""
|
|
if ty is None: return 0
|
|
match ty:
|
|
case mlirlite.LLVMType.Int(bits):
|
|
return (bits + 7) // 8
|
|
case mlirlite.LLVMType.Float(bits):
|
|
return bits // 8
|
|
case mlirlite.LLVMType.Ptr(pointee):
|
|
return 8
|
|
case mlirlite.LLVMType.Array(elem_ty, count):
|
|
return self._get_type_size(elem_ty) * count
|
|
case mlirlite.LLVMType.Struct(fields, fcount, name):
|
|
# Compute the struct size with field-alignment padding: each field's alignment is its
|
|
# natural alignment and the total is padded to the largest field alignment. packed structs have no padding.
|
|
is_packed: int = self.Gen.StructGen.struct_type_is_packed(ty)
|
|
total: int = 0
|
|
max_align: int = 1
|
|
cur: mlirlite.ParamNode* = fields
|
|
i: int = 0
|
|
while cur is not None and i < fcount:
|
|
if cur.Ty is not None:
|
|
fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(cur.Ty)
|
|
fsize: int = self._get_type_size(fty)
|
|
falign: int = self._get_type_align(fty)
|
|
# Align the current offset to the field boundary (packed inserts no padding)
|
|
if is_packed == 0 and falign > 0:
|
|
rem: int = total % falign
|
|
if rem != 0: total += falign - rem
|
|
total += fsize
|
|
if is_packed == 0 and falign > max_align: max_align = falign
|
|
cur = cur.Next
|
|
i += 1
|
|
# packed has no tail alignment; non-packed totals are padded to the max field alignment
|
|
if is_packed != 0: return total
|
|
rem2: int = total % max_align
|
|
if rem2 != 0: total += max_align - rem2
|
|
return total
|
|
case _:
|
|
return 8
|
|
def _get_type_align(self, ty: mlirlite.LLVMType*) -> int:
|
|
"""Return the natural alignment of a type in bytes"""
|
|
if ty is None: return 1
|
|
match ty:
|
|
case mlirlite.LLVMType.Int(bits):
|
|
if bits <= 8: return 1
|
|
elif bits <= 16: return 2
|
|
elif bits <= 32: return 4
|
|
else: return 8
|
|
case mlirlite.LLVMType.Float(bits):
|
|
if bits <= 32: return 4
|
|
else: return 8
|
|
case mlirlite.LLVMType.Ptr(pointee):
|
|
return 8
|
|
case mlirlite.LLVMType.Array(elem_ty, count):
|
|
return self._get_type_align(elem_ty)
|
|
case mlirlite.LLVMType.Struct(fields, fcount, name):
|
|
# A packed struct is byte-aligned (alignment 1, no padding)
|
|
if self.Gen.StructGen.struct_type_is_packed(ty) != 0: return 1
|
|
# A struct's alignment is the largest field alignment
|
|
max_align: int = 1
|
|
cur: mlirlite.ParamNode* = fields
|
|
i: int = 0
|
|
while cur is not None and i < fcount:
|
|
if cur.Ty is not None:
|
|
fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(cur.Ty)
|
|
fa: int = self._get_type_align(fty)
|
|
if fa > max_align: max_align = fa
|
|
cur = cur.Next
|
|
i += 1
|
|
return max_align
|
|
case _:
|
|
return 8
|
|
def _translate_union_def(self, trans: HT.Translator*, cd: ast.ClassDef*) -> int:
|
|
"""Translate a union definition, return 0"""
|
|
if cd is None or cd.name is None or trans is None: return 0
|
|
|
|
pool: memhub.MemBuddy* = trans.Pool
|
|
class_name: str = cd.name
|
|
|
|
# Namespace isolation: mark visible in the current file (before the existing check)
|
|
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
|
|
|
# Check whether already registered (SHA1 distinguishes cross-module same-name classes)
|
|
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
|
if existing is not None:
|
|
# Phase B: register the existing named struct into the current module
|
|
if existing.Ty is not None: mlirlite.module_add_named_type(trans.Module, pool, existing.Ty)
|
|
# SHA1 was already set by register_struct; no need to set it again
|
|
return 0
|
|
|
|
children: list[ast.AST*]* = cd.children
|
|
if children is None:
|
|
VLogger.warning("{class_name} has no body", "UNION")
|
|
return 0
|
|
|
|
cn: sizet = len(children)
|
|
|
|
# Temporary arrays holding field info
|
|
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
|
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
|
if field_names_buf is None or field_types_buf is None: return 0
|
|
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
|
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
|
|
|
field_count: int = 0
|
|
max_size: int = 0
|
|
|
|
for ci in range(cn):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk: int = stmt.kind()
|
|
|
|
if sk == ast.ASTKind.AnnAssign:
|
|
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
|
if aa is None or aa.target is None: continue
|
|
|
|
# Get the field name
|
|
if aa.target.kind() != ast.ASTKind.Name: continue
|
|
tgt: ast.Name* = ast.Name*(aa.target)
|
|
if tgt.id is None: continue
|
|
|
|
# Resolve the field type
|
|
field_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
|
if field_ty is None:
|
|
# Phase A-pre (declare_only=1): a dependency class may not be registered yet;
|
|
# return 1 so multi-pass convergence kicks in (renum returns 1 = skip this class)
|
|
if trans._declare_only == 1: return 1
|
|
# Phase B (declare_only=0): a real type resolution error; fail-fast
|
|
type_name: str = self.Gen.TypeGenResolve.annotation_to_str(aa.annotation)
|
|
self.Gen.TypeGenErr.fatal_error(aa, f"'{type_name}' is not defined", "NameError")
|
|
field_ty = mlirlite.Int32(pool)
|
|
|
|
# Compute the field size and update max_size
|
|
fsize: int = self._get_type_size(field_ty)
|
|
if fsize > max_size: max_size = fsize
|
|
|
|
# Store into the array
|
|
if field_count < GenTypes.FIELD_MAX:
|
|
field_names_buf[field_count] = sizet(tgt.id)
|
|
field_types_buf[field_count] = sizet(field_ty)
|
|
field_count += 1
|
|
|
|
if field_count == 0:
|
|
VLogger.warning(f"{class_name} has no fields", "UNION")
|
|
return 0
|
|
|
|
if max_size < 1: max_size = 1
|
|
|
|
# Create the union's LLVM type: Struct([Array(Int8, max_size)]), i.e. { [max_size x i8] }
|
|
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
|
array_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, max_size)
|
|
|
|
# Build the ParamNode list (single field: byte array)
|
|
pnode: mlirlite.ParamNode* = pool.alloc(mlirlite.ParamNode.__sizeof__())
|
|
if pnode is None: return 0
|
|
string.memset(pnode, 0, mlirlite.ParamNode.__sizeof__())
|
|
pnode.Ty = array_ty
|
|
|
|
# Build the named struct type name: "sha1.ClassName" or "ClassName"
|
|
union_type_name: str = class_name
|
|
if trans.ModuleSha1 is not None:
|
|
union_name_buf: str = pool.alloc(64)
|
|
if union_name_buf is not None:
|
|
viperlib.snprintf(union_name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
|
|
union_type_name = union_name_buf
|
|
|
|
union_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, pnode, 1, union_type_name)
|
|
if union_ty is None:
|
|
VLogger.error(f"failed to create type for {class_name}", "UNION")
|
|
return 0
|
|
|
|
# Deep-copy the newly registered union struct_ty to stdlib (survives cross-file reset).
|
|
# Needed even under StdlibAlloc=1 (after Phase2 mark2): else reset_to(mark2) leaves entry.Ty dangling.
|
|
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
|
|
union_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, union_ty)
|
|
if union_copy is not None: union_ty = union_copy
|
|
|
|
# Register the named struct into the module
|
|
mlirlite.module_add_named_type(trans.Module, pool, union_ty)
|
|
|
|
# Register into HandlesStruct (SHA1 is set directly at registration to avoid picking the wrong entry)
|
|
entry: GenTypes.StructEntry* = self.Gen.StructGen.register_struct(pool, class_name, union_ty, trans.ModuleSha1)
|
|
if entry is None:
|
|
VLogger.error(f"failed to register {class_name}", "UNION")
|
|
return 0
|
|
|
|
# Mark visible in the current file (namespace isolation)
|
|
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
|
|
|
# Mark as a union (using entry directly to avoid cross-module same-name lookup errors)
|
|
entry.IsUnion = 1
|
|
|
|
# Add field info (all fields have Index=0 because they share offset 0)
|
|
for fi in range(field_count):
|
|
fname_addr: sizet = field_names_buf[fi]
|
|
if fname_addr == 0: continue
|
|
fname: str = rawptr(fname_addr)
|
|
fty_addr2: sizet = field_types_buf[fi]
|
|
if fty_addr2 == 0: continue
|
|
fty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr2)
|
|
if fname is not None and fty2 is not None: self.Gen.StructGen.add_field(pool, entry, fname, fty2, None, None)
|
|
|
|
VLogger.info(f"registered {class_name} with {field_count} fields, max_size={max_size}", "UNION")
|
|
return 0
|
|
def _get_annotation_field_size(self, pool: memhub.MemBuddy*, annotation: ast.AST*) -> int:
|
|
"""Compute the annotation type's byte size for REnum layout"""
|
|
if annotation is None: return 8
|
|
k: int = annotation.kind()
|
|
# Union type A | B -> pointer size 8
|
|
if k == ast.ASTKind.BinOp: return 8
|
|
# Name(id) or Attribute(attr) -> resolve the type name
|
|
type_name: str = None
|
|
if k == ast.ASTKind.Name:
|
|
nm: ast.Name* = ast.Name*(annotation)
|
|
if nm is not None: type_name = nm.id
|
|
elif k == ast.ASTKind.Attribute:
|
|
at: ast.Attribute* = ast.Attribute*(annotation)
|
|
if at is not None: type_name = at.attr
|
|
if type_name is None: return 8
|
|
# [stage 3] Unified sizing: library typedefs (BYTE/DWORD etc.) resolve through normal library
|
|
# analysis; the hardcoded size table below is only a fallback for t/c core primitives.
|
|
dyn_sz: int = self.Gen.TypeGenResolve.get_type_size_bytes(pool, type_name, None, None)
|
|
if dyn_sz > 0: return dyn_sz
|
|
# Builtin types
|
|
if string.strcmp(type_name, "int") == 0: return 4
|
|
if string.strcmp(type_name, "CInt") == 0: return 4
|
|
if string.strcmp(type_name, "CInt8T") == 0: return 1
|
|
if string.strcmp(type_name, "CInt16T") == 0: return 2
|
|
if string.strcmp(type_name, "CInt32T") == 0: return 4
|
|
if string.strcmp(type_name, "CInt64T") == 0: return 8
|
|
if string.strcmp(type_name, "CChar") == 0: return 1
|
|
if string.strcmp(type_name, "CDouble") == 0: return 8
|
|
if string.strcmp(type_name, "CFloat") == 0: return 4
|
|
if string.strcmp(type_name, "CPtr") == 0: return 8
|
|
if string.strcmp(type_name, "CSizeT") == 0: return 8
|
|
if string.strcmp(type_name, "str") == 0: return 8
|
|
if string.strcmp(type_name, "bytes") == 0: return 8
|
|
if string.strcmp(type_name, "bool") == 0: return 1
|
|
# Registered struct type
|
|
struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(type_name)
|
|
if struct_ty is not None:
|
|
sz: int = self._get_type_size(struct_ty)
|
|
if sz > 0: return sz
|
|
# Unregistered type -> default pointer size 8 (may be self- or forward-referenced)
|
|
return 8
|
|
def _translate_renum_def(self, trans: HT.Translator*, cd: ast.ClassDef*) -> int:
|
|
"""Translate an REnum class definition, return 0"""
|
|
if cd is None or cd.name is None or trans is None: return 0
|
|
|
|
pool: memhub.MemBuddy* = trans.Pool
|
|
class_name: str = cd.name
|
|
|
|
# Namespace isolation: mark visible in the current file
|
|
self.Gen.StructGen.add_visible_struct(pool, class_name)
|
|
|
|
# Check whether already registered (SHA1 distinguishes cross-module same-name classes)
|
|
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
|
if existing is not None:
|
|
# existing only guarantees the struct entry, not the REnum semantics (variant table): the entry
|
|
# lives in StructTable across phases, but the variant table is a per-Generator field reset each time.
|
|
if self._is_renum_class_name(class_name) != 0:
|
|
if existing.Ty is not None: mlirlite.module_add_named_type(trans.Module, pool, existing.Ty)
|
|
return 0
|
|
# Variant table missing: neutralize the old entry so find_struct_by_module no longer matches,
|
|
# then fall through to full registration to fix both the layout and the variant table.
|
|
if existing.Name is not None: existing.Name[0] = '\0'
|
|
existing.Ty = None
|
|
existing.FieldCount = 0
|
|
|
|
children: list[ast.AST*]* = cd.children
|
|
if children is None:
|
|
# [D32 fail-fast] Previously only warned and returned: the variant table was silently unregistered,
|
|
# so _is_renum_class_name stayed 0 and variant construction lost its tag. No silent skip here.
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"REnum class '{class_name}' has no class body (children is None), cannot register the variant table", "TypeError")
|
|
return 0
|
|
|
|
cn: sizet = len(children)
|
|
|
|
# At most 32 variants per class, 16 fields per variant; variant_field_sizes[i][j] = variant i's field j size.
|
|
variant_field_sizes: sizet* = pool.alloc(8 * 64 * 16)
|
|
if variant_field_sizes is None: return 0
|
|
string.memset(variant_field_sizes, 0, 8 * 64 * 16)
|
|
|
|
variant_count: int = 0
|
|
max_field_count: int = 0 # max field count across all variants
|
|
|
|
for ci in range(cn):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk: int = stmt.kind()
|
|
|
|
if sk == ast.ASTKind.ClassDef:
|
|
# Nested variant class
|
|
variant_cd: ast.ClassDef* = ast.ClassDef*(stmt)
|
|
if variant_cd is None or variant_cd.children is None:
|
|
variant_count += 1
|
|
continue
|
|
variant_children: list[ast.AST*]* = variant_cd.children
|
|
vcn: sizet = len(variant_children)
|
|
field_idx: int = 0
|
|
for vi in range(vcn):
|
|
vstmt: ast.AST* = variant_children.get(vi)
|
|
if vstmt is None: continue
|
|
vk: int = vstmt.kind()
|
|
if vk != ast.ASTKind.AnnAssign: continue
|
|
vaa: ast.AnnAssign* = ast.AnnAssign*(vstmt)
|
|
if vaa is None or vaa.target is None or vaa.annotation is None: continue
|
|
if vaa.target.kind() != ast.ASTKind.Name: continue
|
|
# Extract the field name
|
|
vnm: ast.Name* = ast.Name*(vaa.target)
|
|
# Compute the field size
|
|
fsize: int = self._get_annotation_field_size(pool, vaa.annotation)
|
|
if variant_count < 32 and field_idx < 16:
|
|
# variant_field_sizes[variant_count][field_idx] = fsize
|
|
offset: sizet = variant_count * 16 + field_idx
|
|
variant_field_sizes[offset] = sizet(fsize)
|
|
# Register the variant field-name alias directly, resolving its actual type for match
|
|
# binding. Self-referencing annotations fail to resolve here and fall back to the slot type.
|
|
if vnm is not None and vnm.id is not None:
|
|
mem_ty_l: mlirlite.LLVMType* = None
|
|
if trans is not None: mem_ty_l = self.Gen.TypeGenResolve.resolve_annotation_type(pool, vaa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
|
self._register_renum_field_alias(pool, class_name, vnm.id, field_idx + 1, mem_ty_l, variant_cd.name)
|
|
field_idx += 1
|
|
if field_idx > max_field_count: max_field_count = field_idx
|
|
variant_count += 1
|
|
elif sk == ast.ASTKind.Assign:
|
|
# Assign-form payload-less variants (e.g. `Empty = 1`): same rule as TPC, still a variant (occupies
|
|
# a slot) with no fields. Tags are assigned by _register_renum_variants (explicit values first).
|
|
ag_v: ast.Assign* = ast.Assign*(stmt)
|
|
if ag_v is None or ag_v.targets is None: continue
|
|
tgts_v: list[ast.AST*]* = ag_v.targets
|
|
if len(tgts_v) == 0: continue
|
|
t0_v: ast.AST* = tgts_v.get(0)
|
|
if t0_v is None or t0_v.kind() != ast.ASTKind.Name: continue
|
|
tgt_v: ast.Name* = ast.Name*(t0_v)
|
|
if tgt_v.id is None: continue
|
|
variant_count += 1
|
|
|
|
if variant_count == 0:
|
|
# [D32 fail-fast] Same as above: variant_count 0 previously returned silently, skipping registration.
|
|
# For `class X(t.REnum)` the count must be > 0; 0 means the AST variant classes were lost.
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"REnum class '{class_name}' has 0 variants (nested variant classes lost), cannot register the variant table", "TypeError")
|
|
return 0
|
|
|
|
# Compute the max field size for each position.
|
|
# Position 0 = __tag (i32, 4 bytes); positions 1+ = variant fields.
|
|
total_positions: int = max_field_count + 1 # +1 for tag
|
|
if total_positions < 2:
|
|
total_positions = 2 # at least {i32, i32}
|
|
|
|
position_max_sizes: sizet* = pool.alloc(8 * total_positions)
|
|
if position_max_sizes is None: return 0
|
|
string.memset(position_max_sizes, 0, 8 * total_positions)
|
|
|
|
# Position 0 = tag = 4 bytes
|
|
position_max_sizes[0] = sizet(4)
|
|
|
|
# Positions 1+: take the max field size across all variants at each position
|
|
for pos in range(1, total_positions):
|
|
pos_max: sizet = 0
|
|
for vi in range(variant_count):
|
|
if vi >= 64:
|
|
break # variant_field_sizes 64-class slot cap; guards against out-of-bounds reads
|
|
field_pos: int = pos - 1 # variant fields start at position 0
|
|
if field_pos >= 16:
|
|
continue # beyond the 16-field-per-variant cap: not written (memset 0), skip
|
|
offset2: sizet = vi * 16 + field_pos
|
|
fsize_val: sizet = variant_field_sizes[offset2]
|
|
if fsize_val > pos_max: pos_max = fsize_val
|
|
position_max_sizes[pos] = sizet(pos_max)
|
|
|
|
# Build the ParamNode list
|
|
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
|
i64_ty: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
|
|
|
first_node: mlirlite.ParamNode* = None
|
|
tail_node: mlirlite.ParamNode* = None
|
|
|
|
for pos in range(total_positions):
|
|
psize: int = int(position_max_sizes[pos])
|
|
field_ty: mlirlite.LLVMType* = i32_ty
|
|
if psize > 4: field_ty = i64_ty
|
|
pnode: mlirlite.ParamNode* = mlirlite.new_param_node(pool, field_ty)
|
|
if pnode is None: continue
|
|
tail_node = mlirlite.param_list_append(first_node, tail_node, pnode)
|
|
if first_node is None: first_node = tail_node
|
|
|
|
if first_node is None:
|
|
VLogger.error(f"failed to build field list for {class_name}", "RENUM")
|
|
return 0
|
|
|
|
# Build the named struct type name: "sha1.ClassName" or "ClassName"
|
|
renum_type_name: str = class_name
|
|
if trans.ModuleSha1 is not None:
|
|
renum_name_buf: str = pool.alloc(64)
|
|
if renum_name_buf is not None:
|
|
viperlib.snprintf(renum_name_buf, 64, "%s.%s", trans.ModuleSha1, class_name)
|
|
renum_type_name = renum_name_buf
|
|
|
|
renum_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, first_node, total_positions, renum_type_name)
|
|
if renum_ty is None:
|
|
VLogger.error(f"failed to create type for {class_name}", "RENUM")
|
|
return 0
|
|
|
|
# Deep-copy the newly registered REnum struct_ty to stdlib (survives cross-file reset and keeps the
|
|
# same pointer as entry.Ty so find_struct_by_type still matches); required under StdlibAlloc=1 too.
|
|
if trans._declare_only == 0 or self.Gen.StructGen.get_stdlib_alloc() != 0:
|
|
renum_copy: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, renum_ty)
|
|
if renum_copy is not None: renum_ty = renum_copy
|
|
|
|
# Register the named struct into the module
|
|
mlirlite.module_add_named_type(trans.Module, pool, renum_ty)
|
|
|
|
# Register into HandlesStruct.
|
|
# A forward-declared placeholder for THIS class may have been created *during the variant walk*
|
|
# above: a variant field like `Pointee: LLVMType*` is `BinOp(LLVMType | t.CPtr)` (parser's
|
|
# _make_ptr_type expands the C-style suffix into a union, NOT into `rawptr[...]`), so resolving it
|
|
# resolves the class name and calls ensure_forward_class. register_struct de-duplicates by Ty
|
|
# pointer, so it would add a SECOND same-named entry and get_struct_type/find_struct (first match
|
|
# wins) would keep returning the opaque placeholder -> __sizeof__() == 0 at the first use
|
|
# ("registered but size 0, field chain unfilled"). Fill the placeholder in place instead, which is
|
|
# exactly what ensure_forward_class documents ("the definition side fills the same Ty in place").
|
|
entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
|
if entry is not None and entry.IsForwardDecl != 0:
|
|
entry.Ty = renum_ty
|
|
entry.IsForwardDecl = 0
|
|
else:
|
|
entry = self.Gen.StructGen.register_struct(pool, class_name, renum_ty, trans.ModuleSha1)
|
|
if entry is None:
|
|
VLogger.error(f"failed to register {class_name}", "RENUM")
|
|
return 0
|
|
|
|
# Add the __tag field (position 0, i32)
|
|
self.Gen.StructGen.add_field(pool, entry, "__tag", i32_ty, None, None)
|
|
|
|
# Add payload fields (positions 1+, i32 or i64)
|
|
for pos in range(1, total_positions):
|
|
psize2: int = int(position_max_sizes[pos])
|
|
payload_ty: mlirlite.LLVMType* = i32_ty
|
|
if psize2 > 4: payload_ty = i64_ty
|
|
field_name_buf: str = pool.alloc(16)
|
|
if field_name_buf is not None:
|
|
viperlib.snprintf(field_name_buf, 16, "_p%d", pos)
|
|
self.Gen.StructGen.add_field(pool, entry, field_name_buf, payload_ty, None, None)
|
|
|
|
# Register variant names into the global variant table (translate_call detects REnum construction)
|
|
self._register_renum_variants(pool, class_name, cd, variant_count)
|
|
|
|
# Variant field-name aliases were registered during the variant walk, which runs before register_struct;
|
|
# self-referencing annotations left a 0 type slot, so refill it now (see _refill_renum_field_types).
|
|
self._refill_renum_field_types(trans, pool, class_name, cd)
|
|
|
|
VLogger.info(f"registered {class_name} with {variant_count} variants, {total_positions} positions", "RENUM")
|
|
return 0
|
|
def _refill_renum_field_types(self, trans: HT.Translator*, pool: memhub.MemBuddy*, class_name: str, cd: ast.ClassDef*):
|
|
"""After REnum registration, re-resolve variant field types and backfill only aliases whose type slot is still 0."""
|
|
if trans is None or pool is None or class_name is None or cd is None: return
|
|
children: list[ast.AST*]* = cd.children
|
|
if children is None: return
|
|
cn: sizet = len(children)
|
|
for ci in range(cn):
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None or stmt.kind() != ast.ASTKind.ClassDef: continue
|
|
variant_cd: ast.ClassDef* = ast.ClassDef*(stmt)
|
|
if variant_cd is None or variant_cd.name is None or variant_cd.children is None: continue
|
|
vchildren: list[ast.AST*]* = variant_cd.children
|
|
vcn: sizet = len(vchildren)
|
|
field_idx: int = 0
|
|
for vi in range(vcn):
|
|
vstmt: ast.AST* = vchildren.get(vi)
|
|
if vstmt is None: continue
|
|
if vstmt.kind() != ast.ASTKind.AnnAssign: continue
|
|
vaa: ast.AnnAssign* = ast.AnnAssign*(vstmt)
|
|
if vaa is None or vaa.target is None or vaa.annotation is None:
|
|
# Same rule as the first pass: an invalid AnnAssign does not occupy a payload slot
|
|
continue
|
|
if vaa.target.kind() != ast.ASTKind.Name: continue
|
|
vnm: ast.Name* = ast.Name*(vaa.target)
|
|
if vnm.id is None:
|
|
# Same rule as the first pass: a valid AnnAssign still occupies a payload slot
|
|
field_idx += 1
|
|
continue
|
|
mem_ty_l: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, vaa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
|
# Resolved -> trigger the alias dedupe path (backfill when the type slot is 0; skip a non-zero slot)
|
|
if mem_ty_l is not None: self._register_renum_field_alias(pool, class_name, vnm.id, field_idx + 1, mem_ty_l, variant_cd.name)
|
|
field_idx += 1
|
|
def _register_renum_variants(self, pool: memhub.MemBuddy*, class_name: str, cd: ast.ClassDef*, variant_count: int):
|
|
"""Register an REnum class's variant names and tag values into the global table"""
|
|
if pool is None or class_name is None or cd is None: return
|
|
if variant_count == 0: return
|
|
children: list[ast.AST*]* = cd.children
|
|
if children is None: return
|
|
|
|
# Lazily initialize the tables on first registration. They must be stdlib-allocated: if pool-allocated
|
|
# they would be reclaimed by reset_to(mark1) and later matches would read freed memory and crash.
|
|
if self.RenumClassNames is None:
|
|
self.RenumClassNames = CompilerFactory.factory.PermPool.alloc(8 * RENUM_CLASS_MAX)
|
|
self.RenumClassVariantCounts = CompilerFactory.factory.PermPool.alloc(8 * RENUM_CLASS_MAX)
|
|
self.RenumVariantNames = CompilerFactory.factory.PermPool.alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS)
|
|
self.RenumVariantTags = CompilerFactory.factory.PermPool.alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS)
|
|
if (self.RenumClassNames is None or self.RenumClassVariantCounts is None or self.RenumVariantNames is None or self.RenumVariantTags is None):
|
|
# A None table means the perm-pool request failed; returning here left the table empty while the
|
|
# caller still reported "registered", so every REnum case in match dispatch was silently lost.
|
|
self.RenumClassNames = None
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"failed to allocate REnum variant table (class='{class_name}'): PermPool returned NULL, variant table unavailable", "InternalCompilerError")
|
|
return
|
|
string.memset(self.RenumClassNames, 0, 8 * RENUM_CLASS_MAX)
|
|
string.memset(self.RenumClassVariantCounts, 0, 8 * RENUM_CLASS_MAX)
|
|
string.memset(self.RenumVariantNames, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS)
|
|
string.memset(self.RenumVariantTags, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS)
|
|
self.RenumClassCount = 0
|
|
# [D30] Register the pointers into the compile-time factory singleton (same lifetime as struct
|
|
# entries); InitClassGen restores them for new Generators instead of zeroing them.
|
|
CompilerFactory.factory.RenumClassNames = self.RenumClassNames
|
|
CompilerFactory.factory.RenumClassVariantCounts = self.RenumClassVariantCounts
|
|
CompilerFactory.factory.RenumVariantNames = self.RenumVariantNames
|
|
CompilerFactory.factory.RenumVariantTags = self.RenumVariantTags
|
|
CompilerFactory.factory.RenumClassCount = self.RenumClassCount
|
|
|
|
#rcv_chk: uint64 = uint64(self.RenumClassVariantCounts)
|
|
# VLogger.error(f"[RENUM-FAIL] RCV={rcv_chk} abnormal (perm_pool address expected after lazy init)", "RENUM")
|
|
|
|
# Check whether already registered (avoid duplicate registration)
|
|
for i in range(self.RenumClassCount):
|
|
existing: str = str(self.RenumClassNames[i])
|
|
if existing is not None and string.strcmp(existing, class_name) == 0: return
|
|
|
|
# Table full
|
|
if self.RenumClassCount >= RENUM_CLASS_MAX:
|
|
# Silently dropping the class made its later `match` lose every case; a
|
|
# real limit breach must be reported instead.
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"REnum class count exceeds the limit {RENUM_CLASS_MAX}: cannot register class='{class_name}'", "InternalCompilerError")
|
|
return
|
|
|
|
# Register the class name (strcpy into perm_pool: after each file's 1a reset the AST raw pointer
|
|
# dangles, and strcmp in _lookup_renum_variant/_is_renum_class_name would read poisoned memory).
|
|
idx: int = self.RenumClassCount
|
|
cn_len_rv: sizet = string.strlen(class_name)
|
|
cn_buf_rv: str = CompilerFactory.factory.PermPool.alloc(cn_len_rv + 1)
|
|
if cn_buf_rv is None:
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"failed to allocate REnum class name copy (class='{class_name}'): PermPool returned NULL", "InternalCompilerError")
|
|
return
|
|
string.strcpy(cn_buf_rv, class_name)
|
|
self.RenumClassNames[idx] = sizet(cn_buf_rv)
|
|
self.RenumClassVariantCounts[idx] = variant_count
|
|
|
|
# Walk children and register variant names + tags. Two forms: `class Some: ...` (payload variant,
|
|
# tags numbered from max(explicit)+1) and `Empty = 1` (payload-less, tag = explicit integer value).
|
|
cn: sizet = len(children)
|
|
# First pass: find the upper bound of Assign variants' explicit integer values (stays -1 if none)
|
|
max_explicit: int = -1
|
|
for ci in range(cn):
|
|
stmt_p: ast.AST* = children.get(ci)
|
|
if stmt_p is None or stmt_p.kind() != ast.ASTKind.Assign: continue
|
|
ag_p: ast.Assign* = ast.Assign*(stmt_p)
|
|
if ag_p is None or ag_p.targets is None or ag_p.value is None: continue
|
|
tgts_p: list[ast.AST*]* = ag_p.targets
|
|
if len(tgts_p) == 0: continue
|
|
t0_p: ast.AST* = tgts_p.get(0)
|
|
if t0_p is None or t0_p.kind() != ast.ASTKind.Name: continue
|
|
if ag_p.value.kind() != ast.ASTKind.Constant: continue
|
|
ev_v: int = int(self._extract_int_value(ag_p.value))
|
|
if ev_v > max_explicit: max_explicit = ev_v
|
|
|
|
# Second pass: write the table in declaration order. slot is the contiguous table index (0..count-1),
|
|
# while the cell stores the tag; they are no longer equal, so a single counter cannot serve both.
|
|
slot: int = 0
|
|
next_class_tag: int = max_explicit + 1
|
|
variant_index: int = 0
|
|
for ci in range(cn):
|
|
if slot >= variant_count: break
|
|
if slot >= RENUM_VARIANT_PER_CLASS:
|
|
# Fixed 32 slots per class: exceeding it would overflow into the next class's slots; report fail-fast
|
|
VLogger.error(f"REnum variant count exceeds the limit {RENUM_VARIANT_PER_CLASS}: {class_name}", "RENUM")
|
|
break
|
|
stmt: ast.AST* = children.get(ci)
|
|
if stmt is None: continue
|
|
sk_rv: int = stmt.kind()
|
|
vname_ptr: sizet = 0
|
|
tag_val: int = 0
|
|
if sk_rv == ast.ASTKind.ClassDef:
|
|
vcd: ast.ClassDef* = ast.ClassDef*(stmt)
|
|
if vcd is None or vcd.name is None: continue
|
|
vname_ptr = sizet(vcd.name)
|
|
tag_val = next_class_tag
|
|
next_class_tag += 1
|
|
elif sk_rv == ast.ASTKind.Assign:
|
|
# Assign-form payload-less variant (same filter as _translate_renum_def's first pass)
|
|
ag_rv: ast.Assign* = ast.Assign*(stmt)
|
|
if ag_rv is None or ag_rv.targets is None: continue
|
|
tgts_rv: list[ast.AST*]* = ag_rv.targets
|
|
if len(tgts_rv) == 0: continue
|
|
t0_rv: ast.AST* = tgts_rv.get(0)
|
|
if t0_rv is None or t0_rv.kind() != ast.ASTKind.Name: continue
|
|
tgt_rv: ast.Name* = ast.Name*(t0_rv)
|
|
if tgt_rv.id is None: continue
|
|
vname_ptr = sizet(tgt_rv.id)
|
|
if ag_rv.value is not None and ag_rv.value.kind() == ast.ASTKind.Constant: tag_val = int(self._extract_int_value(ag_rv.value))
|
|
else:
|
|
# No explicit value: use a separate counter (distinct from the class-variant counter, as
|
|
# TPC's variant_index) so it cannot collide with next_class_tag.
|
|
tag_val = variant_index
|
|
variant_index += 1
|
|
else: continue
|
|
name_offset: sizet = idx * RENUM_VARIANT_PER_CLASS + slot
|
|
# The variant name is also strcpy'd into perm_pool (the AST name dangles after each file's 1a reset)
|
|
vname_src: str = str(vname_ptr)
|
|
vn_len_rv: sizet = string.strlen(vname_src)
|
|
vn_buf_rv: str = CompilerFactory.factory.PermPool.alloc(vn_len_rv + 1)
|
|
if vn_buf_rv is None:
|
|
# Breaking here registered a partial variant table: the class would
|
|
# keep some variants and silently lose every later one.
|
|
self.Gen.TypeGenErr.fatal_error(cd, f"failed to allocate REnum variant name copy (class='{class_name}'): PermPool returned NULL", "InternalCompilerError")
|
|
break
|
|
string.strcpy(vn_buf_rv, vname_src)
|
|
self.RenumVariantNames[name_offset] = sizet(vn_buf_rv)
|
|
self.RenumVariantTags[name_offset] = tag_val
|
|
slot += 1
|
|
|
|
self.RenumClassCount = idx + 1
|
|
# [D30] Write the count back to the factory singleton (new Generators restore from it; otherwise they
|
|
# would see a stale count and miss subsequently registered variants).
|
|
CompilerFactory.factory.RenumClassCount = self.RenumClassCount
|
|
def _lookup_renum_variant(self, variant_name: str) -> str:
|
|
"""Find the REnum class name owning a variant name; None if absent"""
|
|
if variant_name is None or self.RenumClassNames is None: return None
|
|
|
|
for i in range(self.RenumClassCount):
|
|
vcount: int = int(self.RenumClassVariantCounts[i])
|
|
for j in range(vcount):
|
|
name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j
|
|
vname: str = str(self.RenumVariantNames[name_offset])
|
|
if vname is not None and string.strcmp(vname, variant_name) == 0:
|
|
cls_nm: str = str(self.RenumClassNames[i])
|
|
return cls_nm
|
|
return None
|
|
def RenumTableDump(self) -> str:
|
|
"""Diagnostic variant-table summary to tell 'class never registered' from 'class present but variant missing'."""
|
|
buf: str = CompilerFactory.factory.PermPool.alloc(256)
|
|
if buf is None: return "-"
|
|
buf[0] = '\0'
|
|
cls_first: str = "-"
|
|
v_first: str = "-"
|
|
n_first_v: int = 0
|
|
if self.RenumClassNames is not None and self.RenumClassCount > 0:
|
|
cls_first = str(self.RenumClassNames[0])
|
|
if self.RenumVariantNames is not None and self.RenumClassVariantCounts is not None:
|
|
n_first_v = int(self.RenumClassVariantCounts[0])
|
|
if n_first_v > 0: v_first = str(self.RenumVariantNames[0])
|
|
viperlib.snprintf(buf, 256, "count=%d first=%s vcount=%d vfirst=%s", self.RenumClassCount, cls_first, n_first_v, v_first)
|
|
return buf
|
|
|
|
def _is_renum_by_entry(self, class_name: str) -> int:
|
|
"""Variant-table-free REnum test: a struct definition carrying a `__tag` field is an REnum, so match dispatch keeps variant bindings even if the table is missing."""
|
|
if class_name is None: return 0
|
|
st: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(class_name)
|
|
if st is None: return 0
|
|
if self.Gen.StructGen.lookup_field(st, "__tag") is None: return 0
|
|
return 1
|
|
|
|
def _is_renum_class_name(self, class_name: str) -> int:
|
|
"""Check whether a name is a registered REnum class, 1=yes / 0=no"""
|
|
if class_name is None or self.RenumClassNames is None: return 0
|
|
for i in range(self.RenumClassCount):
|
|
existing: str = str(self.RenumClassNames[i])
|
|
if existing is not None and string.strcmp(existing, class_name) == 0: return 1
|
|
return 0
|
|
def _lookup_renum_variant_in_class(self, class_name: str, variant_name: str) -> int:
|
|
"""Find a variant in a given REnum class, return its tag value or -1"""
|
|
if class_name is None or variant_name is None or self.RenumClassNames is None: return -1
|
|
for i in range(self.RenumClassCount):
|
|
existing: str = str(self.RenumClassNames[i])
|
|
if existing is None or string.strcmp(existing, class_name) != 0: continue
|
|
vcount: int = int(self.RenumClassVariantCounts[i])
|
|
for j in range(vcount):
|
|
name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j
|
|
vname: str = str(self.RenumVariantNames[name_offset])
|
|
if vname is not None and string.strcmp(vname, variant_name) == 0: return self.RenumVariantTags[name_offset]
|
|
return -1
|
|
return -1
|
|
def _register_renum_field_alias(self, pool: memhub.MemBuddy*, class_name: str, field_name: str, payload_idx: int, field_ty: mlirlite.LLVMType* = None, variant_name: str = None):
|
|
"""Register an REnum variant field name alias in the global table; optional field type and variant name aid match binding."""
|
|
if pool is None or class_name is None or field_name is None: return
|
|
if payload_idx < 1: return
|
|
|
|
# Lazily initialize the tables (perm_pool: the tables themselves survive each file's 1a reset)
|
|
if self.RenumAliasClass is None:
|
|
alias_pool_rfa: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
|
self.RenumAliasClass = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX)
|
|
self.RenumAliasField = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX)
|
|
self.RenumAliasIndex = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX)
|
|
self.RenumAliasTy = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX)
|
|
self.RenumAliasVariant = alias_pool_rfa.alloc(8 * RENUM_ALIAS_MAX)
|
|
if (self.RenumAliasClass is None or self.RenumAliasField is None or self.RenumAliasIndex is None or self.RenumAliasTy is None or self.RenumAliasVariant is None):
|
|
# Same as the variant table: a silent bail left the alias table empty,
|
|
# so match bindings later failed with an unrelated NameError.
|
|
self.RenumAliasClass = None
|
|
self.Gen.TypeGenErr.fatal_error(None, f"failed to allocate REnum field alias table (class='{class_name}'): PermPool returned NULL", "InternalCompilerError")
|
|
return
|
|
string.memset(self.RenumAliasClass, 0, 8 * RENUM_ALIAS_MAX)
|
|
string.memset(self.RenumAliasField, 0, 8 * RENUM_ALIAS_MAX)
|
|
string.memset(self.RenumAliasIndex, 0, 8 * RENUM_ALIAS_MAX)
|
|
string.memset(self.RenumAliasTy, 0, 8 * RENUM_ALIAS_MAX)
|
|
string.memset(self.RenumAliasVariant, 0, 8 * RENUM_ALIAS_MAX)
|
|
self.RenumAliasCount = 0
|
|
# [D30] Same as above: register the alias table pointers into the factory singleton to survive Generators
|
|
CompilerFactory.factory.RenumAliasClass = self.RenumAliasClass
|
|
CompilerFactory.factory.RenumAliasField = self.RenumAliasField
|
|
CompilerFactory.factory.RenumAliasIndex = self.RenumAliasIndex
|
|
CompilerFactory.factory.RenumAliasTy = self.RenumAliasTy
|
|
CompilerFactory.factory.RenumAliasVariant = self.RenumAliasVariant
|
|
CompilerFactory.factory.RenumAliasCount = self.RenumAliasCount
|
|
|
|
if self.RenumAliasCount >= RENUM_ALIAS_MAX:
|
|
# Dropping the record silently made the variant field bind to a wrong-typed
|
|
# slot (or fail with NameError) instead of reporting the limit breach.
|
|
self.Gen.TypeGenErr.fatal_error(None, f"REnum field alias count exceeds the limit {RENUM_ALIAS_MAX}: cannot register {class_name}.{field_name}", "InternalCompilerError")
|
|
return
|
|
|
|
# Dedupe check (pointer-free): the class slot holds a perm_pool string copy compared with strcmp,
|
|
# so it no longer relies on pointer equality (registration uses AST pointers, lookup uses pool copies).
|
|
for i in range(self.RenumAliasCount):
|
|
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
|
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
|
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
|
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0:
|
|
# Existing record: if the first pass left the type slot 0 (self-referencing field, see
|
|
# _refill_renum_field_types), backfill the real type now; a non-zero slot is left as is.
|
|
if self.RenumAliasTy[i] == 0 and field_ty is not None:
|
|
ty_copy_rf2: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, field_ty)
|
|
if ty_copy_rf2 is not None: self.RenumAliasTy[i] = sizet(ty_copy_rf2)
|
|
return
|
|
|
|
idx: int = self.RenumAliasCount
|
|
# class_name is also strcpy'd into perm_pool (pointer-free: any class_name string from the lookup
|
|
# side matches via strcmp, so no _update_renum_alias_class_ptr is needed).
|
|
cls_len_rfa: sizet = string.strlen(class_name)
|
|
cls_buf_rfa: str = CompilerFactory.factory.PermPool.alloc(cls_len_rfa + 1)
|
|
if cls_buf_rfa is None: return
|
|
string.strcpy(cls_buf_rfa, class_name)
|
|
self.RenumAliasClass[idx] = sizet(cls_buf_rfa)
|
|
# field_name stored as a raw AST pointer dangles after each file's 1a reset; since
|
|
# _lookup_renum_field_alias compares with strcmp, copying into perm_pool does not affect lookup.
|
|
fn_len_rfa: sizet = string.strlen(field_name)
|
|
fn_buf_rfa: str = CompilerFactory.factory.PermPool.alloc(fn_len_rfa + 1)
|
|
if fn_buf_rfa is None: return
|
|
string.strcpy(fn_buf_rfa, field_name)
|
|
self.RenumAliasField[idx] = sizet(fn_buf_rfa)
|
|
self.RenumAliasIndex[idx] = sizet(payload_idx)
|
|
# Deep-copy the member type into perm_pool (pool types dangle after each file's 1a reset; on an
|
|
# unresolved type such as a self reference, store 0 and fall back to the slot type when binding).
|
|
self.RenumAliasTy[idx] = 0
|
|
if field_ty is not None:
|
|
ty_copy_rfa: mlirlite.LLVMType* = mlirlite.DeepCopyType(CompilerFactory.factory.PermPool, field_ty)
|
|
if ty_copy_rfa is not None: self.RenumAliasTy[idx] = sizet(ty_copy_rfa)
|
|
# The variant name is also strcpy'd into perm_pool (variant_cd.name dangles after the 1a reset)
|
|
self.RenumAliasVariant[idx] = 0
|
|
if variant_name is not None:
|
|
vn_len_rfa: sizet = string.strlen(variant_name)
|
|
vn_buf_rfa: str = CompilerFactory.factory.PermPool.alloc(vn_len_rfa + 1)
|
|
if vn_buf_rfa is not None:
|
|
string.strcpy(vn_buf_rfa, variant_name)
|
|
self.RenumAliasVariant[idx] = sizet(vn_buf_rfa)
|
|
self.RenumAliasCount = idx + 1
|
|
# [D30] Write the count back to the factory singleton (new Generators restore from it)
|
|
CompilerFactory.factory.RenumAliasCount = self.RenumAliasCount
|
|
def _lookup_renum_field_alias(self, class_name: str, field_name: str) -> int:
|
|
"""Look up an REnum variant field name's payload index, return it or -1 (class/field compared by content)."""
|
|
if class_name is None or field_name is None or self.RenumAliasClass is None: return -1
|
|
for i in range(self.RenumAliasCount):
|
|
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
|
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
|
# field_name is compared by strcmp content (pointers differ, content is the same)
|
|
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
|
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: return self.RenumAliasIndex[i]
|
|
return -1
|
|
def _lookup_renum_field_alias_ty(self, class_name: str, field_name: str) -> mlirlite.LLVMType*:
|
|
"""Look up an REnum variant member's actual LLVM type, or None when the type slot is 0 (binding falls back to the slot type)."""
|
|
if class_name is None or field_name is None or self.RenumAliasTy is None: return None
|
|
for i in range(self.RenumAliasCount):
|
|
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
|
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
|
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
|
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0:
|
|
if self.RenumAliasTy[i] == 0: return None
|
|
return (mlirlite.LLVMType*)(self.RenumAliasTy[i])
|
|
return None
|
|
def _lookup_renum_member_ty(self, class_name: str, variant_name: str, payload_idx: int) -> mlirlite.LLVMType*:
|
|
"""Look up a variant member's actual LLVM type by (class, variant name, payload index) for match binding; None if unresolved."""
|
|
if class_name is None or variant_name is None or self.RenumAliasVariant is None: return None
|
|
if payload_idx < 1: return None
|
|
for i in range(self.RenumAliasCount):
|
|
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
|
if cls_existing_ptr is None or string.strcmp(cls_existing_ptr, class_name) != 0: continue
|
|
if self.RenumAliasVariant[i] == 0: continue
|
|
vnt_ptr: str = rawptr(self.RenumAliasVariant[i])
|
|
if vnt_ptr is None or string.strcmp(vnt_ptr, variant_name) != 0: continue
|
|
if self.RenumAliasIndex[i] != payload_idx: continue
|
|
if self.RenumAliasTy[i] == 0: return None
|
|
return (mlirlite.LLVMType*)(self.RenumAliasTy[i])
|
|
return None |