自举实验失败,C1 编译 Test 成功,暂未达到自举收敛点

This commit is contained in:
2026-07-30 15:44:32 +08:00
parent 68481a5a7f
commit 32af3f93fa
22 changed files with 2158 additions and 442 deletions

View File

@@ -176,6 +176,77 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
return 0
# ============================================================
# _init_global_array_from_list — 从列表字面量初始化全局数组
#
# 为每个元素生成 GEP + store 指令,将值存入全局数组的对应位置。
# 适用于模块级 t.CArray[elem_ty, count] = [v0, v1, ...] 的初始化。
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池
# mod: LLVMModule
# array_alloca: 全局数组变量的 Value 引用(类型为 [N x elem_ty]*
# list_node: ast.List 节点
# trans: Translator 对象
#
# Returns:
# 0 成功1 失败
# ============================================================
def _init_global_array_from_list(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
array_alloca: llvmlite.Value | t.CPtr,
list_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> int:
"""从列表字面量初始化全局数组,为每个元素生成 GEP + store"""
if builder is None or array_alloca is None or list_node is None:
return 1
# 获取数组类型Pointee of [N x elem_ty]*
arr_ty: llvmlite.LLVMType | t.CPtr = None
if array_alloca.Ty is not None:
arr_ty = array_alloca.Ty.Pointee
if arr_ty is None:
return 1
# 匹配数组类型获取元素类型
elem_ty: llvmlite.LLVMType | t.CPtr = None
match arr_ty:
case llvmlite.LLVMType.Array(et, _):
elem_ty = et
if elem_ty is None:
return 1
# 获取列表元素
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
if lst is None or lst.elts is None:
return 1
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
elts_count: t.CSizeT = elts.__len__()
# 为每个元素生成 GEP + store
i: t.CSizeT = 0
while i < elts_count:
elem_node: ast.AST | t.CPtr = elts.get(i)
elem_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, elem_node, None, 0, trans)
if elem_val is not None:
idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i)
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, array_alloca, idx_val)
if elem_ptr is not None:
# 类型转换(如 i32 → i8 截断)
store_val: llvmlite.Value | t.CPtr = elem_val
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = HandlesExpr.coerce_to_type(
builder, elem_val, elem_ptr.Ty.Pointee)
llvmlite.build_store(builder, store_val, elem_ptr)
i += 1
return 0
@t.NoVTable
class AnnAssignHandle(HandlesBase.Mixin):
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
@@ -221,6 +292,11 @@ class AnnAssignHandle(HandlesBase.Mixin):
if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
return 0
# 模块级全局变量已由 handle_module_level_var 注册到模块作用域,
# 不需要创建局部 alloca否则会导致局部变量遮蔽全局变量
if HandlesVar.lookup_module_var(self.Trans.SymTab, nm.id) is not None:
return 0
# 检查是否已存在
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
self.Trans.SymTab, nm.id)
@@ -350,6 +426,29 @@ class AnnAssignHandle(HandlesBase.Mixin):
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
return 0
# 模块级全局变量(由 handle_module_level_var 注册到模块作用域)
# 非函数内 global 声明,但变量已在模块作用域中(如模块级 t.CArray 初始化)
mod_glob: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
self.Trans.SymTab, nm.id)
if mod_glob is not None:
if aa.value is not None:
# 列表字面量 → 逐元素 GEP + store 初始化数组
if aa.value.kind() == ast.ASTKind.List:
_init_global_array_from_list(
builder, pool, mod, mod_glob, aa.value, self.Trans)
else:
rhs_val_mg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, aa.value,
None, 0, self.Trans)
if rhs_val_mg is not None:
target_ty_mg: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
target_ty_mg = mod_glob.Ty.Pointee
if target_ty_mg is not None:
rhs_val_mg = HandlesExpr.coerce_to_type(builder, rhs_val_mg, target_ty_mg)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
return 0
# 普通局部变量
# 创建 alloca
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym(

View File

@@ -15,6 +15,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.StubMerger as StubMerger
# ============================================================
@@ -62,23 +63,70 @@ class AssignHandle(HandlesBase.Mixin):
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, asgn.value, None, 0, self.Trans)
if rhs_val is None:
# 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None and asgn.value is not None:
sha1: str = self.Trans.ModuleSha1
val_kind: int = asgn.value.kind()
val_line: t.CInt = asgn.value.lineno
# 通过 sha1 查找文件名(人类可读)
rel_path: str = None
if sha1 is not None:
viperlib.snprintf(fb, 1024,
"rhs_val is None [sha1=%s lineno=%d kind=%d]",
sha1, val_line, val_kind)
rel_path = StubMerger.LookupSha1RelPath(sha1)
# 根据 AST 节点类型提取详情
if val_kind == ast.ASTKind.Attribute:
at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value)
attr_nm: str = at_nv.attr
target_nm: str = "?"
if at_nv.value is not None:
if at_nv.value.kind() == ast.ASTKind.Name:
tn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_nv.value)
target_nm = tn_nm.id
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d '%s.%s' 属性访问失败",
rel_path, val_line, target_nm, attr_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d '%s.%s' 属性访问失败",
sha1, val_line, target_nm, attr_nm)
elif val_kind == ast.ASTKind.Name:
nm_nv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asgn.value)
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 变量 '%s' 未找到",
rel_path, val_line, nm_nv.id)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 变量 '%s' 未找到",
sha1, val_line, nm_nv.id)
elif val_kind == ast.ASTKind.Call:
cl_nv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(asgn.value)
func_nm: str = "?"
if cl_nv.func is not None:
if cl_nv.func.kind() == ast.ASTKind.Name:
fn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cl_nv.func)
func_nm = fn_nm.id
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d 调用 '%s(...)' 返回 None",
rel_path, val_line, func_nm)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d 调用 '%s(...)' 返回 None",
sha1, val_line, func_nm)
else:
viperlib.snprintf(fb, 1024,
"rhs_val is None [lineno=%d kind=%d]",
val_line, val_kind)
if rel_path is not None:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: %s:%d [kind=%d]",
rel_path, val_line, val_kind)
else:
viperlib.snprintf(fb, 1024,
"赋值右侧为 None: sha1=%s:%d [kind=%d]",
sha1, val_line, val_kind)
VLogger.error(fb, "ASGN")
else:
VLogger.error("rhs_val is None", "ASGN")
VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
return 0
new_vars: int = 0

View File

@@ -1700,15 +1700,14 @@ _generic_class_nodes: list[ast.ClassDef | t.CPtr] | t.CPtr = None
_generic_class_sha1s: list[str] | t.CPtr = None
def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int:
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否"""
if cd is None:
return 0
tp: list[str] | t.CPtr = cd.type_params
def _is_generic_class(tp: list[str] | t.CPtr) -> int:
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否
接收 type_params 而非 cd避免 cd.type_params 属性访问
因跨模块类型推断失败返回 None。
"""
if tp is None:
return 0
if tp.__len__() == 0:
return 0
return 1
@@ -1721,29 +1720,37 @@ def _register_generic_template(pool: memhub.MemBuddy | t.CPtr,
_generic_class_names = list[str](pool, 8)
_generic_class_nodes = list[ast.ClassDef | t.CPtr](pool, 8)
_generic_class_sha1s = list[str](pool, 8)
# 检查是否已注册
cn: t.CSizeT = _generic_class_names.__len__()
# 用局部变量接收 global list触发类型推断避免 global 变量 __len__ 失败)
names_local: list[str] | t.CPtr = _generic_class_names
cn: t.CSizeT = names_local.__len__()
i: t.CSizeT
for i in range(cn):
nm: str = _generic_class_names.get(i)
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, cd.name) == 0:
return
_generic_class_names.append(cd.name)
_generic_class_nodes.append(cd)
_generic_class_sha1s.append(module_sha1)
# 诊断日志:确认泛型模板注册
fb_rg: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_rg is not None:
viperlib.snprintf(fb_rg, 1024, "注册泛型模板: %s (module=%s)", cd.name, module_sha1)
VLogger.info(fb_rg, "SPEC")
def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr:
"""查找泛型类模板,返回 ClassDef 节点或 None"""
if _generic_class_names is None or class_name is None:
return None
cn: t.CSizeT = _generic_class_names.__len__()
# 用局部变量接收 global list触发类型推断避免 global 变量 __len__ 失败)
names_local: list[str] | t.CPtr = _generic_class_names
nodes_local: list[ast.ClassDef | t.CPtr] | t.CPtr = _generic_class_nodes
cn: t.CSizeT = names_local.__len__()
i: t.CSizeT
for i in range(cn):
nm: str = _generic_class_names.get(i)
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换
node: ast.ClassDef | t.CPtr = _generic_class_nodes.get(i)
node: ast.ClassDef | t.CPtr = nodes_local.get(i)
return node
return None
@@ -1752,13 +1759,15 @@ def _find_generic_template_sha1(class_name: str) -> str:
"""查找泛型类模板的来源模块 SHA1未找到返回 None"""
if _generic_class_names is None or class_name is None:
return None
cn: t.CSizeT = _generic_class_names.__len__()
# 用局部变量接收 global list触发类型推断
names_local: list[str] | t.CPtr = _generic_class_names
sha1s_local: list[str] | t.CPtr = _generic_class_sha1s
cn: t.CSizeT = names_local.__len__()
i: t.CSizeT
for i in range(cn):
nm: str = _generic_class_names.get(i)
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换
sha1_val: str = _generic_class_sha1s.get(i)
sha1_val: str = sha1s_local.get(i)
return sha1_val
return None
@@ -2191,7 +2200,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
return _translate_renum_def(trans, cd)
# 泛型类:存储为模板,不发射 IR等实例化时特化
if _is_generic_class(cd) == 1:
# 用局部变量接收 cd.type_params触发类型推断
tp_val: list[str] | t.CPtr = cd.type_params
if _is_generic_class(tp_val) == 1:
_register_generic_template(pool, cd, trans.ModuleSha1)
return 0

