自举实验失败,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

@@ -17,6 +17,109 @@ import viperlib
# ============================================================ # ============================================================
# ============================================================
# _eval_binop_int / _eval_binop_valid - 递归计算 BinOp 常量值
#
# 用于 .pyi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B的整数值。
# 支持 BitOr/BitAnd/Add/Sub 以及嵌套的 Name引用已注册的 CDefine 常量)。
# ============================================================
def _eval_binop_valid(node: ast.AST | t.CPtr) -> int:
"""检查 BinOp 节点是否可计算(所有叶子节点都是常量或已注册的 CDefine"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop.operand is not None:
return _eval_binop_valid(uop.operand)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.left is not None and bop.right is not None:
if _eval_binop_valid(bop.left) != 0:
if _eval_binop_valid(bop.right) != 0:
return 1
return 0
if k == ast.ASTKind.Name:
# Name 引用:检查是否是已注册的 CDefine 常量
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
# 从本地 CDefine 表查找
val: int = _lookup_cdefine_for_pyi(nm.id)
if _pyi_cdefine_found() != 0:
return 1
return 0
return 0
def _eval_binop_int(node: ast.AST | t.CPtr) -> int:
"""递归计算 BinOp 节点的整数值"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
return cn.int_val
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop.op == ast.OpKind.USub and uop.operand is not None:
return -_eval_binop_int(uop.operand)
if uop.op == ast.OpKind.UAdd and uop.operand is not None:
return _eval_binop_int(uop.operand)
if uop.op == ast.OpKind.Invert and uop.operand is not None:
return ~_eval_binop_int(uop.operand)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
if bop.left is None or bop.right is None:
return 0
lv: int = _eval_binop_int(bop.left)
rv: int = _eval_binop_int(bop.right)
if bop.op == ast.OpKind.BitOr:
return lv | rv
if bop.op == ast.OpKind.BitAnd:
return lv & rv
if bop.op == ast.OpKind.Add:
return lv + rv
if bop.op == ast.OpKind.Sub:
return lv - rv
return 0
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is not None:
return _lookup_cdefine_for_pyi(nm.id)
return 0
return 0
# CDefine 查找的前向声明(实际实现在 HandlesType
# 避免循环依赖,使用弱引用
_g_pyi_cdefine_found: int = 0
def _lookup_cdefine_for_pyi(name: str) -> int:
"""从本地 CDefine 表查找常量值(用于 .pyi 生成时的 BinOp 计算)"""
import lib.core.Handles.HandlesType as HandlesType
val: int = HandlesType.lookup_cdefine_constant(name)
if HandlesType.is_cdefine_found() != 0:
_g_pyi_cdefine_found = 1
return val
_g_pyi_cdefine_found = 0
return 0
def _pyi_cdefine_found() -> int:
"""返回上次 _lookup_cdefine_for_pyi 的查找结果状态"""
return _g_pyi_cdefine_found
# ============================================================ # ============================================================
# _PyiEmit - 追加字符串到缓冲区 # _PyiEmit - 追加字符串到缓冲区
# #
@@ -525,6 +628,72 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
pos = _PyiEmit(buf, buf_size, pos, " = False") pos = _PyiEmit(buf, buf_size, pos, " = False")
elif cv.const_kind == ast.CONST_NONE: elif cv.const_kind == ast.CONST_NONE:
pos = _PyiEmit(buf, buf_size, pos, " = None") pos = _PyiEmit(buf, buf_size, pos, " = None")
elif aa.value is not None and aa.value.kind() == ast.ASTKind.UnaryOp:
# 处理负值常量(如 INVALID_HANDLE_VALUE = -1
# UnaryOp(USub, Constant(1)) → -1
uop_cv: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(aa.value)
if uop_cv.op == ast.OpKind.USub and uop_cv.operand is not None:
if uop_cv.operand.kind() == ast.ASTKind.Constant:
uop_const: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_cv.operand)
if uop_const.const_kind == ast.CONST_INT:
neg_val: int = -uop_const.int_val
num_buf_u: bytes = mb.alloc(32)
if num_buf_u is not None:
viperlib.snprintf(num_buf_u, 32, " = %d", neg_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_u)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call:
# 处理类型构造函数常量(如 t.CUnsignedLong(-11)
# 提取第一个参数的整数值并输出
call_cv: ast.Call | t.CPtr = (ast.Call | t.CPtr)(aa.value)
if call_cv.args is not None:
call_args_cv: list[ast.AST | t.CPtr] | t.CPtr = call_cv.args
if call_args_cv.__len__() > 0:
first_arg: ast.AST | t.CPtr = call_args_cv.get(0)
if first_arg is not None:
arg_kind: int = first_arg.kind()
if arg_kind == ast.ASTKind.Constant:
ca_cv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(first_arg)
if ca_cv.const_kind == ast.CONST_INT:
num_buf_c: bytes = mb.alloc(32)
if num_buf_c is not None:
viperlib.snprintf(num_buf_c, 32, " = %d", ca_cv.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_c)
elif arg_kind == ast.ASTKind.UnaryOp:
uop_ca: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(first_arg)
if uop_ca.op == ast.OpKind.USub and uop_ca.operand is not None:
if uop_ca.operand.kind() == ast.ASTKind.Constant:
uoc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(uop_ca.operand)
if uoc.const_kind == ast.CONST_INT:
neg_c_val: int = -uoc.int_val
num_buf_cu: bytes = mb.alloc(32)
if num_buf_cu is not None:
viperlib.snprintf(num_buf_cu, 32, " = %d", neg_c_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_cu)
elif arg_kind == ast.ASTKind.BinOp:
# Call 内部的 BinOp如 t.CUnsignedLong(A | B)
# 递归计算 BinOp 值
binop_val_bo: int = _eval_binop_int(first_arg)
if binop_val_bo != 0 or _eval_binop_valid(first_arg) != 0:
num_buf_bo: bytes = mb.alloc(32)
if num_buf_bo is not None:
viperlib.snprintf(num_buf_bo, 32, " = %d", binop_val_bo)
pos = _PyiEmit(buf, buf_size, pos, num_buf_bo)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.BinOp:
# 处理 BinOp 常量(如 FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN
# 递归计算 BinOp 值并输出
binop_val_av: int = _eval_binop_int(aa.value)
if binop_val_av != 0 or _eval_binop_valid(aa.value) != 0:
num_buf_b: bytes = mb.alloc(32)
if num_buf_b is not None:
viperlib.snprintf(num_buf_b, 32, " = %d", binop_val_av)
pos = _PyiEmit(buf, buf_size, pos, num_buf_b)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
# 处理 Name 引用常量(如 ALIAS = OTHER_CONST
# 输出引用的名字,让消费方在本地查找
name_val: ast.Name | t.CPtr = (ast.Name | t.CPtr)(aa.value)
if name_val.id is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiEmit(buf, buf_size, pos, name_val.id)
pos = _PyiEmit(buf, buf_size, pos, "\n") pos = _PyiEmit(buf, buf_size, pos, "\n")
# 全局赋值(无注解)— 从 Constant value 推断类型 # 全局赋值(无注解)— 从 Constant value 推断类型

View File

@@ -30,7 +30,7 @@ import lib.Projectrans.Config as Config
_mbuddy: memhub.MemBuddy | t.CPtr _mbuddy: memhub.MemBuddy | t.CPtr
# 源代码缓冲区大小1MB # 源代码缓冲区大小1MB
SRC_BUF_SIZE: t.CDefine = 1048576 SRC_BUF_SIZE: t.CDefine = 1048576 # 2^20 = 1MB
# ============================================================ # ============================================================
@@ -282,8 +282,76 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
return 0 return 0
def _CollectObjFilesImpl(base_dir: str, bd_len: t.CSizeT,
out_buf: bytes, out_pos: t.CSizeT, out_size: t.CSizeT) -> t.CSizeT:
"""递归扫描 base_dir收集 .obj 文件路径到 out_buf返回新的 out_pos
支持切片子目录(如 dir/43/{sha1}.obj
"""
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
return out_pos
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
pattern: bytes = _mbuddy.alloc(bd_len + 8)
if pattern is None:
return out_pos
viperlib.snprintf(pattern, bd_len + 8, "%s/*", base_dir)
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
_mbuddy.free(pattern)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
return out_pos
while True:
fname: str = find_data.cFileName
if fname is not None:
fname_len: t.CSizeT = string.strlen(fname)
if fname_len > 0:
# 跳过 . 和 ..
is_dot: int = 0
if fname_len == 1 and fname[0] == '.':
is_dot = 1
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.':
is_dot = 1
if is_dot == 0:
attrs: ULONG = find_data.dwFileAttributes
is_dir: int = 0
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0:
is_dir = 1
if is_dir != 0:
# 递归扫描子目录
sub_dir: bytes = _mbuddy.alloc(bd_len + fname_len + 2)
if sub_dir is not None:
viperlib.snprintf(sub_dir, bd_len + fname_len + 2, "%s/%s", base_dir, fname)
sub_len: t.CSizeT = string.strlen(sub_dir)
out_pos = _CollectObjFilesImpl(sub_dir, sub_len, out_buf, out_pos, out_size)
_mbuddy.free(sub_dir)
else:
# 检查是否是 .obj 文件
if fname_len > 4:
if fname[fname_len - 4] == '.' and fname[fname_len - 3] == 'o' and fname[fname_len - 2] == 'b' and fname[fname_len - 1] == 'j':
need: t.CSizeT = bd_len + 1 + fname_len + 2
if out_pos + need < out_size:
if out_pos > 0:
out_buf[out_pos] = ' '
out_pos += 1
viperlib.snprintf(out_buf + out_pos, need, "%s/%s", base_dir, fname)
out_pos += bd_len + 1 + fname_len
else:
break
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
w32.win32file.FindClose(handle)
return out_pos
def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT: def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSizeT) -> t.CSizeT:
"""扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf """递归扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf
支持切片子目录(如 includes_binary_dir/43/{sha1}.obj
Args: Args:
includes_binary_dir: includes.binary 目录路径 includes_binary_dir: includes.binary 目录路径
@@ -297,47 +365,8 @@ def collect_obj_files(includes_binary_dir: str, out_buf: bytes, out_size: t.CSiz
return 0 return 0
out_buf[0] = '\0' out_buf[0] = '\0'
out_pos: t.CSizeT = 0
# 构造搜索模式: dir/*.obj
dir_len: t.CSizeT = string.strlen(includes_binary_dir) dir_len: t.CSizeT = string.strlen(includes_binary_dir)
pattern: bytes = _mbuddy.alloc(dir_len + 8) out_pos: t.CSizeT = _CollectObjFilesImpl(includes_binary_dir, dir_len, out_buf, 0, out_size)
if pattern is None:
return 0
viperlib.snprintf(pattern, dir_len + 8, "%s/*.obj", includes_binary_dir)
# FindFirstFileA
find_data: w32.win32file.WIN32_FIND_DATAA | t.CPtr = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
return 0
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
return 0
# 需要 win32base 的 INVALID_HANDLE_VALUE
while True:
fname: str = find_data.cFileName
if fname is not None:
fname_len: t.CSizeT = string.strlen(fname)
# 构造完整路径并追加到 out_buf
# 路径格式: "dir/fname "
need: t.CSizeT = dir_len + 1 + fname_len + 2
if out_pos + need < out_size:
if out_pos > 0:
out_buf[out_pos] = ' '
out_pos += 1
viperlib.snprintf(out_buf + out_pos, need, "%s/%s", includes_binary_dir, fname)
out_pos += dir_len + 1 + fname_len
else:
# 缓冲区不足,停止
break
if w32.win32file.FindNextFileA(handle, find_data) == 0:
break
w32.win32file.FindClose(handle)
out_buf[out_pos] = '\0' out_buf[out_pos] = '\0'
return out_pos return out_pos
@@ -509,13 +538,23 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT,
viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s", viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s",
linker_cmd, obj_paths, linker_output, linker_flags) linker_cmd, obj_paths, linker_output, linker_flags)
# 用 stdlib.system() 直接在控制台运行链接,输出直接显示 # 用 subprocess.run 捕获 clang++ 的 stdout/stderr 输出
# 避免 subprocess 管道捕获丢失输出TransPyV 标准句柄可能无效) # 之前用 stdlib.system() 无法捕获具体错误undefined reference 等),
sys_ret: int = stdlib.system(cmd) # 导致 1.txt 中只看到"链接失败,返回码: 1"而看不到未解析符号
if sys_ret != 0: result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("subprocess.run 返回 None", "link")
return 1
if result.returncode != 0:
# 先用 stdio.printf 直接输出 clang++ 的完整错误输出(含 undefined reference
# 必须在 VLogger.error 之前输出,因为 VLogger.error 会调用 sys.exit(1) 终止进程
if result.stdout is not None:
stdio.printf("=== clang++ 链接错误输出 ===\n%s\n", result.stdout)
if result.stderr is not None:
stdio.printf("=== clang++ stderr ===\n%s\n", result.stderr)
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: if fb is not None:
viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", sys_ret, cmd) viperlib.snprintf(fb, 1024, "链接失败,返回码: %d, 命令: %s", result.returncode, cmd)
VLogger.error(fb, "link") VLogger.error(fb, "link")
return 1 return 1
return 0 return 0

