feat(global): 支持模块级符号地址初始化器

新增 init_kind=5(符号地址):分类阶段识别 UnaryOp(AddrOf, Name),不再把它
误送进整数常量提取器;发射阶段取模块作用域里该全局的引用文本(@mangled /
@mangled 带引号两种形式)作为初始化器,MLIR 层渲染为 llvm.mlir.addressof。
类型/操作数/引用目标不合法时按 fail-fast 报错。

复现:_v: int = 1 / _p: rawptr = &_v
      修复前报 CDefine value extraction failed(该分支原假设所有 UnaryOp 都是负数/取反字面量)
This commit is contained in:
TermiNexus committed 2026-10-07 14:54:44 +08:00
1 parent 5993affd9c
commit 12b15958c6
1 file changed
+52 -7
+52 -7
View File
@@ -495,7 +495,8 @@ novirtual class TranslatorGenMixin:
resolved_stg_entry: GenTypes.StructEntry* = None # Object-class global: matching StructEntry (for vtable init)
init_val: int64 = 0
init_fval: double = 0.0 # Value when init_kind==4 (float literal)
init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str, 4=float
init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str, 4=float, 5=symbol address
init_sym_node: ast.AST* = None # Operand of `= &global` (init_kind==5)
init_str_val: str = None
init_list_node: ast.List* = None
# Explicit `= None` init keeps lazy-initialization pointer semantics: the global must stay
@@ -571,9 +572,16 @@ novirtual class TranslatorGenMixin:
init_kind = 4
elif val_kind == ast.ASTKind.UnaryOp:
# Negative/bitwise-invert literal: the AST is UnaryOp(USub, Constant); unhandled
# it would compile to zeroinitializer and lose the initial value.
init_val = self.Gen.AssignGen.extract_cdefine_int_value(aa.value)
init_kind = 1
# it would compile to zeroinitializer and lose the initial value. `&global`
# (UnaryOp(AddrOf, Name)) is a symbol-address initializer instead and must not be
# fed to the integer constant extractor.
uop_g: ast.UnaryOp* = ast.UnaryOp*(aa.value)
if uop_g is not None and uop_g.op == ast.OpKind.AddrOf:
init_sym_node = uop_g.operand
init_kind = 5
else:
init_val = self.Gen.AssignGen.extract_cdefine_int_value(aa.value)
init_kind = 1
elif val_kind == ast.ASTKind.List:
init_list_node = ast.List*(aa.value)
init_kind = 2
@@ -603,9 +611,15 @@ novirtual class TranslatorGenMixin:
init_kind = 4
var_ty = mlirlite.Double(pool)
elif val_kind2 == ast.ASTKind.UnaryOp:
# Negative literal (same as the AnnAssign branch above): UnaryOp(USub, Constant)
init_val = self.Gen.AssignGen.extract_cdefine_int_value(asgn.value)
init_kind = 1
# Negative literal (same as the AnnAssign branch above): UnaryOp(USub, Constant).
# `= &global` is a symbol-address initializer instead (see the AnnAssign branch).
uop_g2: ast.UnaryOp* = ast.UnaryOp*(asgn.value)
if uop_g2 is not None and uop_g2.op == ast.OpKind.AddrOf:
init_sym_node = uop_g2.operand
init_kind = 5
else:
init_val = self.Gen.AssignGen.extract_cdefine_int_value(asgn.value)
init_kind = 1
elif val_kind2 == ast.ASTKind.List:
init_list_node = ast.List*(asgn.value)
init_kind = 2
@@ -729,6 +743,37 @@ novirtual class TranslatorGenMixin:
fc_g: mlirlite.Value* = mlirlite.ConstFloat(pool, var_ty, init_fval)
if fc_g is not None and fc_g.Name is not None: gv.Initializer = fc_g.Name
else: self.Gen.TypeGenErr.fatal_error(node, f"failed to build the float initializer of module-level variable '{var_name}'", "TypeError")
elif init_kind == 5:
# Symbol-address initializer (`= &global`): the referenced global's mangled symbol is
# emitted as `@name` (LLVM-quoted when required); the MLIR layer renders it as
# llvm.mlir.addressof. Only a pointer-typed global can hold an address.
is_ptr_g: int = 0
match var_ty:
case mlirlite.LLVMType.Ptr(_):
is_ptr_g = 1
case _:
pass
if is_ptr_g == 0:
self.Gen.TypeGenErr.fatal_error(node, f"module-level variable '{var_name}' is initialized with an address-of expression, but its type is not a pointer (add a pointer annotation or drop the '&')", "TypeError")
return 0
if init_sym_node is None or init_sym_node.kind() != ast.ASTKind.Name:
self.Gen.TypeGenErr.fatal_error(node, f"module-level variable '{var_name}' is initialized with an address-of expression whose operand is not a variable name", "TypeError")
return 0
sym_nm: ast.Name* = ast.Name*(init_sym_node)
sym_val: mlirlite.Value* = None
if sym_nm.id is not None: sym_val = self.Gen.VarGen.lookup_module_var(trans.SymTab, sym_nm.id)
if sym_val is None or sym_val.Name is None:
self.Gen.TypeGenErr.fatal_error(node, f"module-level variable '{var_name}' is initialized with &{sym_nm.id}, but '{sym_nm.id}' is not a module-level variable declared before this line", "NameError")
return 0
sym_buf: str = pool.alloc(256)
if sym_buf is None: return 0
# The module-scope entry already holds the LLVM reference text of that global
# (`@mangled` or `@"mangled"`), so it is used verbatim; only a bare name (no '@') needs
# the reference prefix added here.
if sym_val.Name[0] == '@': viperlib.snprintf(sym_buf, 256, "%s", sym_val.Name)
elif self.Gen.ClassGenVTable._vtable_name_needs_quote(sym_val.Name) != 0: viperlib.snprintf(sym_buf, 256, "@\"%s\"", sym_val.Name)
else: viperlib.snprintf(sym_buf, 256, "@%s", sym_val.Name)
gv.Initializer = sym_buf
else:
# Use zeroinitializer (valid for pointer/integer/aggregate types). Object-class globals
# with storage get &@storage below; `= None` lazy globals still need it to stay complete.