View File

@@ -420,7 +420,14 @@ def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找上下文中的 pool 变量,返回 alloca 或 None"""
"""查找上下文中的 pool 变量,返回 alloca 或 None
查找顺序: pool → _mbuddy → mbuddy → mb
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
@_mbuddy 由入口模块(如 Phase2.py定义并初始化其他模块以 external 引用。
"""
if trans is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
@@ -435,7 +442,32 @@ def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
return None
# 兜底: 创建外部全局 @_mbuddy 引用i8** 类型)
# 仅用于函数内动态 list 字面量,@_mbuddy 由入口模块定义并初始化
pool: memhub.MemBuddy | t.CPtr = trans.Pool
if pool is None or trans.Module is None or trans.SymTab is None:
return None
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(trans.SymTab, "_mbuddy")
if existing is not None:
return existing
# 创建 i8* 类型t.CVoid | t.CPtr = i8*
i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
# 创建外部全局 @_mbuddyexternal linkage无初始值
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb)
if gv_ext is not None:
gv_ext.Linkage = "external"
llvmlite.module_add_global(trans.Module, gv_ext)
# 创建 Value 引用(@_mbuddy, 类型为 i8**
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
ref_name_fb: t.CChar | t.CPtr = pool.alloc(16)
if ref_name_fb is not None:
string.strcpy(ref_name_fb, "@_mbuddy")
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
# 注册到模块作用域,避免重复创建
HandlesVar.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
return gv_ref_fb
# ============================================================
@@ -781,15 +813,32 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
if comparators is None or comparators.__len__() == 0:
return None
rhs_node: ast.AST | t.CPtr = comparators.get(0)
rhs: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, comparators.get(0), None, 0, trans)
builder, pool, mod, rhs_node, None, 0, trans)
if rhs is None:
return None
# === None 比较优化: x == None / x != None 走身份比较icmp eq/ne null===
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
lhs_node: ast.AST | t.CPtr = cmp.left
cmp_with_none: int = 0
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node)
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
lhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(lhs_node)
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
cmp_with_none = 1
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter用 alloca 指针尝试重载
lhs_node: ast.AST | t.CPtr = cmp.left
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
# None 比较跳过重载(走身份比较)
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
@@ -808,10 +857,12 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
# None 比较跳过重载(走身份比较)
if cmp_with_none == 0:
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt:
@@ -875,8 +926,12 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
return None
if uo.op == ast.OpKind.USub:
# -x = 0 - x
# 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配)
# -x
# 浮点类型用 fneg直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
operand_fbits: int = get_llvm_float_bits(operand.Ty)
if operand_fbits > 0:
return llvmlite.build_fneg(builder, operand)
# 整数类型:-x = 0 - x根据位宽选择零值常量避免 i32 与 i64 类型不匹配)
operand_bits: int = get_llvm_type_bits(operand.Ty)
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
if operand_bits == 64:
@@ -1115,29 +1170,73 @@ def _build_source_path_from_sha1(sha1: str) -> str:
# ============================================================
def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
name: str,
from_imports: str) -> int:
"""跨模块查找 CDefine 常量,返回值或 -1未找到"""
if name is None or from_imports is None:
stdio.printf("[XMCD] FAIL name=%s reason=from_imports_is_none\n", name)
from_imports: str,
imported_modules: str = None) -> int:
"""跨模块查找 CDefine 常量,返回值或 -1未找到
查找顺序:
1. from_imports 精确匹配from X import NAME
2. from_imports star 回退from X import *
3. imported_modules 遍历import X当通过 import X 导入模块时,
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
"""
if name is None:
return -1
if from_imports is None:
from_imports = ""
# 1. 从 from_imports 查找名称对应的模块名
# 先尝试精确匹配allow_star_fallback=0避免 CDefine 常量被 star import 误导
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
if mod_name_raw is None:
# 精确匹配失败:尝试 star import 回退
# CDefine 常量(如 INVALID_HANDLE_VALUE通过 from w32.win32base import * 导入,
# 不会作为精确条目出现在 from_imports 中,而是作为 *:w32.win32base 条目。
mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1)
if mod_name_raw is None:
stdio.printf("[XMCD] FAIL name=%s reason=lookup_from_import_returned_none\n", name)
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6
# 注意star fallback 可能返回错误模块(如 stdint常量实际不在其中
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
if mod_name_raw is None:
# from_imports 完全没有匹配(精确和 star 都失败)
# 直接进入 imported_modules 遍历(步骤 9.6
if imported_modules is None:
return -1
stdio.printf("[XMCD] star_fallback name=%s mod=%s\n", name, mod_name_raw)
# 打印 from_imports 用于诊断精确匹配失败原因
fi_len: t.CSizeT = string.strlen(from_imports)
stdio.printf("[XMCD] from_imports (len=%d): %s\n", fi_len, from_imports)
else:
stdio.printf("[XMCD] exact_match name=%s mod=%s\n", name, mod_name_raw)
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
# 设置 result_found=0 跳过步骤 3-9直接到 9.6
HandlesType.set_cdefine_found(0)
# 直接遍历 imported_modules
im_len: t.CSizeT = string.strlen(imported_modules)
im_ci: t.CSizeT = 0
while im_ci < im_len:
while im_ci < im_len and imported_modules[im_ci] == ' ':
im_ci += 1
if im_ci >= im_len:
break
im_start: t.CSizeT = im_ci
while im_ci < im_len and imported_modules[im_ci] != ' ':
im_ci += 1
im_end: t.CSizeT = im_ci
im_nlen: t.CSizeT = im_end - im_start
if im_nlen > 0 and im_nlen < 256:
im_buf: str = pool.alloc(im_nlen + 1)
if im_buf is not None:
mk: t.CSizeT = 0
while mk < im_nlen:
im_buf[mk] = imported_modules[im_start + mk]
mk += 1
im_buf[im_nlen] = '\0'
sub_fi: str = pool.alloc(im_nlen + 3)
if sub_fi is not None:
sub_fi[0] = '*'
sub_fi[1] = ':'
mk2: t.CSizeT = 0
while mk2 < im_nlen:
sub_fi[2 + mk2] = im_buf[mk2]
mk2 += 1
sub_fi[2 + im_nlen] = '\0'
sub_val: int = _lookup_cross_module_cdefine(pool, name, sub_fi, None)
if HandlesType.is_cdefine_found() != 0:
return sub_val
return -1
# 2. 复制模块名到新缓冲区lookup_from_import 返回的是内部指针)
# 截断于空格、null、或 ':'(别名格式的分隔符)
@@ -1161,7 +1260,6 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
if sha1 is None:
sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod)
if sha1 is None:
stdio.printf("[XMCD] FAIL name=%s base_mod=%s reason=sha1_not_found\n", name, base_mod)
return -1
# 5. 获取 temp_dir
@@ -1529,6 +1627,45 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
stdlib.free(pyi_buf)
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
# 当 `from stdint import *` 导致 star fallback 返回 stdint但目标常量
# (如 SHARE_READ实际在 `import w32.fileio` 导入的模块中时,
# 需要遍历 imported_modules 逐个查找
if result_found == 0 and imported_modules is not None:
im2_len: t.CSizeT = string.strlen(imported_modules)
im2_ci: t.CSizeT = 0
while im2_ci < im2_len and result_found == 0:
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
im2_ci += 1
if im2_ci >= im2_len:
break
im2_start: t.CSizeT = im2_ci
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
im2_ci += 1
im2_end: t.CSizeT = im2_ci
im2_nlen: t.CSizeT = im2_end - im2_start
if im2_nlen > 0 and im2_nlen < 256:
im2_buf: str = pool.alloc(im2_nlen + 1)
if im2_buf is not None:
mk3: t.CSizeT = 0
while mk3 < im2_nlen:
im2_buf[mk3] = imported_modules[im2_start + mk3]
mk3 += 1
im2_buf[im2_nlen] = '\0'
sub_fi2: str = pool.alloc(im2_nlen + 3)
if sub_fi2 is not None:
sub_fi2[0] = '*'
sub_fi2[1] = ':'
mk4: t.CSizeT = 0
while mk4 < im2_nlen:
sub_fi2[2 + mk4] = im2_buf[mk4]
mk4 += 1
sub_fi2[2 + im2_nlen] = '\0'
sub_val2: int = _lookup_cross_module_cdefine(pool, name, sub_fi2, None)
if HandlesType.is_cdefine_found() != 0:
result_val = sub_val2
result_found = 1
# 10. 缓存到当前模块的 CDefine 表中
if result_found != 0:
HandlesType.register_cdefine_constant(pool, name, result_val)
@@ -1592,8 +1729,9 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
# 仅对符合 CDefine 命名约定ALL_CAPS的名字触发跨模块查找
# 避免对普通变量名mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if _is_cdefine_name_pattern(nm_id) != 0:
if trans is not None and trans._from_imports is not None:
cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports)
if trans is not None:
cdef_val = _lookup_cross_module_cdefine(
pool, nm_id, trans._from_imports, trans._imported_modules)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val)
@@ -2121,6 +2259,92 @@ def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
# ============================================================
def _is_t_type_object_name(name: str) -> int:
"""检查 name 是否是 t.py 中的类型对象名CPtr/CInt/CDouble 等)
这些类型对象在运行时作为全局变量 @CPtr/@CInt 等被引用,
但 t.py 不被编译,需要在使用处生成带 null 初值的定义。
"""
if name is None:
return 0
# t.py 中的类型对象名PascalCase首字母大写
if string.strcmp(name, "CPtr") == 0:
return 1
if string.strcmp(name, "CInt") == 0:
return 1
if string.strcmp(name, "CChar") == 0:
return 1
if string.strcmp(name, "CShort") == 0:
return 1
if string.strcmp(name, "CLong") == 0:
return 1
if string.strcmp(name, "CLongLong") == 0:
return 1
if string.strcmp(name, "CDouble") == 0:
return 1
if string.strcmp(name, "CFloat") == 0:
return 1
if string.strcmp(name, "CBool") == 0:
return 1
if string.strcmp(name, "CVoid") == 0:
return 1
if string.strcmp(name, "CInt8T") == 0:
return 1
if string.strcmp(name, "CInt16T") == 0:
return 1
if string.strcmp(name, "CInt32T") == 0:
return 1
if string.strcmp(name, "CInt64T") == 0:
return 1
if string.strcmp(name, "CUInt8T") == 0:
return 1
if string.strcmp(name, "CUInt16T") == 0:
return 1
if string.strcmp(name, "CUInt32T") == 0:
return 1
if string.strcmp(name, "CUInt64T") == 0:
return 1
if string.strcmp(name, "CUnsigned") == 0:
return 1
if string.strcmp(name, "CUnsignedInt") == 0:
return 1
if string.strcmp(name, "CUnsignedChar") == 0:
return 1
if string.strcmp(name, "CUnsignedShort") == 0:
return 1
if string.strcmp(name, "CUnsignedLong") == 0:
return 1
if string.strcmp(name, "CUnsignedLongLong") == 0:
return 1
if string.strcmp(name, "CSizeT") == 0:
return 1
if string.strcmp(name, "CIntPtrT") == 0:
return 1
if string.strcmp(name, "CUIntPtrT") == 0:
return 1
if string.strcmp(name, "CPtrDiffT") == 0:
return 1
if string.strcmp(name, "CType") == 0:
return 1
if string.strcmp(name, "CDefine") == 0:
return 1
if string.strcmp(name, "CStatic") == 0:
return 1
if string.strcmp(name, "CExport") == 0:
return 1
if string.strcmp(name, "CArray") == 0:
return 1
if string.strcmp(name, "CTypedef") == 0:
return 1
if string.strcmp(name, "CNoVTable") == 0:
return 1
if string.strcmp(name, "CNeedPtr") == 0:
return 1
if string.strcmp(name, "CAutoPtr") == 0:
return 1
return 0
def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
@@ -2150,10 +2374,33 @@ def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
use_ty = llvmlite.Int8(pool)
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
# 特殊处理: t.py 中的类型对象CPtr/CInt/CDouble 等)
# t.py 不被编译,但这些类型对象在运行时被引用(如 va_arg(ap, t.CPtr))。
# 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。
# LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。
is_t_type_obj: int = 0
if string.strcmp(mod_nm.id, "t") == 0:
is_t_type_obj = _is_t_type_object_name(at.attr)
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
if ext_gv is not None:
ext_gv.Linkage = "external"
ext_gv.Initializer = None
if is_t_type_obj != 0:
# t.CPtr 等类型对象: 创建带 null 初值的定义linkonce_odr
ext_gv.Linkage = "linkonce_odr"
# i8* null 的初值文本
null_init: bytes = pool.alloc(8)
if null_init is not None:
null_init[0] = 'n'
null_init[1] = 'u'
null_init[2] = 'l'
null_init[3] = 'l'
null_init[4] = '\0'
ext_gv.Initializer = null_init
else:
ext_gv.Initializer = None
else:
# 普通模块属性: external 声明(由其他模块提供定义)
ext_gv.Linkage = "external"
ext_gv.Initializer = None
llvmlite.module_add_global(mod, ext_gv)
return make_global_ref(pool, at.attr, use_ty)
@@ -2302,6 +2549,24 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
is_submod = 1
if is_submod != 0:
return None # 是子模块,交给嵌套属性访问路径
# 特殊处理: t.ASM_DESCR 不是 CDefine是内联汇编约束命名空间
# t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理
# 此处返回 None让上层嵌套属性访问路径处理
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0:
return None
# CDefine 未找到且属性名符合 ALL_CAPS 约定: 报 FATAL 错误
# 不再创建未定义的外部全局变量引用(否则 llc 报 "use of undefined value"
if _is_cdefine_name_pattern(at.attr) != 0:
err_buf_sma: str = pool.alloc(256)
if err_buf_sma is not None:
cur_sha1_sma: str = "(unknown)"
if trans is not None and trans.ModuleSha1 is not None:
cur_sha1_sma = trans.ModuleSha1
viperlib.snprintf(err_buf_sma, 256,
"[CD] FATAL: Module attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
nm_ma.id, at.attr, cur_sha1_sma)
VLogger.error(err_buf_sma, "CD")
return None
# 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
@@ -2347,14 +2612,21 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
cdef_val_nested = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val_nested)
# CDefine 未找到: 创建前向引用 (CDefine 通常是 i32)
i32_ty_fwd: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
gv_ref_fwd: llvmlite.Value | t.CPtr = make_global_ref(
pool, at.attr, i32_ty_fwd)
if gv_ref_fwd is not None and gv_ref_fwd.Ty is not None:
load_ty_fwd: llvmlite.LLVMType | t.CPtr = gv_ref_fwd.Ty.Pointee
if load_ty_fwd is not None:
return llvmlite.build_load(builder, load_ty_fwd, gv_ref_fwd)
# CDefine 未找到: 报 FATAL 错误(不再创建未定义的全局变量引用)
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
# 导致 llc 报 "use of undefined value" 错误。
# 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
if _is_cdefine_name_pattern(at.attr) != 0:
err_buf_ma: str = pool.alloc(256)
if err_buf_ma is not None:
cur_sha1_ma: str = "(unknown)"
if trans is not None and trans.ModuleSha1 is not None:
cur_sha1_ma = trans.ModuleSha1
viperlib.snprintf(err_buf_ma, 256,
"[CD] FATAL: Nested attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
qual_buf, at.attr, cur_sha1_ma)
VLogger.error(err_buf_ma, "CD")
return None
# 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None
@@ -2401,7 +2673,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
if alias_ptr is not None:
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 按需过滤: struct_ty 为 i8简化联合类型时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
@@ -2413,7 +2685,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
cur_sha1: str = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 按需过滤成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None:
@@ -2427,33 +2699,11 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保
# 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
# (原始 struct_ty 可能是 i8ensure 无效)
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
if field_info is None:
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_subclass_with_field(
var_entry.AnnotClassName, at.attr)
if sub_entry is not None and sub_entry.Ty is not None:
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
if sub_field is not None:
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, sub_entry.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
if casted_ptr is not None:
sub_field_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_gep_struct(
builder, sub_entry.Ty, sub_field.Ty,
casted_ptr, sub_field.Index)
if sub_field_ptr is not None:
return llvmlite.build_load(
builder, sub_field.Ty, sub_field_ptr)
if field_info is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
# 联合体bitcast obj_ptr 到 field_ty* 后 load
@@ -2483,6 +2733,28 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
return loaded_val
# fast-fail: 字段未找到,打印诊断信息
if trans is not None:
diag_annot: str = None
diag_var_nm: str = None
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
diag_var_nm = dnm.id
if dnm.id is not None:
dve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, dnm.id)
if dve is not None:
diag_annot = dve.AnnotClassName
fb_attr: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_attr is not None:
viperlib.snprintf(fb_attr, 1024,
"属性访问失败: .%s (var=%s annot=%s sha1=%s:%d)",
at.attr,
diag_var_nm if diag_var_nm is not None else "?",
diag_annot if diag_annot is not None else "None",
trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
at.lineno if at.lineno is not None else 0)
VLogger.error(fb_attr, "ATTR")
return None
case _:
return None
@@ -2620,6 +2892,15 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_wma.id is not None:
# CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局
if _is_cdefine_name_pattern(at.attr) != 0:
err_buf_wma: str = pool.alloc(256)
if err_buf_wma is not None:
viperlib.snprintf(err_buf_wma, 256,
"[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n",
nm_wma.id, at.attr)
VLogger.error(err_buf_wma, "CD")
return None
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
@@ -2645,7 +2926,7 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 按需过滤: struct_ty 为 i8简化联合类型时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
@@ -2659,7 +2940,7 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
cur_sha1 = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 按需过滤成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None:
@@ -2673,32 +2954,10 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
if casted_ptr is not None:
obj_ptr = casted_ptr
struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保
# 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty)
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
# orelse 在 If 上不在 AST 上,需搜索 AST 的子类
if field_info is None:
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
HandlesStruct.find_subclass_with_field(
var_entry.AnnotClassName, at.attr)
if sub_entry is not None and sub_entry.Ty is not None:
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
if sub_field is not None:
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
llvmlite.Ptr(pool, sub_entry.Ty)
casted_ptr: llvmlite.Value | t.CPtr = \
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
if casted_ptr is not None:
# 确保子类结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module(
pool, mod, sub_entry.Ty)
return llvmlite.build_gep_struct(
builder, sub_entry.Ty, sub_field.Ty,
casted_ptr, sub_field.Index)
if field_info is not None:
# 联合体bitcast obj_ptr 到 field_ty*(字段指针用于 store
if HandlesStruct.is_union_by_type(struct_ty) == 1:
@@ -2707,6 +2966,28 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
# 普通结构体GEP 获取字段指针
return llvmlite.build_gep_struct(
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
# fast-fail: 字段未找到,打印诊断信息
if trans is not None:
diag_annot_g: str = None
diag_var_nm_g: str = None
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
dnm_g: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
diag_var_nm_g = dnm_g.id
if dnm_g.id is not None:
dve_g: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, dnm_g.id)
if dve_g is not None:
diag_annot_g = dve_g.AnnotClassName
fb_attr_g: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_attr_g is not None:
viperlib.snprintf(fb_attr_g, 1024,
"属性写入失败: .%s (var=%s annot=%s sha1=%s:%d)",
at.attr,
diag_var_nm_g if diag_var_nm_g is not None else "?",
diag_annot_g if diag_annot_g is not None else "None",
trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
at.lineno if at.lineno is not None else 0)
VLogger.error(fb_attr_g, "ATTR")
return None
case _:
return None

View File

@@ -8,6 +8,7 @@ import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
@@ -2257,13 +2258,11 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
"__before_init__", storage_ptr, None, 0, trans)
# 如果有 __init__调用 __init__(ptr, args...)
# 注意: 仅依据 ctor_entry.HasInit 判断,不强制推断
# JsonValue 等类有 __new__ 但无 __init__强制推断会导致 undefined reference
has_init_flag: int = 0
if ctor_entry is not None:
has_init_flag = ctor_entry.HasInit
# 跨模块 OOP 类: HasInit 可能未设置_translate_oop_methods 未被调用)
# 但 IsOOP=1 说明类有方法,推断 HasInit=1
if is_oop != 0 and has_init_flag == 0:
has_init_flag = 1
if has_init_flag != 0:
# 翻译构造函数参数
arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
@@ -2283,8 +2282,9 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
_call_method_on_ptr(pool, builder, mod, class_name,
"__init__", storage_ptr, arg_vals, real_arg_count, trans)
else:
# 无 __init__逐字段 store 参数值
elif has_new_flag == 0:
# 无 __init__ 且无 __new__:逐字段 store 参数值
# (有 __new__ 时参数已由 __new__ 消费,不应存入字段)
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
@@ -2974,6 +2974,262 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 与 _infer_external_func_ret_ty 的默认策略保持一致
return llvmlite.Int32(pool)
# ============================================================
# 跨模块方法返回类型查找(从 text.ll
#
# Phase A 翻译每个源文件后生成 text.ll其中 define 行包含
# 方法的实际返回类型。当 Phase A 翻译后续文件调用跨模块方法时,
# _infer_method_ret_ty 默认返回 i32导致指针返回值被截断。
# _LookupMethodRetTyFromTextLL 读取依赖模块的 text.ll从 define
# 行提取实际返回类型,避免指针截断。
# ============================================================
_g_temp_dir: str = None
_g_temp_dir_len: t.CSizeT = 0
# text.ll 读取缓冲区大小
_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144
# ============================================================
# SetTempDir - 设置 temp 目录路径
#
# 在 Phase2.py 的 Phase A 翻译前调用,使 HandlesExprCall
# 能够读取依赖模块的 text.ll 文件。
# ============================================================
def SetTempDir(temp_dir: str):
"""设置 temp 目录路径(供跨模块方法返回类型查找使用)"""
global _g_temp_dir
global _g_temp_dir_len
_g_temp_dir = temp_dir
if temp_dir is not None:
_g_temp_dir_len = string.strlen(temp_dir)
else:
_g_temp_dir_len = 0
# ============================================================
# _ParseLLVMRetTyStr - 将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType
#
# 从 define 行中提取的返回类型字符串define 与 @ 之间的内容),
# 转换为 llvmlite.LLVMType 对象。
#
# 支持的类型:
# void → Void
# i8/i16/i32/i64 → Int8/Int16/Int32/Int64
# double → Double, float → Float
# 任何含 * 的类型 → Ptr(Int8)(通用指针,保留 64 位地址)
#
# Args:
# pool: 内存池
# buf: 包含类型字符串的缓冲区
# start: 类型字符串起始位置
# end: 类型字符串结束位置(不含)
#
# Returns:
# LLVM 类型对象,或 None无法解析
# ============================================================
def _ParseLLVMRetTyStr(pool: memhub.MemBuddy | t.CPtr,
buf: bytes,
start: t.CSizeT,
end: t.CSizeT) -> llvmlite.LLVMType | t.CPtr:
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
if pool is None or buf is None or start >= end:
return None
# 去除前导空白
s: t.CSizeT = start
while s < end:
c: int = buf[s]
if c != ' ' and c != '\t':
break
s += 1
# 去除尾部空白
e: t.CSizeT = end
while e > s:
c2: int = buf[e - 1]
if c2 != ' ' and c2 != '\t':
break
e -= 1
ty_len: t.CSizeT = e - s
if ty_len == 0:
return None
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
# 包括 i8*, i32*, %"name"*, { i32, i8 }* 等
ci: t.CSizeT = s
while ci < e:
if buf[ci] == '*':
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
ci += 1
# void
if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0:
return llvmlite.Void(pool)
# double
if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0:
return llvmlite.Double(pool)
# float
if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0:
return llvmlite.Float(pool)
# iN (i8, i16, i32, i64)
if ty_len >= 2 and buf[s] == 'i':
bits: int = 0
di: t.CSizeT = s + 1
valid: int = 1
while di < e:
d: int = buf[di]
if d >= '0' and d <= '9':
bits = bits * 10 + (d - '0')
else:
valid = 0
break
di += 1
if valid != 0 and bits > 0:
if bits == 8:
return llvmlite.Int8(pool)
if bits == 16:
return llvmlite.Int16(pool)
if bits == 32:
return llvmlite.Int32(pool)
if bits == 64:
return llvmlite.Int64(pool)
# 无法解析
return None
# ============================================================
# _LookupMethodRetTyFromTextLL - 从依赖模块的 text.ll 查找方法返回类型
#
# 读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),搜索
# define 行中包含 "{sha1}.{class_name}.{method_name}" 的函数,
# 提取返回类型并转换为 llvmlite.LLVMType。
#
# 解决问题: _infer_method_ret_ty 默认返回 i32导致指针返回值
# 被 inttoptr 截断为 32 位coerce_to_type line 238-239
# 本函数从 text.ll 获取实际返回类型,避免截断。
#
# Args:
# pool: 内存池
# class_sha1: 类所属模块的 SHA1
# class_name: 类名
# method_name: 方法名
#
# Returns:
# LLVM 类型对象,或 None未找到/text.ll 不存在)
# ============================================================
def _LookupMethodRetTyFromTextLL(pool: memhub.MemBuddy | t.CPtr,
class_sha1: str,
class_name: str,
method_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从依赖模块的 text.ll 中查找方法返回类型"""
if pool is None or class_sha1 is None or class_name is None or method_name is None:
return None
if _g_temp_dir is None or _g_temp_dir_len == 0:
return None
sha1_len: t.CSizeT = string.strlen(class_sha1)
if sha1_len < 2:
return None
# 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll
# 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致)
path_len: t.CSizeT = _g_temp_dir_len + sha1_len + 14
path_buf: bytes = stdlib.malloc(path_len)
if path_buf is None:
return None
subdir_buf: bytes = stdlib.malloc(3)
if subdir_buf is None:
stdlib.free(path_buf)
return None
subdir_buf[0] = class_sha1[0]
subdir_buf[1] = class_sha1[1]
subdir_buf[2] = '\0'
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
_g_temp_dir, subdir_buf, class_sha1)
stdlib.free(subdir_buf)
# 读取 text.ll
read_buf: bytes = stdlib.malloc(_TEXT_LL_READ_BUF_SIZE)
if read_buf is None:
stdlib.free(path_buf)
return None
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
if tf.closed:
stdlib.free(path_buf)
stdlib.free(read_buf)
return None
text_len: t.CInt64T = tf.read_all(read_buf, _TEXT_LL_READ_BUF_SIZE)
tf.close()
stdlib.free(path_buf)
if text_len <= 0:
stdlib.free(read_buf)
return None
if text_len < _TEXT_LL_READ_BUF_SIZE:
read_buf[text_len] = '\0'
else:
read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0'
# 构造搜索模式: "{sha1}.{class_name}.{method_name}"
cls_len: t.CSizeT = string.strlen(class_name)
meth_len: t.CSizeT = string.strlen(method_name)
search_len: t.CSizeT = sha1_len + cls_len + meth_len + 3
search_buf: bytes = stdlib.malloc(search_len)
if search_buf is None:
stdlib.free(read_buf)
return None
viperlib.snprintf(search_buf, search_len, "%s.%s.%s",
class_sha1, class_name, method_name)
search_str_len: t.CSizeT = string.strlen(search_buf)
# 在 text.ll 中搜索 define 行包含 search_buf
buf_len: t.CSizeT = string.strlen(read_buf)
pos: t.CSizeT = 0
result_ty: llvmlite.LLVMType | t.CPtr = None
while pos < buf_len and result_ty is None:
ls: t.CSizeT = pos
le: t.CSizeT = pos
while le < buf_len:
if read_buf[le] == '\n':
break
le += 1
ll: t.CSizeT = le - ls
# 检查是否是 define 行
if ll >= 7 and string.strncmp(read_buf + ls, "define ", 7) == 0:
# 找 @ 符号
at_pos: t.CSizeT = ls + 7
while at_pos < ls + ll:
if read_buf[at_pos] == '@':
break
at_pos += 1
if at_pos < ls + ll:
# 跳过可选引号
name_start: t.CSizeT = at_pos + 1
if name_start < ls + ll and read_buf[name_start] == '"':
name_start += 1
# 检查是否匹配 search_buf
if name_start + search_str_len <= ls + ll:
if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0:
# 匹配!提取返回类型: [ls+7, at_pos)
result_ty = _ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos)
pos = le + 1
stdlib.free(search_buf)
stdlib.free(read_buf)
return result_ty
# ============================================================
# _translate_method_call - 翻译方法调用 obj.method(args)
#
@@ -3069,8 +3325,14 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr,
if frt is not None:
call_ret_ty = frt
else:
# found_func 为 Nonestub 未注入):根据方法名推断返回类型
call_ret_ty = _infer_method_ret_ty(pool, method_name)
# found_func 为 Nonestub 未注入):尝试从 text.ll 查找实际返回类型
# 避免指针返回值被 _infer_method_ret_ty 默认为 i32 导致 inttoptr 截断
looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
pool, cls_sha1_mc, class_name, method_name)
if looked_up_ty is not None:
call_ret_ty = looked_up_ty
else:
call_ret_ty = _infer_method_ret_ty(pool, method_name)
# 翻译调用参数
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
@@ -3521,6 +3783,9 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr,
# - 整数 → 指针: inttoptr (如 t.CPtr(int_val) → i8*)
# - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8)
# - 指针 → 指针: bitcast (如 t.CPtr(ptr_val) → i8*)
# - 整数 → 浮点: sitofp (如 t.CDouble(i32_val) → double)
# - 浮点 → 整数: fptosi (如 t.CInt(double_val) → i32)
# - 浮点 → 浮点: fpext/fptrunc (如 t.CFloat(double_val) → float)
# ============================================================
def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
@@ -3532,6 +3797,8 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
target_bits: int = HandlesExpr.get_llvm_type_bits(target_ty)
val_fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
target_fbits: int = HandlesExpr.get_llvm_float_bits(target_ty)
# 整数 → 整数: trunc/zext
if val_bits != 0 and target_bits != 0:
@@ -3542,12 +3809,29 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
else:
return llvmlite.build_trunc(builder, val, target_ty)
# 整数 → 浮点: sitofp
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# 浮点 → 整数: fptosi
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 浮点 → 浮点: fpext/fptrunc
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
elif val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
else:
return llvmlite.build_fptrunc(builder, val, target_ty)
# 指针 → 整数: ptrtoint
if val_bits == 0 and target_bits != 0:
if val_bits == 0 and val_fbits == 0 and target_bits != 0:
return llvmlite.build_ptrtoint(builder, val, target_ty)
# 整数 → 指针: inttoptr
if val_bits != 0 and target_bits == 0:
# 整数 → 指针: inttoptr(必须排除浮点 target浮点 target_bits 也是 0
if val_bits != 0 and target_bits == 0 and target_fbits == 0:
return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 指针: bitcast
@@ -3763,6 +4047,28 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
if cargs is not None:
can = cargs.__len__()
# 内置强转函数: str(x)/bytes(x) → i8*, int(x) → i32
# 等价于 (t.CChar | t.CPtr)(x) / (t.CInt32T)(x)
# 单参数str(val) → bitcast val to i8*
# int(val) → trunc/sext val to i32
if func_name is not None and can == 1:
if string.strcmp(func_name, "str") == 0 or string.strcmp(func_name, "bytes") == 0:
# str(x) / bytes(x) → i8* (bitcast)
cast_arg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg is not None:
i8_ptr_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
return _translate_t_type_cast(pool, builder, cast_arg, i8_ptr_ty_builtin)
return None
elif string.strcmp(func_name, "int") == 0:
# int(x) → i32
cast_arg_int: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg_int is not None:
i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin)
return None
# 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等
# 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用)
# 支持:
@@ -3805,6 +4111,19 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# t.XXX 类型转换已确认但不满足参数条件:返回 None不跌落到跨模块 FATAL
return None
# 裸名 typedef 调用: BYTEPTR(ptr1), HANDLE(val), CHARPTR(str) 等
# 用户通过 `from stdint import *` 导入 typedef 后,可直接用裸名做类型转换
# map_t_type 包含所有内置 typedef 名称BYTEPTR/HANDLE/LPCSTR/size_t 等)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, func_name)
if bare_ty is not None and can == 1:
bare_arg_node: ast.AST | t.CPtr = cargs.get(0)
if bare_arg_node is not None:
bare_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, bare_arg_node, None, 0, trans)
if bare_arg_val is not None:
return _translate_t_type_cast(pool, builder, bare_arg_val, bare_ty)
# 检测 ClassName.__sizeof__() — 返回结构体大小常量i64
# __sizeof__ 是内置方法,不走跨模块调用路径
# 支持: Argument.__sizeof__() 和 w32.win32file.WIN32_FIND_DATAA.__sizeof__()
@@ -3907,11 +4226,6 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# 模块属性前向引用回退为 i32)GEP base 会是 i32 而非指针,
# 导致 llc 报错 "base of getelementptr must be a pointer"
if HandlesExpr.is_ptr_type(obj_val.Ty) == 0:
# 诊断: 输出节点类型信息帮助定位根本原因
len_node_kind: int = len_at.value.kind()
stdio.printf(
"[LEN-DIAG] __len__ on non-ptr (kind=%d), fallback to 0\n",
len_node_kind)
return llvmlite.const_int64(pool, 0)
i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
# 注意: 必须用 const_int64 而非 ConstInt(...,"0")
@@ -4049,7 +4363,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
llvmlite.value_set_next(tail3, arg_val3)
tail3 = arg_val3
total3: int = 1 + can
ret_ty3: llvmlite.LLVMType | t.CPtr = _infer_method_ret_ty(pool, func_name)
ret_ty3: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
pool, sha1_fc, cls_nm2, func_name)
if ret_ty3 is None:
ret_ty3 = _infer_method_ret_ty(pool, func_name)
_ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3)
return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0)