View File

@@ -176,6 +176,77 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
return 0 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 @t.NoVTable
class AnnAssignHandle(HandlesBase.Mixin): class AnnAssignHandle(HandlesBase.Mixin):
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针""" """AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
@@ -221,6 +292,11 @@ class AnnAssignHandle(HandlesBase.Mixin):
if HT.is_nonlocal_name(self.Trans, nm.id) != 0: if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
return 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( existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
self.Trans.SymTab, nm.id) self.Trans.SymTab, nm.id)
@@ -350,6 +426,29 @@ class AnnAssignHandle(HandlesBase.Mixin):
llvmlite.build_store(builder, rhs_coerced, nl_ptr) llvmlite.build_store(builder, rhs_coerced, nl_ptr)
return 0 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
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym( 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.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct 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( rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, asgn.value, None, 0, self.Trans) builder, pool, mod, asgn.value, None, 0, self.Trans)
if rhs_val is None: if rhs_val is None:
# 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位 # 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None and asgn.value is not None: if fb is not None and asgn.value is not None:
sha1: str = self.Trans.ModuleSha1 sha1: str = self.Trans.ModuleSha1
val_kind: int = asgn.value.kind() val_kind: int = asgn.value.kind()
val_line: t.CInt = asgn.value.lineno val_line: t.CInt = asgn.value.lineno
# 通过 sha1 查找文件名(人类可读)
rel_path: str = None
if sha1 is not None: if sha1 is not None:
viperlib.snprintf(fb, 1024, rel_path = StubMerger.LookupSha1RelPath(sha1)
"rhs_val is None [sha1=%s lineno=%d kind=%d]", # 根据 AST 节点类型提取详情
sha1, val_line, val_kind) 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: else:
viperlib.snprintf(fb, 1024, if rel_path is not None:
"rhs_val is None [lineno=%d kind=%d]", viperlib.snprintf(fb, 1024,
val_line, val_kind) "赋值右侧为 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") VLogger.error(fb, "ASGN")
else: else:
VLogger.error("rhs_val is None", "ASGN") VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
return 0 return 0
new_vars: int = 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 _generic_class_sha1s: list[str] | t.CPtr = None
def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int: def _is_generic_class(tp: list[str] | t.CPtr) -> int:
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否""" """检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否
if cd is None:
return 0 接收 type_params 而非 cd避免 cd.type_params 属性访问
tp: list[str] | t.CPtr = cd.type_params 因跨模块类型推断失败返回 None。
"""
if tp is None: if tp is None:
return 0 return 0
if tp.__len__() == 0:
return 0
return 1 return 1
@@ -1721,29 +1720,37 @@ def _register_generic_template(pool: memhub.MemBuddy | t.CPtr,
_generic_class_names = list[str](pool, 8) _generic_class_names = list[str](pool, 8)
_generic_class_nodes = list[ast.ClassDef | t.CPtr](pool, 8) _generic_class_nodes = list[ast.ClassDef | t.CPtr](pool, 8)
_generic_class_sha1s = list[str](pool, 8) _generic_class_sha1s = list[str](pool, 8)
# 检查是否已注册 # 用局部变量接收 global list触发类型推断避免 global 变量 __len__ 失败)
cn: t.CSizeT = _generic_class_names.__len__() names_local: list[str] | t.CPtr = _generic_class_names
cn: t.CSizeT = names_local.__len__()
i: t.CSizeT i: t.CSizeT
for i in range(cn): 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: if nm is not None and string.strcmp(nm, cd.name) == 0:
return return
_generic_class_names.append(cd.name) _generic_class_names.append(cd.name)
_generic_class_nodes.append(cd) _generic_class_nodes.append(cd)
_generic_class_sha1s.append(module_sha1) _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: def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr:
"""查找泛型类模板,返回 ClassDef 节点或 None""" """查找泛型类模板,返回 ClassDef 节点或 None"""
if _generic_class_names is None or class_name is None: if _generic_class_names is None or class_name is None:
return 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 i: t.CSizeT
for i in range(cn): 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: if nm is not None and string.strcmp(nm, class_name) == 0:
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 node: ast.ClassDef | t.CPtr = nodes_local.get(i)
node: ast.ClassDef | t.CPtr = _generic_class_nodes.get(i)
return node return node
return None return None
@@ -1752,13 +1759,15 @@ def _find_generic_template_sha1(class_name: str) -> str:
"""查找泛型类模板的来源模块 SHA1未找到返回 None""" """查找泛型类模板的来源模块 SHA1未找到返回 None"""
if _generic_class_names is None or class_name is None: if _generic_class_names is None or class_name is None:
return 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 i: t.CSizeT
for i in range(cn): 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: if nm is not None and string.strcmp(nm, class_name) == 0:
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换 sha1_val: str = sha1s_local.get(i)
sha1_val: str = _generic_class_sha1s.get(i)
return sha1_val return sha1_val
return None return None
@@ -2191,7 +2200,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
return _translate_renum_def(trans, cd) return _translate_renum_def(trans, cd)
# 泛型类:存储为模板,不发射 IR等实例化时特化 # 泛型类:存储为模板,不发射 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) _register_generic_template(pool, cd, trans.ModuleSha1)
return 0 return 0

View File

@@ -420,7 +420,14 @@ def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb" # 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================ # ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: 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: if trans is None:
return None return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool") 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") alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
if alloca is not None: if alloca is not None:
return alloca 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 comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
if comparators is None or comparators.__len__() == 0: if comparators is None or comparators.__len__() == 0:
return None return None
rhs_node: ast.AST | t.CPtr = comparators.get(0)
rhs: llvmlite.Value | t.CPtr = translate_value( 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: if rhs is None:
return 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 且对应结构体变量 === # === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter用 alloca 指针尝试重载 # 对于值类型变量(如 cnt: Counter用 alloca 指针尝试重载
lhs_node: ast.AST | t.CPtr = cmp.left # None 比较跳过重载(走身份比较)
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not 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) nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None: if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( 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) === # === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较 # Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload( # None 比较跳过重载(走身份比较)
pool, builder, mod, lhs, rhs, op, trans, 1) if cmp_with_none == 0:
if ovl_result2 is not None: ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
return ovl_result2 pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
predicate: int = llvmlite.ICMP_SLT predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt: if op == ast.OpKind.Lt:
@@ -875,8 +926,12 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
return None return None
if uo.op == ast.OpKind.USub: if uo.op == ast.OpKind.USub:
# -x = 0 - x # -x
# 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配) # 浮点类型用 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) operand_bits: int = get_llvm_type_bits(operand.Ty)
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
if operand_bits == 64: 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, def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
name: str, name: str,
from_imports: str) -> int: from_imports: str,
"""跨模块查找 CDefine 常量,返回值或 -1未找到""" imported_modules: str = None) -> int:
if name is None or from_imports is None: """跨模块查找 CDefine 常量,返回值或 -1未找到
stdio.printf("[XMCD] FAIL name=%s reason=from_imports_is_none\n", name)
查找顺序:
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 return -1
if from_imports is None:
from_imports = ""
# 1. 从 from_imports 查找名称对应的模块名 # 1. 从 from_imports 查找名称对应的模块名
# 先尝试精确匹配allow_star_fallback=0避免 CDefine 常量被 star import 误导 # 先尝试精确匹配allow_star_fallback=0避免 CDefine 常量被 star import 误导
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0) mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
if mod_name_raw is None: if mod_name_raw is None:
# 精确匹配失败:尝试 star import 回退 # 精确匹配失败:尝试 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) 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 return -1
stdio.printf("[XMCD] star_fallback name=%s mod=%s\n", name, mod_name_raw) # 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
# 打印 from_imports 用于诊断精确匹配失败原因 # 设置 result_found=0 跳过步骤 3-9直接到 9.6
fi_len: t.CSizeT = string.strlen(from_imports) HandlesType.set_cdefine_found(0)
stdio.printf("[XMCD] from_imports (len=%d): %s\n", fi_len, from_imports) # 直接遍历 imported_modules
else: im_len: t.CSizeT = string.strlen(imported_modules)
stdio.printf("[XMCD] exact_match name=%s mod=%s\n", name, mod_name_raw) 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 返回的是内部指针) # 2. 复制模块名到新缓冲区lookup_from_import 返回的是内部指针)
# 截断于空格、null、或 ':'(别名格式的分隔符) # 截断于空格、null、或 ':'(别名格式的分隔符)
@@ -1161,7 +1260,6 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
if sha1 is None: if sha1 is None:
sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod) sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod)
if sha1 is None: if sha1 is None:
stdio.printf("[XMCD] FAIL name=%s base_mod=%s reason=sha1_not_found\n", name, base_mod)
return -1 return -1
# 5. 获取 temp_dir # 5. 获取 temp_dir
@@ -1529,6 +1627,45 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
stdlib.free(pyi_buf) 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 表中 # 10. 缓存到当前模块的 CDefine 表中
if result_found != 0: if result_found != 0:
HandlesType.register_cdefine_constant(pool, name, result_val) HandlesType.register_cdefine_constant(pool, name, result_val)
@@ -1592,8 +1729,9 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
# 仅对符合 CDefine 命名约定ALL_CAPS的名字触发跨模块查找 # 仅对符合 CDefine 命名约定ALL_CAPS的名字触发跨模块查找
# 避免对普通变量名mb/name/ptr/pool/self 等)误触发,造成严重性能损耗 # 避免对普通变量名mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
if _is_cdefine_name_pattern(nm_id) != 0: if _is_cdefine_name_pattern(nm_id) != 0:
if trans is not None and trans._from_imports is not None: if trans is not None:
cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports) cdef_val = _lookup_cross_module_cdefine(
pool, nm_id, trans._from_imports, trans._imported_modules)
if HandlesType.is_cdefine_found() != 0: if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val) return llvmlite.const_int32(pool, cdef_val)
@@ -2121,6 +2259,92 @@ def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退) # ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None # 返回: 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, def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | 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 = llvmlite.Int8(pool)
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明 # 确保 use_ty 中的跨模块结构体类型有 opaque 声明
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty) 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) ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
if ext_gv is not None: if ext_gv is not None:
ext_gv.Linkage = "external" if is_t_type_obj != 0:
ext_gv.Initializer = None # 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) llvmlite.module_add_global(mod, ext_gv)
return make_global_ref(pool, at.attr, use_ty) return make_global_ref(pool, at.attr, use_ty)
@@ -2302,6 +2549,24 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
is_submod = 1 is_submod = 1
if is_submod != 0: if is_submod != 0:
return None # 是子模块,交给嵌套属性访问路径 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* 作为类型提示 (模块级变量通常存储指针) # 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global( 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) cdef_val_nested = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
if HandlesType.is_cdefine_found() != 0: if HandlesType.is_cdefine_found() != 0:
return llvmlite.const_int32(pool, cdef_val_nested) return llvmlite.const_int32(pool, cdef_val_nested)
# CDefine 未找到: 创建前向引用 (CDefine 通常是 i32) # CDefine 未找到: 报 FATAL 错误(不再创建未定义的全局变量引用)
i32_ty_fwd: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) # 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
gv_ref_fwd: llvmlite.Value | t.CPtr = make_global_ref( # 导致 llc 报 "use of undefined value" 错误。
pool, at.attr, i32_ty_fwd) # 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
if gv_ref_fwd is not None and gv_ref_fwd.Ty is not None: if _is_cdefine_name_pattern(at.attr) != 0:
load_ty_fwd: llvmlite.LLVMType | t.CPtr = gv_ref_fwd.Ty.Pointee err_buf_ma: str = pool.alloc(256)
if load_ty_fwd is not None: if err_buf_ma is not None:
return llvmlite.build_load(builder, load_ty_fwd, gv_ref_fwd) 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 结构体) # 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None 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) builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
if alias_ptr is not None: if alias_ptr is not None:
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr) return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找 # 按需过滤: struct_ty 为 i8简化联合类型时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类 # 传递 SHA1 以区分跨模块同名类
if field_info is None: if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not 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 cur_sha1: str = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class( field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1) var_entry.AnnotClassName, at.attr, cur_sha1)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 按需过滤成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8) # 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败 # 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None: if field_info is not None:
@@ -2427,33 +2699,11 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
if casted_ptr is not None: if casted_ptr is not None:
obj_ptr = casted_ptr obj_ptr = casted_ptr
struct_ty = annot_se.Ty struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保 # 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用 # 注解类型结构体定义在当前模块可用
# (原始 struct_ty 可能是 i8ensure 无效) # (原始 struct_ty 可能是 i8ensure 无效)
HandlesStruct.ensure_struct_def_in_module( HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty) 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: if field_info is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
# 联合体bitcast obj_ptr 到 field_ty* 后 load # 联合体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) annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty) return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
return loaded_val 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 return None
case _: case _:
return None 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: 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) nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_wma.id is not None: 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* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global( 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) HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr) struct_ty, at.attr)
# 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找 # 按需过滤: struct_ty 为 i8简化联合类型时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类 # 传递 SHA1 以区分跨模块同名类
if field_info is None: if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not 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 cur_sha1 = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class( field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1) var_entry.AnnotClassName, at.attr, cur_sha1)
# 回退 1 成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 按需过滤成功bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
# 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8) # 原始 struct_ty 可能是 i8X|t.CPtr 简化为 Ptr(i8)
# 需用实际结构体类型做 GEP否则 GEP i8 失败 # 需用实际结构体类型做 GEP否则 GEP i8 失败
if field_info is not None: 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: if casted_ptr is not None:
obj_ptr = casted_ptr obj_ptr = casted_ptr
struct_ty = annot_se.Ty struct_ty = annot_se.Ty
# 回退 1 更新 struct_ty 后,需重新确保 # 按需过滤更新 struct_ty 后,需重新确保
# 注解类型结构体定义在当前模块可用 # 注解类型结构体定义在当前模块可用
HandlesStruct.ensure_struct_def_in_module( HandlesStruct.ensure_struct_def_in_module(
pool, mod, annot_se.Ty) 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: if field_info is not None:
# 联合体bitcast obj_ptr 到 field_ty*(字段指针用于 store # 联合体bitcast obj_ptr 到 field_ty*(字段指针用于 store
if HandlesStruct.is_union_by_type(struct_ty) == 1: if HandlesStruct.is_union_by_type(struct_ty) == 1:
@@ -2707,6 +2966,28 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
# 普通结构体GEP 获取字段指针 # 普通结构体GEP 获取字段指针
return llvmlite.build_gep_struct( return llvmlite.build_gep_struct(
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index) 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 return None
case _: case _:
return None return None

View File

@@ -8,6 +8,7 @@ import stdio
import stdlib import stdlib
import sys import sys
import viperlib import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT 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) "__before_init__", storage_ptr, None, 0, trans)
# 如果有 __init__调用 __init__(ptr, args...) # 如果有 __init__调用 __init__(ptr, args...)
# 注意: 仅依据 ctor_entry.HasInit 判断,不强制推断
# JsonValue 等类有 __new__ 但无 __init__强制推断会导致 undefined reference
has_init_flag: int = 0 has_init_flag: int = 0
if ctor_entry is not None: if ctor_entry is not None:
has_init_flag = ctor_entry.HasInit 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: if has_init_flag != 0:
# 翻译构造函数参数 # 翻译构造函数参数
arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32) 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, _call_method_on_ptr(pool, builder, mod, class_name,
"__init__", storage_ptr, arg_vals, real_arg_count, trans) "__init__", storage_ptr, arg_vals, real_arg_count, trans)
else: elif has_new_flag == 0:
# 无 __init__逐字段 store 参数值 # 无 __init__ 且无 __new__:逐字段 store 参数值
# (有 __new__ 时参数已由 __new__ 消费,不应存入字段)
for ai in range(can): for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai) arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None: if arg is None:
@@ -2974,6 +2974,262 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 与 _infer_external_func_ret_ty 的默认策略保持一致 # 与 _infer_external_func_ret_ty 的默认策略保持一致
return llvmlite.Int32(pool) 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) # _translate_method_call - 翻译方法调用 obj.method(args)
# #
@@ -3069,8 +3325,14 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr,
if frt is not None: if frt is not None:
call_ret_ty = frt call_ret_ty = frt
else: else:
# found_func 为 Nonestub 未注入):根据方法名推断返回类型 # found_func 为 Nonestub 未注入):尝试从 text.ll 查找实际返回类型
call_ret_ty = _infer_method_ret_ty(pool, method_name) # 避免指针返回值被 _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 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*) # - 整数 → 指针: inttoptr (如 t.CPtr(int_val) → i8*)
# - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8) # - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8)
# - 指针 → 指针: bitcast (如 t.CPtr(ptr_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, def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | 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) val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
target_bits: int = HandlesExpr.get_llvm_type_bits(target_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 # 整数 → 整数: trunc/zext
if val_bits != 0 and target_bits != 0: if val_bits != 0 and target_bits != 0:
@@ -3542,12 +3809,29 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
else: else:
return llvmlite.build_trunc(builder, val, target_ty) 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 # 指针 → 整数: 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) return llvmlite.build_ptrtoint(builder, val, target_ty)
# 整数 → 指针: inttoptr # 整数 → 指针: inttoptr(必须排除浮点 target浮点 target_bits 也是 0
if val_bits != 0 and target_bits == 0: if val_bits != 0 and target_bits == 0 and target_fbits == 0:
return llvmlite.build_inttoptr(builder, val, target_ty) return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 指针: bitcast # 指针 → 指针: bitcast
@@ -3763,6 +4047,28 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
if cargs is not None: if cargs is not None:
can = cargs.__len__() 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) 等 # 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等
# 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用) # 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用)
# 支持: # 支持:
@@ -3805,6 +4111,19 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# t.XXX 类型转换已确认但不满足参数条件:返回 None不跌落到跨模块 FATAL # t.XXX 类型转换已确认但不满足参数条件:返回 None不跌落到跨模块 FATAL
return None 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 # 检测 ClassName.__sizeof__() — 返回结构体大小常量i64
# __sizeof__ 是内置方法,不走跨模块调用路径 # __sizeof__ 是内置方法,不走跨模块调用路径
# 支持: Argument.__sizeof__() 和 w32.win32file.WIN32_FIND_DATAA.__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 而非指针, # 模块属性前向引用回退为 i32)GEP base 会是 i32 而非指针,
# 导致 llc 报错 "base of getelementptr must be a pointer" # 导致 llc 报错 "base of getelementptr must be a pointer"
if HandlesExpr.is_ptr_type(obj_val.Ty) == 0: 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) return llvmlite.const_int64(pool, 0)
i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
# 注意: 必须用 const_int64 而非 ConstInt(...,"0") # 注意: 必须用 const_int64 而非 ConstInt(...,"0")
@@ -4049,7 +4363,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
llvmlite.value_set_next(tail3, arg_val3) llvmlite.value_set_next(tail3, arg_val3)
tail3 = arg_val3 tail3 = arg_val3
total3: int = 1 + can 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) _ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3)
return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) 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: trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1 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 init_val: llvmlite.Value | t.CPtr = start_val
if start_val is not None and start_val.Ty is not None: if start_val is not None and start_val.Ty is not None:
start_bits: int = HandlesExpr.get_llvm_type_bits(start_val.Ty) start_bits: int = HandlesExpr.get_llvm_type_bits(start_val.Ty)
if start_bits != 0 and start_bits != 32: if start_bits != 0 and start_bits != loop_var_bits:
init_val = llvmlite.build_trunc(builder, start_val, i32_ty) 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) llvmlite.build_store(builder, init_val, var_alloca)
# 6. 创建基本块: cond / body / incr / end使用 trans._label_counter不与 SSA 名共享) # 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 # 8. cond 块: load i, icmp slt i, stop, cond_br body/end
llvmlite.position_at_end(builder, cond_bb) 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 类型 # 类型对齐: stop_val 可能是 i64 (如 range(strlen(s))),需将 cur_i 提升到 stop_val 类型
cmp_lhs: llvmlite.Value | t.CPtr = cur_i cmp_lhs: llvmlite.Value | t.CPtr = cur_i
cmp_rhs: llvmlite.Value | t.CPtr = stop_val cmp_rhs: llvmlite.Value | t.CPtr = stop_val
@@ -184,7 +198,7 @@ class ForHandle(HandlesBase.Mixin):
if cur_bits < stop_bits: if cur_bits < stop_bits:
cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty) cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty)
else: 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( cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs) builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs)
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb) 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 # 10. incr 块: i = i + step, 跳回 cond
llvmlite.position_at_end(builder, incr_bb) llvmlite.position_at_end(builder, incr_bb)
cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca) cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca)
# 类型对齐: step_val 可能是 i64需截断到 i32 与 cur_i2 类型一致 # 类型对齐: step_val 可能是 i32/i64需对齐到 loop_var_ty 与 cur_i2 类型一致
incr_step: llvmlite.Value | t.CPtr = step_val incr_step: llvmlite.Value | t.CPtr = step_val
if step_val is not None and step_val.Ty is not None: if step_val is not None and step_val.Ty is not None:
step_bits: int = HandlesExpr.get_llvm_type_bits(step_val.Ty) step_bits: int = HandlesExpr.get_llvm_type_bits(step_val.Ty)
cur2_bits: int = HandlesExpr.get_llvm_type_bits(cur_i2.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 != 0 and cur2_bits != 0 and step_bits != cur2_bits:
if 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: 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) next_i: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_i2, incr_step)
llvmlite.build_store(builder, next_i, var_alloca) llvmlite.build_store(builder, next_i, var_alloca)
llvmlite.build_br(builder, cond_bb) llvmlite.build_br(builder, cond_bb)

