Files
Viper_VRTE/llvmlite/__module.vp
T
TermiNexus 7bd4cda5be fix(llvmlite): 打印端跳过被同模块定义取代的 external global(消除 redefinition of global)
模块内不变量:同一符号名不得同时打印 external 声明与定义,否则 llc 报 redefinition of global(SysLibTest os/__init__ 的 _mbuddy:循环 import 使 LoadDepVpiIntoModule 把本模块自身 vpi 注入自身 → 先发 external,handle_module_level_var 再发定义)。新增 _module_has_initializer_global:OUTPUT_FULL/STUB/TEXT 三模式对无初值 GV,若模块内存在同名带初值定义则跳过。只处理本模块内同名声明+定义,依赖模块的合法 external(本模块无同名定义)一律保留,不做跨模块去重(与 TransPyC Phase2Translator 合并期 external 去重同构)。
2026-10-03 12:17:39 +08:00

991 lines
44 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 t, c
from stdint import *
import string
import stdio
import stdlib
import viperlib
import memhub
from linkedlist import GSList, GSListNode
from .__types import LLVMType, TypePrint, Array, PrintStructDefinition, get_struct_name, Struct, ParamNode
from .__function import Function, FunctionPrint, _ll_name_needs_quote, Param, param_get_next, param_get_ty
# ============================================================
# GlobalVariable: LLVM IR 全局变量
#
# 持有名称 + 类型 + 初始化值 + 常量标志 + 链接类型。
# 继承 GSListNode[GlobalVariable] 获取强类型 Next。
# ============================================================
@t.NoVTable
class GlobalVariable(GSListNode[GlobalVariable]):
Name: str # 全局变量名(如 ".str" / "my_global")
Ty: rawptr[LLVMType] # 变量类型
Initializer: rawptr # 初始化值 Value*(None = 无初始化)
IsConstant: int # 1=constant, 0=variable
Linkage: str # 链接类型("private"/"internal"/"external"/...)
Section: str # 段名(None = 默认)
UnnamedAddr: int # 1=unnamed_addr, 2=local_unnamed_addr, 0=none
Align: int # 对齐字节数(0=默认对齐,如 @c.Attribute(t.attr.aligned(16)))
# ============================================================
# NamedTypeNode: 命名结构体类型链表节点
#
# 持有 LLVMType* 指针(必须是命名 Struct 类型)。
# 继承 GSListNode[NamedTypeNode] 获取强类型 Next。
# ============================================================
@t.NoVTable
class NamedTypeNode(GSListNode[NamedTypeNode]):
Ty: rawptr[LLVMType] # 命名结构体类型指针
IsPacked: int # 1=packed 结构体(输出 <{...}>,无字段填充)
# ============================================================
# DINode: DI metadata node, emitted as "!N = <Text>".
#
# Text is the node body (may contain !M references to other nodes).
# ============================================================
@t.NoVTable
class DINode(GSListNode[DINode]):
Text: str # node body, e.g. "!DIFile(filename: \"a.vp\", directory: \"/x\")"
# ============================================================
# DINamedMeta: named metadata, emitted as "!<Name> = !{ <Refs> }".
#
# Refs is the brace body (e.g. "!1" / "!1, !2").
# ============================================================
@t.NoVTable
class DINamedMeta(GSListNode[DINamedMeta]):
Name: str # e.g. "llvm.dbg.cu" / "llvm.module.flags"
Refs: str # reference list text, e.g. "!1"
# ============================================================
# LLVMModule: LLVM IR 模块容器
#
# 使用嵌入式 Head/Tail/Count 字段代替 GSList 指针,
# 避免泛型 GSList 的 __sizeof__ 返回 0 和 vtable 偏移不一致问题。
# LLVMModulePrint 输出完整 .ll 文件内容。
# 注意: 类名必须为 LLVMModule 而非 Module,避免与 ast.Module 命名冲突
# ============================================================
@t.NoVTable
class LLVMModule:
Name: str # 模块名
FuncHead: rawptr[Function] # 函数链表头
FuncTail: rawptr[Function] # 函数链表尾
FuncCount: sizet # 函数计数
GlobalHead: rawptr[GlobalVariable] # 全局变量链表头
GlobalTail: rawptr[GlobalVariable] # 全局变量链表尾
GlobalCount: sizet # 全局变量计数
TargetTriple: str # 目标三元组(如 "x86_64-pc-linux-gnu")
DataLayout: str # 数据布局(如 "e-m:e-p270:32:32-...")
NamedTypeHead: rawptr[NamedTypeNode] # 命名结构体类型链表头
NamedTypeTail: rawptr[NamedTypeNode] # 命名结构体类型链表尾
NamedTypeCount: sizet # 命名结构体类型计数
DIMetaHead: rawptr[DINode] # DI metadata node list head
DIMetaTail: rawptr[DINode] # DI metadata node list tail
DIMetaCount: sizet # DI node count (= next !N id)
DINamedHead: rawptr[DINamedMeta] # named metadata list head
DINamedTail: rawptr[DINamedMeta] # named metadata list tail
DINamedCount: sizet # named metadata count
# 总计: 18 字段 * 8 = 144 字节(无 vtable)
# ============================================================
# 实例方法(对象化重构:原 module_* 顶层函数改写为方法,
# 原顶层函数保留为一行委托)
# ============================================================
# ---- 链表操作 ----
def add_function(self, func: rawptr[Function]):
"""将函数追加到模块函数链表(O(1))"""
if self is None or func is None: return
func.Next = None
if self.FuncHead is None:
self.FuncHead = func
else:
self.FuncTail.Next = func
self.FuncTail = func
self.FuncCount += 1
def add_global(self, gv: rawptr[GlobalVariable]):
"""将全局变量追加到模块全局变量链表(O(1))"""
if self is None or gv is None: return
gv.Next = None
if self.GlobalHead is None:
self.GlobalHead = gv
else:
self.GlobalTail.Next = gv
self.GlobalTail = gv
self.GlobalCount += 1
# ---- 目标属性 ----
def set_target(self, triple: str):
"""设置目标三元组"""
if self is None: return
self.TargetTriple = triple
def set_datalayout(self, layout: str):
"""设置数据布局"""
if self is None: return
self.DataLayout = layout
# ---- DI debug metadata ----
def add_di_node(self, pool: memhub.MemBuddy*, text: str) -> int:
"""Append a DI metadata node; return its !N id (0-based, unique), -1 on failure.
text is the node body incl. !M references built from earlier ids.
"""
if self is None or pool is None or text is None:
return -1
node: rawptr[DINode] = pool.alloc(DI_NODE_SIZE)
if node is None:
return -1
string.memset(node, 0, DI_NODE_SIZE)
# copy body (caller buffer may be reused)
tlen: sizet = string.strlen(text)
tcopy: str = pool.alloc(tlen + 1)
if tcopy is None:
return -1
string.strcpy(tcopy, text)
node.Text = tcopy
if self.DIMetaHead is None:
self.DIMetaHead = node
else:
self.DIMetaTail.Next = node
self.DIMetaTail = node
node_id: int = int(self.DIMetaCount)
self.DIMetaCount += 1
return node_id
def add_named_metadata(self, pool: memhub.MemBuddy*, name: str, refs: str):
"""Register named metadata "!<name> = !{ <refs> }"; merge into an existing
entry with the same name (refs is the brace body, e.g. "!3")."""
if self is None or pool is None or name is None:
return
cur: rawptr[DINamedMeta] = self.DINamedHead
cur_steps: sizet = 0
while cur is not None:
cur_steps += 1
if cur_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module di named(DINamedCount=%zu,dump 终止)\n",
self.DINamedCount)
stdlib.exit(1)
if cur.Name is not None and string.strcmp(cur.Name, name) == 0:
if refs is not None:
old_len: sizet = 0
if cur.Refs is not None:
old_len = string.strlen(cur.Refs)
merged: str = pool.alloc(old_len + string.strlen(refs) + 3)
if merged is not None:
merged[0] = '\0'
if cur.Refs is not None:
string.strcat(merged, cur.Refs)
string.strcat(merged, ", ")
string.strcat(merged, refs)
cur.Refs = merged
return
cur = cur.Next
entry: rawptr[DINamedMeta] = pool.alloc(DI_NAMED_SIZE)
if entry is None:
return
string.memset(entry, 0, DI_NAMED_SIZE)
ncopy: str = pool.alloc(string.strlen(name) + 1)
if ncopy is None:
return
string.strcpy(ncopy, name)
entry.Name = ncopy
if refs is not None:
rcopy: str = pool.alloc(string.strlen(refs) + 1)
if rcopy is None:
return
string.strcpy(rcopy, refs)
entry.Refs = rcopy
if self.DINamedHead is None:
self.DINamedHead = entry
else:
self.DINamedTail.Next = entry
self.DINamedTail = entry
self.DINamedCount += 1
# ---- 命名结构体类型管理 ----
def add_named_type(self, pool: memhub.MemBuddy*, ty: rawptr[LLVMType]):
"""将命名结构体类型添加到模块(用于输出 %"name" = type { ... } 定义行)
非命名结构体(Name 为 None 或非 Struct 类型)会被忽略。
按 Name 去重;同名时 opaque 声明可被完整定义替换(供跨模块值类型 alloca)。
"""
if self is None or pool is None or ty is None:
return
name: str = get_struct_name(ty)
if name is None:
return
# 去重检查(opaque 声明可被完整定义替换)
cur: rawptr[NamedTypeNode] = self.NamedTypeHead
cnt_dedup: int = 0
while cur is not None:
# 步数守卫:NamedTypeHead 链表成环(内存损坏/插入 bug)会导致
# 无限遍历挂起(G3 编译 LinkedNode.prepend 挂起根因)。
# 正常链表节点数远小于此上限,超限即链表损坏 → 立即报错终止。
cnt_dedup += 1
if cnt_dedup > 100000:
stdio.printf("[ANT-LOOP] NamedTypeHead 链表疑似成环, name=%s, 遍历节点=%d, 强制终止\n",
name if name is not None else "(null)", cnt_dedup)
stdlib.exit(1)
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None:
if string.strcmp(existing_name, name) == 0:
# 已有完整定义则无需替换
if _is_opaque_struct(cur.Ty) == 0:
return
# 已有 opaque,新类型也是 opaque 则不重复
if _is_opaque_struct(ty) != 0:
return
# 用完整定义替换 opaque 声明(原地替换 Ty 指针)
cur.Ty = ty
return
cur = cur.Next
# 创建新节点
node: rawptr[NamedTypeNode] = pool.alloc(NAMED_TYPE_NODE_SIZE)
if node is None:
return
string.memset(node, 0, NAMED_TYPE_NODE_SIZE)
node.Ty = ty
node.IsPacked = 0
# opaque 类型插入链表头部,确保在引用它的完整定义之前声明。
# 例如 TypeRegistry{HashTable*, ...} 中,HashTable 的 opaque 声明
# 必须在 TypeRegistry 定义之前,否则 llc 报 "use of undefined type"。
# 完整定义追加到链表尾部,保持原有顺序。
if _is_opaque_struct(ty) != 0:
node.Next = self.NamedTypeHead
if self.NamedTypeHead is None:
self.NamedTypeTail = node
self.NamedTypeHead = node
else:
node.Next = None
if self.NamedTypeHead is None:
self.NamedTypeHead = node
else:
self.NamedTypeTail.Next = node
self.NamedTypeTail = node
self.NamedTypeCount += 1
def _is_type_defined_in_module(self, name: str) -> int:
"""检查模块中是否已定义指定名称的结构体类型,返回 1=已定义 / 0=未定义"""
if self is None or name is None:
return 0
cur: rawptr[NamedTypeNode] = self.NamedTypeHead
cnt_tdm: int = 0
while cur is not None:
# 步数守卫:同上,防 NamedTypeHead 链表成环无限遍历
cnt_tdm += 1
if cnt_tdm > 100000:
stdio.printf("[TDM-LOOP] NamedTypeHead 链表疑似成环, name=%s, 强制终止\n",
name if name is not None else "(null)")
stdlib.exit(1)
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
if existing_name[0] == name[0] and string.strcmp(existing_name, name) == 0:
return 1
cur = cur.Next
return 0
def ensure_opaque_for_type(self, pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
"""为类型中的跨模块结构体引用添加 opaque 声明
当类型引用了 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时,
如果该名称未在模块中定义,则添加 opaque 声明。
"""
_scan_type_for_cross_module_refs(pool, self, ty)
# ---- 打印 ----
def PrintModule(self, buf: str, size: sizet,
pool: memhub.MemBuddy*, mode: int):
"""将模块序列化为 .ll 文本写入 buf"""
if self is None or buf is None or size == 0: return
# 头部注释(stub 和 full 模式输出,text 模式不输出)
if mode == OUTPUT_FULL or mode == OUTPUT_STUB:
_append_cstr(buf, size, "; ModuleID = '")
if self.Name is not None:
_append_cstr(buf, size, self.Name)
_append_cstr(buf, size, "'\n")
# 目标三元组
if self.TargetTriple is not None:
_append_cstr(buf, size, "target triple = \"")
_append_cstr(buf, size, self.TargetTriple)
_append_cstr(buf, size, "\"\n")
# 数据布局
if self.DataLayout is not None:
_append_cstr(buf, size, "target datalayout = \"")
_append_cstr(buf, size, self.DataLayout)
_append_cstr(buf, size, "\"\n")
# 空行
_append_cstr(buf, size, "\n")
# 命名结构体类型定义(stub 和 full 模式输出,text 模式不输出)
if mode == OUTPUT_FULL or mode == OUTPUT_STUB:
# 扫描所有函数签名,为跨模块结构体引用添加 opaque 定义
# 确保 stub.ll 包含被 declare/define 引用的跨模块类型定义
# 修复:alloc_buf 返回 viperio.Buf,但 Buf 类型未在 NamedTypeHead 中注册
func_scan: rawptr[Function] = self.FuncHead
func_scan_steps: sizet = 0
while func_scan is not None:
func_scan_steps += 1
if func_scan_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module funcs(FuncCount=%zu,dump 终止)\n",
self.FuncCount)
stdlib.exit(1)
if func_scan.RetTy is not None:
self.ensure_opaque_for_type(pool, func_scan.RetTy)
if func_scan.Params is not None:
params_scan: rawptr[GSList[Param]] = func_scan.Params
param_scan: rawptr[Param] = params_scan.Head
param_scan_steps: sizet = 0
while param_scan is not None:
param_scan_steps += 1
if param_scan_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表异常: func params(函数名=%s,Params.Count=%zu,param=%llu next=%llu,遍历超 %d 步,终止)\n",
func_scan.Name, params_scan.Count,
t.CUInt64T(param_scan), t.CUInt64T(param_scan.Next), IR_DUMP_CHAIN_LIMIT)
stdlib.exit(1)
# 用访问器(__function.vp 完整类型内)绕开 stub 字段访问
# 陷阱:跨模块 struct 字段访问可能生成错误 GEP/静默跳过,
# 导致 param_scan 永不更新死循环。
if param_get_ty(param_scan) is not None:
self.ensure_opaque_for_type(pool, param_get_ty(param_scan))
param_scan = param_get_next(param_scan)
func_scan = func_scan.Next
nt: rawptr[NamedTypeNode] = self.NamedTypeHead
# 结构体定义缓冲区:48 字段 × ~50 字节/字段类型名 ≈ 2400 字节,4096 足够
# 在循环外分配一次并复用,减少 pool 碎片
ty_buf: str = pool.alloc(4096)
nt_steps: sizet = 0
while nt is not None:
nt_steps += 1
if nt_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module named types(NamedTypeCount=%zu,dump 终止)\n",
self.NamedTypeCount)
stdlib.exit(1)
if nt.Ty is not None:
if ty_buf is not None:
ty_buf[0] = '\0'
PrintStructDefinition(ty_buf, 4096, nt.Ty, pool, nt.IsPacked)
_append_cstr(buf, size, ty_buf)
_append_cstr(buf, size, "\n")
nt = nt.Next
if self.NamedTypeHead is not None:
_append_cstr(buf, size, "\n")
# 全局变量
gv: rawptr[GlobalVariable] = self.GlobalHead
gv_steps: sizet = 0
if mode == OUTPUT_STUB:
# stub 模式:输出 external global(无初值)
while gv is not None:
gv_steps += 1
if gv_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module globals stub(GlobalCount=%zu,dump 终止)\n",
self.GlobalCount)
stdlib.exit(1)
# 模块内不变量:同名 external 声明若已被同名定义取代则跳过,
# 避免 stub 输出出现同一符号两行(见 _module_has_initializer_global)。
if gv.Initializer is None and _module_has_initializer_global(self, gv.Name) != 0:
gv = gv.Next
continue
_print_global_stub(buf, size, gv, pool)
_append_cstr(buf, size, "\n")
gv = gv.Next
else:
# full 和 text 模式:输出完整全局变量
# text 模式跳过无初值的全局变量(纯声明)
gv_idx: int = 0
while gv is not None:
gv_steps += 1
if gv_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module globals(GlobalCount=%zu,dump 终止)\n",
self.GlobalCount)
stdlib.exit(1)
# text 模式跳过无初值的全局变量(纯声明);
# full 模式亦跳过"已被同名定义取代"的纯声明(见
# _module_has_initializer_global)——否则单模块 IR 同名声明+定义
# 并存,llc 报 "redefinition of global"。
if gv.Initializer is None:
if mode == OUTPUT_TEXT or _module_has_initializer_global(self, gv.Name) != 0:
gv = gv.Next
gv_idx += 1
continue
_print_global(buf, size, gv, pool)
_append_cstr(buf, size, "\n")
gv = gv.Next
gv_idx += 1
if self.GlobalHead is not None and (mode == OUTPUT_FULL or mode == OUTPUT_STUB):
_append_cstr(buf, size, "\n")
elif self.GlobalHead is not None and mode == OUTPUT_TEXT:
# text 模式下如果输出了全局变量,也加空行
had_global: int = 0
gv2: rawptr[GlobalVariable] = self.GlobalHead
gv2_steps: sizet = 0
while gv2 is not None:
gv2_steps += 1
if gv2_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module globals text(GlobalCount=%zu,dump 终止)\n",
self.GlobalCount)
stdlib.exit(1)
if gv2.Initializer is not None:
had_global = 1
break
gv2 = gv2.Next
if had_global != 0:
_append_cstr(buf, size, "\n")
# 遍历函数
func: rawptr[Function] = self.FuncHead
func_idx: int = 0
func_steps: sizet = 0
while func is not None:
func_steps += 1
if func_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module funcs print(FuncCount=%zu,dump 终止)\n",
self.FuncCount)
stdlib.exit(1)
if mode == OUTPUT_STUB:
# stub 模式:仅输出 declare(IsDeclared==1)
# FunctionPrint 的 declare 分支已以 "\n" 收尾(L768),这里不再
# 补换行,否则每个 declare 之间多出一个空行(美观/文件瘦身)。
if func.IsDeclared == 1:
FunctionPrint(buf, size, func, pool)
elif mode == OUTPUT_TEXT:
# text 模式:仅输出 define(IsDeclared==0)
if func.IsDeclared == 0:
FunctionPrint(buf, size, func, pool)
_append_cstr(buf, size, "\n")
else:
# full 模式:输出所有函数
FunctionPrint(buf, size, func, pool)
_append_cstr(buf, size, "\n")
func = func.Next
func_idx += 1
# DI metadata, text/full only: never in stub, since StubMerger concatenates
# stub + text into one .ll and stub metadata would duplicate !N / llvm.dbg.cu.
# Node defs precede named metadata: ids ascend from 0, so every !M ref is
# backward (no forward reference needed).
if (mode == OUTPUT_TEXT or mode == OUTPUT_FULL) and self.DIMetaHead is not None:
node: rawptr[DINode] = self.DIMetaHead
nid: int = 0
id_buf: str = pool.alloc(32)
node_steps: sizet = 0
while node is not None:
node_steps += 1
if node_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module di meta(DIMetaCount=%zu,dump 终止)\n",
self.DIMetaCount)
stdlib.exit(1)
if node.Text is not None and id_buf is not None:
viperlib.snprintf(id_buf, 32, "!%d = ", nid)
_append_cstr(buf, size, id_buf)
_append_cstr(buf, size, node.Text)
_append_cstr(buf, size, "\n")
node = node.Next
nid += 1
# named metadata "!<name> = !{ <refs> }" (llvm.dbg.cu / llvm.module.flags ...)
if (mode == OUTPUT_TEXT or mode == OUTPUT_FULL) and self.DINamedHead is not None:
if self.DIMetaHead is not None:
_append_cstr(buf, size, "\n")
nm: rawptr[DINamedMeta] = self.DINamedHead
nm_steps: sizet = 0
while nm is not None:
nm_steps += 1
if nm_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: module di named(DINamedCount=%zu,dump 终止)\n",
self.DINamedCount)
stdlib.exit(1)
_append_cstr(buf, size, "!")
if nm.Name is not None:
_append_cstr(buf, size, nm.Name)
_append_cstr(buf, size, " = !{ ")
if nm.Refs is not None:
_append_cstr(buf, size, nm.Refs)
_append_cstr(buf, size, " }\n")
nm = nm.Next
# ============================================================
# 工厂函数
# ============================================================
def new_module(pool: memhub.MemBuddy*, name: str) -> rawptr[LLVMModule]:
"""创建一个模块"""
# 硬编码 144 字节:18 字段 * 8 字节(无 vtable,@t.NoVTable)
ptr: rawptr[LLVMModule] = pool.alloc(144)
if ptr is None: return None
string.memset(ptr, 0, 144)
ptr.Name = name
ptr.FuncHead = None
ptr.FuncTail = None
ptr.FuncCount = 0
ptr.GlobalHead = None
ptr.GlobalTail = None
ptr.GlobalCount = 0
ptr.TargetTriple = None
ptr.DataLayout = None
ptr.NamedTypeHead = None
ptr.NamedTypeTail = None
ptr.NamedTypeCount = 0
ptr.DIMetaHead = None
ptr.DIMetaTail = None
ptr.DIMetaCount = 0
ptr.DINamedHead = None
ptr.DINamedTail = None
ptr.DINamedCount = 0
return ptr
# GlobalVariable 硬编码大小: Next(8) + Name(8) + Ty(8) + Initializer(8) + IsConstant(4) + Linkage(8) + Section(8) + UnnamedAddr(4) + Align(4) = 72 字节
GV_SIZE: t.CDefine = 72
# NamedTypeNode 硬编码大小: Next(8) + Ty(8) + IsPacked(8) = 24 字节
NAMED_TYPE_NODE_SIZE: t.CDefine = 24
# DINode hardcoded size: Next(8) + Text(8) = 16 bytes
DI_NODE_SIZE: t.CDefine = 16
# DINamedMeta hardcoded size: Next(8) + Name(8) + Refs(8) = 24 bytes
DI_NAMED_SIZE: t.CDefine = 24
def new_global_variable(pool: memhub.MemBuddy*,
name: str, ty: rawptr[LLVMType]) -> rawptr[GlobalVariable]:
"""创建一个全局变量"""
ptr: rawptr[GlobalVariable] = pool.alloc(GV_SIZE)
if ptr is None: return None
string.memset(ptr, 0, GV_SIZE)
ptr.Name = name
ptr.Ty = ty
ptr.Initializer = None
ptr.IsConstant = 0
ptr.Linkage = "external"
ptr.Section = None
ptr.UnnamedAddr = 0
ptr.Align = 0
return ptr
# ============================================================
# 链表操作(嵌入式 Head/Tail/Count,O(1) 追加)
# ============================================================
def module_add_function(mod: rawptr[LLVMModule], func: rawptr[Function]):
mod.add_function(func)
def module_get_func_head(mod: rawptr[LLVMModule]) -> rawptr[Function]:
"""获取模块函数链表头(跨模块访问器,绕 stub 字段访问陷阱)"""
if mod is None:
return None
return mod.FuncHead
def module_add_global(mod: rawptr[LLVMModule], gv: rawptr[GlobalVariable]):
mod.add_global(gv)
def module_set_target(mod: rawptr[LLVMModule], triple: str):
mod.set_target(triple)
def module_set_datalayout(mod: rawptr[LLVMModule], layout: str):
mod.set_datalayout(layout)
# ============================================================
# DI debug metadata
# ============================================================
def module_add_di_node(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
text: str) -> int:
"""Append a DI metadata node; return its !N id (-1 on failure)"""
return mod.add_di_node(pool, text)
def module_add_named_metadata(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
name: str, refs: str):
"""Register named metadata "!<name> = !{ <refs> }" """
mod.add_named_metadata(pool, name, refs)
# ============================================================
# 命名结构体类型管理
# ============================================================
# ============================================================
# _is_opaque_struct - 检查类型是否是 opaque 结构体
#
# opaque 结构体特征: Struct 类型,fcount == -1(哨兵标记,跨模块前向
# 声明,仅名称无字段定义)。通过 Struct(pool, None, -1, name) 创建,
# IR 输出为 %"name" = type opaque。注意区分空结构体(fcount==0,
# fields==None,无字段类)——它是合法完整定义(= type {},可 alloca)。
# 用于在 module_add_named_type 中区分 opaque 声明和完整定义,
# 确保 opaque 声明插入链表头部(在使用之前声明)。
#
# Returns: 1=是 opaque / 0=否
# ============================================================
def _is_opaque_struct(ty: rawptr[LLVMType]) -> int:
"""检查类型是否是 opaque 结构体
opaque 是跨模块前向声明(仅名称无字段定义),由
_scan_type_for_cross_module_refs 创建,fcount=-1 哨兵标记。
空结构体(fcount==0, fields==None,无字段类)是合法完整定义
(LLVM {} 类型,可 alloca),必须与 opaque 区分——此前混淆导致
空类结构体被输出为 "= type opaque",alloca 报 Cannot allocate
unsized type。
"""
if ty is None:
return 0
match ty:
case LLVMType.Struct(fields, fcount, name):
# opaque: fcount == -1(哨兵)
if fcount < 0:
return 1
return 0
case _:
return 0
def module_add_named_type(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
mod.add_named_type(pool, ty)
def module_set_named_type_packed(mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
ty: rawptr[LLVMType], is_packed: int):
"""按名称设置命名结构体的 packed 标记(输出 %"name" = type <{...}>)。
编译器解析 @c.Attribute(t.attr.packed) 后调用,使 llc 按 packed 布局
(无字段填充)计算字段偏移/GEP。ty 为命名 Struct 类型(按 get_struct_name
定位已注册节点);未找到则静默跳过(opaque/跨模块 stub 场景无布局需求)。
"""
if mod is None or ty is None:
return
name: str = get_struct_name(ty)
if name is None:
return
cur: rawptr[NamedTypeNode] = mod.NamedTypeHead
cnt_p: int = 0
while cur is not None:
cnt_p += 1
if cnt_p > 100000:
return
if cur.Ty is not None:
existing_name: str = get_struct_name(cur.Ty)
if existing_name is not None and string.strcmp(existing_name, name) == 0:
cur.IsPacked = is_packed
return
cur = cur.Next
# ============================================================
# module_ensure_opaque_for_type - 为类型中的跨模块结构体引用添加 opaque 声明
#
# 递归扫描 LLVMType 树,当遇到 Ptr(Struct(name)) 且 name 包含 '.'(SHA1 限定名)时,
# 如果该名称未在模块中定义,则添加 opaque 声明(%"name" = type opaque)。
# 这解决了跨模块结构体引用导致的 llc "use of undefined type" 错误。
# ============================================================
def _is_type_defined_in_module(mod: rawptr[LLVMModule], name: str) -> int:
return mod._is_type_defined_in_module(name)
def _scan_type_for_cross_module_refs(pool: memhub.MemBuddy*,
mod: rawptr[LLVMModule],
ty: rawptr[LLVMType],
depth: int = 0):
"""递归扫描类型树,为跨模块结构体引用添加 opaque 声明"""
if ty is None or mod is None or pool is None:
return
# 深度守卫:自引用结构体(链表/树节点,如 Node{...; Ptr(Node) next})
# 会无限递归栈溢出,与其他递归(TypePrint/DeepCopyType)一致设上限
if depth >= 100:
return
match ty:
case LLVMType.Ptr(pointee):
if pointee is not None:
match pointee:
case LLVMType.Struct(sfields, sfcount, sname):
if sname is not None:
# 检查是否为跨模块引用(名称包含 '.')
if string.strchr(sname, 46) is not None:
# 检查是否已在模块中定义
if _is_type_defined_in_module(mod, sname) == 0:
# 创建 opaque 类型并添加到模块
# fcount=-1 哨兵标记 opaque(区别于空结构体 {},
# 后者 fcount==0 是合法完整定义,可 alloca)
opaque_ty: rawptr[LLVMType] = Struct(pool, None, -1, sname)
if opaque_ty is not None:
module_add_named_type(mod, pool, opaque_ty)
# 递归扫描结构体字段中的跨模块引用
# 例如 TypeRegistry{HashTable*, MemManager*} 中的 HashTable 引用
if sfields is not None:
fld: rawptr[ParamNode] = sfields
fld_steps: sizet = 0
while fld is not None:
fld_steps += 1
if fld_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: struct fields(sname=%s,dump 终止)\n",
sname)
stdlib.exit(1)
if fld.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld.Ty, depth + 1)
fld = fld.Next
case _:
_scan_type_for_cross_module_refs(pool, mod, pointee, depth + 1)
case LLVMType.Struct(sfields2, sfcount2, sname2):
# 顶层结构体(非指针):递归扫描字段
if sfields2 is not None:
fld2: rawptr[ParamNode] = sfields2
fld2_steps: sizet = 0
while fld2 is not None:
fld2_steps += 1
if fld2_steps > IR_DUMP_CHAIN_LIMIT:
stdio.printf("[IR-DUMP] 链表成环: struct fields2(sname=%s,dump 终止)\n",
sname2)
stdlib.exit(1)
if fld2.Ty is not None:
_scan_type_for_cross_module_refs(pool, mod, fld2.Ty, depth + 1)
fld2 = fld2.Next
case LLVMType.Array(elem_ty, acount):
if elem_ty is not None:
_scan_type_for_cross_module_refs(pool, mod, elem_ty, depth + 1)
case _:
pass
def module_ensure_opaque_for_type(mod: rawptr[LLVMModule],
pool: memhub.MemBuddy*,
ty: rawptr[LLVMType]):
mod.ensure_opaque_for_type(pool, ty)
# ============================================================
# 全局变量属性设置
# ============================================================
def global_set_constant(gv: rawptr[GlobalVariable], is_const: int):
"""设置全局变量是否为常量"""
if gv is None: return
gv.IsConstant = is_const
def global_set_linkage(gv: rawptr[GlobalVariable], linkage: str):
"""设置链接类型 (private/internal/external/... )"""
if gv is None: return
gv.Linkage = linkage
def global_set_unnamed_addr(gv: rawptr[GlobalVariable], level: int):
"""设置 unnamed_addr 属性 (0=none, 1=unnamed_addr, 2=local_unnamed_addr)"""
if gv is None: return
gv.UnnamedAddr = level
def global_set_section(gv: rawptr[GlobalVariable], section: str):
"""设置段名"""
if gv is None: return
gv.Section = section
# ============================================================
# 输出模式常量
# ============================================================
OUTPUT_FULL: t.CDefine = 0 # 完整 IR(stub + text)
OUTPUT_STUB: t.CDefine = 1 # 仅声明(header + 类型定义 + declare + external global)
OUTPUT_TEXT: t.CDefine = 2 # 仅代码(define + global 带初值)
# dump 哨兵:IR 链表遍历步数上限(成环检测,fail-fast)。
# 任何一条链(函数/全局/命名类型/结构体字段)的 Next 被越界写破坏成环时,
# 遍历会无限循环导致 tpv.exe 挂起(builtins.vp dump 卡死根因排查)。
# 超过上限即打印链表名并 exit(1),把"卡死"变成"确定性报错定位"
# (对齐 memhub.self_check 的 max_steps 哲学)。
IR_DUMP_CHAIN_LIMIT: t.CDefine = 100000
# ============================================================
# LLVMModulePrint: 将模块序列化为 .ll 文本
#
# mode: OUTPUT_FULL=完整, OUTPUT_STUB=仅声明, OUTPUT_TEXT=仅代码
# ============================================================
def LLVMModulePrint(buf: str, size: sizet,
mod: rawptr[LLVMModule], pool: memhub.MemBuddy*,
mode: int):
mod.PrintModule(buf, size, pool, mode)
# ============================================================
# 全局变量打印
# ============================================================
# _module_has_initializer_global - 模块内是否存在同名"定义"全局(带初值)
#
# 模块内不变量:同一符号名不得同时出现 external 声明与定义,否则 llc 报
# "redefinition of global '@<sha1>.<name>'"。
# 触发场景:循环 import(如 os/__init__ 同时 import os._win32/os._posix,二者又
# 导入包 os ⇒ imported_modules 含 "os" 自身)使**本模块自己的 .vpi** 被当作依赖
# 注入;VpiReader._inject_global_variable_declare 先在本模块加一个同名 external
# 声明全局,随后 handle_module_level_var 再发射同名定义 ⇒ 单模块 IR 同名冲突。
# 打印端按"同名 + 有初值"判定并跳过纯声明,保证单模块 IR 自足合法(对齐旧编译器
# TransPyC/Phase2Translator 合并期的 external 去重:同名的 external 声明被定义取代)。
# 只跳过"无初值且有同名初值定义"的声明;依赖模块的合法 external(本模块无同名
# 定义)一律保留,不做跨模块去重。
# ============================================================
def _module_has_initializer_global(mod: rawptr[LLVMModule], name: str) -> int:
if mod is None or name is None:
return 0
cur: rawptr[GlobalVariable] = mod.GlobalHead
steps: sizet = 0
while cur is not None:
steps += 1
if steps > IR_DUMP_CHAIN_LIMIT:
break
if cur.Name is not None and cur.Initializer is not None:
if string.strcmp(cur.Name, name) == 0:
return 1
cur = cur.Next
return 0
def _print_global(buf: str, size: sizet,
gv: rawptr[GlobalVariable], pool: memhub.MemBuddy*):
"""打印单个全局变量: @<name> = [linkage] [unnamed_addr] [constant] <ty> <init>"""
if gv is None: return
_append_cstr(buf, size, "@")
if gv.Name is not None:
if _ll_name_needs_quote(gv.Name) != 0:
_append_cstr(buf, size, "\"")
_append_cstr(buf, size, gv.Name)
_append_cstr(buf, size, "\"")
else:
_append_cstr(buf, size, gv.Name)
_append_cstr(buf, size, " = ")
# 链接类型(有初始值时不输出 external,因为 external + 初始值是非法 IR)
if gv.Linkage is not None:
if gv.Initializer is not None and string.strcmp(gv.Linkage, "external") == 0:
pass
else:
_append_cstr(buf, size, gv.Linkage)
_append_cstr(buf, size, " ")
# unnamed_addr
if gv.UnnamedAddr == 1:
_append_cstr(buf, size, "unnamed_addr ")
elif gv.UnnamedAddr == 2:
_append_cstr(buf, size, "local_unnamed_addr ")
# constant / global
if gv.IsConstant == 1:
_append_cstr(buf, size, "constant ")
else:
_append_cstr(buf, size, "global ")
# 类型
ty_buf: str = pool.alloc(512)
if ty_buf is not None:
ty_buf[0] = '\0'
TypePrint(ty_buf, 512, gv.Ty, pool)
_append_cstr(buf, size, ty_buf)
# 初始化值
if gv.Initializer is not None:
_append_cstr(buf, size, " ")
_append_cstr(buf, size, gv.Initializer)
# 段名
if gv.Section is not None:
_append_cstr(buf, size, ", section \"")
_append_cstr(buf, size, gv.Section)
_append_cstr(buf, size, "\"")
# 对齐(@c.Attribute(t.attr.aligned(N)) 类的全局实例,如 fpu_context 16 对齐)
if gv.Align > 0:
align_buf: str = pool.alloc(32)
if align_buf is not None:
viperlib.snprintf(align_buf, 32, ", align %d", gv.Align)
_append_cstr(buf, size, align_buf)
# ============================================================
# _print_global_stub - 打印全局变量的声明形式(external global,无初值)
# ============================================================
def _print_global_stub(buf: str, size: sizet,
gv: rawptr[GlobalVariable], pool: memhub.MemBuddy*):
"""打印全局变量声明: @<name> = external global <ty>"""
if gv is None: return
_append_cstr(buf, size, "@")
if gv.Name is not None:
if _ll_name_needs_quote(gv.Name) != 0:
_append_cstr(buf, size, "\"")
_append_cstr(buf, size, gv.Name)
_append_cstr(buf, size, "\"")
else:
_append_cstr(buf, size, gv.Name)
_append_cstr(buf, size, " = external global ")
# 类型
ty_buf: str = pool.alloc(512)
if ty_buf is not None:
ty_buf[0] = '\0'
TypePrint(ty_buf, 512, gv.Ty, pool)
_append_cstr(buf, size, ty_buf)
# ============================================================
# 内部辅助
# ============================================================
# _append_cstr 写入位置缓存(性能优化:同一缓冲连续追加时 O(1) 定位,
# 避免每次 strlen(dst) 从头扫描导致的 O(n²) —— IR 序列化热点)。
# 缓冲切换(dst 不同)或缓冲被重置(dst[0]=0 且 pos>0)时自动回退 strlen。
_g_last_dst: t.CUInt64T = 0
_g_last_pos: sizet = 0
def _append_cstr(dst: str, dst_size: sizet, src: str):
"""将 C 字符串 src 追加到 dst 末尾"""
global _g_last_dst, _g_last_pos
if dst is None or src is None: return
dlen: sizet = 0
if _g_last_dst == t.CUInt64T(dst):
dlen = _g_last_pos
# 缓冲被重置(调用方 buf[0]=0 后复用同一地址)→ 回到头部
if dlen > 0 and dst[0] == '\0':
dlen = 0
# 缓存一致性校验:缓存结尾必须仍是当前文本末端(末端 NUL 且前一字符非 NUL)。
# 多个模块(__module/__function 各自一份 _append_cstr 缓存)轮流写同一 buf、
# 或 pool 复用地址残留旧内容时,缓存 pos 会过期 → 追加落到首个 NUL 之后,
# 内容"看不见"(LLvmLiteTest Test5/9 模块函数链为空根因)。校验失败回退
# strlen 重定位;正常热路径不受影响(仍 O(1))。
if dlen == 0:
if dst[0] != '\0':
dlen = string.strlen(dst)
elif dst[dlen] != '\0' or dst[dlen - 1] == '\0':
dlen = string.strlen(dst)
else:
dlen = string.strlen(dst)
_g_last_dst = t.CUInt64T(dst)
# 无符号下溢防护:dlen 超过缓冲大小时(缓冲已被破坏/悬空)直接返回,
# 否则 remain = dst_size - dlen 下溢成巨值会把 dst[dlen+i] 写穿到缓冲外
if dlen >= dst_size:
return
slen: sizet = string.strlen(src)
remain: sizet = dst_size - dlen
if remain <= 0: return
i: sizet = 0
while i < slen and i + 1 < remain:
dst[dlen + i] = src[i]
i += 1
dst[dlen + i] = '\0'
_g_last_pos = dlen + i