View File

@@ -143,12 +143,26 @@ class ForHandle(HandlesBase.Mixin):
trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1
# 5. 存储初始值 (类型对齐: start_val 可能是 i64需截断到 i32)
# 获取循环变量的实际元素类型alloca 是指针类型,需解引用 Pointee
# 当循环变量已声明为 i64如 t.CSizeT使用 i64 而非硬编码 i32
# 避免 load i32, i64* / store i32, i64* 类型不匹配
loop_var_ty: llvmlite.LLVMType | t.CPtr = i32_ty
if var_alloca is not None and var_alloca.Ty is not None:
match var_alloca.Ty:
case llvmlite.LLVMType.Ptr(pointee_ty):
if pointee_ty is not None:
loop_var_ty = pointee_ty
loop_var_bits: int = HandlesExpr.get_llvm_type_bits(loop_var_ty)
# 5. 存储初始值 (类型对齐: start_val 可能是 i32/i64需对齐到 loop_var_ty)
init_val: llvmlite.Value | t.CPtr = start_val
if start_val is not None and start_val.Ty is not None:
start_bits: int = HandlesExpr.get_llvm_type_bits(start_val.Ty)
if start_bits != 0 and start_bits != 32:
init_val = llvmlite.build_trunc(builder, start_val, i32_ty)
if start_bits != 0 and start_bits != loop_var_bits:
if start_bits < loop_var_bits:
init_val = llvmlite.build_sext(builder, start_val, loop_var_ty)
else:
init_val = llvmlite.build_trunc(builder, start_val, loop_var_ty)
llvmlite.build_store(builder, init_val, var_alloca)
# 6. 创建基本块: cond / body / incr / end使用 trans._label_counter不与 SSA 名共享)
@@ -173,7 +187,7 @@ class ForHandle(HandlesBase.Mixin):
# 8. cond 块: load i, icmp slt i, stop, cond_br body/end
llvmlite.position_at_end(builder, cond_bb)
cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca)
cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca)
# 类型对齐: stop_val 可能是 i64 (如 range(strlen(s))),需将 cur_i 提升到 stop_val 类型
cmp_lhs: llvmlite.Value | t.CPtr = cur_i
cmp_rhs: llvmlite.Value | t.CPtr = stop_val
@@ -184,7 +198,7 @@ class ForHandle(HandlesBase.Mixin):
if cur_bits < stop_bits:
cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty)
else:
cmp_rhs = llvmlite.build_trunc(builder, stop_val, i32_ty)
cmp_rhs = llvmlite.build_trunc(builder, stop_val, loop_var_ty)
cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs)
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb)
@@ -215,17 +229,17 @@ class ForHandle(HandlesBase.Mixin):
# 10. incr 块: i = i + step, 跳回 cond
llvmlite.position_at_end(builder, incr_bb)
cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca)
# 类型对齐: step_val 可能是 i64需截断到 i32 与 cur_i2 类型一致
cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca)
# 类型对齐: step_val 可能是 i32/i64需对齐到 loop_var_ty 与 cur_i2 类型一致
incr_step: llvmlite.Value | t.CPtr = step_val
if step_val is not None and step_val.Ty is not None:
step_bits: int = HandlesExpr.get_llvm_type_bits(step_val.Ty)
cur2_bits: int = HandlesExpr.get_llvm_type_bits(cur_i2.Ty)
if step_bits != 0 and cur2_bits != 0 and step_bits != cur2_bits:
if step_bits > cur2_bits:
incr_step = llvmlite.build_trunc(builder, step_val, i32_ty)
incr_step = llvmlite.build_trunc(builder, step_val, loop_var_ty)
else:
incr_step = llvmlite.build_sext(builder, step_val, i32_ty)
incr_step = llvmlite.build_sext(builder, step_val, loop_var_ty)
next_i: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_i2, incr_step)
llvmlite.build_store(builder, next_i, var_alloca)
llvmlite.build_br(builder, cond_bb)