View File

@@ -58,12 +58,17 @@ class IfHandle(HandlesBase.Mixin):
trans._funcs, trans._func_count, trans) trans._funcs, trans._func_count, trans)
# 2. 转换为 i1 条件 # 2. 转换为 i1 条件
# Compare/Not 表达式已返回 i1直接使用其他类型与 0 比较 # Compare/Not 表达式已返回 i1直接使用指针类型与 null 比较;其他类型与 0 比较
if cond_val is None: if cond_val is None:
cond_val = llvmlite.const_int32(pool, 0) cond_val = llvmlite.const_int32(pool, 0)
cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty) cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1: if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val 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: else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp( cond_i1 = llvmlite.build_icmp(

View File

@@ -559,21 +559,6 @@ class ImportsHandle(HandlesBase.Mixin):
resolved: str = _resolve_relative_module( resolved: str = _resolve_relative_module(
self.Trans.Pool, self.Trans.CurrentPackage, self.Trans.Pool, self.Trans.CurrentPackage,
impf.level, impf.module) 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: if resolved is not None:
names: list[ast.AST | t.CPtr] | t.CPtr = impf.names names: list[ast.AST | t.CPtr] | t.CPtr = impf.names
if names is not None: if names is not None:

View File

@@ -129,11 +129,136 @@ def translate_children(trans: HT.Translator | t.CPtr,
return added_total 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 全局变量 # handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量
# #
# 当用户已定义 main无 wrapper main builder模块级 # 当用户已定义 main无 wrapper main builder模块级
# AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域 # AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域
# 支持整数、列表字面量、字符串字面量初始值
# ============================================================ # ============================================================
def handle_module_level_var(trans: HT.Translator | t.CPtr, def handle_module_level_var(trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int: 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_name: str = None
var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
init_val: t.CInt64T = 0 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: if k == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) 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) pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
if resolved is not None: if resolved is not None:
var_ty = resolved var_ty = resolved
# 解析初始值 # 解析初始值(支持整数、列表字面量、字符串字面量)
if aa.value is not None and aa.value.kind() == ast.ASTKind.Constant: if aa.value is not None:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value) val_kind: int = aa.value.kind()
if cn.const_kind == ast.CONST_INT: if val_kind == ast.ASTKind.Constant:
init_val = cn.int_val cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value)
has_init = 1 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: elif k == ast.ASTKind.Assign:
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node) asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
if asgn is None or asgn.targets is None: 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 return 0
nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0) nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0)
var_name = nm2.id var_name = nm2.id
if asgn.value is not None and asgn.value.kind() == ast.ASTKind.Constant: if asgn.value is not None:
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value) val_kind2: int = asgn.value.kind()
if cn2.const_kind == ast.CONST_INT: if val_kind2 == ast.ASTKind.Constant:
init_val = cn2.int_val cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value)
has_init = 1 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: if var_name is None:
return 0 return 0
@@ -225,11 +368,26 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
# 设置初始值(有初始值时清除 external linkage因为 LLVM 22+ 不允许 external global 带初始值) # 设置初始值(有初始值时清除 external linkage因为 LLVM 22+ 不允许 external global 带初始值)
gv.Linkage = None gv.Linkage = None
if has_init != 0: if init_kind == 1:
# 整数初始值
init_buf: t.CChar | t.CPtr = pool.alloc(48) init_buf: t.CChar | t.CPtr = pool.alloc(48)
if init_buf is not None: if init_buf is not None:
viperlib.snprintf(init_buf, 48, "%lld", init_val) viperlib.snprintf(init_buf, 48, "%lld", init_val)
gv.Initializer = init_buf 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: else:
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer
# 整数/聚合类型也兼容 zeroinitializer避免 "integer constant must have integer type" # 整数/聚合类型也兼容 zeroinitializer避免 "integer constant must have integer type"

