fix(codegen): keep module-level DefineT initializers

This commit is contained in:
TermiNexus committed 2026-10-28 21:45:38 +08:00
1 parent fde6571a3b
commit 79d53e691c
1 file changed
+45 -5
+45 -5
View File
@@ -478,6 +478,30 @@ novirtual class TranslatorGenMixin:
return 0
# _module_init_from_cdefine - resolve a module-level initializer that is a bare DefineT name.
# Without this the initializer is silently dropped and the global becomes zero/BSS
# (e.g. `_text_mode: int = TEXT_MODE_MANAGED` ran as 0).
def _module_init_from_cdefine(self, name: str, kind_out: int*, val_out: int64*, fval_out: double*, sval_out: str*) -> int:
"""Fill the initializer kind/value from a DefineT name; 1 when resolved, 0 otherwise."""
if name is None: return 0
cv: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(name)
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return 0
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(name)
if sv is None: return 0
if sval_out is not None: sval_out[0] = sv
if kind_out is not None: kind_out[0] = 3
return 1
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
dv: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name)
if fval_out is not None: fval_out[0] = dv
# Kind 6: float value already known, so emission must not re-read a source literal.
if kind_out is not None: kind_out[0] = 6
return 1
if val_out is not None: val_out[0] = cv
if kind_out is not None: kind_out[0] = 1
return 1
def handle_module_level_var(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Handle a module-level variable declaration by creating its global."""
pool: memhub.MemBuddy* = trans.Pool
@@ -627,6 +651,12 @@ novirtual class TranslatorGenMixin:
elif val_kind == ast.ASTKind.List:
init_list_node = ast.List*(aa.value)
init_kind = 2
elif val_kind == ast.ASTKind.Name:
# DefineT reference (`g: int = SOME_DEFINE`): resolve at compile time, else the
# global is created without an initializer and silently becomes 0/BSS.
nv_g: ast.Name* = ast.Name*(aa.value)
if nv_g is not None and nv_g.id is not None:
self._module_init_from_cdefine(nv_g.id, &init_kind, &init_val, &init_fval, &init_str_val)
elif k == ast.ASTKind.Assign:
asgn: ast.Assign* = ast.Assign*(node)
if asgn is None or asgn.targets is None: return 0
@@ -675,6 +705,14 @@ novirtual class TranslatorGenMixin:
elif val_kind2 == ast.ASTKind.List:
init_list_node = ast.List*(asgn.value)
init_kind = 2
elif val_kind2 == ast.ASTKind.Name:
# DefineT reference without an annotation; the define's kind decides the
# inferred type (int -> i32, float -> double, string -> str pointer).
nv_g2: ast.Name* = ast.Name*(asgn.value)
if nv_g2 is not None and nv_g2.id is not None:
self._module_init_from_cdefine(nv_g2.id, &init_kind, &init_val, &init_fval, &init_str_val)
if init_kind == 6: var_ty = mlirlite.Double(pool)
elif init_kind == 3: var_ty = mlirlite.Ptr(pool, mlirlite.Int8(pool))
elif val_kind2 == ast.ASTKind.Attribute:
# Module-level re-export alias (e.g. `_read32 = types._read32`): the RHS is an
# entity reference with no value semantics, so emit no LLVM global (same as __all__).
@@ -688,7 +726,8 @@ novirtual class TranslatorGenMixin:
# Float literal init (init_kind==4): re-read the value from source by name instead of the
# AST float_val (which pool ops may overwrite); also validate that the annotation is float.
if init_kind == 4:
# init_kind==6 is a float already resolved from a DefineT, so no source re-read applies.
if init_kind == 4 or init_kind == 6:
is_fty_g: int = 0
match var_ty:
case mlirlite.LLVMType.Float(_):
@@ -698,9 +737,10 @@ novirtual class TranslatorGenMixin:
if is_fty_g == 0:
self.Gen.TypeGenErr.fatal_error(node, f"module-level variable '{var_name}' is initialized with a float literal, but its annotation is not a float type", "TypeError")
return 0
if self.Gen.AssignGen._recover_float_literal(var_name, &init_fval, 0) != 1:
self.Gen.TypeGenErr.fatal_error(node, f"failed to recover the initializer value of module-level float variable '{var_name}' from the source file", "InternalCompilerError")
return 0
if init_kind == 4:
if self.Gen.AssignGen._recover_float_literal(var_name, &init_fval, 0) != 1:
self.Gen.TypeGenErr.fatal_error(node, f"failed to recover the initializer value of module-level float variable '{var_name}' from the source file", "InternalCompilerError")
return 0
# Check whether it is already registered
existing: mlirlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, var_name)
@@ -799,7 +839,7 @@ novirtual class TranslatorGenMixin:
str_init: str = self._build_string_ptr_initializer(pool, mod, var_name, init_str_val)
if str_init is not None: gv.Initializer = str_init
else: self.Gen.TypeGenErr.fatal_error(node, f"failed to build the initializer of module-level variable '{var_name}'", "TypeError")
elif init_kind == 4:
elif init_kind == 4 or init_kind == 6:
# Float initializer: LLVM float constants must be written as bit-pattern text
# (0x%016llx / 0x%08x); decimal text has ulp errors, and omitting this zeroes the global.
fc_g: mlirlite.Value* = mlirlite.ConstFloat(pool, var_ty, init_fval)