Files
ViperC/App/lib/core/Gen/TranslatorGenMixin.vp
T
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00

1391 lines
79 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
# ============================================================
# TranslatorGenMixin - 翻译驱动 Mixin
#
# 职责:模块级翻译入口(translate_children/create_wrapper_main/
# handle_module_level_var 等)与 global/nonlocal 名称管理辅助。
# 容量常量 MAX_VARS/MAX_FUNCS/MAX_GLOBAL_NAMES 不在此定义(本文件无使用点),
# 统一经 HT.<NAME> 引用 Translator.vp 的唯一真源(与 FuncGenMixin 同一引用路径)。
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
# ============================================================
# ============================================================
# TranslatorGenMixin - 翻译驱动(模块级翻译入口 + 作用域名称管理)
# ============================================================
novirtual class TranslatorGenMixin:
"""翻译驱动 Mixin"""
# ============================================================
# _register_cexport_from_funcdef - Phase 1a 预注册 CExport/State 函数
#
# 解决翻译顺序依赖问题:Phase 1b 按字母序翻译,后翻译的模块的
# CExport 函数无法被先翻译的模块正确识别为裸名调用。
# Phase 1a 预注册所有 CExport/State 函数名到全局表。
# ============================================================
Gen: Generator.Generator*
def _register_cexport_from_funcdef(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""预注册 CExport/State 函数到全局表(仅注册,不生成 IR)"""
fd: ast.FunctionDef* = ast.FunctionDef*(node)
if fd is None or fd.name is None: return 0
if trans is None or trans.ModuleSha1 is None: return 0
# 检查返回类型是否有 CExport 或 State 标记
has_export: int = 0
has_state: int = 0
if fd.returns is not None:
has_export = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExport", 0)
has_state = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0)
if has_export != 0 or has_state != 0:
self.Gen.CallGenBase.register_cexport_func(trans.ModuleSha1, fd.name)
return 0
# ============================================================
# translate_children - 遍历 AST 子节点并分派翻译
#
# 对应 TransPyC translator._translate_children()
# 共享状态从 trans 获取,imported_modules/from_imports 更新到 trans
# ============================================================
def translate_children(self, trans: HT.Translator*, tree: ast.AST*) -> int:
"""遍历 tree.children 并翻译每个子节点
Args:
trans: 翻译器(含所有共享状态)
tree: AST 模块节点
Returns:
int: 新增的变量数
"""
ch: list[ast.AST*]* = tree.children
if ch is None: return 0
cn_count: sizet = len(ch)
added_total: int = 0
for ci in range(cn_count):
child: ast.AST* = ch.get(ci)
if child is None: continue
kd: int = child.kind()
# _declare_only == 0 时,_translate_module_level 已预处理导入语句,
# 此处跳过避免重复处理(from_imports 条目重复)
if trans._declare_only == 0 and (kd == ast.ASTKind.Import or kd == ast.ASTKind.ImportFrom): continue
if kd == ast.ASTKind.Import:
self.Gen.ImportsGen.translate_import(trans, child)
elif kd == ast.ASTKind.ImportFrom:
self.Gen.ImportsGen.translate_import_from_module(trans, child)
self.Gen.ImportsGen.translate_import_from_names(trans, child)
elif kd == ast.ASTKind.FunctionDef:
# Phase 1a 声明模式:只注册 CExport/State 函数到全局表(解决翻译顺序依赖)
# Phase 1b 全量翻译:正常翻译函数体
if trans._declare_only == 0:
added: int = self.Gen.FuncGenDef.translate_function_def(trans, child)
added_total += added
elif trans._declare_only == 1:
self._register_cexport_from_funcdef(trans, child)
# 预解析函数签名类型,触发泛型类特化(如 numpy 的 _empty_like
# 签名 ndarray[t.CDouble] → 特化结构体注册)。1a 阶段在 mark1 之前,
# 特化结构体的 Ty/Fields 分配在 mark1 前,不被 1b 的 mark/reset 回收,
# 避免 Phase2 翻译 App 文件时读悬空结构体(UAF)。
# 仅触发特化,不验证结果(1b 全量翻译时仍会重新解析)。
self._prescan_funcdef_signatures(trans, child)
elif kd == ast.ASTKind.ClassDef:
# ClassDef 在模块级直接处理(不需要 builder)
# _declare_only=2(import扫描模式)时跳过,只处理 import 依赖
if trans._declare_only != 2:
self.Gen.ClassGenDef.translate_class_def(trans, child)
elif trans._declare_only == 0 and trans._cur_builder is not None:
# 有 builder → 委托 HandlesBody 分派
added = self.Gen.StmtGen.translate_stmt(trans, child)
added_total += added
elif kd == ast.ASTKind.AnnAssign and trans._declare_only != 2:
# 模块级 AnnAssign
# _declare_only=2(import扫描模式)时跳过
# _declare_only=1(struct注册模式)时只处理 CDefine(在 handle_module_level_var 内部判断)
# _declare_only=0(全量翻译)时处理所有模块级 AnnAssign
added = self.handle_module_level_var(trans, child)
added_total += added
elif trans._declare_only == 0 and kd == ast.ASTKind.Assign:
# 无 builder 的模块级 Assign → 创建全局变量(仅全量翻译模式)
added = self.handle_module_level_var(trans, child)
added_total += added
return added_total
# ============================================================
# _prescan_funcdef_signatures - 1a 阶段预解析模块级函数签名
#
# 触发泛型类特化(如 numpy 的 _empty_like 签名 ndarray[t.CDouble]),
# 使特化结构体在 mark1 前注册(Ty/Fields 不被 1b 的 mark/reset 回收),
# 避免 Phase2 翻译 App 文件时访问悬空结构体(UAF)。
# 仅解析类型触发特化,不验证结果(1b 全量翻译时重新解析)。
# ============================================================
def _prescan_funcdef_signatures(self, trans: HT.Translator*, node: ast.AST*) -> None:
"""预解析模块级函数签名的返回/参数类型,触发泛型类特化"""
if trans is None or node is None:
return
# 入口收基类 ast.AST*(模块级调度器只持有 ast.AST* 子节点,与同一处调用的
# translate_function_def / translate_class_def / _register_cexport_from_funcdef
# 形参口径一致),在此显式窄化为 ast.FunctionDef*:基类指针传派生类形参属
# 向下转型,严格指针规则下禁止隐式转换。
fd: ast.FunctionDef* = ast.FunctionDef*(node)
if fd is None:
return
pool: memhub.MemBuddy* = trans.Pool
if pool is None:
return
imp: str = trans._imported_modules
fi: str = trans._from_imports
if fd.returns is not None:
self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imp, fi, trans, 0)
if fd.args is not None:
ags: ast.Arguments* = ast.Arguments*(fd.args)
if ags.args is not None:
al: list[ast.AST*]* = ags.args
an: sizet = len(al)
ai: sizet
for ai in range(an):
ag: ast.Arg* = ast.Arg*(al.get(ai))
if ag is not None and ag.annotation is not None:
self.Gen.TypeGenResolve.resolve_annotation_type(pool, ag.annotation, imp, fi, trans, 0)
# ============================================================
# _build_array_initializer_text - 从 AST List 生成 LLVM IR 数组初始化器
#
# 生成格式: [N x elem_ty] [elem_ty val0, elem_ty val1, ...]
# 用于模块级 t.CArray 列表字面量初始化(不依赖 builder,直接生成常量初始化器)
# ============================================================
def _build_array_initializer_text(self, pool: memhub.MemBuddy*,
var_ty: llvmlite.LLVMType*,
list_node: ast.List*) -> str:
"""从 AST List 节点生成 LLVM IR 数组初始化器文本,None 失败"""
if var_ty is None or list_node is None:
return None
# 重新进行类型转换,确保 TPV 编译器正确识别 ast.List 类型
# (避免 init_list_node 初始值为 None 时类型推断退化为 t.CPtr)
ln: ast.List* = ast.List*(list_node)
if ln is None:
return None
# 从 var_ty 提取数组元素类型和数量
elem_ty: llvmlite.LLVMType* = None
arr_count: int = 0
match var_ty:
case llvmlite.LLVMType.Array(et, cnt):
elem_ty = et
arr_count = cnt
if elem_ty is None or arr_count <= 0:
return None
# 确定元素类型的 IR 表示
elem_ir_ty: str = "i8"
is_int_elem: int = 1
match elem_ty:
case llvmlite.LLVMType.Int(bits):
is_int_elem = 1
if bits == 8:
elem_ir_ty = "i8"
elif bits == 16:
elem_ir_ty = "i16"
elif bits == 32:
elem_ir_ty = "i32"
elif bits == 64:
elem_ir_ty = "i64"
case _:
is_int_elem = 0
# 获取列表元素(通过 ln.elts 而非 list_node.elts,确保类型正确识别)
elts: list[ast.AST*]* = ln.elts
if elts is None:
return None
elts_count: sizet = len(elts)
# 元素数超过声明长度 → fail-fast(不按声明长度截断、静默丢弃多余元素)。
if sizet(arr_count) < elts_count:
self.Gen.TypeGenErr.fatal_error(list_node, f"数组初始化元素数 {elts_count} 超过数组声明长度 {arr_count},不允许静默截断", "OverflowError")
return None
# 非整数元素类型(struct/指针等聚合):不存在 "%s %lld" 文本展开,仅当列表
# 全零(元素缺失按零填充)时返回整体 "zeroinitializer"——对任意聚合类型合法
# (否则会生成与声明类型不匹配的 [i8 0, ...] → llc type mismatch)。
# 含非零/非整数常量无法文本表达 → None → 调用方 fail-fast。
if is_int_elem == 0:
# 嵌套数组元素(如 t.CArray[t.CArray[uint8, 16], 256]):elem_ty 是 Array →
# 递归构建子数组初始化器文本,内层类型省略(由外层全局类型推断),llc 合法。
is_nested_arr: int = 0
match elem_ty:
case llvmlite.LLVMType.Array(_, _):
is_nested_arr = 1
case _:
pass
if is_nested_arr != 0:
cnt_total_n: sizet = sizet(arr_count)
buf_size_n: sizet = 128 + cnt_total_n * 192
buf_n: str = pool.alloc(buf_size_n)
if buf_n is None:
return None
written_n: int = viperlib.snprintf(buf_n, buf_size_n, "[")
pos_n: sizet = sizet(written_n)
# 子数组元素类型前缀(如 "[16 x i8]"):嵌套数组初始化器内层
# 必须显式类型(llc 对省略内层类型的 [[i8 0,...],...] 报
# "expected number in address space";用户规范要求
# [[16 x i8][i8 0, ...], ...] 形式)。TypePrint 序列化 elem_ty。
elem_ty_str_n: str = pool.alloc(96)
if elem_ty_str_n is None:
return None
llvmlite.TypePrint(elem_ty_str_n, 96, elem_ty, pool)
i_n: sizet = 0
while i_n < cnt_total_n:
sub_n: str = None
if i_n < elts_count:
en_n: ast.AST* = elts.get(i_n)
if en_n is not None:
ek_n: int = en_n.kind()
if ek_n == ast.ASTKind.List:
sub_n = self._build_array_initializer_text(pool, elem_ty, ast.List*(en_n))
elif ek_n == ast.ASTKind.Set:
# 花括号行字面量(如点阵字库 {0x00, ...}):Set.elts
# 与 List 同构,包装为临时 List 递归生成子数组文本
se_n: rawptr[ast.Set] = rawptr[ast.Set](en_n)
if se_n is not None and se_n.elts is not None:
sub_n = self._build_array_initializer_text(pool, elem_ty, ast.List(pool, se_n.elts, ast.ASTCtx.Load))
if sub_n is None:
# 元素缺失/无法展开:子数组整体零初始化
sub_n = "zeroinitializer"
if i_n > 0:
written_n = viperlib.snprintf(buf_n + pos_n, buf_size_n - pos_n, ", ")
pos_n += sizet(written_n)
written_n = viperlib.snprintf(buf_n + pos_n, buf_size_n - pos_n, "%s%s", elem_ty_str_n, sub_n)
pos_n += sizet(written_n)
i_n += 1
viperlib.snprintf(buf_n + pos_n, buf_size_n - pos_n, "]")
return buf_n
for zi in range(elts_count):
ze: ast.AST* = elts.get(zi)
if ze is None:
continue
zk: int = ze.kind()
if zk == ast.ASTKind.Constant:
zc: ast.Constant* = ast.Constant*(ze)
if zc is None:
continue
# None(CONST_NONE)视为零:函数指针数组
# irq_handlers: t.CArray[irq_handler_t, 16] = [None] 表示
# 全空(idt.vp L387)——CONST_NONE 不是 CONST_INT,此前
# 被拒 → "初始化器构建失败" fail-fast。None 归一零填充。
if zc.const_kind == ast.CONST_NONE:
continue
if zc.const_kind != ast.CONST_INT or zc.int_val != 0:
return None
elif zk == ast.ASTKind.UnaryOp:
if self.Gen.AssignGen.extract_cdefine_int_value(ze) != 0:
return None
else:
return None
return "zeroinitializer"
# 计算缓冲区大小: 每个元素最多 "i32 -9223372036854775808, " 约 26 字符
# 按数组声明长度 arr_count 计算(元素不足时补零,可能多于 elts_count)
cnt_total: sizet = sizet(arr_count)
buf_size: sizet = 64 + cnt_total * 32
buf: str = pool.alloc(buf_size)
if buf is None:
return None
# 写入前缀: "["(类型前缀由 _print_global 输出,初始化器只需元素列表)
written: int = viperlib.snprintf(buf, buf_size, "[")
pos: sizet = sizet(written)
# 逐个元素写入:元素数必须与数组声明长度 arr_count 一致,
# 列表元素不足时补零(如 g_arr: t.CArray[int, 4] = [0] →
# [i32 0, i32 0, i32 0, i32 0]),否则 llc 报
# "constant expression type mismatch: got type '[1 x i32]' but expected '[4 x i32]'"
i: sizet = 0
while i < cnt_total:
elem_val: int64 = 0
if i < elts_count:
elem_node: ast.AST* = elts.get(i)
# 直接从 Constant 节点提取 int_val(避免 extract_cdefine_int_value 的 int 截断)
if elem_node is not None:
ek: int = elem_node.kind()
if ek == ast.ASTKind.Constant:
ec: ast.Constant* = ast.Constant*(elem_node)
# CONST_CHAR 字符字面量(如 `_ps2_set1 = ['\x1b', '1', ...]`):
# int_val 即字符码(不识别则字符元素全编成 0)。
if ec is not None and (ec.const_kind == ast.CONST_INT or ec.const_kind == ast.CONST_CHAR):
elem_val = ec.int_val
# CONST_STR 单字符(双引号如 "'" / '"'):parser 把双引号一律判为
# CONST_STR,strlen == 1 时取首字符码;多字符字符串无法表达为整型
# 元素 → 保持 0(由下方按声明长度补零)。
elif ec is not None and ec.const_kind == ast.CONST_STR:
if ec.str_val is not None and string.strlen(ec.str_val) == 1:
elem_val = int64(ec.str_val[0])
elif ek == ast.ASTKind.UnaryOp:
# 负数: UnaryOp(USub, Constant)
elem_val = self.Gen.AssignGen.extract_cdefine_int_value(elem_node)
if i > 0:
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
pos += sizet(written)
written = viperlib.snprintf(buf + pos, buf_size - pos, "%s %lld", elem_ir_ty, elem_val)
pos += sizet(written)
i += 1
# 写入后缀 "]"
viperlib.snprintf(buf + pos, buf_size - pos, "]")
return buf
# _build_string_array_initializer_text - 字符串字面量初始化数组全局
# t.CArray[char, N] = "..." → [i8 b0, i8 b1, ..., i8 0, ...](字符串字节 +
# 零填充到数组长度;空串返回 zeroinitializer)。
# 不能用字符串指针 GEP 初始化器:会导致 @g = global [N x i8] <ptr> 类型不匹配。
def _build_string_array_initializer_text(self, pool: memhub.MemBuddy*,
var_ty: llvmlite.LLVMType*,
str_val: str) -> str:
"""字符串字面量初始化数组全局,返回初始化器文本或 None"""
if var_ty is None or str_val is None:
return None
arr_count: int = 0
match var_ty:
case llvmlite.LLVMType.Array(_, cnt):
arr_count = cnt
if arr_count <= 0:
return None
slen: sizet = string.strlen(str_val)
if slen == 0:
return "zeroinitializer"
buf_size: sizet = 64 + sizet(arr_count) * 16
buf: str = pool.alloc(buf_size)
if buf is None:
return None
written: int = viperlib.snprintf(buf, buf_size, "[")
pos: sizet = sizet(written)
i: sizet = 0
while i < sizet(arr_count):
if i > 0:
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
pos += sizet(written)
bv: int64 = 0
if i < slen:
bv = int64(str_val[i])
written = viperlib.snprintf(buf + pos, buf_size - pos, "i8 %lld", bv)
pos += sizet(written)
i += 1
viperlib.snprintf(buf + pos, buf_size - pos, "]")
return buf
# ============================================================
# _build_string_ptr_initializer - 为字符串字面量初始化指针类型全局变量
#
# 创建内部字符串常量全局 @.str.{var_name} = private constant [len+1 x i8] c"...\00"
# 返回 GEP 初始化器文本: getelementptr inbounds ([len+1 x i8], [len+1 x i8]* @.str.{var_name}, i32 0, i32 0)
# 用于 b64_tab: t.CArray[char, None] = "ABC..." 等字符串初始化指针类型
# ============================================================
def _build_string_ptr_initializer(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
var_name: str,
str_val: str) -> str:
"""为字符串字面量创建内部常量全局并返回 GEP 初始化器文本,None 失败"""
if str_val is None or var_name is None:
return None
# 构造内部字符串常量名称: .str.{var_name}
str_name_buf: str = pool.alloc(64)
if str_name_buf is None:
return None
viperlib.snprintf(str_name_buf, 64, ".str.%s", var_name)
# 调用 llvmlite.create_global_string 创建字符串常量全局
str_gv: llvmlite.GlobalVariable* = llvmlite.create_global_string(pool, mod, str_name_buf, str_val)
if str_gv is None:
return None
# 生成 GEP 初始化器文本
str_len: sizet = string.strlen(str_val)
init_buf: str = pool.alloc(128)
if init_buf is None:
return None
viperlib.snprintf(init_buf, 128,
"getelementptr inbounds ([%d x i8], [%d x i8]* @%s, i32 0, i32 0)",
str_len + 1, str_len + 1, str_name_buf)
return init_buf
def _stg_field_zero_text(self, pool: memhub.MemBuddy*,
fty: llvmlite.LLVMType*,
ty_str: str) -> str:
"""按 LLVM 类型生成 storage struct 字段的零值初始化器文本
LLVM struct 初始化器必须列全所有字段(部分初始化 llc 报
"initializer with struct type has wrong # elements")。
Ptr→null、Int→0、Float→0.0、Array/Struct→zeroinitializer。
"""
if fty is None or ty_str is None:
return None
buf: str = pool.alloc(160)
if buf is None:
return None
match fty:
case llvmlite.LLVMType.Ptr(_):
viperlib.snprintf(buf, 160, "%s null", ty_str)
case llvmlite.LLVMType.Int(_):
viperlib.snprintf(buf, 160, "%s 0", ty_str)
case llvmlite.LLVMType.Float(_):
viperlib.snprintf(buf, 160, "%s 0.0", ty_str)
case _:
# Array / Struct 等 aggregate → zeroinitializer
viperlib.snprintf(buf, 160, "%s zeroinitializer", ty_str)
return buf
# ============================================================
# _default_val_const_text - 字段默认值 → LLVM 常量元素文本
#
# 仅处理可常量折叠的整型默认值(CONST_INT/CONST_CHAR 及负号字面量);
# 其余(浮点/字符串/None/名字/表达式)返回 None,由调用方按零值兜底。
# ============================================================
def _default_val_const_text(self, pool: memhub.MemBuddy*,
fty: llvmlite.LLVMType*,
ty_str: str,
dval: ast.AST*) -> str:
"""字段默认值 → "ty 值" 文本;不可折叠返回 None"""
if dval is None or fty is None or ty_str is None:
return None
is_int: int = 0
dv_int: int64 = 0
if dval.kind() == ast.ASTKind.Constant:
dc: ast.Constant* = ast.Constant*(dval)
if dc is not None:
if dc.const_kind == ast.CONST_INT or dc.const_kind == ast.CONST_CHAR:
dv_int = dc.int_val
is_int = 1
elif dval.kind() == ast.ASTKind.UnaryOp:
# 负数/按位取反字面量(如 -1 / 0xFFFFFFFFFFFFFFFF)
dv_int = self.Gen.AssignGen.extract_cdefine_int_value(dval)
is_int = 1
if is_int == 0:
return None
# 仅整型字段可写整数常量(浮点/指针/聚合交给零值兜底)
match fty:
case llvmlite.LLVMType.Int(_):
pass
case _:
return None
buf: str = pool.alloc(48)
if buf is None:
return None
viperlib.snprintf(buf, 48, "%s %lld", ty_str, dv_int)
return buf
# _build_stg_struct_body_text - storage struct 字段元素文本(不含大括号)
# 生成 "elem0, elem1, ..."(含字段默认值折叠,缺失处按类型零值兜底),供两处复用:
# - _build_stg_struct_init_text:外包 "{ ... }" 作为完整初始化器;
# - vtable 对象 storage:首元素为 vtable 指针,其后追加本函数结果(start_idx=1 跳过字段 0)。
# 无任何非零默认值时返回 None,调用方据此保持原零值路径(避免无谓文本)。
def _build_stg_struct_body_text(self, pool: memhub.MemBuddy*,
se: GenTypes.StructEntry*,
start_idx: int) -> str:
"""按字段默认值生成 "e0, e1, ..." 元素文本;无非零默认值返回 None"""
if se is None or se.Fields is None:
return None
fc: int = se.FieldCount
if fc <= start_idx:
return None
buf_size: sizet = 256 + sizet(fc) * 160
buf: str = pool.alloc(buf_size)
if buf is None:
return None
pos: sizet = 0
first: int = 1
has_nonzero: int = 0
i: int = start_idx
while i < fc:
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(se, i)
i += 1
if fe is None or fe.Ty is None:
continue
ty_str: str = pool.alloc(128)
if ty_str is None:
continue
llvmlite.TypePrint(ty_str, 128, fe.Ty, pool)
elem: str = self._default_val_const_text(pool, fe.Ty, ty_str, fe.DefaultVal)
is_zero: int = 0
if elem is None:
elem = self._stg_field_zero_text(pool, fe.Ty, ty_str)
is_zero = 1
if elem is None:
continue
written: int = 0
if first == 0:
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
pos += sizet(written)
written = viperlib.snprintf(buf + pos, buf_size - pos, "%s", elem)
pos += sizet(written)
first = 0
if is_zero == 0:
has_nonzero = 1
if has_nonzero == 0:
return None
return buf
# _build_stg_struct_init_text - storage struct 完整常量初始化器(含字段默认值)
# 用于模块级对象全局 `X: T = T()`:按字段默认值生成完整 struct 常量
# (LLVM 要求列全每个字段);不生成则字段默认值全丢。
# 无任何非零默认值时返回 None,由调用方保持 zeroinitializer(避免无谓文本)。
def _build_stg_struct_init_text(self, pool: memhub.MemBuddy*,
se: GenTypes.StructEntry*,
start_idx: int) -> str:
"""按字段默认值生成完整 struct 初始化器;无非零默认值返回 None"""
body: str = self._build_stg_struct_body_text(pool, se, start_idx)
if body is None:
return None
body_len: sizet = sizet(string.strlen(body))
buf: str = pool.alloc(body_len + 8)
if buf is None:
return None
viperlib.snprintf(buf, body_len + 8, "{ %s }", body)
return buf
# ============================================================
# 模块级全局变量 t.attr.* 属性提取
#
# 语法:属性写在类型注解的联合里(`T | t.attr.section(".x")`),因为
# Assign/AnnAssign 节点没有 decorator_list(解析器对 `@c.Attribute(...)`
# 后接赋值会直接报 "decorator must be followed by def/class")。
# 与函数侧口径差异:函数属性来自 decorator_list,提取为属性组文本
# `"section"="xxx"`(LLVM 字符串属性必须带引号,走 attributes #N);
# 全局变量属性没有属性组,直接写入 GlobalVariable.Section/Align/Linkage
# 字段,打印时由 includes/llvmlite/__module.vp 的 _print_global 补引号
# (`, section "xxx"` / `, align N`),故此处 Section 值不带引号。
# ============================================================
def _scan_global_tattr(self, node: ast.AST*,
want_name: str) -> ast.AST*:
"""在全局变量类型注解中查找 t.attr.<want_name> 节点。
注解形态是 BinOp(BitOr) 左深链(`A | B | C` = BinOp(BinOp(A,B),C)),
递归左右子节点。命中返回该 t.attr 节点(Call: t.attr.aligned(16) /
Attribute: t.attr.weak),未命中返回 None。属性名识别复用类侧
ClassGenBase._tattr_name(Call→func→attr / Attribute→attr)。
"""
if node is None:
return None
if node.kind() == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if bop.op == ast.OpKind.BitOr:
hit_l: ast.AST* = self._scan_global_tattr(bop.left, want_name)
if hit_l is not None:
return hit_l
return self._scan_global_tattr(bop.right, want_name)
return None
if self.Gen.ClassGenBase._tattr_name(node, want_name) != 0:
return node
return None
def _extract_global_attr_section(self, node: ast.AST*,
var_name: str) -> str:
"""取 `T | t.attr.section("xxx")` 的段名(不含引号)。
缺参/空段名属非法值 → fail-fast(与 handle_module_level_var 既有
TypeGenErr.fatal_error 风格一致):空段名会生成 `section ""` 这种
无意义/非法的 IR,静默忽略只会掩盖拼写错误。
"""
hit: ast.AST* = self._scan_global_tattr(node, "section")
if hit is None:
return None
sec_val: str = self.Gen.FuncGenBase._get_attr_call_arg_str(hit)
if sec_val is None or sec_val[0] == 0:
self.Gen.TypeGenErr.fatal_error(node, f"模块级变量 '{var_name}' 的 t.attr.section(...) 段名缺失或为空", "ValueError")
return None
return sec_val
def _extract_global_attr_aligned(self, node: ast.AST*,
var_name: str) -> int:
"""取 `T | t.attr.aligned(N)` 的对齐字节数(无该属性返回 0)。
N 必须是正整数字面量:aligned(0)/aligned(-4)/aligned("x") 均属非法值
→ fail-fast(llc 亦拒绝 `align 0`,静默忽略会掩盖参数错误)。
"""
hit: ast.AST* = self._scan_global_tattr(node, "aligned")
if hit is None:
return 0
al_val: int = self.Gen.ClassGenBase._tattr_aligned_int(hit)
if al_val <= 0:
self.Gen.TypeGenErr.fatal_error(node, f"模块级变量 '{var_name}' 的 t.attr.aligned(N) 需要正整数字面量 N", "ValueError")
return 0
return al_val
def _extract_global_attr_weak(self, node: ast.AST*) -> int:
"""检测 `T | t.attr.weak` / `t.attr.weak()`(1=有,0=无)"""
if self._scan_global_tattr(node, "weak") is not None:
return 1
return 0
# ============================================================
# handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量
#
# 当用户已定义 main(无 wrapper main builder)时,模块级
# AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域
# 支持整数、列表字面量、字符串字面量初始值
# ============================================================
# ============================================================
# _RegisterStaticModuleVar - 模块级 static 变量登记
#
# `static x: int = 0`(解析层已归一为 `x: t.CStatic | int`)与手写
# `x: t.CStatic | int` 均命中 → 登记 (变量名, 定义模块 SHA1) 到 static 符号表,
# 供引用侧(ExprGenAttr / CallGenMixin / ImportsGenMixin)拦截跨模块使用。
# 变量无装饰器通道(@t.CStatic 只作用于 def),故只看注解。
# 时机:调用点位于 handle_module_level_var 的 `_declare_only` 提前返回之后,
# 预声明遍不会走到;登记接口本身也做了幂等,重复登记无副作用。
# 独立成函数:不内联(内联新增分支会干扰调用方的类型推断)。
# ============================================================
def _RegisterStaticModuleVar(self, trans: HT.Translator*,
annot_node: ast.AST*,
var_name: str) -> int:
if trans is None or annot_node is None or var_name is None:
return 0
if trans.ModuleSha1 is None:
return 0
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CStatic", 0) == 0:
return 0
self.Gen.CallGenBase.RegisterStaticSymbol(trans.ModuleSha1, var_name)
return 0
def handle_module_level_var(self, trans: HT.Translator*,
node: ast.AST*) -> int:
"""处理模块级变量声明,创建全局变量"""
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
# CDefine 注解: 编译期常量,不创建全局变量
# 注册到全局 CDefine 表供 t.CArray[elem_ty, NAME] 解析
k: int = node.kind()
if k == ast.ASTKind.AnnAssign:
aa_cd: ast.AnnAssign* = ast.AnnAssign*(node)
if aa_cd is not None and aa_cd.target is not None:
if aa_cd.target.kind() == ast.ASTKind.Name:
nm_cd: ast.Name* = ast.Name*(aa_cd.target)
if nm_cd.id is not None:
if self.Gen.AssignGen.is_cdefine_annotation(aa_cd.annotation) != 0:
# 浮点/整数自动识别注册(浮点 CDefine 如 vipermath U_M_PI)
self.Gen.AssignGen._register_cdefine_from_value(pool, nm_cd.id, aa_cd.value, aa_cd.annotation)
return 0
# CTypedef 注解(如 posix.vp mode_t: t.CTypedef = uint32):
# 不创建全局变量(否则生成 @mode_t 符号 → 链接 duplicate symbol)。
# 预注册阶段按需注册到 CTypedef 表:翻译期表已被 clear_ctypedefs 清空
# (每文件隔离),不注册则函数参数注解查表失败 → NameError。
if self.Gen.TypeGenCType._is_ctypedef_annotation(aa_cd.annotation) != 0:
ctd_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa_cd.value)
if ctd_str is not None:
self.Gen.TypeGenCType.register_ctypedef(pool, nm_cd.id, ctd_str)
return 0
# _declare_only=1(struct注册模式)时只处理 CDefine,不创建全局变量
# CDefine 已在上面处理并返回,到这里说明不是 CDefine,直接跳过
if trans._declare_only == 1:
return 0
var_name: str = None
var_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
resolved_stg_ty: llvmlite.LLVMType* = None # 对象类全局:静态 storage 类型(BSS 实例)
resolved_stg_entry: GenTypes.StructEntry* = None # 对象类全局:对应 StructEntry(vtable 初始化用)
init_val: int64 = 0
init_fval: t.CDouble = 0.0 # init_kind==4(浮点字面量)时的数值
init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str, 4=float
init_str_val: str = None
init_list_node: ast.List* = None
# = None 显式初始化(如 tokens.vp `_kw_head: KwEntry | t.CPtr = None`):
# 对象类全局的懒初始化指针语义——必须保持 NULL,不能创建 BSS storage
# (storage 会让 @g = &g.stg 恒非 NULL,破坏 `if _kw_head != None` 守卫,
# 懒初始化代码永不执行)。
is_none_init: int = 0
annot_node: ast.AST* = None # 类型注解节点(检测 t.CExport 标记)
if k == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign* = ast.AnnAssign*(node)
if aa is None or aa.target is None:
return 0
if aa.target.kind() != ast.ASTKind.Name:
return 0
nm: ast.Name* = ast.Name*(aa.target)
var_name = nm.id
annot_node = aa.annotation
# f-string 逃逸:模块级变量的初始值是**全局**(函数外可见),而 f-string
# 结果指向当前函数的栈缓冲(且模块级初始化根本没有可用的函数栈)⇒
# fail-fast;需要字面量以外的初值请用显式拷贝(GenApi.escape_fstring_copy)。
GenApi.fail_if_fstring_escape(aa.value, "全局变量初始化")
# static 符号(禁止跨模块使用):模块级变量带 t.CStatic 标记
# (`static x: int = 0` 经解析层归一为 `x: t.CStatic | int`;亦覆盖
# 手写 `x: t.CStatic | int`)→ 登记 (变量名, 定义模块 SHA1)。
# 逻辑收敛在 _RegisterStaticModuleVar 内(不内联——本函数含多分支,
# 内联新增分支会干扰其类型推断,与 ExprGenAttr/CallGenMixin 同款处理)。
self._RegisterStaticModuleVar(trans, annot_node, var_name)
# t.Callable 模块级函数指针:登记 (变量名 → 返回类型) 签名。此路径原先
# 只创建全局变量、不登记签名,模块级 t.Callable 变量被间接调用时在
# CallGenLeafKit._translate_func_ptr_call 报"返回类型未注册(t.Callable
# 注解缺失)"(CallGenLeafKit.vp:843-850)。与函数内 AnnAssign 同一登记函数
# (AssignGenMixin.vp:1465-1474),参数形态照抄该处:
# (pool, 注解, trans._imported_modules, trans._from_imports, trans)。
# 时机:_translate_module_level 在 translate_children 之前先遍历全部模块级
# AnnAssign/Assign(Translator.vp:568-575,其后才是 :578 translate_children),
# 故登记早于本文件任何函数体翻译;签名表本体与返回类型深拷贝都在 PermPool
# (CallGenLeafKit.vp:698-699、:783-788),跨文件查找可见。
# t.CDefine/t.CTypedef 等分支已在 :675-689 提前返回,行为不变。
if annot_node is not None:
ml_callable_ret: llvmlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot_node, trans._imported_modules,trans._from_imports, trans)
if ml_callable_ret is not None:
# 返回值:0=成功/同类型幂等命中;非 0 已在 register_func_ptr_sig
# 内部 fatal_error 终止编译,此处分支仅防御(见 AssignGenMixin 同处)。
if self.Gen.CallGenLeafKit.register_func_ptr_sig(pool, var_name, ml_callable_ret, aa) != 0:
return 0
# 解析类型
if aa.annotation is not None:
resolved: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
# 对象类模块级变量(@t.Object/@t.CVTable:对象即指针)→ Ptr(struct):
# 裸类名注解 resolve 返回 struct 值,模块级变量此处需包装 Ptr;若建成
# 值实例,跨模块字段访问/虚表调用会按指针处理读首 8 字节当 vtable → 崩溃。
# 纯 t.CStruct(无方法无虚表无 __new__)保持值实例。
if resolved.kind() == llvmlite.LLVMType.Struct:
gv_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(resolved)
if gv_entry is not None \
and (gv_entry.HasNew != 0 or gv_entry.HasVTable != 0 or gv_entry.IsOOP != 0):
# 对象类全局(裸类名注解 → 对象即指针):记录原 Struct 值类型,
# 用于创建静态 storage 全局承载对象实例(BSS);否则 @g 为 NULL,
# 对象字段落在物理页 0。
resolved_stg_ty = resolved
resolved_stg_entry = gv_entry
resolved = llvmlite.Ptr(pool, resolved)
elif resolved.kind() == llvmlite.LLVMType.Ptr:
# 对象类联合注解(T | t.CPtr → T*):同样创建 storage;否则入口/方法
# 虚调用 load @g 得 NULL → 读 [NULL] 拿 vtable → #PF。
# var_ty 保持 Ptr(联合注解语义:指针全局),storage 承载对象实例。
pointee_uv: llvmlite.LLVMType* = resolved.Pointee
if pointee_uv is not None and pointee_uv.kind() == llvmlite.LLVMType.Struct:
gv_entry_u: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(pointee_uv)
if gv_entry_u is not None \
and (gv_entry_u.HasNew != 0 or gv_entry_u.HasVTable != 0 or gv_entry_u.IsOOP != 0):
resolved_stg_ty = pointee_uv
resolved_stg_entry = gv_entry_u
var_ty = resolved
else:
# [S4 fail-fast] 模块级变量有注解但解析失败 → 不再静默用 i32
# (类型错会使全局存储尺寸/字段访问/ABI 全错);前向引用未注册类
# 应收敛为多遍注册而非回退 i32。
self.Gen.TypeGenErr.fatal_error(aa.annotation, f"模块级变量 '{var_name}' 类型注解解析失败", "NameError")
return 0
# 解析初始值(支持整数、负数字面量、列表字面量、字符串字面量)
if aa.value is not None:
val_kind: int = aa.value.kind()
if val_kind == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(aa.value)
if cn.const_kind == ast.CONST_NONE:
is_none_init = 1
elif cn.const_kind == ast.CONST_INT:
init_val = cn.int_val
init_kind = 1
elif cn.const_kind == ast.CONST_STR:
init_str_val = cn.str_val
init_kind = 3
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点字面量(如 torch `LCG_M: t.CDouble = 2147483647.0`):必须设
# init_kind,否则全局退化成 zeroinitializer、初始化值整体丢失。
# 数值不在此处取 AST 的 float_val,见下方 init_kind==4 的源文件恢复逻辑。
init_kind = 4
elif val_kind == ast.ASTKind.UnaryOp:
# 负数/按位取反字面量(如 `_vblank_ok: int = -1`):AST 是
# UnaryOp(USub, Constant),不处理则全局编成 zeroinitializer、初始化值丢失。
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
elif k == ast.ASTKind.Assign:
asgn: ast.Assign* = ast.Assign*(node)
if asgn is None or asgn.targets is None:
return 0
targets: list[ast.AST*]* = asgn.targets
if len(targets) < 1:
return 0
t0: ast.AST* = targets.get(0)
if t0 is None or t0.kind() != ast.ASTKind.Name:
return 0
nm2: ast.Name* = ast.Name*(t0)
var_name = nm2.id
if asgn.value is not None:
val_kind2: int = asgn.value.kind()
if val_kind2 == ast.ASTKind.Constant:
cn2: ast.Constant* = ast.Constant*(asgn.value)
if cn2.const_kind == ast.CONST_NONE:
is_none_init = 1
elif cn2.const_kind == ast.CONST_INT:
init_val = cn2.int_val
init_kind = 1
elif cn2.const_kind == ast.CONST_STR:
init_str_val = cn2.str_val
init_kind = 3
elif cn2.const_kind == ast.CONST_FLOAT:
# 无注解的模块级浮点赋值:字面量本身即类型依据,提升为 double
# (默认 var_ty 是 i32,不提升会把浮点位模式写进 i32 全局)
init_kind = 4
var_ty = llvmlite.Double(pool)
elif val_kind2 == ast.ASTKind.UnaryOp:
# 负数字面量(同 AnnAssign 分支,见上):UnaryOp(USub, Constant)
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
elif val_kind2 == ast.ASTKind.Attribute:
# 模块级 re-export 别名(如 fat32_core.vp `_read32 = types._read32`):
# RHS 是函数/实体引用,无值语义可落入 LLVM 全局。不识别则 var_ty 默认 i32
# → 生成假符号 `global i32 zeroinitializer`(被引用即调用地址 0)。
# 与 __all__ 同类:别名/元数据实体不产出 LLVM 全局。
# 真正的函数别名机制(可调用 + 可 re-export)属独立特性,待后续实现。
return 0
if var_name is None:
return 0
# __all__ 是 Python 模块元数据(公开名列表),无运行时语义:StubGen 的 .vpi 不为其
# 生成 stub 声明,此处同样跳过全局创建——否则无注解 list-of-strings 全局类型推断
# 缺失(var_ty 默认 i32)→ _build_array_initializer_text 返回 None → 误报
# "初始化器构建失败"。与 CDefine/CTypedef 同类:编译期/元数据实体不落入 LLVM 全局。
if string.strcmp(var_name, "__all__") == 0:
return 0
# 浮点字面量初始化(init_kind==4):数值一律从当前源文件按变量名重读,
# 不用 AST 节点的 float_val —— 该字段可能被后续 pool 操作覆写,且跨模块
# double 返回值传递本身有缺陷(详见 AssignGenMixin._recover_float_literal)。
# 同时校验注解类型:注解不是浮点却给浮点字面量属类型错误,直接终止,
# 不静默零初始化。
if init_kind == 4:
is_fty_g: int = 0
match var_ty:
case llvmlite.LLVMType.Float(_):
is_fty_g = 1
case _:
pass
if is_fty_g == 0:
self.Gen.TypeGenErr.fatal_error(node, f"模块级变量 '{var_name}' 用浮点字面量初始化,但注解不是浮点类型",
"TypeError")
return 0
if self.Gen.AssignGen._recover_float_literal(var_name, t.CPtr(c.Addr(init_fval)), 0) != 1:
self.Gen.TypeGenErr.fatal_error(node, f"模块级浮点变量 '{var_name}' 初始化值源文件恢复失败", "InternalCompilerError")
return 0
# 检查是否已注册
existing: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, var_name)
if existing is not None:
return 0
# 创建全局变量 @var_name
# 先确保 var_ty 中的跨模块结构体类型在模块中有 opaque 声明
# 否则 @var_name = global %"sha1.ClassName"* zeroinitializer 会报
# "use of undefined type named 'sha1.ClassName'" 错误
# (如 _mbuddy: rawptr[memhub.MemManager] → Ptr(Struct("aeb3...MemManager")))
llvmlite.module_ensure_opaque_for_type(mod, pool, var_ty)
# 模块级全局命名带 SHA1 前缀(对齐函数名 mangling):多个模块声明同名
# 全局(如 _mbuddy)时各自独立命名,消除 ld.lld duplicate symbol。
# SymTab 注册 key 保持裸名(查找逻辑零改动),LLVM 符号名用 mangled 名。
# t.CExport 标记的全局用裸名(内联汇编引用 / 外部符号对接,如
# VKernel gdt/gp/_switch_old_rsp/_coro_* 被 c.Asm 引用,带 SHA1 前缀
# 无法在 LLVM MC 中解析)。
is_global_cexport: int = 0
if annot_node is not None:
is_global_cexport = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CExport", 0)
# extern 全局(`extern NAME: T` → 注解含 t.State / t.CExtern):符号定义在
# 外部(C 库 / 汇编 / 其它翻译单元),本模块只生成外部声明
# `@裸名 = external global T`——external linkage、无初始值、无 storage。
# 与 export 的差别:export 是**本模块定义**(裸名导出);extern 是**仅声明**。
# `t.State` = t.CExtern | t.CExport(parser 的 `extern NAME: T` 展开产物)。
is_global_extern: int = 0
if annot_node is not None:
is_global_extern = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CExtern", 0)
if is_global_extern == 0:
is_global_extern = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "State", 0)
# 全局变量侧 t.attr.* 属性(`T | t.attr.xxx` 注解联合形态):
# section→gv.Section、aligned→gv.Align、weak→gv.Linkage(见下方应用点)。
# 仅在 AnnAssign(annot_node 非 None)时存在,与 t.CExport 同一条路。
gv_attr_section: str = None
gv_attr_align: int = 0
gv_attr_weak: int = 0
if annot_node is not None:
gv_attr_section = self._extract_global_attr_section(annot_node, var_name)
gv_attr_align = self._extract_global_attr_aligned(annot_node, var_name)
gv_attr_weak = self._extract_global_attr_weak(annot_node)
mangled_name: str = var_name
if is_global_cexport == 0 and is_global_extern == 0:
mangled_name = self.Gen.FuncGenBase._mangle_name(trans, var_name)
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, mangled_name, var_ty)
if gv is None:
return 0
llvmlite.module_add_global(mod, gv)
# 段名:直接写字段(_print_global 会补 `, section "xxx"` 的引号)。
if gv_attr_section is not None:
llvmlite.global_set_section(gv, gv_attr_section)
# B5:@c.Attribute(t.attr.aligned(N)) 类的全局实例施加对齐(如 fpu_context 16 对齐,
# FXSAVE 区域要求 16 字节对齐,否则 fxsave/fxrstor #GP)。
# 优先级:变量级显式 `T | t.attr.aligned(N)` 比类级(struct entry Align)更具体,
# 显式值胜出;无显式属性时沿用类级对齐(既有行为不变)。
al_ty: llvmlite.LLVMType* = var_ty
match var_ty:
case llvmlite.LLVMType.Ptr(pointee_al):
al_ty = pointee_al
case _:
pass
al_entry: GenTypes.StructEntry* = None
if al_ty is not None and al_ty.kind() == llvmlite.LLVMType.Struct:
al_entry = self.Gen.StructGen.find_struct_by_type(al_ty)
if gv_attr_align > 0:
gv.Align = gv_attr_align
elif al_entry is not None and al_entry.Align > 0:
gv.Align = al_entry.Align
# 设置初始值(有初始值时清除 external linkage,因为 LLVM 22+ 不允许 external global 带初始值)
# extern 全局例外:保持 external(仅外部声明,绝不写初始值 / storage)。
if is_global_extern != 0:
gv.Linkage = "external"
else:
gv.Linkage = None
# t.attr.weak → weak linkage(须在 Linkage 清空之后设置)。
if gv_attr_weak != 0:
llvmlite.global_set_linkage(gv, "weak")
if init_kind == 1:
# 整数初始值
init_buf: str = pool.alloc(48)
if init_buf is not None:
viperlib.snprintf(init_buf, 48, "%lld", init_val)
gv.Initializer = init_buf
elif init_kind == 2:
# 列表字面量 → 数组初始化器 [N x elem_ty] [elem_ty val0, ...]
arr_init: str = self._build_array_initializer_text(pool, var_ty, init_list_node)
if arr_init is not None:
gv.Initializer = arr_init
else:
# 初始化器构建失败:直接报错终止(fail-fast),不再回退 zeroinitializer
self.Gen.TypeGenErr.fatal_error(node, f"模块级变量 '{var_name}' 初始化器构建失败", "TypeError")
elif init_kind == 3:
# 字符串字面量初始化
# 数组类型(t.CArray[char, N]):字节数组初始化器(字符串内容 + 零填充)。
# 不能用字符串指针 GEP 初始化器(@g = global [N x i8] <ptr> 类型不匹配)。
is_arr_g: int = 0
match var_ty:
case llvmlite.LLVMType.Array(_, _):
is_arr_g = 1
case _:
pass
if is_arr_g != 0:
arr_str_init: str = self._build_string_array_initializer_text(pool, var_ty, init_str_val)
if arr_str_init is not None:
gv.Initializer = arr_str_init
else:
self.Gen.TypeGenErr.fatal_error(node, f"模块级数组变量 '{var_name}' 字符串初始化器构建失败", "TypeError")
else:
# 指针类型(如 b64_tab: t.CArray[char, None] = "ABC..."):
# 创建字符串常量全局 + GEP 引用
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"模块级变量 '{var_name}' 初始化器构建失败", "TypeError")
elif init_kind == 4:
# 浮点初始值:LLVM IR 的浮点常量必须写成位模式文本(0x%016llx / 0x%08x)——
# 十进制文本经 llc 解析对部分值有 ±1 ulp 误差(见 llvmlite.ConstFloat 注释)。
# 浮点分支缺失时模块级 t.CDouble 全局会退化成 zeroinitializer(值全变 0)。
fc_g: llvmlite.Value* = llvmlite.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"模块级变量 '{var_name}' 浮点初始化器构建失败", "TypeError")
else:
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer,
# 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type"
# 对象类全局(有 storage)的 Initializer 由下方 storage 逻辑设为 &@storage,
# 此处不覆盖;但 = None 懒初始化全局(is_none_init)既无 storage 也无其他
# Initializer → 必须显式 zeroinitializer(LLVM 对指针类型输出为 null),
# 否则 gv 无初始化器 → 全局声明残缺。
if (resolved_stg_ty is None or is_none_init != 0) and is_global_extern == 0:
gv.Initializer = "zeroinitializer"
# 对象类全局:创建静态 storage 全局(BSS)承载对象实例,@g 编译期引用它。
# 函数内 `g = T()` 构造时把对象复制到 storage(见 AssignGenMixin 全局分支),
# 不再依赖栈 alloca(悬垂);c.Addr(g) load @g 得到 &storage,均非 NULL。
# 豁免:显式 `= None` 初始化(is_none_init)的懒初始化指针全局
# (如 _kw_head/_op_head)不建 storage,保持 NULL 语义。
# extern 全局(is_global_extern)同样不建 storage:外部声明无本模块实例。
if resolved_stg_ty is not None and is_none_init == 0 and is_global_extern == 0:
# 值类型 storage 需要完整 struct 定义(跨模块类默认 opaque →
# @g.stg = global %T zeroinitializer 报 "use of undefined type")
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, resolved_stg_ty)
# @t.CVTable 对象类:storage 首字段(vtable 指针,偏移 0)初始化为 vtable 地址,
# 否则 zeroinitializer → 入口虚调用 call [vtable+0x10] → 跳 0x0 #PF。
# @t.Object(无 CVTable)/普通对象:保持 zeroinitializer(无 vtable 需求)。
stg_init_text: str = "zeroinitializer"
if resolved_stg_entry is not None and resolved_stg_entry.Name is not None \
and resolved_stg_entry.HasVTable != 0:
vt_name_buf: str = pool.alloc(128)
if vt_name_buf is not None:
viperlib.snprintf(vt_name_buf, 128, "%s.vtable", resolved_stg_entry.Name)
# vtable 符号前缀必须用类定义模块的 sha1(对齐 CallGenLeafKit._make_vtable_ref_i8):
# 用当前模块 _mangle_name 会使跨模块对象类全局 storage 初始化引用
# "@当前sha1.Class.vtable",而 vtable 实为定义模块的符号 → llc "use of
# undefined value"。resolved_stg_entry.ModuleSha1 即定义模块,比裸名反查精确。
vt_sha1: str = None
if resolved_stg_entry.ModuleSha1 is not None:
vt_sha1 = resolved_stg_entry.ModuleSha1
if vt_sha1 is None:
vt_sha1 = trans.ModuleSha1
vt_mangled: str = self.Gen.FuncGenBase._mangle_name_with_sha1(vt_sha1, vt_name_buf)
if vt_mangled is not None:
# 完整 struct 初始化器(LLVM 拒绝部分初始化):
# 字段 0 = __vtable__(i8* @vtable),其余用户字段按类型零值。
init_full_len: sizet = 16384
init_full: str = pool.alloc(init_full_len)
if init_full is not None:
written_v: int = viperlib.snprintf(init_full, init_full_len, "{ ")
pos_v: sizet = sizet(written_v)
if self.Gen.ClassGenVTable._vtable_name_needs_quote(vt_mangled) != 0:
written_v = viperlib.snprintf(init_full + pos_v, init_full_len - pos_v, "i8* @\"%s\"", vt_mangled)
else:
written_v = viperlib.snprintf(init_full + pos_v, init_full_len - pos_v, "i8* @%s", vt_mangled)
pos_v += sizet(written_v)
# 字段 1..N 优先按字段默认值折叠(vtable 对象全局
# 如 `_mbuddy: MemBuddy` 的字段默认不再丢失);
# 全部默认值恰为零时回退逐字段零值,避免无谓文本。
# 两者均列全字段(LLVM 要求初始化器元素数匹配)。
rest_v: str = self._build_stg_struct_body_text(pool, resolved_stg_entry, 1)
if rest_v is not None:
written_v = viperlib.snprintf(init_full + pos_v, init_full_len - pos_v, ", %s", rest_v)
pos_v += sizet(written_v)
else:
fc_v: int = resolved_stg_entry.FieldCount
for fi_v in range(1, fc_v):
fe_v: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(resolved_stg_entry, fi_v)
if fe_v is None or fe_v.Ty is None:
continue
ty_buf_v: str = pool.alloc(128)
if ty_buf_v is None:
continue
llvmlite.TypePrint(ty_buf_v, 128, fe_v.Ty, pool)
zero_v: str = self._stg_field_zero_text(pool, fe_v.Ty, ty_buf_v)
if zero_v is None:
continue
written_v = viperlib.snprintf(init_full + pos_v, init_full_len - pos_v, ", %s", zero_v)
pos_v += sizet(written_v)
viperlib.snprintf(init_full + pos_v, init_full_len - pos_v, " }")
stg_init_text = init_full
elif resolved_stg_entry is not None:
# 无 vtable 的对象全局(@t.Object / 普通类):按字段默认值生成常量初始化器;
# 一律 zeroinitializer 会丢掉全部字段默认值(模块级 X: T = T() 的构造调用
# 不发射)。无任何非零默认值时返回 None → 保持 zeroinitializer(零开销)。
stg_default_text: str = self._build_stg_struct_init_text(pool, resolved_stg_entry, 0)
if stg_default_text is not None:
stg_init_text = stg_default_text
stg_name_len: sizet = sizet(string.strlen(mangled_name)) + 8
stg_name_buf: str = pool.alloc(stg_name_len)
if stg_name_buf is not None:
viperlib.snprintf(stg_name_buf, stg_name_len, "%s.stg", mangled_name)
stg_gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, stg_name_buf, resolved_stg_ty)
if stg_gv is not None:
llvmlite.module_add_global(mod, stg_gv)
stg_gv.Linkage = None
stg_gv.Initializer = stg_init_text
# B5:storage(BSS 对象实例)同样施加 aligned 对齐
if resolved_stg_entry is not None and resolved_stg_entry.Align > 0:
stg_gv.Align = resolved_stg_entry.Align
# @g 初始值 = &@storage(对象引用指向 BSS 实例,非 NULL)
stg_init_len: sizet = sizet(string.strlen(mangled_name)) + 8
stg_init_buf: str = pool.alloc(stg_init_len)
if stg_init_buf is not None:
if self.Gen.ClassGenVTable._vtable_name_needs_quote(stg_name_buf) != 0:
viperlib.snprintf(stg_init_buf, stg_init_len, "@\"%s\"", stg_name_buf)
else:
viperlib.snprintf(stg_init_buf, stg_init_len, "@%s", stg_name_buf)
gv.Initializer = stg_init_buf
# 注册 storage 引用(key: var_name.__stg),供 AssignGenMixin
# 全局赋值分支检测对象类全局并复制对象到 storage。
stg_ref: llvmlite.Value* = self.Gen.ExprGenAttr.make_global_ref(pool, stg_name_buf, resolved_stg_ty)
if stg_ref is not None:
stg_key_len: sizet = sizet(string.strlen(var_name)) + 8
stg_key: str = pool.alloc(stg_key_len)
if stg_key is not None:
viperlib.snprintf(stg_key, stg_key_len, "%s.__stg", var_name)
self.Gen.VarGen.define_module_var(trans.SymTab, stg_key, stg_ref)
# 创建 Value 引用(@mangled_name, 类型为 var_ty*)
var_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, var_ty)
ref_name: str = pool.alloc(96)
if ref_name is not None:
# mangled 名带 SHA1 前缀(数字开头),引用必须加引号(对齐 make_global_ref)
if self.Gen.ClassGenVTable._vtable_name_needs_quote(mangled_name) != 0:
viperlib.snprintf(ref_name, 96, "@\"%s\"", mangled_name)
else:
viperlib.snprintf(ref_name, 96, "@%s", mangled_name)
gv_ref: llvmlite.Value* = llvmlite.SSAValue(pool, var_ptr_ty, ref_name)
# 注册到模块作用域
if self.Gen.VarGen.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
# t.CVolatile 存储类标记(注解联合含 t.CVolatile)→ 标记全局变量
# volatile:所有 load/store 带 LLVM volatile,禁止优化器把忙等循环
# 的重复 load 提升/消除(如 `locked: t.CVolatile | t.CInt`)。
if annot_node is not None:
# const 对象全局:注解含 t.CConst 且类型非指针 ⇒ 登记 IsConst,
# 之后对该全局名的赋值/增量赋值/元素赋值 fail-fast(判据同 TPC
# 的 Gen.global_const_flags,见 Debugger/type-descriptors.md)。
if self.Gen.TypeGenErr.is_const_object_marker(annot_node, var_ty) != 0:
self.Gen.VarGen.set_var_const(trans.SymTab, var_name)
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, var_name)
# rawptr[t.CVolatile | T](volatile 指针指向物):解引用(p[i]/
# p.field/c.Deref(p))的 load/store 带 volatile。与上一分支互斥:
# 前者是变量自身限定(BinOp 联合),后者是 rawptr 下标实参标记。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot_node) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, var_name)
# 设置 AnnotClassName(联合类型如 Logger|t.CPtr 简化为 Ptr(i8) 时,
# 属性访问需要通过 AnnotClassName 回退到类名查找结构体)
if k == ast.ASTKind.AnnAssign:
aa_ml: ast.AnnAssign* = ast.AnnAssign*(node)
if aa_ml is not None and aa_ml.annotation is not None:
cls_nm_ml: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(aa_ml.annotation, trans._imported_modules, pool, trans._from_imports)
if cls_nm_ml is not None:
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, var_name, cls_nm_ml)
# 模块级 str|t.CPtr / bytes|t.CPtr 全局:标记 IsPtrElement
# (i8** 语义,var[i] 按 8 字节指针步长),对齐 TPC 的
# global_var_ptr_element(_argv 等字符串指针数组)。否则
# var[i] 退化为 1 字节字符步长 GEP + load i8 截断指针。
if aa_ml.annotation.kind() == ast.ASTKind.BinOp:
bop_ml: ast.BinOp* = ast.BinOp*(aa_ml.annotation)
if bop_ml.op == ast.OpKind.BitOr:
if self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "str") != 0 \
or self.Gen.AssignGen._annotation_contains_name(aa_ml.annotation, "bytes") != 0:
self.Gen.VarGen.set_var_ptr_element(trans.SymTab, var_name)
return 1
return 0
# ============================================================
# create_wrapper_main - 创建包装 main 函数
#
# 当用户未定义 main 函数时调用。
# 创建 main() -> i32 函数 → 设置 trans._cur_func/_cur_builder →
# 预扫描 alloca → 翻译子节点 → ret 0
# ============================================================
def create_wrapper_main(self, trans: HT.Translator*,
tree: ast.AST*,
i32_ty: llvmlite.LLVMType*) -> int:
"""创建包装 main 函数并翻译所有顶层语句
Args:
trans: 翻译器(含所有共享状态)
tree: AST 模块节点
i32_ty: i32 LLVMType
Returns:
int: 新增的变量数
"""
pool: memhub.MemBuddy* = trans.Pool
mod: llvmlite.LLVMModule* = trans.Module
main_func: llvmlite.Function* = llvmlite.create_function(pool, mod, "main", i32_ty)
if main_func is None:
VLogger.error("CreateFunction main returned NULL", "TR")
return 0
entry_blk: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, main_func, "entry")
if entry_blk is None:
VLogger.error("CreateBlock returned NULL", "TR")
return 0
builder: llvmlite.IRBuilder* = llvmlite.new_builder(pool, main_func)
if builder is None:
VLogger.error("NewBuilder returned NULL", "TR")
return 0
llvmlite.position_at_end(builder, entry_blk)
# 设置当前翻译上下文(供 Handle 通过 self.Trans._cur_builder 访问)
trans._cur_func = main_func
trans._cur_builder = builder
# 预处理模块级变量:为 AnnAssign/Assign 创建 LLVM 全局变量
# 必须在 pre_scan_allocas 之前执行,否则 PreScan 会创建局部 alloca,
# 导致其他函数通过 SSA 编号引用 wrapper main 的局部变量(无效 IR)
ch: list[ast.AST*]* = tree.children
if ch is not None:
cn_count: sizet = len(ch)
for ci in range(cn_count):
child: ast.AST* = ch.get(ci)
if child is not None:
kd: int = child.kind()
if kd == ast.ASTKind.AnnAssign or kd == ast.ASTKind.Assign:
self.handle_module_level_var(trans, child)
# 预扫描顶层语句:为函数内 AnnAssign 提前创建 alloca
# (模块级变量已在上面注册到模块作用域,PreScan 的 lookup_current 会跳过它们)
if ch is not None:
for ci in range(cn_count):
child: ast.AST* = ch.get(ci)
if child is not None:
self.Gen.StmtGenDecl.pre_scan_allocas(trans, child)
# 翻译子节点
added_total: int = self.translate_children(trans, tree)
# 返回 0
zero_val: llvmlite.Value* = llvmlite.const_int32(pool, 0)
llvmlite.build_ret(builder, zero_val)
return added_total
# ============================================================
# global/nonlocal 名称管理(迁移自 HandlesTranslator.vp 模块级函数)
# ============================================================
def add_global_name(self, trans: HT.Translator*, name: str):
"""添加一个 global 变量名"""
if trans is None or name is None:
return
if trans._global_names is None:
return
# 检查是否已存在
if self.is_global_name(trans, name) != 0:
return
if trans._global_name_count >= HT.MAX_GLOBAL_NAMES:
pool_err: memhub.MemBuddy* = trans.Pool
err_buf: str = None
if pool_err is not None:
err_buf = pool_err.alloc(256)
if err_buf is not None:
viperlib.snprintf(err_buf, 256, "global 变量数量超过上限 MAX_GLOBAL_NAMES=%d,请减少 global 声明或增大 MAX_GLOBAL_NAMES", HT.MAX_GLOBAL_NAMES)
VLogger.error(err_buf, "ScopeDecl")
else:
VLogger.error("global 变量数量超过上限,请减少 global 声明或增大 MAX_GLOBAL_NAMES", "ScopeDecl")
return
entry_addr: uint64 = uint64(trans._global_names) + trans._global_name_count * 8
slot_ptr: str | t.CPtr = (str | t.CPtr)(entry_addr)
slot_ptr[0] = name
trans._global_name_count += 1
def is_global_name(self, trans: HT.Translator*, name: str) -> int:
"""检查 name 是否在当前函数的 global 集合中"""
if trans is None or name is None or trans._global_names is None:
return 0
for i in range(trans._global_name_count):
entry_addr: uint64 = uint64(trans._global_names) + i * 8
slot_ptr: str | t.CPtr = (str | t.CPtr)(entry_addr)
entry: str = slot_ptr[0]
if entry is not None:
if string.strcmp(entry, name) == 0:
return 1
return 0
def add_nonlocal_name(self, trans: HT.Translator*, name: str,
var_ty: llvmlite.LLVMType* = None):
"""添加一个 nonlocal 变量名
Args:
name: nonlocal 变量名
var_ty: 变量的 LLVM 类型(用于 env 指针数组方案的类型恢复,
None 时回退为 i32)
"""
if trans is None or name is None:
return
if trans._nonlocal_names is None:
return
if self.is_nonlocal_name(trans, name) != 0:
# 已存在:更新类型信息(可能在预扫描时类型未知,正式翻译时更新)
# _nonlocal_types 每槽 8 字节,存 LLVMType* 指针。
# 用 str|t.CPtr (i8**) 二级指针存取(与 _nonlocal_names 模式一致),
# [0] 解引用只拷贝 8 字节指针,避免值拷贝 24+ 字节 LLVMType 结构体溢出。
if var_ty is not None and trans._nonlocal_types is not None:
idx: int = self.get_nonlocal_index(trans, name)
if idx >= 0:
ty_addr: uint64 = uint64(trans._nonlocal_types) + idx * 8
ty_slot: str | t.CPtr = (str | t.CPtr)(ty_addr)
ty_slot[0] = var_ty
return
if trans._nonlocal_name_count >= HT.MAX_GLOBAL_NAMES:
pool_err_nl: memhub.MemBuddy* = trans.Pool
err_buf_nl: str = None
if pool_err_nl is not None:
err_buf_nl = pool_err_nl.alloc(256)
if err_buf_nl is not None:
viperlib.snprintf(err_buf_nl, 256, "nonlocal 变量数量超过上限 MAX_GLOBAL_NAMES=%d,请减少 nonlocal 声明或增大 MAX_GLOBAL_NAMES", HT.MAX_GLOBAL_NAMES)
VLogger.error(err_buf_nl, "ScopeDecl")
else:
VLogger.error("nonlocal 变量数量超过上限,请减少 nonlocal 声明或增大 MAX_GLOBAL_NAMES", "ScopeDecl")
return
entry_addr: uint64 = uint64(trans._nonlocal_names) + trans._nonlocal_name_count * 8
slot_ptr: str | t.CPtr = (str | t.CPtr)(entry_addr)
slot_ptr[0] = name
# 存储类型信息(用 str|t.CPtr = i8** 二级指针,与 _nonlocal_names 一致)
if trans._nonlocal_types is not None:
ty_addr2: uint64 = uint64(trans._nonlocal_types) + trans._nonlocal_name_count * 8
ty_slot2: str | t.CPtr = (str | t.CPtr)(ty_addr2)
# var_ty 为 None 时直接报错终止(fail-fast),不再默认 i32
stored_ty: llvmlite.LLVMType* = var_ty
if stored_ty is None:
self.Gen.TypeGenErr.fatal_error(None, f"nonlocal 变量 '{name}' 类型缺失,无法注册", "TypeError")
return
ty_slot2[0] = stored_ty
trans._nonlocal_name_count += 1
trans._closure_env_count += 1
def get_nonlocal_type(self, trans: HT.Translator*,
name: str) -> llvmlite.LLVMType*:
"""获取 nonlocal 变量的 LLVM 类型
Returns: 找到的类型指针,找不到返回 None
"""
if trans is None or name is None or trans._nonlocal_types is None:
return None
idx: int = self.get_nonlocal_index(trans, name)
if idx < 0:
return None
ty_addr: uint64 = uint64(trans._nonlocal_types) + idx * 8
ty_slot: str | t.CPtr = (str | t.CPtr)(ty_addr)
return (llvmlite.LLVMType*)(ty_slot[0])
def is_nonlocal_name(self, trans: HT.Translator*, name: str) -> int:
"""检查 name 是否在当前函数的 nonlocal 集合中"""
if trans is None or name is None or trans._nonlocal_names is None:
return 0
for i in range(trans._nonlocal_name_count):
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
slot_ptr: str | t.CPtr = (str | t.CPtr)(entry_addr)
entry: str = slot_ptr[0]
if entry is not None:
if string.strcmp(entry, name) == 0:
return 1
return 0
def get_nonlocal_index(self, trans: HT.Translator*, name: str) -> int:
"""获取 nonlocal 变量在 env 中的索引(0-based),找不到返回 -1"""
if trans is None or name is None or trans._nonlocal_names is None:
return -1
for i in range(trans._nonlocal_name_count):
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
slot_ptr: str | t.CPtr = (str | t.CPtr)(entry_addr)
entry: str = slot_ptr[0]
if entry is not None:
if string.strcmp(entry, name) == 0:
return i
return -1
def clear_scope_names(self, trans: HT.Translator*):
"""清空当前函数的 global/nonlocal 名称集合(进入新函数时调用)"""
if trans is None:
return
trans._global_name_count = 0
trans._nonlocal_name_count = 0
trans._closure_env_count = 0