View File

@@ -39,8 +39,10 @@ class ReturnHandle(HandlesBase.Mixin):
val: llvmlite.Value | t.CPtr = None val: llvmlite.Value | t.CPtr = None
if rt.value is not None: if rt.value is not None:
# return self: 直接返回指针self 是 SSA 参数 Ptr(struct_ty) # return self: 返回 self 指针本身(Ptr(StructTy),而非结构体值
# translate_value 会 load 得到结构体值,但 return self 需要指针本身 # translate_value 会 load 得到结构体值,但 return self 需要指针
# 注意: lookup_var 返回的是存储 self 的 alloca (Ptr(Ptr(StructTy)))
# 需要 load 得到实际的 self 指针 (Ptr(StructTy))
if rt.value.kind() == ast.ASTKind.Name: if rt.value.kind() == ast.ASTKind.Name:
ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rt.value) 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: 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_ptr: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
self.Trans.SymTab, "self") self.Trans.SymTab, "self")
if self_ptr is not None: 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: if val is None:
val = HandlesExpr.translate_value( val = HandlesExpr.translate_value(
builder, pool, mod, rt.value, builder, pool, mod, rt.value,

View File

@@ -2,6 +2,7 @@ import t, c
from stdint import * from stdint import *
import memhub import memhub
import string import string
import stdlib
import llvmlite import llvmlite
import stdio import stdio
import ast import ast
@@ -521,10 +522,28 @@ def lookup_field_by_class(class_name: str,
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个 # 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
entry = find_struct(class_name) entry = find_struct(class_name)
if entry is None: if entry is None:
# 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中 # 诊断:目标类未注册
for diag_i in range(_struct_count): fb_lfbc: t.CChar | t.CPtr = VLogger.fmt_buf()
diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i) 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 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): for fi in range(entry.FieldCount):
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi) fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
if fe is not None and fe.Name is not None: 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 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 类型 # get_struct_type — 按类名获取结构体的 LLVM 类型
# ============================================================ # ============================================================

View File

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

View File

@@ -9,6 +9,8 @@ import sys
import stdlib import stdlib
import viperlib import viperlib
import w32.fileio as fileio 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.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesStruct as HandlesStruct
@@ -143,6 +145,95 @@ def clear_cdefine_constants() -> None:
_g_cdefine_values = 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, def set_generic_context(tp_names: list[str] | t.CPtr,
type_args: 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) mod_name: str = HandlesImports.lookup_from_import(from_imports, nm.id)
if mod_name is not None: if mod_name is not None:
return map_t_type(pool, nm.id) 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 return None
# Attribute 节点: t.CInt, t.CUInt64T, namespace_defs.PlainStruct 等 # 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: if mod_struct_ty is not None:
return mod_struct_ty return mod_struct_ty
return None 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 return None
# BinOp 节点: 联合类型注解 A | B # BinOp 节点: 联合类型注解 A | B
@@ -929,7 +1032,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
# value 是 Name泛型类名slice 是类型实参 # value 是 Name泛型类名slice 是类型实参
if sub.value.kind() == ast.ASTKind.Name: if sub.value.kind() == ast.ASTKind.Name:
gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value) 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 gen_nm.id is not None:
if string.strcmp(gen_nm.id, "tuple") == 0:
return None
# 先收集类型实参名,再分配缓冲区拼接 mangled name # 先收集类型实参名,再分配缓冲区拼接 mangled name
gen_arg_names: list[str] | t.CPtr = list[str](pool, 4) gen_arg_names: list[str] | t.CPtr = list[str](pool, 4)
if sub.slice.kind() == ast.ASTKind.Tuple: 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) pool, elem_node, imported_modules, from_imports, trans)
if elem_ty is None: if elem_ty is None:
fatal_type_error(elem_node, "t.CArray 元素类型解析失败") 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, None] 等价于单参数形式 → Ptr(elem_ty)
# t.CArray[elem_ty, NAME] 其中 NAME: t.CDefine = value → [value x 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 count_val: int = 0
if count_node.kind() == ast.ASTKind.Constant: if count_node.kind() == ast.ASTKind.Constant:
cnt_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node) cnt_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node)
if cnt_cn.const_kind == ast.CONST_NONE: if cnt_cn.const_kind == ast.CONST_NONE:
return llvmlite.Ptr(pool, elem_ty) return llvmlite.Ptr(pool, elem_ty)
if cnt_cn.const_kind != ast.CONST_INT: # 统一用 _eval_const_int_expr 计算常量整数表达式
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名") # 支持 Constant(INT)、Name(CDefine)、BinOp(2*MAX 等算术/位运算)
count_val = cnt_cn.int_val count_val = _eval_const_int_expr(count_node)
elif count_node.kind() == ast.ASTKind.Name: if _g_const_eval_ok == 0:
# CDefine 常量名查找 fatal_type_error(count_node, "t.CArray count 必须是整数常量、None、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 常量名")
if count_val <= 0: if count_val <= 0:
fatal_type_error(count_node, "t.CArray count 必须为正数") fatal_type_error(count_node, "t.CArray count 必须为正数")
# 创建 ArrayType不包装 Ptr # 创建 ArrayType不包装 Ptr

View File