View File

@@ -58,12 +58,17 @@ class IfHandle(HandlesBase.Mixin):
trans._funcs, trans._func_count, trans)
# 2. 转换为 i1 条件
# Compare/Not 表达式已返回 i1直接使用其他类型与 0 比较
# Compare/Not 表达式已返回 i1直接使用指针类型与 null 比较;其他类型与 0 比较
if cond_val is None:
cond_val = llvmlite.const_int32(pool, 0)
cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val
elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较(不能用整数 0否则 llc 报 "integer constant must have integer type"
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(
builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(

View File

@@ -559,21 +559,6 @@ class ImportsHandle(HandlesBase.Mixin):
resolved: str = _resolve_relative_module(
self.Trans.Pool, self.Trans.CurrentPackage,
impf.level, impf.module)
# [IFD] 诊断 from-import 名称处理
mod_dbg: str = "(null)"
if impf.module is not None:
mod_dbg = impf.module
pkg_dbg: str = "(null)"
if self.Trans.CurrentPackage is not None:
pkg_dbg = self.Trans.CurrentPackage
res_dbg: str = "(null)"
if resolved is not None:
res_dbg = resolved
names_dbg: t.CSizeT = 0
if impf.names is not None:
names_dbg = impf.names.__len__()
stdio.printf("[IFD] level=%d module=%s pkg=%s resolved=%s names=%d\n",
impf.level, mod_dbg, pkg_dbg, res_dbg, names_dbg)
if resolved is not None:
names: list[ast.AST | t.CPtr] | t.CPtr = impf.names
if names is not None:

View File

@@ -129,11 +129,136 @@ def translate_children(trans: HT.Translator | t.CPtr,
return added_total
# ============================================================
# _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(pool: memhub.MemBuddy | t.CPtr,
var_ty: llvmlite.LLVMType | t.CPtr,
list_node: ast.List | t.CPtr) -> 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 | t.CPtr = (ast.List | t.CPtr)(list_node)
if ln is None:
return None
# 从 var_ty 提取数组元素类型和数量
elem_ty: llvmlite.LLVMType | t.CPtr = None
arr_count: t.CInt = 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"
match elem_ty:
case llvmlite.LLVMType.Int(bits):
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"
# 获取列表元素(通过 ln.elts 而非 list_node.elts确保类型正确识别
elts: list[ast.AST | t.CPtr] | t.CPtr = ln.elts
if elts is None:
return None
elts_count: t.CSizeT = elts.__len__()
# 计算缓冲区大小: 每个元素最多 "i32 -9223372036854775808, " 约 26 字符
buf_size: t.CSizeT = 64 + elts_count * 32
buf: t.CChar | t.CPtr = pool.alloc(buf_size)
if buf is None:
return None
# 写入前缀: "["(类型前缀由 _print_global 输出,初始化器只需元素列表)
written: t.CInt = viperlib.snprintf(buf, buf_size, "[")
pos: t.CSizeT = t.CSizeT(written)
# 逐个元素写入
i: t.CSizeT = 0
while i < elts_count:
elem_node: ast.AST | t.CPtr = elts.get(i)
# 直接从 Constant 节点提取 int_val避免 extract_cdefine_int_value 的 t.CInt 截断)
elem_val: t.CInt64T = 0
if elem_node is not None:
ek: int = elem_node.kind()
if ek == ast.ASTKind.Constant:
ec: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(elem_node)
if ec is not None and ec.const_kind == ast.CONST_INT:
elem_val = ec.int_val
elif ek == ast.ASTKind.UnaryOp:
# 负数: UnaryOp(USub, Constant)
elem_val = HandlesAnnAssign.extract_cdefine_int_value(elem_node)
if i > 0:
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
pos += t.CSizeT(written)
written = viperlib.snprintf(buf + pos, buf_size - pos, "%s %lld", elem_ir_ty, elem_val)
pos += t.CSizeT(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[t.CChar, None] = "ABC..." 等字符串初始化指针类型
# ============================================================
def _build_string_ptr_initializer(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
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: t.CChar | t.CPtr = 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 | t.CPtr = llvmlite.create_global_string(
pool, mod, str_name_buf, str_val)
if str_gv is None:
return None
# 生成 GEP 初始化器文本
str_len: t.CSizeT = string.strlen(str_val)
init_buf: t.CChar | t.CPtr = 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
# ============================================================
# handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量
#
# 当用户已定义 main无 wrapper main builder模块级
# AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域
# 支持整数、列表字面量、字符串字面量初始值
# ============================================================
def handle_module_level_var(trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
@@ -163,7 +288,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
var_name: str = None
var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
init_val: t.CInt64T = 0
has_init: int = 0
init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str
init_str_val: str = None
init_list_node: ast.List | t.CPtr = None
if k == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
@@ -179,12 +306,20 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
if resolved is not None:
var_ty = resolved
# 解析初始值
if aa.value is not None and aa.value.kind() == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value)
if cn.const_kind == ast.CONST_INT:
init_val = cn.int_val
has_init = 1
# 解析初始值(支持整数、列表字面量、字符串字面量)
if aa.value is not None:
val_kind: int = aa.value.kind()
if val_kind == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value)
if 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 val_kind == ast.ASTKind.List:
init_list_node = (ast.List | t.CPtr)(aa.value)
init_kind = 2
elif k == ast.ASTKind.Assign:
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asgn is None or asgn.targets is None:
@@ -197,11 +332,19 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
return 0
nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0)
var_name = nm2.id
if asgn.value is not None and asgn.value.kind() == ast.ASTKind.Constant:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value)
if cn2.const_kind == ast.CONST_INT:
init_val = cn2.int_val
has_init = 1
if asgn.value is not None:
val_kind2: int = asgn.value.kind()
if val_kind2 == ast.ASTKind.Constant:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value)
if 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 val_kind2 == ast.ASTKind.List:
init_list_node = (ast.List | t.CPtr)(asgn.value)
init_kind = 2
if var_name is None:
return 0
@@ -225,11 +368,26 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
# 设置初始值(有初始值时清除 external linkage因为 LLVM 22+ 不允许 external global 带初始值)
gv.Linkage = None
if has_init != 0:
if init_kind == 1:
# 整数初始值
init_buf: t.CChar | t.CPtr = 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 = _build_array_initializer_text(pool, var_ty, init_list_node)
if arr_init is not None:
gv.Initializer = arr_init
else:
gv.Initializer = "zeroinitializer"
elif init_kind == 3:
# 字符串字面量 → 创建字符串常量全局 + GEP 引用
str_init: str = _build_string_ptr_initializer(pool, mod, var_name, init_str_val)
if str_init is not None:
gv.Initializer = str_init
else:
gv.Initializer = "zeroinitializer"
else:
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer
# 整数/聚合类型也兼容 zeroinitializer避免 "integer constant must have integer type"

View File

@@ -39,8 +39,10 @@ class ReturnHandle(HandlesBase.Mixin):
val: llvmlite.Value | t.CPtr = None
if rt.value is not None:
# return self: 直接返回指针self 是 SSA 参数 Ptr(struct_ty)
# translate_value 会 load 得到结构体值,但 return self 需要指针本身
# return self: 返回 self 指针本身(Ptr(StructTy),而非结构体值
# translate_value 会 load 得到结构体值,但 return self 需要指针
# 注意: lookup_var 返回的是存储 self 的 alloca (Ptr(Ptr(StructTy)))
# 需要 load 得到实际的 self 指针 (Ptr(StructTy))
if rt.value.kind() == ast.ASTKind.Name:
ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rt.value)
if ret_nm is not None and ret_nm.id is not None:
@@ -48,7 +50,16 @@ class ReturnHandle(HandlesBase.Mixin):
self_ptr: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
self.Trans.SymTab, "self")
if self_ptr is not None:
val = self_ptr
# 检查 self_ptr 是否是 Ptr(Ptr(...))alloca 存储指针)
self_pointee_ty: llvmlite.LLVMType | t.CPtr = None
match self_ptr.Ty:
case llvmlite.LLVMType.Ptr(inner_ty):
self_pointee_ty = inner_ty
if self_pointee_ty is not None and HandlesExpr.is_ptr_type(self_pointee_ty) != 0:
# Ptr(Ptr(StructTy)) → load 得到 Ptr(StructTy)
val = llvmlite.build_load(builder, self_pointee_ty, self_ptr)
else:
val = self_ptr
if val is None:
val = HandlesExpr.translate_value(
builder, pool, mod, rt.value,

View File

@@ -2,6 +2,7 @@ import t, c
from stdint import *
import memhub
import string
import stdlib
import llvmlite
import stdio
import ast
@@ -521,10 +522,28 @@ def lookup_field_by_class(class_name: str,
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
entry = find_struct(class_name)
if entry is None:
# 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中
for diag_i in range(_struct_count):
diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i)
# 诊断:目标类未注册
fb_lfbc: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_lfbc is not None:
viperlib.snprintf(fb_lfbc, 1024,
"lookup_field_by_class: 类 '%s' 未注册 (struct_count=%d)",
class_name, _struct_count)
VLogger.error(fb_lfbc, "STRUCT")
return None
# 诊断:找到结构体但字段未找到,打印字段表
fb_fields: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_fields is not None:
viperlib.snprintf(fb_fields, 1024,
"lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到",
class_name, entry.FieldCount, field_name)
VLogger.error(fb_fields, "STRUCT")
for fi in range(entry.FieldCount):
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
if fe is not None and fe.Name is not None:
fb_fn: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_fn is not None:
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: %s", fi, fe.Name)
VLogger.error(fb_fn, "STRUCT")
for fi in range(entry.FieldCount):
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
if fe is not None and fe.Name is not None:
@@ -534,45 +553,6 @@ def lookup_field_by_class(class_name: str,
return None
# ============================================================
# find_subclass_with_field — 在基类的所有子类中搜索包含指定字段的结构体
#
# 用于处理 "注解类型是基类但实际值是派生类" 的场景:
# node: AST | t.CPtr = If(...)
# node.orelse = orelse ← orelse 在 If 上,不在 AST 上
#
# 遍历所有已注册结构体,通过 ParentName 链检查继承关系,
# 返回第一个包含 field_name 的子类 StructEntry。
# ============================================================
def find_subclass_with_field(base_class_name: str,
field_name: str) -> StructEntry | t.CPtr:
"""在 base_class_name 的所有子类中搜索包含 field_name 的结构体
返回第一个找到的 StructEntry包含正确的 TyNone=未找到
"""
if base_class_name is None or field_name is None:
return None
sc_i: int
for sc_i in range(_struct_count):
sc_entry: StructEntry | t.CPtr = _get_struct_entry(sc_i)
if sc_entry is None or sc_entry.Name is None:
continue
# 检查 sc_entry 是否是 base_class_name 的子类(传递性)
cur_parent: str = get_parent_name(sc_entry.Name)
while cur_parent is not None:
if string.strcmp(cur_parent, base_class_name) == 0:
# 是子类,检查是否有该字段
sc_fi: int
for sc_fi in range(sc_entry.FieldCount):
sc_fe: FieldEntry | t.CPtr = _get_field_entry(sc_entry, sc_fi)
if sc_fe is not None and sc_fe.Name is not None:
if string.strcmp(sc_fe.Name, field_name) == 0:
return sc_entry
break
cur_parent = get_parent_name(cur_parent)
return None
# ============================================================
# get_struct_type — 按类名获取结构体的 LLVM 类型
# ============================================================

View File

@@ -6,6 +6,7 @@ import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprCall as HandlesExprCall

View File

@@ -9,6 +9,8 @@ import sys
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct
@@ -143,6 +145,95 @@ def clear_cdefine_constants() -> None:
_g_cdefine_values = None
# 常量整数表达式计算成功标志(供 _eval_const_int_expr 使用)
_g_const_eval_ok: int = 0
# ============================================================
# _eval_const_int_expr - 递归计算常量整数表达式
#
# 支持:
# - Constant (CONST_INT): 整数字面量
# - Name: CDefine 常量名(通过 lookup_cdefine_constant 查找)
# - BinOp (Add/Sub/Mult/FloorDiv/BitOr/BitAnd): 递归计算左右操作数
#
# 成功: 设置 _g_const_eval_ok=1, 返回计算值
# 失败: 设置 _g_const_eval_ok=0, 返回 0
#
# 用于 t.CArray count 的静态计算,如 2 * ZHUFF_MAX_CODES
# ============================================================
def _eval_const_int_expr(node: ast.AST | t.CPtr) -> int:
"""递归计算常量整数表达式"""
global _g_const_eval_ok
if node is None:
_g_const_eval_ok = 0
return 0
k: int = node.kind()
# Constant: 整数字面量
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
_g_const_eval_ok = 1
return cn.int_val
_g_const_eval_ok = 0
return 0
# Name: CDefine 常量名
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
_g_const_eval_ok = 0
return 0
val: int = lookup_cdefine_constant(nm.id)
if is_cdefine_found() == 0:
_g_const_eval_ok = 0
return 0
_g_const_eval_ok = 1
return val
# BinOp: 算术/位运算
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
# 递归计算左操作数
lv: int = _eval_const_int_expr(bop.left)
if _g_const_eval_ok == 0:
return 0
# 递归计算右操作数
rv: int = _eval_const_int_expr(bop.right)
if _g_const_eval_ok == 0:
return 0
# 根据运算符执行计算
if bop.op == ast.OpKind.Add:
_g_const_eval_ok = 1
return lv + rv
if bop.op == ast.OpKind.Sub:
_g_const_eval_ok = 1
return lv - rv
if bop.op == ast.OpKind.Mult:
_g_const_eval_ok = 1
return lv * rv
if bop.op == ast.OpKind.FloorDiv:
if rv == 0:
_g_const_eval_ok = 0
return 0
_g_const_eval_ok = 1
return lv // rv
if bop.op == ast.OpKind.BitOr:
_g_const_eval_ok = 1
return lv | rv
if bop.op == ast.OpKind.BitAnd:
_g_const_eval_ok = 1
return lv & rv
# 不支持的运算符
_g_const_eval_ok = 0
return 0
# 不支持的节点类型
_g_const_eval_ok = 0
return 0
def set_generic_context(tp_names: list[str] | t.CPtr,
type_args: list[str] | t.CPtr):
"""设置泛型特化上下文(进入泛型类方法体翻译时调用)"""
@@ -850,6 +941,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
mod_name: str = HandlesImports.lookup_from_import(from_imports, nm.id)
if mod_name is not None:
return map_t_type(pool, nm.id)
# 兜底: 尝试 map_t_type 中的硬编码 typedef 映射VOIDPTR, HANDLE, ULONG 等)
# 这些是跨模块通用的 C typedef 名,即使 from-import 查找失败也能解析
mapped_ty_fallback: llvmlite.LLVMType | t.CPtr = map_t_type(pool, nm.id)
if mapped_ty_fallback is not None:
return mapped_ty_fallback
return None
# Attribute 节点: t.CInt, t.CUInt64T, namespace_defs.PlainStruct 等
@@ -874,6 +970,13 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
if mod_struct_ty is not None:
return mod_struct_ty
return None
# 嵌套 Attribute: w32.win32base.SECURITY_ATTRIBUTES 等三级及以上限定名
# at.value 是 Attribute 而非 Name递归提取最终类名at.attr查结构体表
# find_struct 支持模块限定名回退,此处用最外层 attr简单类名即可
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
nested_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(at.attr)
if nested_struct_ty is not None:
return nested_struct_ty
return None
# BinOp 节点: 联合类型注解 A | B
@@ -929,7 +1032,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
# value 是 Name泛型类名slice 是类型实参
if sub.value.kind() == ast.ASTKind.Name:
gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
# tuple[...] 类型注解TransPyV 暂不支持 tuple 返回类型,返回 None 让上层默认处理
# 避免走到 list[str](pool, 4) 构造路径崩溃tuple 未定义为泛型类)
if gen_nm.id is not None:
if string.strcmp(gen_nm.id, "tuple") == 0:
return None
# 先收集类型实参名,再分配缓冲区拼接 mangled name
gen_arg_names: list[str] | t.CPtr = list[str](pool, 4)
if sub.slice.kind() == ast.ASTKind.Tuple:
@@ -1031,28 +1138,20 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
pool, elem_node, imported_modules, from_imports, trans)
if elem_ty is None:
fatal_type_error(elem_node, "t.CArray 元素类型解析失败")
# 解析 count必须是整数常量、NoneCDefine 常量名)
# 解析 count支持整数常量、NoneCDefine 常量名、常量表达式如 2*MAX
# t.CArray[elem_ty, None] 等价于单参数形式 → Ptr(elem_ty)
# t.CArray[elem_ty, NAME] 其中 NAME: t.CDefine = value → [value x elem_ty]
# t.CArray[elem_ty, 2 * MAX] 常量表达式静态计算 → [2*MAX x elem_ty]
count_val: int = 0
if count_node.kind() == ast.ASTKind.Constant:
cnt_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node)
if cnt_cn.const_kind == ast.CONST_NONE:
return llvmlite.Ptr(pool, elem_ty)
if cnt_cn.const_kind != ast.CONST_INT:
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名")
count_val = cnt_cn.int_val
elif count_node.kind() == ast.ASTKind.Name:
# CDefine 常量名查找
cnt_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(count_node)
if cnt_nm.id is None:
fatal_type_error(count_node, "t.CArray count Name 节点 id 为 None")
looked_up: int = lookup_cdefine_constant(cnt_nm.id)
if is_cdefine_found() == 0:
fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量")
count_val = looked_up
else:
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名")
# 统一用 _eval_const_int_expr 计算常量整数表达式
# 支持 Constant(INT)、Name(CDefine)、BinOp(2*MAX 等算术/位运算)
count_val = _eval_const_int_expr(count_node)
if _g_const_eval_ok == 0:
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None、CDefine 常量名或常量表达式")
if count_val <= 0:
fatal_type_error(count_node, "t.CArray count 必须为正数")
# 创建 ArrayType不包装 Ptr

View File

@@ -78,10 +78,15 @@ class WhileHandle(HandlesBase.Mixin):
if cond_val is None:
cond_val = llvmlite.const_int32(pool, 0)
# Compare/Not 表达式已返回 i1直接使用其他类型与 0 比较
# Compare/Not 表达式已返回 i1直接使用指针类型与 null 比较;其他类型与 0 比较
cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val
elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较(不能用整数 0否则 llc 报 "integer constant must have integer type"
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(
builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(