@@ -78,10 +78,15 @@ class WhileHandle(HandlesBase.Mixin):
if cond_val is None: if cond_val is None:
cond_val = llvmlite.const_int32(pool, 0) 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) cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1: if cond_bits == 1:
cond_i1: llvmlite.Value | t.CPtr = cond_val 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: else:
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0) zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp( cond_i1 = llvmlite.build_icmp(

View File

@@ -73,7 +73,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
sha1_set: str, set_count: int, sha1_set: str, set_count: int,
class_names_buf: bytes, class_names_buf: bytes,
parent_names_buf: bytes, parent_names_buf: bytes,
def_sha1s_buf: bytes) -> int: def_sha1s_buf: bytes,
generic_file_indices: t.CPtr,
generic_file_count_box: t.CPtr) -> int:
"""预扫描 includes 文件,收集类继承关系""" """预扫描 includes 文件,收集类继承关系"""
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
return -1 return -1
@@ -88,6 +90,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
continue continue
sha1_e: str = entry.Sha1 sha1_e: str = entry.Sha1
file_has_generic: int = 0
# 按需翻译过滤 # 按需翻译过滤
in_set: int = 0 in_set: int = 0
@@ -147,6 +150,22 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
if cd is None or cd.name is None: if cd is None or cd.name is None:
continue continue
# 检测泛型类(有 type_params记录文件索引以供 Phase 1a 预注册
if file_has_generic == 0:
tp_scan: list[str] | t.CPtr = cd.type_params
if tp_scan is not None:
file_has_generic = 1
if generic_file_count_box is not None:
gfc_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr))
if gfc_ptr_scan is not None:
gfc_scan: int = gfc_ptr_scan[0]
if gfc_scan < MAX_FILE_DEPS and generic_file_indices is not None:
gi_addr_scan: t.CUInt64T = t.CUInt64T(generic_file_indices) + gfc_scan * 4
gi_ptr_scan: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_scan, t.CPtr))
if gi_ptr_scan is not None:
gi_ptr_scan[0] = i
gfc_ptr_scan[0] = gfc_scan + 1
if class_count >= MAX_CLASSES: if class_count >= MAX_CLASSES:
break break
@@ -513,14 +532,15 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
continue continue
string.strcpy(sha1_set + set_count * 17, se_ent.Sha1) string.strcpy(sha1_set + set_count * 17, se_ent.Sha1)
set_count += 1 set_count += 1
# 先填充全局 SHA1 映射存储器(即使 set_count==0 也填充,确保 Phase2 能获取 includes 条目)
# 注意PopulateSha1MapStore 会先 free 旧存储器再重新分配
StubMerger.PopulateSha1MapStore(result)
# 写入 _sha1_map.txt人类可读输出程序内部不读取机器分析使用 PopulateSha1MapStore 内存存储器)
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
if set_count <= 0: if set_count <= 0:
VLogger.warning("扫描结果无有效 SHA1跳过 Phase1项目可能无 includes 依赖)", "Phase1") VLogger.warning("扫描结果无有效 SHA1跳过 Phase1项目可能无 includes 依赖)", "Phase1")
stdlib.free(sha1_set) stdlib.free(sha1_set)
return 0 return 0
# 写入 _sha1_map.txt人类可读输出程序内部不读取机器分析使用 PopulateSha1MapStore 内存存储器)
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
# 填充全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
StubMerger.PopulateSha1MapStore(result)
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) # 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
td_len_p1map: t.CSizeT = string.strlen(temp_dir) td_len_p1map: t.CSizeT = string.strlen(temp_dir)
p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
@@ -621,6 +641,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
p1a_registered += 1 p1a_registered += 1
# 生成 .deps.txt记录依赖模块名供依赖图按需翻译使用 # 生成 .deps.txt记录依赖模块名供依赖图按需翻译使用
# 同时填充内存依赖存储器PopulateIncludesDeps供 _BuildReachableSha1Set
# 直接从内存读取,不依赖 .deps.txt 过程文件
td_len_a: t.CSizeT = string.strlen(temp_dir) td_len_a: t.CSizeT = string.strlen(temp_dir)
deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt") deps_path_a: str = StubMerger._sliced_path(temp_dir, td_len_a, sha1_a, "deps.txt")
if deps_path_a is not None: if deps_path_a is not None:
@@ -631,6 +653,9 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
df_a.write(tr_a._imported_modules, dl_a) df_a.write(tr_a._imported_modules, dl_a)
df_a.close() df_a.close()
stdlib.free(deps_path_a) stdlib.free(deps_path_a)
# 填充内存依赖存储器(供 _BuildReachableSha1Set 使用,不读 .deps.txt
if tr_a._imported_modules is not None:
StubMerger.PopulateIncludesDeps(sha1_a, tr_a._imported_modules)
# 释放 Translator 的 C malloc 资源 # 释放 Translator 的 C malloc 资源
if tr_a._global_names is not None: if tr_a._global_names is not None:
@@ -689,6 +714,12 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
def_sha1s_buf: bytes = mb.alloc(MAX_CLASSES * SHA1_LEN) def_sha1s_buf: bytes = mb.alloc(MAX_CLASSES * SHA1_LEN)
topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS) topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS)
topo_count_box: t.CPtr = mb.alloc(4) topo_count_box: t.CPtr = mb.alloc(4)
generic_file_indices: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS)
generic_file_count_box: t.CPtr = mb.alloc(4)
if generic_file_indices is not None:
string.memset(generic_file_indices, 0, 4 * MAX_FILE_DEPS)
if generic_file_count_box is not None:
string.memset(generic_file_count_box, 0, 4)
topo_count: int = 0 topo_count: int = 0
if class_names_buf is not None and parent_names_buf is not None and def_sha1s_buf is not None and topo_order is not None and topo_count_box is not None: if class_names_buf is not None and parent_names_buf is not None and def_sha1s_buf is not None and topo_order is not None and topo_count_box is not None:
@@ -697,11 +728,16 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
string.memset(def_sha1s_buf, 0, MAX_CLASSES * SHA1_LEN) string.memset(def_sha1s_buf, 0, MAX_CLASSES * SHA1_LEN)
string.memset(topo_order, 0, 4 * MAX_FILE_DEPS) string.memset(topo_order, 0, 4 * MAX_FILE_DEPS)
string.memset(topo_count_box, 0, 4) string.memset(topo_count_box, 0, 4)
if generic_file_indices is not None:
string.memset(generic_file_indices, 0, 4 * MAX_FILE_DEPS)
if generic_file_count_box is not None:
string.memset(generic_file_count_box, 0, 4)
class_count_scanned: int = _ScanClassInheritance( class_count_scanned: int = _ScanClassInheritance(
mb, result, reachable_set, reachable_count, mb, result, reachable_set, reachable_count,
use_reachable, sha1_set, set_count, use_reachable, sha1_set, set_count,
class_names_buf, parent_names_buf, def_sha1s_buf) class_names_buf, parent_names_buf, def_sha1s_buf,
generic_file_indices, generic_file_count_box)
if class_count_scanned > 0: if class_count_scanned > 0:
ret_topo: int = _TopoSortFiles( ret_topo: int = _TopoSortFiles(
@@ -726,6 +762,87 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1") VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
topo_count = 0 topo_count = 0
# ============================================================
# Phase 1a-pre-generic: 预注册所有泛型类模板
#
# 在 Phase 1astruct 注册)之前执行,确保所有泛型类模板
#(如 GSListNode[T]、GSList[T])已注册。这样当其他文件的
# 结构体字段使用 GSListNode[Value] 等特化类型时,模板能被找到。
# ============================================================
generic_file_count: int = 0
if generic_file_count_box is not None:
gfc_box_ptr: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(generic_file_count_box, t.CPtr))
if gfc_box_ptr is not None:
generic_file_count = gfc_box_ptr[0]
if generic_file_count > 0 and generic_file_indices is not None:
fb_gpre: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_gpre is not None:
viperlib.snprintf(fb_gpre, 1024, "预注册 %d 个泛型类模板文件", generic_file_count)
VLogger.info(fb_gpre, "Phase1")
for gi in range(generic_file_count):
gi_addr_p: t.CUInt64T = t.CUInt64T(generic_file_indices) + gi * 4
gi_ptr_p: t.CPtr = (t.CInt | t.CPtr)(t.CVoid(gi_addr_p, t.CPtr))
if gi_ptr_p is None:
continue
file_idx_gen: int = gi_ptr_p[0]
gen_entry_addr: t.CUInt64T = t.CUInt64T(result.Entries) + file_idx_gen * entry_size
gen_entry: IncludesScanner.FileEntry | t.CPtr = (IncludesScanner.FileEntry | t.CPtr)(t.CVoid(gen_entry_addr, t.CPtr))
if gen_entry is None or gen_entry.Sha1 is None:
continue
gen_path: str = gen_entry.Path
gen_f: fileio.File | t.CPtr = fileio.File(gen_path, fileio.MODE.R)
if gen_f.closed:
continue
gen_src: bytes = stdlib.malloc(SRC_BUF_SIZE)
if gen_src is None:
gen_f.close()
continue
gen_br: LONG = gen_f.read_all(gen_src, SRC_BUF_SIZE)
gen_f.close()
if gen_br <= 0:
stdlib.free(gen_src)
continue
if gen_br < SRC_BUF_SIZE:
gen_src[gen_br] = 0
else:
gen_src[SRC_BUF_SIZE - 1] = 0
gen_lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
if gen_lx is None:
stdlib.free(gen_src)
continue
ast._lexer_init(gen_lx, gen_src, mb)
gen_tokens: ast.Token | t.CPtr = ast.tokenize(gen_lx)
gen_tree: ast.AST | t.CPtr = ast.parse_tokens(mb, gen_tokens)
if gen_tree is None:
stdlib.free(gen_src)
continue
gen_tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if gen_tr is None:
stdlib.free(gen_src)
continue
gen_tr.ModuleSha1 = gen_entry.Sha1
gen_tr._declare_only = 1
gen_tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, gen_entry.RelPath)
HandlesType.set_current_file(gen_path)
HandlesType.set_current_module_sha1(gen_entry.Sha1)
HandlesType.clear_cdefine_constants()
HandlesStruct.reset_visible_structs(mb, 0)
gen_tr.translate(gen_tree)
if gen_tr._global_names is not None:
stdlib.free(gen_tr._global_names)
if gen_tr._nonlocal_names is not None:
stdlib.free(gen_tr._nonlocal_names)
stdlib.free(gen_src)
# 遍历文件:优先使用拓扑顺序,回退到字母序 # 遍历文件:优先使用拓扑顺序,回退到字母序
iter_count: int = topo_count if topo_count > 0 else result.Count iter_count: int = topo_count if topo_count > 0 else result.Count
for i in range(iter_count): for i in range(iter_count):
@@ -834,6 +951,10 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
mb.free(topo_order) mb.free(topo_order)
if topo_count_box is not None: if topo_count_box is not None:
mb.free(topo_count_box) mb.free(topo_count_box)
if generic_file_indices is not None:
mb.free(generic_file_indices)
if generic_file_count_box is not None:
mb.free(generic_file_count_box)
# ============================================================ # ============================================================
# Phase 1b: 全量翻译struct 已注册,走 existing 路径翻译方法体) # Phase 1b: 全量翻译struct 已注册,走 existing 路径翻译方法体)

View File

@@ -19,6 +19,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.BuildPipeline as BuildPipeline import lib.core.BuildPipeline as BuildPipeline
import lib.core.IncludesScanner as IncludesScanner
import lib.core.StubMerger as StubMerger import lib.core.StubMerger as StubMerger
import lib.Projectrans.Utils as Utils import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config import lib.Projectrans.Config as Config
@@ -207,6 +208,19 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
BuildPipeline.ensure_dir(temp_dir) BuildPipeline.ensure_dir(temp_dir)
BuildPipeline.ensure_dir(output_dir) BuildPipeline.ensure_dir(output_dir)
# === 0.5 扫描 includes 目录填充 Sha1Store消除 Phase1 依赖)===
# Phase2 不依赖 Phase1 的副作用:如果 Sha1Store 为空Phase1 未运行或提前返回),
# 自行扫描 includes 目录并填充 Sha1Store确保后续"编译缺失 includes"逻辑能正常工作
if includes_dir is not None:
if StubMerger.GetSha1StoreCount() == 0:
inc_scan_result: IncludesScanner.ScanResult | t.CPtr = IncludesScanner.scan_includes(mb, includes_dir)
if inc_scan_result is not None:
inc_filled: int = StubMerger.PopulateSha1MapStore(inc_scan_result)
fb_inc: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc is not None:
viperlib.snprintf(fb_inc, 1024, "Phase2 自主填充 includes Sha1Store: %d", inc_filled)
VLogger.info(fb_inc, "project")
# 追加 App 源文件 SHA1 到 _sha1_map.txt人类可读输出程序内部不读取此文件 # 追加 App 源文件 SHA1 到 _sha1_map.txt人类可读输出程序内部不读取此文件
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py # 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py
# 同步追加到内存存储器AppendToSha1MapStore供跨模块 CDefine 查找) # 同步追加到内存存储器AppendToSha1MapStore供跨模块 CDefine 查找)
@@ -296,10 +310,62 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
# === 2. Phase A: 为每个文件生成 stub + text === # === 2. Phase A: 为每个文件生成 stub + text ===
# app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔)
# 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist实现按需编译
# 声明在 if 块外,确保 Phase B+ 和清理代码能访问
APP_DEPS_BUF_SIZE: t.CSizeT = 8192
app_deps_buf: bytes = None
app_deps_len: t.CSizeT = 0
if do_phase1 != 0: if do_phase1 != 0:
if log is not None: if log is not None:
log.banner("Phase A: 生成 stub + text") log.banner("Phase A: 生成 stub + text")
# === Phase A-pre-inc: includes 预注册 struct + 依赖填充 ===
# 必须在 App 源文件预注册之前执行App 源文件strict_mode=1可能继承
# 或引用 includes 中的 struct如 ast.AST。若 includes struct 未注册,
# App 源文件翻译会失败 → .obj 缺失 → undefined reference。
# 同时填充 PopulateIncludesDeps供 Phase B+ 的 _BuildReachableSha1Set 使用。
# 多遍扫描:解决跨文件继承问题(如 Constant(AST) 在 AST 未注册时被跳过)。
if includes_dir is not None and includes_binary_dir is not None:
store_count_inc_pre: int = StubMerger.GetSha1StoreCount()
if store_count_inc_pre > 0:
store_sha1_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
store_rel_inc_pre: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
inc_dir_len_pre: t.CSizeT = string.strlen(includes_dir)
deps_filled_pre: int = 0
PHASE_A_INC_MAX_PASSES: t.CInt = 3
for pass_inc_i in range(PHASE_A_INC_MAX_PASSES):
struct_count_before_inc: int = HandlesStruct.get_struct_count()
for si_inc in range(store_count_inc_pre):
inc_sha1_pre: str = store_sha1_inc_pre + t.CSizeT(si_inc) * 17
inc_rel_pre: str = store_rel_inc_pre + t.CSizeT(si_inc) * StubMerger.MAX_REL_PATH_LEN
if inc_rel_pre is None or inc_rel_pre[0] == '\0':
continue
rel_len_pre: t.CSizeT = string.strlen(inc_rel_pre)
full_path_pre: bytes = stdlib.malloc(inc_dir_len_pre + rel_len_pre + 2)
if full_path_pre is None:
continue
viperlib.snprintf(full_path_pre, inc_dir_len_pre + rel_len_pre + 2,
"%s/%s", includes_dir, inc_rel_pre)
pkg_inc_pre: str = HandlesImports.compute_package_from_relpath(mb, inc_rel_pre)
tr_inc_pre: HandlesTranslator.Translator | t.CPtr = BuildPipeline.TranslateFileGetTrans(
mb, full_path_pre, inc_sha1_pre, pkg_inc_pre, 1)
stdlib.free(full_path_pre)
if tr_inc_pre is not None:
# 依赖填充只在第一遍执行PopulateIncludesDeps 内部有去重)
if pass_inc_i == 0:
if tr_inc_pre._imported_modules is not None:
if StubMerger.PopulateIncludesDeps(inc_sha1_pre, tr_inc_pre._imported_modules) == 0:
deps_filled_pre = deps_filled_pre + 1
struct_count_after_inc: int = HandlesStruct.get_struct_count()
# 收敛检查:本遍没有新结构体注册 → 所有类已注册
if struct_count_after_inc == struct_count_before_inc:
break
fb_inc_pre: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc_pre is not None:
viperlib.snprintf(fb_inc_pre, 1024, "includes 预注册 + 依赖填充: %d/%d", deps_filled_pre, store_count_inc_pre)
VLogger.info(fb_inc_pre, "prePhaseA")
# === Phase A-pre: 预注册所有源文件的 struct/enum/union === # === Phase A-pre: 预注册所有源文件的 struct/enum/union ===
# 解决循环引用问题circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义 # 解决循环引用问题circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
# 仅注册 struct/enum/uniondeclare_only=1不翻译方法体 # 仅注册 struct/enum/uniondeclare_only=1不翻译方法体
@@ -334,6 +400,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
PHASE_A_IR_SIZE: t.CSizeT = 1048576 PHASE_A_IR_SIZE: t.CSizeT = 1048576
td_len_pa: t.CSizeT = string.strlen(temp_dir) td_len_pa: t.CSizeT = string.strlen(temp_dir)
# 设置 temp 目录路径,使 HandlesExprCall 能读取依赖模块的 text.ll
# 用于跨模块方法返回类型查找(避免指针返回值被截断为 i32
HandlesExprCall.SetTempDir(temp_dir)
# app_deps_buf: 收集所有 App 源文件的直接依赖模块名(空格分隔)
# 供 Phase B+ 的 _BuildReachableSha1Set 作为初始 worklist 使用
# 声明已在 if 块外,此处仅赋值
app_deps_buf = stdlib.malloc(APP_DEPS_BUF_SIZE)
app_deps_len = 0
if app_deps_buf is not None:
app_deps_buf[0] = '\0'
for i in range(file_count): for i in range(file_count):
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr)) ent: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea, t.CPtr))
@@ -394,8 +472,21 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
df_a.close() df_a.close()
stdlib.free(deps_path_a) stdlib.free(deps_path_a)
# 收集 _imported_modules 到 app_deps_buf供 Phase B+ 按需编译)
if tr_a._imported_modules is not None and app_deps_buf is not None:
im_len_a: t.CSizeT = string.strlen(tr_a._imported_modules)
if im_len_a > 0 and app_deps_len + im_len_a + 1 < APP_DEPS_BUF_SIZE:
if app_deps_len > 0:
app_deps_buf[app_deps_len] = ' '
app_deps_len = app_deps_len + 1
string.strcpy(app_deps_buf + app_deps_len, tr_a._imported_modules)
app_deps_len = app_deps_len + im_len_a
app_deps_buf[app_deps_len] = '\0'
if do_phase2 == 0: if do_phase2 == 0:
VLogger.info("Phase A 完成(仅 stub 生成)", "project") VLogger.info("Phase A 完成(仅 stub 生成)", "project")
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
stdlib.free(entries) stdlib.free(entries)
return 0 return 0
@@ -404,8 +495,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if log is not None: if log is not None:
log.banner("Phase B: 编译 .obj") log.banner("Phase B: 编译 .obj")
# 收集 .obj 路径 # 收集 .obj 路径(增大到 64KB避免 includes .obj 路径溢出导致链接丢失符号)
OBJ_PATHS_SIZE: t.CSizeT = 8192 # reachable_set/reachable_count: Phase B+ 填充Phase C 用于按需链接
reachable_set: bytes = None
reachable_count: int = 0
OBJ_PATHS_SIZE: t.CSizeT = 65536
obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE) obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE)
if obj_paths is None: if obj_paths is None:
return 1 return 1
@@ -457,6 +551,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
sys.exit(1) sys.exit(1)
compiled_count += 1 compiled_count += 1
# 显示编译完成的文件
fb_comp: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_comp is not None:
viperlib.snprintf(fb_comp, 1024, "[%d/%d] 编译完成: %s", i + 1, file_count, ent.Path)
VLogger.info(fb_comp, "PhaseB")
# 构造 .obj 路径,检测是否是 main 模块test_main.py 或 main.py # 构造 .obj 路径,检测是否是 main 模块test_main.py 或 main.py
od_len: t.CSizeT = string.strlen(output_dir) od_len: t.CSizeT = string.strlen(output_dir)
is_main_mod: int = 0 is_main_mod: int = 0
@@ -497,39 +596,85 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
stdlib.free(entries) stdlib.free(entries)
return 1 return 1
# === 3.5 编译缺失的 includes 文件 === # === 3.5 编译缺失的 includes 文件(按依赖图按需编译)===
# includes.binary 可能缺少某些 includes .obj如 testcheck.py # 只编译被 App 源文件直接/间接引用的 includes 文件(可达集合
# 这些文件被用户项目导入但未被 TransPyV 自身依赖Projectrans.py 未编译它们 # 而非遍历整个 includes 目录。通过 _BuildReachableSha1Set 构建依赖图
# 检测并编译缺失的 includes 文件到 output_dir加入链接命令。
# 数据来源StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件)
if includes_dir is not None and includes_binary_dir is not None: if includes_dir is not None and includes_binary_dir is not None:
inc_compiled: int = 0 inc_compiled: int = 0
td_len_mi: t.CSizeT = string.strlen(temp_dir) td_len_mi: t.CSizeT = string.strlen(temp_dir)
# 诊断日志:确认 Phase B+ 入口和 Sha1Store 状态
fb_pbp_enter: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_pbp_enter is not None:
viperlib.snprintf(fb_pbp_enter, 1024,
"PhaseB+ 入口: store_count=%d includes_binary_dir=%s",
StubMerger.GetSha1StoreCount(), includes_binary_dir)
VLogger.info(fb_pbp_enter, "PhaseB+")
# 创建 includes_binary_dir 目录(确保 .obj 能写入和 collect_obj_files 能扫描)
BuildPipeline.ensure_dir(includes_binary_dir)
# declare_only 预处理已移至 Phase A 之前Phase A-pre-inc
# 确保 Phase A 翻译 App 源文件时 includes struct 已注册。
# PopulateIncludesDeps 已在 Phase A-pre-inc 中填充。
# 构建可达 SHA1 集合(依赖图遍历)
# app_deps_buf 为 None 时回退到扫描 source_dir非递归
REACHABLE_SET_SIZE: t.CSizeT = t.CSizeT(StubMerger.MAX_INCLUDES_SHA1) * 17
reachable_set: bytes = stdlib.malloc(REACHABLE_SET_SIZE)
reachable_count: int = 0
if reachable_set is not None:
string.memset(reachable_set, 0, REACHABLE_SET_SIZE)
reachable_count = StubMerger._BuildReachableSha1Set(
mb, source_dir, temp_dir, reachable_set, app_deps_buf)
fb_reach: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_reach is not None:
viperlib.snprintf(fb_reach, 1024, "可达 includes SHA1: %d", reachable_count)
VLogger.info(fb_reach, "PhaseB+")
# fast-fail: 可达集合构建失败(依赖未找到),永不回退,直接终止
if reachable_count < 0:
VLogger.error("可达集合构建失败fast-fail终止编译", "PhaseB+")
return 1
else:
VLogger.error("reachable_set 分配失败,终止编译", "PhaseB+")
return 1
# 释放 app_deps_buf已构建完 reachable_set不再需要
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
app_deps_buf = None
# 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题) # 从全局存储器获取 SHA1/模块名/rel_path 数组(直接访问器,避免 box 解引用问题)
store_count_mi: int = StubMerger.GetSha1StoreCount() store_count_mi: int = StubMerger.GetSha1StoreCount()
if store_count_mi > 0: if store_count_mi > 0:
store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr() store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr() store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr()
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr() store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
for si_mi in range(store_count_mi): for si_mi in range(store_count_mi):
# 获取当前条目的 SHA1 和 rel_path # 获取当前条目的 SHA1 和 rel_path
inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17 inc_sha1_mi: str = store_sha1_arr_mi + t.CSizeT(si_mi) * 17
inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN inc_rel_mi: str = store_rel_arr_mi + t.CSizeT(si_mi) * StubMerger.MAX_REL_PATH_LEN
# 检查 .obj 是否已存在于 includes.binary # 按需编译过滤:只处理 reachable_set 中的 includes
# 永不回退reachable_count > 0 时严格按可达集合过滤
if reachable_count > 0:
if StubMerger._is_in_sha1_set(inc_sha1_mi, reachable_set, reachable_count) == 0:
continue
# 检查 .obj 是否已存在于 includes.binary切片子目录
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir) ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35) check_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
if check_pat_mi is None:
continue
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__()) check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
obj_exists_mi: int = 0 obj_exists_mi: int = 0
if check_fd_mi is not None: if check_pat_mi is not None and check_fd_mi is not None:
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__()) string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi) check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE: if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
w32.win32file.FindClose(check_h_mi) w32.win32file.FindClose(check_h_mi)
obj_exists_mi = 1 obj_exists_mi = 1
if obj_exists_mi != 0: if obj_exists_mi != 0:
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
stdlib.free(check_fd_mi)
continue continue
# .obj 不存在,需要编译 # .obj 不存在,需要编译
@@ -538,6 +683,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir) inc_dir_len_mi: t.CSizeT = string.strlen(includes_dir)
src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1) src_fp_mi: bytes = stdlib.malloc(inc_dir_len_mi + 1 + rel_path_len_mi + 1)
if src_fp_mi is None: if src_fp_mi is None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
continue continue
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
"%s/%s", includes_dir, inc_rel_mi) "%s/%s", includes_dir, inc_rel_mi)
@@ -558,7 +706,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
tbuf_mi[tbr_mi] = '\0' tbuf_mi[tbr_mi] = '\0'
else: else:
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0' tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
# 逐行扫描: 找 define 行中含 @" 的(混淆函数名) # 逐行扫描: 找 define 行即为实现文件
# (不再要求行中含 @",因为 memhub 等模块的函数名
# 如 @ca40915f11b8b6ad._align_up 不带引号但仍是实现文件)
tpos_mi: t.CSizeT = 0 tpos_mi: t.CSizeT = 0
while tpos_mi < tbr_mi: while tpos_mi < tbr_mi:
tls_mi: t.CSizeT = tpos_mi tls_mi: t.CSizeT = tpos_mi
@@ -570,21 +720,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
if tpos_mi < tbr_mi: if tpos_mi < tbr_mi:
tpos_mi += 1 tpos_mi += 1
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0: if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
# 临时在行尾加 \0 供 strstr 使用 is_decl_mi = 0
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi] break
tbuf_mi[tls_mi + tll_mi] = '\0'
if string.strstr(tbuf_mi + tls_mi, "@\"") is not None:
tbuf_mi[tls_mi + tll_mi] = saved_mi
is_decl_mi = 0
break
tbuf_mi[tls_mi + tll_mi] = saved_mi
stdlib.free(tbuf_mi) stdlib.free(tbuf_mi)
tf_mi.close() tf_mi.close()
stdlib.free(tpath_mi) stdlib.free(tpath_mi)
# is_decl_mi == -1: text.ll 不存在Phase1 未翻译此文件,可能不被项目导入 # is_decl_mi == -1: text.ll 不存在Phase1 未翻译此文件)
# is_decl_mi == 1: 声明文件(仅 declare 无 define # is_decl_mi == 1: 声明文件(仅 declare 无 define
# 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件 # is_decl_mi == 0: 实现文件(有 define
if is_decl_mi != 0: # 只跳过声明文件is_decl_mi == 1text.ll 不存在时继续,让后续翻译逻辑处理
if is_decl_mi == 1:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue continue
# 尝试 BuildCombinedIRstub/text 应已由 Phase1 生成) # 尝试 BuildCombinedIRstub/text 应已由 Phase1 生成)
@@ -633,19 +782,43 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE) inc_combined_len_mi = StubMerger.BuildCombinedIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
if inc_combined_len_mi == 0: if inc_combined_len_mi == 0:
# 诊断:检查 stub.ll/text.ll 是否存在
diag_stub: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "stub.ll")
diag_text: str = StubMerger._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "text.ll")
diag_stub_ex: int = 0
diag_text_ex: int = 0
if diag_stub is not None:
df_diag_s: fileio.File | t.CPtr = fileio.File(diag_stub, fileio.MODE.R)
if not df_diag_s.closed:
diag_stub_ex = 1
df_diag_s.close()
stdlib.free(diag_stub)
if diag_text is not None:
df_diag_t: fileio.File | t.CPtr = fileio.File(diag_text, fileio.MODE.R)
if not df_diag_t.closed:
diag_text_ex = 1
df_diag_t.close()
stdlib.free(diag_text)
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: if fb is not None:
viperlib.snprintf(fb, 1024, "BuildCombinedIR 失败: %s", src_fp_mi) viperlib.snprintf(fb, 1024,
"BuildCombinedIR 失败: %s (stub=%d text=%d is_decl=%d)",
src_fp_mi, diag_stub_ex, diag_text_ex, is_decl_mi)
VLogger.error(fb, "PhaseB+") VLogger.error(fb, "PhaseB+")
if inc_combined_mi is not None: if inc_combined_mi is not None:
stdlib.free(inc_combined_mi) stdlib.free(inc_combined_mi)
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
continue continue
# 编译为 .obj # 编译为 .obj(输出到 includes_binary_dir与存在性检查和链接收集一致
inc_cret_mi: int = BuildPipeline.compile_module_to_obj( inc_cret_mi: int = BuildPipeline.compile_module_to_obj(
inc_combined_mi, inc_combined_len_mi, temp_dir, output_dir, inc_sha1_mi, inc_combined_mi, inc_combined_len_mi, temp_dir, includes_binary_dir, inc_sha1_mi,
cc_cmd, cc_flags) cc_cmd, cc_flags)
stdlib.free(inc_combined_mi) stdlib.free(inc_combined_mi)
if inc_cret_mi != 0: if inc_cret_mi != 0:
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: if fb is not None:
@@ -654,10 +827,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
sys.exit(1) sys.exit(1)
inc_compiled += 1 inc_compiled += 1
# 显示编译完成的 includes 文件
fb_inc_comp: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_inc_comp is not None:
viperlib.snprintf(fb_inc_comp, 1024, "[includes %d] 编译完成: %s", inc_compiled, src_fp_mi)
VLogger.info(fb_inc_comp, "PhaseB+")
# 添加到 obj_paths (切片路径,从 includes_binary_dir 取)
ibd_len_mi2: t.CSizeT = string.strlen(includes_binary_dir)
inc_obj_sliced: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi2, inc_sha1_mi, "obj")
# 添加到 obj_paths (切片路径)
od_len_mi: t.CSizeT = string.strlen(output_dir)
inc_obj_sliced: str = StubMerger._sliced_path(output_dir, od_len_mi, inc_sha1_mi, "obj")
if inc_obj_sliced is not None: if inc_obj_sliced is not None:
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced) inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE: if obj_pos + inc_obj_len + 2 < OBJ_PATHS_SIZE:
@@ -669,11 +848,25 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
obj_paths[obj_pos] = '\0' obj_paths[obj_pos] = '\0'
stdlib.free(inc_obj_sliced) stdlib.free(inc_obj_sliced)
# 释放本次迭代的临时内存
if check_pat_mi is not None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None:
stdlib.free(check_fd_mi)
stdlib.free(src_fp_mi)
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: if fb is not None:
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d", inc_compiled) viperlib.snprintf(fb, 1024, "编译缺失 includes: %d", inc_compiled)
VLogger.info(fb, "PhaseB+") VLogger.info(fb, "PhaseB+")
# 释放 app_deps_bufPhase B+ 块未执行时此处兜底释放)
if app_deps_buf is not None:
stdlib.free(app_deps_buf)
app_deps_buf = None
# === 4. Phase C: 链接所有 .obj → .exe === # === 4. Phase C: 链接所有 .obj → .exe ===
if log is not None: if log is not None:
log.banner("Phase C: 链接") log.banner("Phase C: 链接")
@@ -708,6 +901,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
final_obj_paths[fop_pos] = '\0' final_obj_paths[fop_pos] = '\0'
obj_paths_len: t.CSizeT = fop_pos obj_paths_len: t.CSizeT = fop_pos
# 暂时禁用按图链接:始终全量链接 includes.binary 中的 .obj
# 按图链接在 --clean 后 deps.txt 缺失时无法正确工作(跨包传递依赖丢失)
# 按图编译Phase B+)仍保留:只编译 reachable_set 中的缺失 includes
# TODO: 待 deps.txt 生成机制完善后(如 Phase A 翻译所有 includes 的 imports重新启用按图链接
# 释放 reachable_setPhase B+ 已使用完毕)
if reachable_set is not None:
stdlib.free(reachable_set)
reachable_set = None
# 始终传 includes_binary_dir 给 link_objs_to_exe由其全量收集 .obj
lret: int = BuildPipeline.link_objs_to_exe( lret: int = BuildPipeline.link_objs_to_exe(
final_obj_paths, obj_paths_len, final_obj_paths, obj_paths_len,
linker_cmd, linker_flags, exe_path, linker_cmd, linker_flags, exe_path,

View File

@@ -51,6 +51,19 @@ _sha1_store_mod: bytes # 模块名数组(每个 64 字节MAX_INCLUDES 个
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节MAX_INCLUDES 个) _sha1_store_rel: bytes # 相对路径数组(每个 256 字节MAX_INCLUDES 个)
_sha1_store_count: int # 条目数 _sha1_store_count: int # 条目数
# 可达集合是否不完整1=有 includes deps.txt 缺失Phase C 应回退全量链接)
_g_reachable_incomplete: int
# includes 依赖内存存储(不依赖 deps.txt 过程文件)
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入此处
# _BuildReachableSha1Set 从此处读取依赖,不读 deps.txt
_includes_deps_sha1: bytes # SHA1 数组(每个 17 字节MAX_INCLUDES 个)
_includes_deps_str: bytes # 依赖字符串数组(每个 512 字节MAX_INCLUDES 个)
_includes_deps_count: int # 条目数
# 单条依赖字符串最大长度
MAX_DEPS_STR_LEN: t.CDefine = 512
# rel_path 最大长度 # rel_path 最大长度
MAX_REL_PATH_LEN: t.CDefine = 256 MAX_REL_PATH_LEN: t.CDefine = 256
@@ -493,6 +506,85 @@ def GetSha1StoreCount() -> int:
return _sha1_store_count return _sha1_store_count
def IsReachableIncomplete() -> int:
"""返回可达集合是否不完整1=有 deps.txt 缺失,应回退全量链接)"""
return _g_reachable_incomplete
# ============================================================
# PopulateIncludesDeps - 将 includes 文件的依赖存入内存
#
# Phase B+ 前用 declare_only 翻译 includes 文件,提取 _imported_modules
# 后调用此函数存入内存。_BuildReachableSha1Set 从内存读取,不读 deps.txt。
#
# Args:
# sha1: includes 文件的 SHA116字符
# deps_str: 依赖模块名字符串(空格分隔,如 "t c stdint string"
#
# Returns:
# 0 成功,非 0 失败
# ============================================================
def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
"""将 includes 文件的依赖存入内存"""
global _includes_deps_sha1, _includes_deps_str, _includes_deps_count
if sha1 is None:
return 1
# 延迟初始化
if _includes_deps_sha1 is None:
_includes_deps_sha1 = stdlib.malloc(MAX_INCLUDES * 17)
_includes_deps_str = stdlib.malloc(MAX_INCLUDES * MAX_DEPS_STR_LEN)
if _includes_deps_sha1 is None or _includes_deps_str is None:
return 1
string.memset(_includes_deps_sha1, 0, MAX_INCLUDES * 17)
string.memset(_includes_deps_str, 0, MAX_INCLUDES * MAX_DEPS_STR_LEN)
_includes_deps_count = 0
if _includes_deps_count >= MAX_INCLUDES:
return 1
# 去重:若 SHA1 已存在则跳过
for i in range(_includes_deps_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0:
return 0
# 写入 SHA1
idx: t.CSizeT = t.CSizeT(_includes_deps_count) * 17
string.strcpy(_includes_deps_sha1 + idx, sha1)
# 写入依赖字符串
idx2: t.CSizeT = t.CSizeT(_includes_deps_count) * MAX_DEPS_STR_LEN
if deps_str is not None:
dl: t.CSizeT = string.strlen(deps_str)
if dl < MAX_DEPS_STR_LEN:
string.strcpy(_includes_deps_str + idx2, deps_str)
else:
string.strncpy(_includes_deps_str + idx2, deps_str, MAX_DEPS_STR_LEN - 1)
else:
_includes_deps_str[idx2] = '\0'
_includes_deps_count = _includes_deps_count + 1
return 0
# ============================================================
# LookupIncludesDeps - 从内存查找 includes 文件的依赖
#
# Args:
# sha1: includes 文件的 SHA116字符
#
# Returns:
# 依赖模块名字符串指针空格分隔None 表示未找到
# ============================================================
def LookupIncludesDeps(sha1: str) -> str:
"""从内存查找 includes 文件的依赖"""
if sha1 is None or _includes_deps_sha1 is None:
return None
if _includes_deps_count <= 0:
return None
for i in range(_includes_deps_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(_includes_deps_sha1 + sidx, sha1) == 0:
idx2: t.CSizeT = t.CSizeT(i) * MAX_DEPS_STR_LEN
return _includes_deps_str + idx2
return None
def GetSha1StoreArrPtr() -> bytes | t.CPtr: def GetSha1StoreArrPtr() -> bytes | t.CPtr:
"""返回 SHA1 数组指针(每个条目 17 字节)""" """返回 SHA1 数组指针(每个条目 17 字节)"""
return _sha1_store_arr return _sha1_store_arr
@@ -538,6 +630,32 @@ def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CP
return _sha1_store_count return _sha1_store_count
# ============================================================
# LookupSha1RelPath - 通过 SHA1 查找相对路径
#
# 遍历全局存储器,返回 sha1 对应的相对路径指针(只读,不要 free
# 用于错误提示:将 sha1 转换为人类可读的文件路径。
#
# Args:
# sha1: SHA1 字符串16 字符)
#
# Returns:
# 相对路径字符串指针只读None=未找到
# ============================================================
def LookupSha1RelPath(sha1: str) -> str:
"""通过 SHA1 查找相对路径返回只读指针None=未找到"""
if sha1 is None:
return None
if _sha1_store_arr is None or _sha1_store_rel is None:
return None
for i in range(_sha1_store_count):
sidx: t.CSizeT = t.CSizeT(i) * 17
if string.strcmp(_sha1_store_arr + sidx, sha1) == 0:
ridx: t.CSizeT = t.CSizeT(i) * MAX_REL_PATH_LEN
return _sha1_store_rel + ridx
return None
# ============================================================ # ============================================================
# _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define # _IsFuncDeclaredOrDefined - 检查 out_buf 中是否已有函数的 declare/define
# #
@@ -1265,11 +1383,19 @@ def _AddSha1ToSet(set_buf: t.CChar | t.CPtr, count: int, sha1: str) -> int:
# ============================================================ # ============================================================
def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
temp_dir: str, temp_dir: str,
reachable_set: t.CChar | t.CPtr) -> int: reachable_set: t.CChar | t.CPtr,
"""构建可达 SHA1 集合(依赖图按需翻译)""" app_deps: str = None) -> int:
"""构建可达 SHA1 集合(依赖图按需翻译)
app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤
"""
global _g_reachable_incomplete
if source_dir is None or temp_dir is None or reachable_set is None: if source_dir is None or temp_dir is None or reachable_set is None:
return -1 return -1
# 重置不完整标志
_g_reachable_incomplete = 0
SRC_BUF_SIZE_R: t.CSizeT = 1048576 SRC_BUF_SIZE_R: t.CSizeT = 1048576
# 1. 构建 SHA1→module_name 映射 # 1. 构建 SHA1→module_name 映射
@@ -1293,29 +1419,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
viperlib.snprintf(fb, 1024, "SHA1 映射: %d", map_count) viperlib.snprintf(fb, 1024, "SHA1 映射: %d", map_count)
VLogger.debug(fb, "Reachable") VLogger.debug(fb, "Reachable")
# 2. 扫描 source_dir 下的 .py 文件,收集直接依赖
dir_len: t.CSizeT = string.strlen(source_dir)
pattern: bytes = stdlib.malloc(dir_len + 8)
if pattern is None:
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir)
find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__()
find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16)
if find_data is None:
stdlib.free(pattern)
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
return -1
string.memset(find_data, 0, find_data_size + 16)
# 工作列表(空格分隔的模块名) # 工作列表(空格分隔的模块名)
worklist: bytes = stdlib.malloc(8192) worklist: bytes = stdlib.malloc(8192)
if worklist is None: if worklist is None:
stdlib.free(pattern)
stdlib.free(find_data)
stdlib.free(sha1_arr) stdlib.free(sha1_arr)
stdlib.free(mod_arr) stdlib.free(mod_arr)
return -1 return -1
@@ -1324,70 +1430,104 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
reachable_count: int = 0 reachable_count: int = 0
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data) # 2. 如果 app_deps 非空,直接使用它作为初始 worklist跳过自己的非递归扫描
if handle == win32base.INVALID_HANDLE_VALUE: # 否则回退到自己的非递归扫描source_dir/*.py只扫描顶层
fb: t.CChar | t.CPtr = VLogger.fmt_buf() if app_deps is not None and app_deps[0] != '\0':
if fb is not None: ad_len: t.CSizeT = string.strlen(app_deps)
viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern) if ad_len > 0 and ad_len < 8192:
VLogger.warning(fb, "Reachable") string.strcpy(worklist, app_deps)
stdlib.free(pattern) wl_len = ad_len
stdlib.free(find_data) else:
stdlib.free(sha1_arr) # 回退到自己的非递归扫描
stdlib.free(mod_arr) dir_len: t.CSizeT = string.strlen(source_dir)
stdlib.free(worklist) pattern: bytes = stdlib.malloc(dir_len + 8)
return -1 if pattern is None:
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
return -1
viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir)
while True: find_data_size: t.CSizeT = win32file.WIN32_FIND_DATAA.__sizeof__()
fname: str = find_data.cFileName find_data: win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(find_data_size + 16)
if fname is not None: if find_data is None:
fname_len: t.CSizeT = string.strlen(fname) stdlib.free(pattern)
if fname_len > 3: stdlib.free(sha1_arr)
is_py: int = 0 stdlib.free(mod_arr)
if fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y': stdlib.free(worklist)
is_py = 1 return -1
if is_py != 0: string.memset(find_data, 0, find_data_size + 16)
full_path: bytes = stdlib.malloc(dir_len + fname_len + 2)
if full_path is not None: handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", source_dir, fname) if handle == win32base.INVALID_HANDLE_VALUE:
sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R) fb_fd: t.CChar | t.CPtr = VLogger.fmt_buf()
if not sf.closed: if fb_fd is not None:
sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R) viperlib.snprintf(fb_fd, 1024, "未找到 .py 文件: %s", pattern)
if sbuf is not None: VLogger.warning(fb_fd, "Reachable")
br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R) stdlib.free(pattern)
sf.close() stdlib.free(find_data)
if br > 0: stdlib.free(sha1_arr)
if br < SRC_BUF_SIZE_R: stdlib.free(mod_arr)
sbuf[br] = 0 stdlib.free(worklist)
else: return -1
sbuf[SRC_BUF_SIZE_R - 1] = 0
# 解析 AST 获取 _imported_modules while True:
lx: ast.Lexer | t.CPtr = ast.new_lexer(mb) fname: str = find_data.cFileName
if lx is not None: if fname is not None:
ast._lexer_init(lx, sbuf, mb) fname_len: t.CSizeT = string.strlen(fname)
tokens: ast.Token | t.CPtr = ast.tokenize(lx) if fname_len > 3:
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens) is_py: int = 0
if tree is not None: if fname[fname_len - 3] == '.' and fname[fname_len - 2] == 'p' and fname[fname_len - 1] == 'y':
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator() is_py = 1
if tr is not None: if is_py != 0:
tr._declare_only = 2 full_path: bytes = stdlib.malloc(dir_len + fname_len + 2)
# 源文件包名fname 是顶级文件名(无目录分隔符),包为 None if full_path is not None:
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname) viperlib.snprintf(full_path, dir_len + fname_len + 2, "%s/%s", source_dir, fname)
HandlesStruct.reset_visible_structs(mb, 0) sf: fileio.File | t.CPtr = fileio.File(full_path, fileio.MODE.R)
tr.translate(tree) if not sf.closed:
# 获取 _imported_modules sbuf: bytes = stdlib.malloc(SRC_BUF_SIZE_R)
if tr._imported_modules is not None: if sbuf is not None:
im_len: t.CSizeT = string.strlen(tr._imported_modules) br: LONG = sf.read_all(sbuf, SRC_BUF_SIZE_R)
if im_len > 0 and wl_len + im_len + 1 < 8192: sf.close()
string.strcpy(worklist + wl_len, tr._imported_modules) if br > 0:
wl_len += im_len if br < SRC_BUF_SIZE_R:
worklist[wl_len] = ' ' sbuf[br] = 0
wl_len += 1 else:
worklist[wl_len] = '\0' sbuf[SRC_BUF_SIZE_R - 1] = 0
# 释放 Translator 资源 # 解析 AST 获取 _imported_modules
if tr._global_names is not None: lx: ast.Lexer | t.CPtr = ast.new_lexer(mb)
stdlib.free(tr._global_names) if lx is not None:
if tr._nonlocal_names is not None: ast._lexer_init(lx, sbuf, mb)
stdlib.free(tr._nonlocal_names) tokens: ast.Token | t.CPtr = ast.tokenize(lx)
tree: ast.AST | t.CPtr = ast.parse_tokens(mb, tokens)
if tree is not None:
tr: HandlesTranslator.Translator | t.CPtr = HandlesTranslator.Translator()
if tr is not None:
tr._declare_only = 2
# 源文件包名fname 是顶级文件名(无目录分隔符),包为 None
tr.CurrentPackage = HandlesImports.compute_package_from_relpath(mb, fname)
HandlesStruct.reset_visible_structs(mb, 0)
tr.translate(tree)
# 获取 _imported_modules
if tr._imported_modules is not None:
im_len: t.CSizeT = string.strlen(tr._imported_modules)
if im_len > 0 and wl_len + im_len + 1 < 8192:
string.strcpy(worklist + wl_len, tr._imported_modules)
wl_len += im_len
worklist[wl_len] = ' '
wl_len += 1
worklist[wl_len] = '\0'
# 计算 App 源文件 SHA1填充依赖到内存存储器
# worklist 遍历时LookupIncludesDeps 能找到 App 源文件的依赖
app_sha1_r: str = IncludesScanner.compute_file_sha1(mb, full_path)
if app_sha1_r is not None:
PopulateIncludesDeps(app_sha1_r, tr._imported_modules)
stdlib.free(app_sha1_r)
# 释放 Translator 资源
if tr._global_names is not None:
stdlib.free(tr._global_names)
if tr._nonlocal_names is not None:
stdlib.free(tr._nonlocal_names)
stdlib.free(sbuf) stdlib.free(sbuf)
else: else:
stdlib.free(sbuf) stdlib.free(sbuf)
@@ -1395,16 +1535,16 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
sf.close() sf.close()
stdlib.free(full_path) stdlib.free(full_path)
if win32file.FindNextFileA(handle, find_data) == 0: if win32file.FindNextFileA(handle, find_data) == 0:
break break
win32base.FindClose(handle) win32base.FindClose(handle)
stdlib.free(pattern) stdlib.free(pattern)
stdlib.free(find_data) stdlib.free(find_data)
# 去掉末尾多余空格 # 去掉末尾多余空格
if wl_len > 0 and worklist[wl_len - 1] == ' ': if wl_len > 0 and worklist[wl_len - 1] == ' ':
worklist[wl_len - 1] = '\0' worklist[wl_len - 1] = '\0'
wl_len -= 1 wl_len -= 1
fb: t.CChar | t.CPtr = VLogger.fmt_buf() fb: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb is not None: if fb is not None:
@@ -1512,36 +1652,55 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
# 加入 reachable_set # 加入 reachable_set
reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1) reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1)
# 读取 .deps.txt 追加到 worklist # 从内存查找依赖(不读 deps.txt 过程文件)
deps_path: str = _sliced_path(temp_dir, td_len_r, found_sha1, "deps.txt") # Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存
if deps_path is not None: deps_str: str = LookupIncludesDeps(found_sha1)
df: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R) if deps_str is not None:
if not df.closed: dl: t.CSizeT = string.strlen(deps_str)
deps_buf: bytes = stdlib.malloc(2048) if dl > 0:
if deps_buf is not None: if wl_len + dl + 2 < 8192:
dbr: t.CInt64T = df.read_all(deps_buf, 2048) if wl_len > 0 and worklist[wl_len - 1] != ' ':
df.close() worklist[wl_len] = ' '
if dbr > 0: wl_len = wl_len + 1
if dbr < 2048: string.strcpy(worklist + wl_len, deps_str)
deps_buf[dbr] = '\0' wl_len = wl_len + dl
else: worklist[wl_len] = ' '
deps_buf[2047] = '\0' wl_len = wl_len + 1
# 追加到 worklist嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug worklist[wl_len] = '\0'
dl: t.CSizeT = string.strlen(deps_buf) # deps_str 为空字符串视为无依赖(合法情况,如叶子模块)
if worklist is not None: else:
if dl > 0 and wl_len + dl + 2 < 8192: # 依赖未在内存中找到
if wl_len > 0 and worklist[wl_len - 1] != ' ': # 按需过滤: 检查是否是 App 源文件source_dir 下的 .py 文件)
worklist[wl_len] = ' ' # App 源文件不需要加入 reachable_set只用于 includes 过滤),跳过
wl_len = wl_len + 1 sd_len_ff: t.CSizeT = string.strlen(source_dir)
string.strcpy(worklist + wl_len, deps_buf) mb_len_ff: t.CSizeT = string.strlen(mod_buf)
wl_len = wl_len + dl app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6)
worklist[wl_len] = ' ' is_app_src: int = 0
wl_len = wl_len + 1 if app_mod_path is not None:
worklist[wl_len] = '\0' viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf)
stdlib.free(deps_buf) af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R)
else: if not af.closed:
df.close() is_app_src = 1
stdlib.free(deps_path) af.close()
stdlib.free(app_mod_path)
if is_app_src != 0:
# App 源文件,跳过(不 fast-fail
fb_skip: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_skip is not None:
viperlib.snprintf(fb_skip, 1024, "跳过 App 源文件: %s (sha1=%s)", mod_buf, found_sha1)
VLogger.debug(fb_skip, "Reachable")
else:
# fast-fail: includes 文件依赖未在内存中找到Phase 1a-pre 未正确填充
fb_ff: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_ff is not None:
viperlib.snprintf(fb_ff, 1024, "依赖未找到 fast-fail: 模块 %s (sha1=%s) 的依赖未在内存中注册", mod_buf, found_sha1)
VLogger.error(fb_ff, "Reachable")
stdlib.free(sha1_arr)
stdlib.free(mod_arr)
stdlib.free(worklist)
stdlib.free(processed)
stdlib.free(mod_buf)
return -1
stdlib.free(mod_buf) stdlib.free(mod_buf)
stdlib.free(sha1_arr) stdlib.free(sha1_arr)
stdlib.free(mod_arr) stdlib.free(mod_arr)

View File

@@ -101,7 +101,7 @@ class Logger:
self._reset_color() self._reset_color()
# category 可选 # category 可选
if category is not None and category[0] != 0: if category is not None and category[0] != 0:
stdio.printf("[%s]", category) stdio.printf(" [%s]", category)
stdio.printf(": %s\n", msg) stdio.printf(": %s\n", msg)
return 0 return 0

View File

@@ -227,7 +227,7 @@ def main() -> int:
Config.resolve_paths(proj_dir) Config.resolve_paths(proj_dir)
Config.print_config() Config.print_config()
# === --clean: 清理 tempoutput 目录 === # === --clean: 清理 tempoutput 和 includes.binary 目录 ===
if _gb_clean: if _gb_clean:
if log is not None: if log is not None:
log.info("清理临时目录...", "clean") log.info("清理临时目录...", "clean")
@@ -245,6 +245,15 @@ def main() -> int:
if fb2 is not None: if fb2 is not None:
viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2) viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2)
log.info(fb2, "clean") log.info(fb2, "clean")
# 清理 includes.binary 目录(旧 .obj 文件会导致跳过重新编译)
mf_inc_bin_clean: str = Config.get_includes_binary_dir()
if mf_inc_bin_clean is not None:
n3: int = Utils.CleanDir(mf_inc_bin_clean)
if log is not None:
fb3: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb3 is not None:
viperlib.snprintf(fb3, 1024, "%s: 删除 %d 个文件", mf_inc_bin_clean, n3)
log.info(fb3, "clean")
# === --phase 参数控制 === # === --phase 参数控制 ===
# phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll不编译 # phase=1: 仅 stub 分离(生成 .stub.ll/.text.ll不编译

View File

@@ -22,7 +22,7 @@
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" "datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
}, },
"options": { "options": {
"slice_level": 3, "slice_level": 3,
"sha1_slice_level": 1, "sha1_slice_level": 1,
"target": "llvm", "target": "llvm",
"strict_mode": true "strict_mode": true