自举实验失败,C1 编译 Test 成功,暂未达到自举收敛点
This commit is contained in:
@@ -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 - 追加字符串到缓冲区
|
||||
#
|
||||
@@ -525,6 +628,72 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
|
||||
pos = _PyiEmit(buf, buf_size, pos, " = False")
|
||||
elif cv.const_kind == ast.CONST_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")
|
||||
|
||||
# 全局赋值(无注解)— 从 Constant value 推断类型
|
||||
|
||||
@@ -30,7 +30,7 @@ import lib.Projectrans.Config as Config
|
||||
_mbuddy: memhub.MemBuddy | t.CPtr
|
||||
|
||||
# 源代码缓冲区大小(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
|
||||
|
||||
|
||||
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:
|
||||
"""扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf
|
||||
"""递归扫描 includes_binary_dir 目录,收集所有 .obj 文件路径到 out_buf
|
||||
|
||||
支持切片子目录(如 includes_binary_dir/43/{sha1}.obj)。
|
||||
|
||||
Args:
|
||||
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
|
||||
|
||||
out_buf[0] = '\0'
|
||||
out_pos: t.CSizeT = 0
|
||||
|
||||
# 构造搜索模式: dir/*.obj
|
||||
dir_len: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
pattern: bytes = _mbuddy.alloc(dir_len + 8)
|
||||
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_pos: t.CSizeT = _CollectObjFilesImpl(includes_binary_dir, dir_len, out_buf, 0, out_size)
|
||||
out_buf[out_pos] = '\0'
|
||||
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",
|
||||
linker_cmd, obj_paths, linker_output, linker_flags)
|
||||
|
||||
# 用 stdlib.system() 直接在控制台运行链接,输出直接显示
|
||||
# 避免 subprocess 管道捕获丢失输出(TransPyV 标准句柄可能无效)
|
||||
sys_ret: int = stdlib.system(cmd)
|
||||
if sys_ret != 0:
|
||||
# 用 subprocess.run 捕获 clang++ 的 stdout/stderr 输出
|
||||
# 之前用 stdlib.system() 无法捕获具体错误(undefined reference 等),
|
||||
# 导致 1.txt 中只看到"链接失败,返回码: 1"而看不到未解析符号
|
||||
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()
|
||||
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")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -176,6 +176,77 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _init_global_array_from_list — 从列表字面量初始化全局数组
|
||||
#
|
||||
# 为每个元素生成 GEP + store 指令,将值存入全局数组的对应位置。
|
||||
# 适用于模块级 t.CArray[elem_ty, count] = [v0, v1, ...] 的初始化。
|
||||
#
|
||||
# Args:
|
||||
# builder: IRBuilder
|
||||
# pool: 编译器内存池
|
||||
# mod: LLVMModule
|
||||
# array_alloca: 全局数组变量的 Value 引用(类型为 [N x elem_ty]*)
|
||||
# list_node: ast.List 节点
|
||||
# trans: Translator 对象
|
||||
#
|
||||
# Returns:
|
||||
# 0 成功,1 失败
|
||||
# ============================================================
|
||||
def _init_global_array_from_list(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
array_alloca: llvmlite.Value | t.CPtr,
|
||||
list_node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr) -> int:
|
||||
"""从列表字面量初始化全局数组,为每个元素生成 GEP + store"""
|
||||
if builder is None or array_alloca is None or list_node is None:
|
||||
return 1
|
||||
|
||||
# 获取数组类型(Pointee of [N x elem_ty]*)
|
||||
arr_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if array_alloca.Ty is not None:
|
||||
arr_ty = array_alloca.Ty.Pointee
|
||||
if arr_ty is None:
|
||||
return 1
|
||||
|
||||
# 匹配数组类型获取元素类型
|
||||
elem_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
match arr_ty:
|
||||
case llvmlite.LLVMType.Array(et, _):
|
||||
elem_ty = et
|
||||
if elem_ty is None:
|
||||
return 1
|
||||
|
||||
# 获取列表元素
|
||||
lst: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
|
||||
if lst is None or lst.elts is None:
|
||||
return 1
|
||||
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
|
||||
elts_count: t.CSizeT = elts.__len__()
|
||||
|
||||
# 为每个元素生成 GEP + store
|
||||
i: t.CSizeT = 0
|
||||
while i < elts_count:
|
||||
elem_node: ast.AST | t.CPtr = elts.get(i)
|
||||
elem_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, elem_node, None, 0, trans)
|
||||
if elem_val is not None:
|
||||
idx_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, i)
|
||||
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||||
builder, arr_ty, elem_ty, array_alloca, idx_val)
|
||||
if elem_ptr is not None:
|
||||
# 类型转换(如 i32 → i8 截断)
|
||||
store_val: llvmlite.Value | t.CPtr = elem_val
|
||||
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
|
||||
store_val = HandlesExpr.coerce_to_type(
|
||||
builder, elem_val, elem_ptr.Ty.Pointee)
|
||||
llvmlite.build_store(builder, store_val, elem_ptr)
|
||||
i += 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
@t.NoVTable
|
||||
class AnnAssignHandle(HandlesBase.Mixin):
|
||||
"""AnnAssign 语句处理器:继承 Mixin 获得 Trans 回指针"""
|
||||
@@ -221,6 +292,11 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
if HT.is_nonlocal_name(self.Trans, nm.id) != 0:
|
||||
return 0
|
||||
|
||||
# 模块级全局变量已由 handle_module_level_var 注册到模块作用域,
|
||||
# 不需要创建局部 alloca(否则会导致局部变量遮蔽全局变量)
|
||||
if HandlesVar.lookup_module_var(self.Trans.SymTab, nm.id) is not None:
|
||||
return 0
|
||||
|
||||
# 检查是否已存在
|
||||
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
|
||||
self.Trans.SymTab, nm.id)
|
||||
@@ -350,6 +426,29 @@ class AnnAssignHandle(HandlesBase.Mixin):
|
||||
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
|
||||
return 0
|
||||
|
||||
# 模块级全局变量(由 handle_module_level_var 注册到模块作用域)
|
||||
# 非函数内 global 声明,但变量已在模块作用域中(如模块级 t.CArray 初始化)
|
||||
mod_glob: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
|
||||
self.Trans.SymTab, nm.id)
|
||||
if mod_glob is not None:
|
||||
if aa.value is not None:
|
||||
# 列表字面量 → 逐元素 GEP + store 初始化数组
|
||||
if aa.value.kind() == ast.ASTKind.List:
|
||||
_init_global_array_from_list(
|
||||
builder, pool, mod, mod_glob, aa.value, self.Trans)
|
||||
else:
|
||||
rhs_val_mg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, aa.value,
|
||||
None, 0, self.Trans)
|
||||
if rhs_val_mg is not None:
|
||||
target_ty_mg: llvmlite.LLVMType | t.CPtr = None
|
||||
if mod_glob.Ty is not None:
|
||||
target_ty_mg = mod_glob.Ty.Pointee
|
||||
if target_ty_mg is not None:
|
||||
rhs_val_mg = HandlesExpr.coerce_to_type(builder, rhs_val_mg, target_ty_mg)
|
||||
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
|
||||
return 0
|
||||
|
||||
# 普通局部变量
|
||||
# 创建 alloca
|
||||
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym(
|
||||
|
||||
@@ -15,6 +15,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||||
import lib.core.Handles.HandlesType as HandlesType
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.StubMerger as StubMerger
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -62,23 +63,70 @@ class AssignHandle(HandlesBase.Mixin):
|
||||
rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, asgn.value, None, 0, self.Trans)
|
||||
if rhs_val is None:
|
||||
# 增强错误信息:包含 sha1 + lineno + AST 节点类型,便于定位
|
||||
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None and asgn.value is not None:
|
||||
sha1: str = self.Trans.ModuleSha1
|
||||
val_kind: int = asgn.value.kind()
|
||||
val_line: t.CInt = asgn.value.lineno
|
||||
# 通过 sha1 查找文件名(人类可读)
|
||||
rel_path: str = None
|
||||
if sha1 is not None:
|
||||
rel_path = StubMerger.LookupSha1RelPath(sha1)
|
||||
# 根据 AST 节点类型提取详情
|
||||
if val_kind == ast.ASTKind.Attribute:
|
||||
at_nv: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(asgn.value)
|
||||
attr_nm: str = at_nv.attr
|
||||
target_nm: str = "?"
|
||||
if at_nv.value is not None:
|
||||
if at_nv.value.kind() == ast.ASTKind.Name:
|
||||
tn_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_nv.value)
|
||||
target_nm = tn_nm.id
|
||||
if rel_path is not None:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"rhs_val is None [sha1=%s lineno=%d kind=%d]",
|
||||
sha1, val_line, val_kind)
|
||||
"赋值右侧为 None: %s:%d '%s.%s' 属性访问失败",
|
||||
rel_path, val_line, target_nm, attr_nm)
|
||||
else:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"rhs_val is None [lineno=%d kind=%d]",
|
||||
val_line, val_kind)
|
||||
"赋值右侧为 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:
|
||||
if rel_path is not None:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"赋值右侧为 None: %s:%d [kind=%d]",
|
||||
rel_path, val_line, val_kind)
|
||||
else:
|
||||
viperlib.snprintf(fb, 1024,
|
||||
"赋值右侧为 None: sha1=%s:%d [kind=%d]",
|
||||
sha1, val_line, val_kind)
|
||||
VLogger.error(fb, "ASGN")
|
||||
else:
|
||||
VLogger.error("rhs_val is None", "ASGN")
|
||||
VLogger.error("赋值右侧为 None (asgn.value 为空)", "ASGN")
|
||||
return 0
|
||||
|
||||
new_vars: int = 0
|
||||
|
||||
@@ -1700,15 +1700,14 @@ _generic_class_nodes: list[ast.ClassDef | t.CPtr] | t.CPtr = None
|
||||
_generic_class_sha1s: list[str] | t.CPtr = None
|
||||
|
||||
|
||||
def _is_generic_class(cd: ast.ClassDef | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否"""
|
||||
if cd is None:
|
||||
return 0
|
||||
tp: list[str] | t.CPtr = cd.type_params
|
||||
def _is_generic_class(tp: list[str] | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否有类型参数(泛型类),返回 1=是 / 0=否
|
||||
|
||||
接收 type_params 而非 cd,避免 cd.type_params 属性访问
|
||||
因跨模块类型推断失败返回 None。
|
||||
"""
|
||||
if tp is None:
|
||||
return 0
|
||||
if tp.__len__() == 0:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
@@ -1721,29 +1720,37 @@ def _register_generic_template(pool: memhub.MemBuddy | t.CPtr,
|
||||
_generic_class_names = list[str](pool, 8)
|
||||
_generic_class_nodes = list[ast.ClassDef | t.CPtr](pool, 8)
|
||||
_generic_class_sha1s = list[str](pool, 8)
|
||||
# 检查是否已注册
|
||||
cn: t.CSizeT = _generic_class_names.__len__()
|
||||
# 用局部变量接收 global list,触发类型推断(避免 global 变量 __len__ 失败)
|
||||
names_local: list[str] | t.CPtr = _generic_class_names
|
||||
cn: t.CSizeT = names_local.__len__()
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = _generic_class_names.get(i)
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, cd.name) == 0:
|
||||
return
|
||||
_generic_class_names.append(cd.name)
|
||||
_generic_class_nodes.append(cd)
|
||||
_generic_class_sha1s.append(module_sha1)
|
||||
# 诊断日志:确认泛型模板注册
|
||||
fb_rg: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_rg is not None:
|
||||
viperlib.snprintf(fb_rg, 1024, "注册泛型模板: %s (module=%s)", cd.name, module_sha1)
|
||||
VLogger.info(fb_rg, "SPEC")
|
||||
|
||||
|
||||
def _find_generic_template(class_name: str) -> ast.ClassDef | t.CPtr:
|
||||
"""查找泛型类模板,返回 ClassDef 节点或 None"""
|
||||
if _generic_class_names is None or class_name is None:
|
||||
return None
|
||||
cn: t.CSizeT = _generic_class_names.__len__()
|
||||
# 用局部变量接收 global list,触发类型推断(避免 global 变量 __len__ 失败)
|
||||
names_local: list[str] | t.CPtr = _generic_class_names
|
||||
nodes_local: list[ast.ClassDef | t.CPtr] | t.CPtr = _generic_class_nodes
|
||||
cn: t.CSizeT = names_local.__len__()
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = _generic_class_names.get(i)
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换
|
||||
node: ast.ClassDef | t.CPtr = _generic_class_nodes.get(i)
|
||||
node: ast.ClassDef | t.CPtr = nodes_local.get(i)
|
||||
return node
|
||||
return None
|
||||
|
||||
@@ -1752,13 +1759,15 @@ def _find_generic_template_sha1(class_name: str) -> str:
|
||||
"""查找泛型类模板的来源模块 SHA1,未找到返回 None"""
|
||||
if _generic_class_names is None or class_name is None:
|
||||
return None
|
||||
cn: t.CSizeT = _generic_class_names.__len__()
|
||||
# 用局部变量接收 global list,触发类型推断
|
||||
names_local: list[str] | t.CPtr = _generic_class_names
|
||||
sha1s_local: list[str] | t.CPtr = _generic_class_sha1s
|
||||
cn: t.CSizeT = names_local.__len__()
|
||||
i: t.CSizeT
|
||||
for i in range(cn):
|
||||
nm: str = _generic_class_names.get(i)
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
# 用局部变量接收 list.get() 结果,触发旧编译器 inttoptr 类型转换
|
||||
sha1_val: str = _generic_class_sha1s.get(i)
|
||||
sha1_val: str = sha1s_local.get(i)
|
||||
return sha1_val
|
||||
return None
|
||||
|
||||
@@ -2191,7 +2200,9 @@ def translate_class_def(trans: HT.Translator | t.CPtr,
|
||||
return _translate_renum_def(trans, cd)
|
||||
|
||||
# 泛型类:存储为模板,不发射 IR(等实例化时特化)
|
||||
if _is_generic_class(cd) == 1:
|
||||
# 用局部变量接收 cd.type_params,触发类型推断
|
||||
tp_val: list[str] | t.CPtr = cd.type_params
|
||||
if _is_generic_class(tp_val) == 1:
|
||||
_register_generic_template(pool, cd, trans.ModuleSha1)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -420,7 +420,14 @@ def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
|
||||
# ============================================================
|
||||
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""查找上下文中的 pool 变量,返回 alloca 或 None"""
|
||||
"""查找上下文中的 pool 变量,返回 alloca 或 None
|
||||
|
||||
查找顺序: pool → _mbuddy → mbuddy → mb
|
||||
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
|
||||
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
|
||||
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
|
||||
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
|
||||
"""
|
||||
if trans is None:
|
||||
return None
|
||||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
|
||||
@@ -435,7 +442,32 @@ def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
|
||||
if alloca is not None:
|
||||
return alloca
|
||||
# 兜底: 创建外部全局 @_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)
|
||||
# 创建外部全局 @_mbuddy(external linkage,无初始值)
|
||||
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb)
|
||||
if gv_ext is not None:
|
||||
gv_ext.Linkage = "external"
|
||||
llvmlite.module_add_global(trans.Module, gv_ext)
|
||||
# 创建 Value 引用(@_mbuddy, 类型为 i8**)
|
||||
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
|
||||
ref_name_fb: t.CChar | t.CPtr = pool.alloc(16)
|
||||
if ref_name_fb is not None:
|
||||
string.strcpy(ref_name_fb, "@_mbuddy")
|
||||
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||||
# 注册到模块作用域,避免重复创建
|
||||
HandlesVar.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||||
return gv_ref_fb
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -781,15 +813,32 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
|
||||
if comparators is None or comparators.__len__() == 0:
|
||||
return None
|
||||
rhs_node: ast.AST | t.CPtr = comparators.get(0)
|
||||
rhs: llvmlite.Value | t.CPtr = translate_value(
|
||||
builder, pool, mod, comparators.get(0), None, 0, trans)
|
||||
builder, pool, mod, rhs_node, None, 0, trans)
|
||||
if rhs is None:
|
||||
return None
|
||||
|
||||
# === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)===
|
||||
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
|
||||
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
|
||||
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
|
||||
lhs_node: ast.AST | t.CPtr = cmp.left
|
||||
cmp_with_none: int = 0
|
||||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
|
||||
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
|
||||
rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node)
|
||||
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
|
||||
cmp_with_none = 1
|
||||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
|
||||
lhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(lhs_node)
|
||||
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
|
||||
cmp_with_none = 1
|
||||
|
||||
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||||
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
|
||||
lhs_node: ast.AST | t.CPtr = cmp.left
|
||||
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||||
# None 比较跳过重载(走身份比较)
|
||||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
|
||||
if nm is not None and nm.id is not None:
|
||||
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||||
@@ -808,6 +857,8 @@ def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
|
||||
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
|
||||
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
|
||||
# None 比较跳过重载(走身份比较)
|
||||
if cmp_with_none == 0:
|
||||
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
|
||||
pool, builder, mod, lhs, rhs, op, trans, 1)
|
||||
if ovl_result2 is not None:
|
||||
@@ -875,8 +926,12 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
return None
|
||||
|
||||
if uo.op == ast.OpKind.USub:
|
||||
# -x = 0 - x
|
||||
# 根据 operand 类型选择零值常量(避免 i32 与 i64 类型不匹配)
|
||||
# -x
|
||||
# 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
|
||||
operand_fbits: int = get_llvm_float_bits(operand.Ty)
|
||||
if operand_fbits > 0:
|
||||
return llvmlite.build_fneg(builder, operand)
|
||||
# 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 i32 与 i64 类型不匹配)
|
||||
operand_bits: int = get_llvm_type_bits(operand.Ty)
|
||||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
if operand_bits == 64:
|
||||
@@ -1115,29 +1170,73 @@ def _build_source_path_from_sha1(sha1: str) -> str:
|
||||
# ============================================================
|
||||
def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
|
||||
name: str,
|
||||
from_imports: str) -> int:
|
||||
"""跨模块查找 CDefine 常量,返回值或 -1(未找到)"""
|
||||
if name is None or from_imports is None:
|
||||
stdio.printf("[XMCD] FAIL name=%s reason=from_imports_is_none\n", name)
|
||||
from_imports: str,
|
||||
imported_modules: str = None) -> int:
|
||||
"""跨模块查找 CDefine 常量,返回值或 -1(未找到)
|
||||
|
||||
查找顺序:
|
||||
1. from_imports 精确匹配(from X import NAME)
|
||||
2. from_imports star 回退(from X import *)
|
||||
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
|
||||
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
|
||||
"""
|
||||
if name is None:
|
||||
return -1
|
||||
if from_imports is None:
|
||||
from_imports = ""
|
||||
|
||||
# 1. 从 from_imports 查找名称对应的模块名
|
||||
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
|
||||
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
|
||||
if mod_name_raw is None:
|
||||
# 精确匹配失败:尝试 star import 回退
|
||||
# CDefine 常量(如 INVALID_HANDLE_VALUE)通过 from w32.win32base import * 导入,
|
||||
# 不会作为精确条目出现在 from_imports 中,而是作为 *:w32.win32base 条目。
|
||||
mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1)
|
||||
|
||||
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
|
||||
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
|
||||
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
|
||||
if mod_name_raw is None:
|
||||
stdio.printf("[XMCD] FAIL name=%s reason=lookup_from_import_returned_none\n", name)
|
||||
# from_imports 完全没有匹配(精确和 star 都失败)
|
||||
# 直接进入 imported_modules 遍历(步骤 9.6)
|
||||
if imported_modules is None:
|
||||
return -1
|
||||
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
|
||||
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
|
||||
HandlesType.set_cdefine_found(0)
|
||||
# 直接遍历 imported_modules
|
||||
im_len: t.CSizeT = string.strlen(imported_modules)
|
||||
im_ci: t.CSizeT = 0
|
||||
while im_ci < im_len:
|
||||
while im_ci < im_len and imported_modules[im_ci] == ' ':
|
||||
im_ci += 1
|
||||
if im_ci >= im_len:
|
||||
break
|
||||
im_start: t.CSizeT = im_ci
|
||||
while im_ci < im_len and imported_modules[im_ci] != ' ':
|
||||
im_ci += 1
|
||||
im_end: t.CSizeT = im_ci
|
||||
im_nlen: t.CSizeT = im_end - im_start
|
||||
if im_nlen > 0 and im_nlen < 256:
|
||||
im_buf: str = pool.alloc(im_nlen + 1)
|
||||
if im_buf is not None:
|
||||
mk: t.CSizeT = 0
|
||||
while mk < im_nlen:
|
||||
im_buf[mk] = imported_modules[im_start + mk]
|
||||
mk += 1
|
||||
im_buf[im_nlen] = '\0'
|
||||
sub_fi: str = pool.alloc(im_nlen + 3)
|
||||
if sub_fi is not None:
|
||||
sub_fi[0] = '*'
|
||||
sub_fi[1] = ':'
|
||||
mk2: t.CSizeT = 0
|
||||
while mk2 < im_nlen:
|
||||
sub_fi[2 + mk2] = im_buf[mk2]
|
||||
mk2 += 1
|
||||
sub_fi[2 + im_nlen] = '\0'
|
||||
sub_val: int = _lookup_cross_module_cdefine(pool, name, sub_fi, None)
|
||||
if HandlesType.is_cdefine_found() != 0:
|
||||
return sub_val
|
||||
return -1
|
||||
stdio.printf("[XMCD] star_fallback name=%s mod=%s\n", name, mod_name_raw)
|
||||
# 打印 from_imports 用于诊断精确匹配失败原因
|
||||
fi_len: t.CSizeT = string.strlen(from_imports)
|
||||
stdio.printf("[XMCD] from_imports (len=%d): %s\n", fi_len, from_imports)
|
||||
else:
|
||||
stdio.printf("[XMCD] exact_match name=%s mod=%s\n", name, mod_name_raw)
|
||||
|
||||
# 2. 复制模块名到新缓冲区(lookup_from_import 返回的是内部指针)
|
||||
# 截断于空格、null、或 ':'(别名格式的分隔符)
|
||||
@@ -1161,7 +1260,6 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
|
||||
if sha1 is None:
|
||||
sha1 = HandlesExprCall._lookup_module_sha1_suffix(base_mod)
|
||||
if sha1 is None:
|
||||
stdio.printf("[XMCD] FAIL name=%s base_mod=%s reason=sha1_not_found\n", name, base_mod)
|
||||
return -1
|
||||
|
||||
# 5. 获取 temp_dir
|
||||
@@ -1529,6 +1627,45 @@ def _lookup_cross_module_cdefine(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
stdlib.free(pyi_buf)
|
||||
|
||||
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
|
||||
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
|
||||
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
|
||||
# 需要遍历 imported_modules 逐个查找
|
||||
if result_found == 0 and imported_modules is not None:
|
||||
im2_len: t.CSizeT = string.strlen(imported_modules)
|
||||
im2_ci: t.CSizeT = 0
|
||||
while im2_ci < im2_len and result_found == 0:
|
||||
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
|
||||
im2_ci += 1
|
||||
if im2_ci >= im2_len:
|
||||
break
|
||||
im2_start: t.CSizeT = im2_ci
|
||||
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
|
||||
im2_ci += 1
|
||||
im2_end: t.CSizeT = im2_ci
|
||||
im2_nlen: t.CSizeT = im2_end - im2_start
|
||||
if im2_nlen > 0 and im2_nlen < 256:
|
||||
im2_buf: str = pool.alloc(im2_nlen + 1)
|
||||
if im2_buf is not None:
|
||||
mk3: t.CSizeT = 0
|
||||
while mk3 < im2_nlen:
|
||||
im2_buf[mk3] = imported_modules[im2_start + mk3]
|
||||
mk3 += 1
|
||||
im2_buf[im2_nlen] = '\0'
|
||||
sub_fi2: str = pool.alloc(im2_nlen + 3)
|
||||
if sub_fi2 is not None:
|
||||
sub_fi2[0] = '*'
|
||||
sub_fi2[1] = ':'
|
||||
mk4: t.CSizeT = 0
|
||||
while mk4 < im2_nlen:
|
||||
sub_fi2[2 + mk4] = im2_buf[mk4]
|
||||
mk4 += 1
|
||||
sub_fi2[2 + im2_nlen] = '\0'
|
||||
sub_val2: int = _lookup_cross_module_cdefine(pool, name, sub_fi2, None)
|
||||
if HandlesType.is_cdefine_found() != 0:
|
||||
result_val = sub_val2
|
||||
result_found = 1
|
||||
|
||||
# 10. 缓存到当前模块的 CDefine 表中
|
||||
if result_found != 0:
|
||||
HandlesType.register_cdefine_constant(pool, name, result_val)
|
||||
@@ -1592,8 +1729,9 @@ def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
|
||||
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
|
||||
if _is_cdefine_name_pattern(nm_id) != 0:
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
cdef_val = _lookup_cross_module_cdefine(pool, nm_id, trans._from_imports)
|
||||
if trans is not None:
|
||||
cdef_val = _lookup_cross_module_cdefine(
|
||||
pool, nm_id, trans._from_imports, trans._imported_modules)
|
||||
if HandlesType.is_cdefine_found() != 0:
|
||||
return llvmlite.const_int32(pool, cdef_val)
|
||||
|
||||
@@ -2121,6 +2259,92 @@ def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
|
||||
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
|
||||
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
|
||||
# ============================================================
|
||||
def _is_t_type_object_name(name: str) -> int:
|
||||
"""检查 name 是否是 t.py 中的类型对象名(CPtr/CInt/CDouble 等)
|
||||
|
||||
这些类型对象在运行时作为全局变量 @CPtr/@CInt 等被引用,
|
||||
但 t.py 不被编译,需要在使用处生成带 null 初值的定义。
|
||||
"""
|
||||
if name is None:
|
||||
return 0
|
||||
# t.py 中的类型对象名(PascalCase,首字母大写)
|
||||
if string.strcmp(name, "CPtr") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CInt") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CChar") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CShort") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CLong") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CLongLong") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CDouble") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CFloat") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CBool") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CVoid") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CInt8T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CInt16T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CInt32T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CInt64T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUInt8T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUInt16T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUInt32T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUInt64T") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsigned") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsignedInt") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsignedChar") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsignedShort") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsignedLong") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUnsignedLongLong") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CSizeT") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CIntPtrT") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CUIntPtrT") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CPtrDiffT") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CType") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CDefine") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CStatic") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CExport") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CArray") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CTypedef") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CNoVTable") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CNeedPtr") == 0:
|
||||
return 1
|
||||
if string.strcmp(name, "CAutoPtr") == 0:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
@@ -2150,8 +2374,31 @@ def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
|
||||
use_ty = llvmlite.Int8(pool)
|
||||
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明
|
||||
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
|
||||
# 特殊处理: t.py 中的类型对象(CPtr/CInt/CDouble 等)
|
||||
# t.py 不被编译,但这些类型对象在运行时被引用(如 va_arg(ap, t.CPtr))。
|
||||
# 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。
|
||||
# LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。
|
||||
is_t_type_obj: int = 0
|
||||
if string.strcmp(mod_nm.id, "t") == 0:
|
||||
is_t_type_obj = _is_t_type_object_name(at.attr)
|
||||
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
|
||||
if ext_gv is not None:
|
||||
if is_t_type_obj != 0:
|
||||
# t.CPtr 等类型对象: 创建带 null 初值的定义(linkonce_odr)
|
||||
ext_gv.Linkage = "linkonce_odr"
|
||||
# i8* null 的初值文本
|
||||
null_init: bytes = pool.alloc(8)
|
||||
if null_init is not None:
|
||||
null_init[0] = 'n'
|
||||
null_init[1] = 'u'
|
||||
null_init[2] = 'l'
|
||||
null_init[3] = 'l'
|
||||
null_init[4] = '\0'
|
||||
ext_gv.Initializer = null_init
|
||||
else:
|
||||
ext_gv.Initializer = None
|
||||
else:
|
||||
# 普通模块属性: external 声明(由其他模块提供定义)
|
||||
ext_gv.Linkage = "external"
|
||||
ext_gv.Initializer = None
|
||||
llvmlite.module_add_global(mod, ext_gv)
|
||||
@@ -2302,6 +2549,24 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
is_submod = 1
|
||||
if is_submod != 0:
|
||||
return None # 是子模块,交给嵌套属性访问路径
|
||||
# 特殊处理: t.ASM_DESCR 不是 CDefine,是内联汇编约束命名空间
|
||||
# t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理
|
||||
# 此处返回 None,让上层嵌套属性访问路径处理
|
||||
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0:
|
||||
return None
|
||||
# CDefine 未找到且属性名符合 ALL_CAPS 约定: 报 FATAL 错误
|
||||
# 不再创建未定义的外部全局变量引用(否则 llc 报 "use of undefined value")
|
||||
if _is_cdefine_name_pattern(at.attr) != 0:
|
||||
err_buf_sma: str = pool.alloc(256)
|
||||
if err_buf_sma is not None:
|
||||
cur_sha1_sma: str = "(unknown)"
|
||||
if trans is not None and trans.ModuleSha1 is not None:
|
||||
cur_sha1_sma = trans.ModuleSha1
|
||||
viperlib.snprintf(err_buf_sma, 256,
|
||||
"[CD] FATAL: Module attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
|
||||
nm_ma.id, at.attr, cur_sha1_sma)
|
||||
VLogger.error(err_buf_sma, "CD")
|
||||
return None
|
||||
# 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
|
||||
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
|
||||
@@ -2347,14 +2612,21 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
cdef_val_nested = _lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
|
||||
if HandlesType.is_cdefine_found() != 0:
|
||||
return llvmlite.const_int32(pool, cdef_val_nested)
|
||||
# CDefine 未找到: 创建前向引用 (CDefine 通常是 i32)
|
||||
i32_ty_fwd: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
gv_ref_fwd: llvmlite.Value | t.CPtr = make_global_ref(
|
||||
pool, at.attr, i32_ty_fwd)
|
||||
if gv_ref_fwd is not None and gv_ref_fwd.Ty is not None:
|
||||
load_ty_fwd: llvmlite.LLVMType | t.CPtr = gv_ref_fwd.Ty.Pointee
|
||||
if load_ty_fwd is not None:
|
||||
return llvmlite.build_load(builder, load_ty_fwd, gv_ref_fwd)
|
||||
# CDefine 未找到: 报 FATAL 错误(不再创建未定义的全局变量引用)
|
||||
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
|
||||
# 导致 llc 报 "use of undefined value" 错误。
|
||||
# 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
|
||||
if _is_cdefine_name_pattern(at.attr) != 0:
|
||||
err_buf_ma: str = pool.alloc(256)
|
||||
if err_buf_ma is not None:
|
||||
cur_sha1_ma: str = "(unknown)"
|
||||
if trans is not None and trans.ModuleSha1 is not None:
|
||||
cur_sha1_ma = trans.ModuleSha1
|
||||
viperlib.snprintf(err_buf_ma, 256,
|
||||
"[CD] FATAL: Nested attr '%s.%s' NOT found (CDefine cross-module lookup failed) in module sha1=%s\n",
|
||||
qual_buf, at.attr, cur_sha1_ma)
|
||||
VLogger.error(err_buf_ma, "CD")
|
||||
return None
|
||||
|
||||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体)
|
||||
obj_ptr: llvmlite.Value | t.CPtr = None
|
||||
@@ -2401,7 +2673,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
|
||||
if alias_ptr is not None:
|
||||
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
|
||||
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找
|
||||
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
|
||||
# 传递 SHA1 以区分跨模块同名类
|
||||
if field_info is None:
|
||||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||||
@@ -2413,7 +2685,7 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
cur_sha1: str = trans.ModuleSha1
|
||||
field_info = HandlesStruct.lookup_field_by_class(
|
||||
var_entry.AnnotClassName, at.attr, cur_sha1)
|
||||
# 回退 1 成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||||
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||||
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
|
||||
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
|
||||
if field_info is not None:
|
||||
@@ -2427,33 +2699,11 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
if casted_ptr is not None:
|
||||
obj_ptr = casted_ptr
|
||||
struct_ty = annot_se.Ty
|
||||
# 回退 1 更新 struct_ty 后,需重新确保
|
||||
# 按需过滤更新 struct_ty 后,需重新确保
|
||||
# 注解类型结构体定义在当前模块可用
|
||||
# (原始 struct_ty 可能是 i8,ensure 无效)
|
||||
HandlesStruct.ensure_struct_def_in_module(
|
||||
pool, mod, annot_se.Ty)
|
||||
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
|
||||
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
|
||||
if field_info is None:
|
||||
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
|
||||
HandlesStruct.find_subclass_with_field(
|
||||
var_entry.AnnotClassName, at.attr)
|
||||
if sub_entry is not None and sub_entry.Ty is not None:
|
||||
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
|
||||
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
|
||||
if sub_field is not None:
|
||||
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
|
||||
llvmlite.Ptr(pool, sub_entry.Ty)
|
||||
casted_ptr: llvmlite.Value | t.CPtr = \
|
||||
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
|
||||
if casted_ptr is not None:
|
||||
sub_field_ptr: llvmlite.Value | t.CPtr = \
|
||||
llvmlite.build_gep_struct(
|
||||
builder, sub_entry.Ty, sub_field.Ty,
|
||||
casted_ptr, sub_field.Index)
|
||||
if sub_field_ptr is not None:
|
||||
return llvmlite.build_load(
|
||||
builder, sub_field.Ty, sub_field_ptr)
|
||||
if field_info is not None:
|
||||
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
|
||||
# 联合体:bitcast obj_ptr 到 field_ty* 后 load
|
||||
@@ -2483,6 +2733,28 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
|
||||
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
|
||||
return loaded_val
|
||||
# fast-fail: 字段未找到,打印诊断信息
|
||||
if trans is not None:
|
||||
diag_annot: str = None
|
||||
diag_var_nm: str = None
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||||
diag_var_nm = dnm.id
|
||||
if dnm.id is not None:
|
||||
dve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||||
trans.SymTab, dnm.id)
|
||||
if dve is not None:
|
||||
diag_annot = dve.AnnotClassName
|
||||
fb_attr: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_attr is not None:
|
||||
viperlib.snprintf(fb_attr, 1024,
|
||||
"属性访问失败: .%s (var=%s annot=%s sha1=%s:%d)",
|
||||
at.attr,
|
||||
diag_var_nm if diag_var_nm is not None else "?",
|
||||
diag_annot if diag_annot is not None else "None",
|
||||
trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
|
||||
at.lineno if at.lineno is not None else 0)
|
||||
VLogger.error(fb_attr, "ATTR")
|
||||
return None
|
||||
case _:
|
||||
return None
|
||||
@@ -2620,6 +2892,15 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||||
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||||
if nm_wma.id is not None:
|
||||
# CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局
|
||||
if _is_cdefine_name_pattern(at.attr) != 0:
|
||||
err_buf_wma: str = pool.alloc(256)
|
||||
if err_buf_wma is not None:
|
||||
viperlib.snprintf(err_buf_wma, 256,
|
||||
"[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n",
|
||||
nm_wma.id, at.attr)
|
||||
VLogger.error(err_buf_wma, "CD")
|
||||
return None
|
||||
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
|
||||
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
|
||||
@@ -2645,7 +2926,7 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||||
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
|
||||
struct_ty, at.attr)
|
||||
# 回退 1: 类型指针比较失败时,通过 AnnotClassName 按类名查找
|
||||
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
|
||||
# 传递 SHA1 以区分跨模块同名类
|
||||
if field_info is None:
|
||||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||||
@@ -2659,7 +2940,7 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
cur_sha1 = trans.ModuleSha1
|
||||
field_info = HandlesStruct.lookup_field_by_class(
|
||||
var_entry.AnnotClassName, at.attr, cur_sha1)
|
||||
# 回退 1 成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||||
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||||
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
|
||||
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
|
||||
if field_info is not None:
|
||||
@@ -2673,32 +2954,10 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
if casted_ptr is not None:
|
||||
obj_ptr = casted_ptr
|
||||
struct_ty = annot_se.Ty
|
||||
# 回退 1 更新 struct_ty 后,需重新确保
|
||||
# 按需过滤更新 struct_ty 后,需重新确保
|
||||
# 注解类型结构体定义在当前模块可用
|
||||
HandlesStruct.ensure_struct_def_in_module(
|
||||
pool, mod, annot_se.Ty)
|
||||
# 回退 2: 子类搜索 — 注解类型是基类但实际值是派生类
|
||||
# 如 node: AST | t.CPtr = If(...),访问 node.orelse
|
||||
# orelse 在 If 上不在 AST 上,需搜索 AST 的子类
|
||||
if field_info is None:
|
||||
sub_entry: HandlesStruct.StructEntry | t.CPtr = \
|
||||
HandlesStruct.find_subclass_with_field(
|
||||
var_entry.AnnotClassName, at.attr)
|
||||
if sub_entry is not None and sub_entry.Ty is not None:
|
||||
sub_field: HandlesStruct.FieldEntry | t.CPtr = \
|
||||
HandlesStruct.lookup_field(sub_entry.Ty, at.attr)
|
||||
if sub_field is not None:
|
||||
sub_ptr_ty: llvmlite.LLVMType | t.CPtr = \
|
||||
llvmlite.Ptr(pool, sub_entry.Ty)
|
||||
casted_ptr: llvmlite.Value | t.CPtr = \
|
||||
llvmlite.build_bitcast(builder, obj_ptr, sub_ptr_ty)
|
||||
if casted_ptr is not None:
|
||||
# 确保子类结构体定义在当前模块可用
|
||||
HandlesStruct.ensure_struct_def_in_module(
|
||||
pool, mod, sub_entry.Ty)
|
||||
return llvmlite.build_gep_struct(
|
||||
builder, sub_entry.Ty, sub_field.Ty,
|
||||
casted_ptr, sub_field.Index)
|
||||
if field_info is not None:
|
||||
# 联合体:bitcast obj_ptr 到 field_ty*(字段指针用于 store)
|
||||
if HandlesStruct.is_union_by_type(struct_ty) == 1:
|
||||
@@ -2707,6 +2966,28 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
|
||||
# 普通结构体:GEP 获取字段指针
|
||||
return llvmlite.build_gep_struct(
|
||||
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
|
||||
# fast-fail: 字段未找到,打印诊断信息
|
||||
if trans is not None:
|
||||
diag_annot_g: str = None
|
||||
diag_var_nm_g: str = None
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
dnm_g: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||||
diag_var_nm_g = dnm_g.id
|
||||
if dnm_g.id is not None:
|
||||
dve_g: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||||
trans.SymTab, dnm_g.id)
|
||||
if dve_g is not None:
|
||||
diag_annot_g = dve_g.AnnotClassName
|
||||
fb_attr_g: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_attr_g is not None:
|
||||
viperlib.snprintf(fb_attr_g, 1024,
|
||||
"属性写入失败: .%s (var=%s annot=%s sha1=%s:%d)",
|
||||
at.attr,
|
||||
diag_var_nm_g if diag_var_nm_g is not None else "?",
|
||||
diag_annot_g if diag_annot_g is not None else "None",
|
||||
trans.ModuleSha1 if trans.ModuleSha1 is not None else "?",
|
||||
at.lineno if at.lineno is not None else 0)
|
||||
VLogger.error(fb_attr_g, "ATTR")
|
||||
return None
|
||||
case _:
|
||||
return None
|
||||
|
||||
@@ -8,6 +8,7 @@ import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesBase as HandlesBase
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
@@ -2257,13 +2258,11 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
|
||||
"__before_init__", storage_ptr, None, 0, trans)
|
||||
|
||||
# 如果有 __init__,调用 __init__(ptr, args...)
|
||||
# 注意: 仅依据 ctor_entry.HasInit 判断,不强制推断
|
||||
# (JsonValue 等类有 __new__ 但无 __init__,强制推断会导致 undefined reference)
|
||||
has_init_flag: int = 0
|
||||
if ctor_entry is not None:
|
||||
has_init_flag = ctor_entry.HasInit
|
||||
# 跨模块 OOP 类: HasInit 可能未设置(_translate_oop_methods 未被调用)
|
||||
# 但 IsOOP=1 说明类有方法,推断 HasInit=1
|
||||
if is_oop != 0 and has_init_flag == 0:
|
||||
has_init_flag = 1
|
||||
if has_init_flag != 0:
|
||||
# 翻译构造函数参数
|
||||
arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||||
@@ -2283,8 +2282,9 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
_call_method_on_ptr(pool, builder, mod, class_name,
|
||||
"__init__", storage_ptr, arg_vals, real_arg_count, trans)
|
||||
else:
|
||||
# 无 __init__:逐字段 store 参数值
|
||||
elif has_new_flag == 0:
|
||||
# 无 __init__ 且无 __new__:逐字段 store 参数值
|
||||
# (有 __new__ 时参数已由 __new__ 消费,不应存入字段)
|
||||
for ai in range(can):
|
||||
arg: ast.AST | t.CPtr = cargs.get(ai)
|
||||
if arg is None:
|
||||
@@ -2974,6 +2974,262 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 与 _infer_external_func_ret_ty 的默认策略保持一致
|
||||
return llvmlite.Int32(pool)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 跨模块方法返回类型查找(从 text.ll)
|
||||
#
|
||||
# Phase A 翻译每个源文件后生成 text.ll,其中 define 行包含
|
||||
# 方法的实际返回类型。当 Phase A 翻译后续文件调用跨模块方法时,
|
||||
# _infer_method_ret_ty 默认返回 i32,导致指针返回值被截断。
|
||||
# _LookupMethodRetTyFromTextLL 读取依赖模块的 text.ll,从 define
|
||||
# 行提取实际返回类型,避免指针截断。
|
||||
# ============================================================
|
||||
|
||||
_g_temp_dir: str = None
|
||||
_g_temp_dir_len: t.CSizeT = 0
|
||||
|
||||
# text.ll 读取缓冲区大小
|
||||
_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144
|
||||
|
||||
|
||||
# ============================================================
|
||||
# SetTempDir - 设置 temp 目录路径
|
||||
#
|
||||
# 在 Phase2.py 的 Phase A 翻译前调用,使 HandlesExprCall
|
||||
# 能够读取依赖模块的 text.ll 文件。
|
||||
# ============================================================
|
||||
def SetTempDir(temp_dir: str):
|
||||
"""设置 temp 目录路径(供跨模块方法返回类型查找使用)"""
|
||||
global _g_temp_dir
|
||||
global _g_temp_dir_len
|
||||
_g_temp_dir = temp_dir
|
||||
if temp_dir is not None:
|
||||
_g_temp_dir_len = string.strlen(temp_dir)
|
||||
else:
|
||||
_g_temp_dir_len = 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ParseLLVMRetTyStr - 将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType
|
||||
#
|
||||
# 从 define 行中提取的返回类型字符串(define 与 @ 之间的内容),
|
||||
# 转换为 llvmlite.LLVMType 对象。
|
||||
#
|
||||
# 支持的类型:
|
||||
# void → Void
|
||||
# i8/i16/i32/i64 → Int8/Int16/Int32/Int64
|
||||
# double → Double, float → Float
|
||||
# 任何含 * 的类型 → Ptr(Int8)(通用指针,保留 64 位地址)
|
||||
#
|
||||
# Args:
|
||||
# pool: 内存池
|
||||
# buf: 包含类型字符串的缓冲区
|
||||
# start: 类型字符串起始位置
|
||||
# end: 类型字符串结束位置(不含)
|
||||
#
|
||||
# Returns:
|
||||
# LLVM 类型对象,或 None(无法解析)
|
||||
# ============================================================
|
||||
def _ParseLLVMRetTyStr(pool: memhub.MemBuddy | t.CPtr,
|
||||
buf: bytes,
|
||||
start: t.CSizeT,
|
||||
end: t.CSizeT) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
|
||||
if pool is None or buf is None or start >= end:
|
||||
return None
|
||||
|
||||
# 去除前导空白
|
||||
s: t.CSizeT = start
|
||||
while s < end:
|
||||
c: int = buf[s]
|
||||
if c != ' ' and c != '\t':
|
||||
break
|
||||
s += 1
|
||||
# 去除尾部空白
|
||||
e: t.CSizeT = end
|
||||
while e > s:
|
||||
c2: int = buf[e - 1]
|
||||
if c2 != ' ' and c2 != '\t':
|
||||
break
|
||||
e -= 1
|
||||
|
||||
ty_len: t.CSizeT = e - s
|
||||
if ty_len == 0:
|
||||
return None
|
||||
|
||||
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
|
||||
# 包括 i8*, i32*, %"name"*, { i32, i8 }* 等
|
||||
ci: t.CSizeT = s
|
||||
while ci < e:
|
||||
if buf[ci] == '*':
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
ci += 1
|
||||
|
||||
# void
|
||||
if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0:
|
||||
return llvmlite.Void(pool)
|
||||
|
||||
# double
|
||||
if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0:
|
||||
return llvmlite.Double(pool)
|
||||
|
||||
# float
|
||||
if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0:
|
||||
return llvmlite.Float(pool)
|
||||
|
||||
# iN (i8, i16, i32, i64)
|
||||
if ty_len >= 2 and buf[s] == 'i':
|
||||
bits: int = 0
|
||||
di: t.CSizeT = s + 1
|
||||
valid: int = 1
|
||||
while di < e:
|
||||
d: int = buf[di]
|
||||
if d >= '0' and d <= '9':
|
||||
bits = bits * 10 + (d - '0')
|
||||
else:
|
||||
valid = 0
|
||||
break
|
||||
di += 1
|
||||
if valid != 0 and bits > 0:
|
||||
if bits == 8:
|
||||
return llvmlite.Int8(pool)
|
||||
if bits == 16:
|
||||
return llvmlite.Int16(pool)
|
||||
if bits == 32:
|
||||
return llvmlite.Int32(pool)
|
||||
if bits == 64:
|
||||
return llvmlite.Int64(pool)
|
||||
|
||||
# 无法解析
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _LookupMethodRetTyFromTextLL - 从依赖模块的 text.ll 查找方法返回类型
|
||||
#
|
||||
# 读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),搜索
|
||||
# define 行中包含 "{sha1}.{class_name}.{method_name}" 的函数,
|
||||
# 提取返回类型并转换为 llvmlite.LLVMType。
|
||||
#
|
||||
# 解决问题: _infer_method_ret_ty 默认返回 i32,导致指针返回值
|
||||
# 被 inttoptr 截断为 32 位(coerce_to_type line 238-239)。
|
||||
# 本函数从 text.ll 获取实际返回类型,避免截断。
|
||||
#
|
||||
# Args:
|
||||
# pool: 内存池
|
||||
# class_sha1: 类所属模块的 SHA1
|
||||
# class_name: 类名
|
||||
# method_name: 方法名
|
||||
#
|
||||
# Returns:
|
||||
# LLVM 类型对象,或 None(未找到/text.ll 不存在)
|
||||
# ============================================================
|
||||
def _LookupMethodRetTyFromTextLL(pool: memhub.MemBuddy | t.CPtr,
|
||||
class_sha1: str,
|
||||
class_name: str,
|
||||
method_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""从依赖模块的 text.ll 中查找方法返回类型"""
|
||||
if pool is None or class_sha1 is None or class_name is None or method_name is None:
|
||||
return None
|
||||
if _g_temp_dir is None or _g_temp_dir_len == 0:
|
||||
return None
|
||||
|
||||
sha1_len: t.CSizeT = string.strlen(class_sha1)
|
||||
if sha1_len < 2:
|
||||
return None
|
||||
|
||||
# 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll
|
||||
# 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致)
|
||||
path_len: t.CSizeT = _g_temp_dir_len + sha1_len + 14
|
||||
path_buf: bytes = stdlib.malloc(path_len)
|
||||
if path_buf is None:
|
||||
return None
|
||||
subdir_buf: bytes = stdlib.malloc(3)
|
||||
if subdir_buf is None:
|
||||
stdlib.free(path_buf)
|
||||
return None
|
||||
subdir_buf[0] = class_sha1[0]
|
||||
subdir_buf[1] = class_sha1[1]
|
||||
subdir_buf[2] = '\0'
|
||||
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
|
||||
_g_temp_dir, subdir_buf, class_sha1)
|
||||
stdlib.free(subdir_buf)
|
||||
|
||||
# 读取 text.ll
|
||||
read_buf: bytes = stdlib.malloc(_TEXT_LL_READ_BUF_SIZE)
|
||||
if read_buf is None:
|
||||
stdlib.free(path_buf)
|
||||
return None
|
||||
|
||||
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
|
||||
if tf.closed:
|
||||
stdlib.free(path_buf)
|
||||
stdlib.free(read_buf)
|
||||
return None
|
||||
text_len: t.CInt64T = tf.read_all(read_buf, _TEXT_LL_READ_BUF_SIZE)
|
||||
tf.close()
|
||||
stdlib.free(path_buf)
|
||||
|
||||
if text_len <= 0:
|
||||
stdlib.free(read_buf)
|
||||
return None
|
||||
if text_len < _TEXT_LL_READ_BUF_SIZE:
|
||||
read_buf[text_len] = '\0'
|
||||
else:
|
||||
read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0'
|
||||
|
||||
# 构造搜索模式: "{sha1}.{class_name}.{method_name}"
|
||||
cls_len: t.CSizeT = string.strlen(class_name)
|
||||
meth_len: t.CSizeT = string.strlen(method_name)
|
||||
search_len: t.CSizeT = sha1_len + cls_len + meth_len + 3
|
||||
search_buf: bytes = stdlib.malloc(search_len)
|
||||
if search_buf is None:
|
||||
stdlib.free(read_buf)
|
||||
return None
|
||||
viperlib.snprintf(search_buf, search_len, "%s.%s.%s",
|
||||
class_sha1, class_name, method_name)
|
||||
search_str_len: t.CSizeT = string.strlen(search_buf)
|
||||
|
||||
# 在 text.ll 中搜索 define 行包含 search_buf
|
||||
buf_len: t.CSizeT = string.strlen(read_buf)
|
||||
pos: t.CSizeT = 0
|
||||
result_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
|
||||
while pos < buf_len and result_ty is None:
|
||||
ls: t.CSizeT = pos
|
||||
le: t.CSizeT = pos
|
||||
while le < buf_len:
|
||||
if read_buf[le] == '\n':
|
||||
break
|
||||
le += 1
|
||||
ll: t.CSizeT = le - ls
|
||||
|
||||
# 检查是否是 define 行
|
||||
if ll >= 7 and string.strncmp(read_buf + ls, "define ", 7) == 0:
|
||||
# 找 @ 符号
|
||||
at_pos: t.CSizeT = ls + 7
|
||||
while at_pos < ls + ll:
|
||||
if read_buf[at_pos] == '@':
|
||||
break
|
||||
at_pos += 1
|
||||
if at_pos < ls + ll:
|
||||
# 跳过可选引号
|
||||
name_start: t.CSizeT = at_pos + 1
|
||||
if name_start < ls + ll and read_buf[name_start] == '"':
|
||||
name_start += 1
|
||||
# 检查是否匹配 search_buf
|
||||
if name_start + search_str_len <= ls + ll:
|
||||
if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0:
|
||||
# 匹配!提取返回类型: [ls+7, at_pos)
|
||||
result_ty = _ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos)
|
||||
|
||||
pos = le + 1
|
||||
|
||||
stdlib.free(search_buf)
|
||||
stdlib.free(read_buf)
|
||||
return result_ty
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _translate_method_call - 翻译方法调用 obj.method(args)
|
||||
#
|
||||
@@ -3069,7 +3325,13 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
if frt is not None:
|
||||
call_ret_ty = frt
|
||||
else:
|
||||
# found_func 为 None(stub 未注入):根据方法名推断返回类型
|
||||
# found_func 为 None(stub 未注入):尝试从 text.ll 查找实际返回类型
|
||||
# 避免指针返回值被 _infer_method_ret_ty 默认为 i32 导致 inttoptr 截断
|
||||
looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
|
||||
pool, cls_sha1_mc, class_name, method_name)
|
||||
if looked_up_ty is not None:
|
||||
call_ret_ty = looked_up_ty
|
||||
else:
|
||||
call_ret_ty = _infer_method_ret_ty(pool, method_name)
|
||||
|
||||
# 翻译调用参数
|
||||
@@ -3521,6 +3783,9 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr,
|
||||
# - 整数 → 指针: inttoptr (如 t.CPtr(int_val) → i8*)
|
||||
# - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8)
|
||||
# - 指针 → 指针: bitcast (如 t.CPtr(ptr_val) → i8*)
|
||||
# - 整数 → 浮点: sitofp (如 t.CDouble(i32_val) → double)
|
||||
# - 浮点 → 整数: fptosi (如 t.CInt(double_val) → i32)
|
||||
# - 浮点 → 浮点: fpext/fptrunc (如 t.CFloat(double_val) → float)
|
||||
# ============================================================
|
||||
def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
@@ -3532,6 +3797,8 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||||
target_bits: int = HandlesExpr.get_llvm_type_bits(target_ty)
|
||||
val_fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
|
||||
target_fbits: int = HandlesExpr.get_llvm_float_bits(target_ty)
|
||||
|
||||
# 整数 → 整数: trunc/zext
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
@@ -3542,12 +3809,29 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
|
||||
else:
|
||||
return llvmlite.build_trunc(builder, val, target_ty)
|
||||
|
||||
# 整数 → 浮点: sitofp
|
||||
if val_bits != 0 and target_fbits != 0:
|
||||
return llvmlite.build_si2fp(builder, val, target_ty)
|
||||
|
||||
# 浮点 → 整数: fptosi
|
||||
if val_fbits != 0 and target_bits != 0:
|
||||
return llvmlite.build_fp2si(builder, val, target_ty)
|
||||
|
||||
# 浮点 → 浮点: fpext/fptrunc
|
||||
if val_fbits != 0 and target_fbits != 0:
|
||||
if val_fbits == target_fbits:
|
||||
return val
|
||||
elif val_fbits < target_fbits:
|
||||
return llvmlite.build_fpext(builder, val, target_ty)
|
||||
else:
|
||||
return llvmlite.build_fptrunc(builder, val, target_ty)
|
||||
|
||||
# 指针 → 整数: ptrtoint
|
||||
if val_bits == 0 and target_bits != 0:
|
||||
if val_bits == 0 and val_fbits == 0 and target_bits != 0:
|
||||
return llvmlite.build_ptrtoint(builder, val, target_ty)
|
||||
|
||||
# 整数 → 指针: inttoptr
|
||||
if val_bits != 0 and target_bits == 0:
|
||||
# 整数 → 指针: inttoptr(必须排除浮点 target,浮点 target_bits 也是 0)
|
||||
if val_bits != 0 and target_bits == 0 and target_fbits == 0:
|
||||
return llvmlite.build_inttoptr(builder, val, target_ty)
|
||||
|
||||
# 指针 → 指针: bitcast
|
||||
@@ -3763,6 +4047,28 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
if cargs is not None:
|
||||
can = cargs.__len__()
|
||||
|
||||
# 内置强转函数: str(x)/bytes(x) → i8*, int(x) → i32
|
||||
# 等价于 (t.CChar | t.CPtr)(x) / (t.CInt32T)(x)
|
||||
# 单参数:str(val) → bitcast val to i8*
|
||||
# int(val) → trunc/sext val to i32
|
||||
if func_name is not None and can == 1:
|
||||
if string.strcmp(func_name, "str") == 0 or string.strcmp(func_name, "bytes") == 0:
|
||||
# str(x) / bytes(x) → i8* (bitcast)
|
||||
cast_arg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
|
||||
if cast_arg is not None:
|
||||
i8_ptr_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
return _translate_t_type_cast(pool, builder, cast_arg, i8_ptr_ty_builtin)
|
||||
return None
|
||||
elif string.strcmp(func_name, "int") == 0:
|
||||
# int(x) → i32
|
||||
cast_arg_int: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
|
||||
if cast_arg_int is not None:
|
||||
i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin)
|
||||
return None
|
||||
|
||||
# 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等
|
||||
# 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用)
|
||||
# 支持:
|
||||
@@ -3805,6 +4111,19 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
# t.XXX 类型转换已确认但不满足参数条件:返回 None,不跌落到跨模块 FATAL
|
||||
return None
|
||||
|
||||
# 裸名 typedef 调用: BYTEPTR(ptr1), HANDLE(val), CHARPTR(str) 等
|
||||
# 用户通过 `from stdint import *` 导入 typedef 后,可直接用裸名做类型转换
|
||||
# map_t_type 包含所有内置 typedef 名称(BYTEPTR/HANDLE/LPCSTR/size_t 等)
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
bare_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, func_name)
|
||||
if bare_ty is not None and can == 1:
|
||||
bare_arg_node: ast.AST | t.CPtr = cargs.get(0)
|
||||
if bare_arg_node is not None:
|
||||
bare_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, bare_arg_node, None, 0, trans)
|
||||
if bare_arg_val is not None:
|
||||
return _translate_t_type_cast(pool, builder, bare_arg_val, bare_ty)
|
||||
|
||||
# 检测 ClassName.__sizeof__() — 返回结构体大小常量(i64)
|
||||
# __sizeof__ 是内置方法,不走跨模块调用路径
|
||||
# 支持: Argument.__sizeof__() 和 w32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
@@ -3907,11 +4226,6 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
# 模块属性前向引用回退为 i32),GEP base 会是 i32 而非指针,
|
||||
# 导致 llc 报错 "base of getelementptr must be a pointer"
|
||||
if HandlesExpr.is_ptr_type(obj_val.Ty) == 0:
|
||||
# 诊断: 输出节点类型信息帮助定位根本原因
|
||||
len_node_kind: int = len_at.value.kind()
|
||||
stdio.printf(
|
||||
"[LEN-DIAG] __len__ on non-ptr (kind=%d), fallback to 0\n",
|
||||
len_node_kind)
|
||||
return llvmlite.const_int64(pool, 0)
|
||||
i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
# 注意: 必须用 const_int64 而非 ConstInt(...,"0")
|
||||
@@ -4049,7 +4363,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
llvmlite.value_set_next(tail3, arg_val3)
|
||||
tail3 = arg_val3
|
||||
total3: int = 1 + can
|
||||
ret_ty3: llvmlite.LLVMType | t.CPtr = _infer_method_ret_ty(pool, func_name)
|
||||
ret_ty3: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
|
||||
pool, sha1_fc, cls_nm2, func_name)
|
||||
if ret_ty3 is None:
|
||||
ret_ty3 = _infer_method_ret_ty(pool, func_name)
|
||||
_ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3)
|
||||
return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0)
|
||||
|
||||
|
||||
@@ -143,12 +143,26 @@ class ForHandle(HandlesBase.Mixin):
|
||||
trans.SymTab, var_name, var_alloca) == 0:
|
||||
new_vars = 1
|
||||
|
||||
# 5. 存储初始值 (类型对齐: start_val 可能是 i64,需截断到 i32)
|
||||
# 获取循环变量的实际元素类型(alloca 是指针类型,需解引用 Pointee)
|
||||
# 当循环变量已声明为 i64(如 t.CSizeT),使用 i64 而非硬编码 i32
|
||||
# 避免 load i32, i64* / store i32, i64* 类型不匹配
|
||||
loop_var_ty: llvmlite.LLVMType | t.CPtr = i32_ty
|
||||
if var_alloca is not None and var_alloca.Ty is not None:
|
||||
match var_alloca.Ty:
|
||||
case llvmlite.LLVMType.Ptr(pointee_ty):
|
||||
if pointee_ty is not None:
|
||||
loop_var_ty = pointee_ty
|
||||
loop_var_bits: int = HandlesExpr.get_llvm_type_bits(loop_var_ty)
|
||||
|
||||
# 5. 存储初始值 (类型对齐: start_val 可能是 i32/i64,需对齐到 loop_var_ty)
|
||||
init_val: llvmlite.Value | t.CPtr = start_val
|
||||
if start_val is not None and start_val.Ty is not None:
|
||||
start_bits: int = HandlesExpr.get_llvm_type_bits(start_val.Ty)
|
||||
if start_bits != 0 and start_bits != 32:
|
||||
init_val = llvmlite.build_trunc(builder, start_val, i32_ty)
|
||||
if start_bits != 0 and start_bits != loop_var_bits:
|
||||
if start_bits < loop_var_bits:
|
||||
init_val = llvmlite.build_sext(builder, start_val, loop_var_ty)
|
||||
else:
|
||||
init_val = llvmlite.build_trunc(builder, start_val, loop_var_ty)
|
||||
llvmlite.build_store(builder, init_val, var_alloca)
|
||||
|
||||
# 6. 创建基本块: cond / body / incr / end(使用 trans._label_counter,不与 SSA 名共享)
|
||||
@@ -173,7 +187,7 @@ class ForHandle(HandlesBase.Mixin):
|
||||
|
||||
# 8. cond 块: load i, icmp slt i, stop, cond_br body/end
|
||||
llvmlite.position_at_end(builder, cond_bb)
|
||||
cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca)
|
||||
cur_i: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca)
|
||||
# 类型对齐: stop_val 可能是 i64 (如 range(strlen(s))),需将 cur_i 提升到 stop_val 类型
|
||||
cmp_lhs: llvmlite.Value | t.CPtr = cur_i
|
||||
cmp_rhs: llvmlite.Value | t.CPtr = stop_val
|
||||
@@ -184,7 +198,7 @@ class ForHandle(HandlesBase.Mixin):
|
||||
if cur_bits < stop_bits:
|
||||
cmp_lhs = llvmlite.build_sext(builder, cur_i, stop_val.Ty)
|
||||
else:
|
||||
cmp_rhs = llvmlite.build_trunc(builder, stop_val, i32_ty)
|
||||
cmp_rhs = llvmlite.build_trunc(builder, stop_val, loop_var_ty)
|
||||
cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_SLT, cmp_lhs, cmp_rhs)
|
||||
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb)
|
||||
@@ -215,17 +229,17 @@ class ForHandle(HandlesBase.Mixin):
|
||||
|
||||
# 10. incr 块: i = i + step, 跳回 cond
|
||||
llvmlite.position_at_end(builder, incr_bb)
|
||||
cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, var_alloca)
|
||||
# 类型对齐: step_val 可能是 i64,需截断到 i32 与 cur_i2 类型一致
|
||||
cur_i2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, loop_var_ty, var_alloca)
|
||||
# 类型对齐: step_val 可能是 i32/i64,需对齐到 loop_var_ty 与 cur_i2 类型一致
|
||||
incr_step: llvmlite.Value | t.CPtr = step_val
|
||||
if step_val is not None and step_val.Ty is not None:
|
||||
step_bits: int = HandlesExpr.get_llvm_type_bits(step_val.Ty)
|
||||
cur2_bits: int = HandlesExpr.get_llvm_type_bits(cur_i2.Ty)
|
||||
if step_bits != 0 and cur2_bits != 0 and step_bits != cur2_bits:
|
||||
if step_bits > cur2_bits:
|
||||
incr_step = llvmlite.build_trunc(builder, step_val, i32_ty)
|
||||
incr_step = llvmlite.build_trunc(builder, step_val, loop_var_ty)
|
||||
else:
|
||||
incr_step = llvmlite.build_sext(builder, step_val, i32_ty)
|
||||
incr_step = llvmlite.build_sext(builder, step_val, loop_var_ty)
|
||||
next_i: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_i2, incr_step)
|
||||
llvmlite.build_store(builder, next_i, var_alloca)
|
||||
llvmlite.build_br(builder, cond_bb)
|
||||
|
||||
@@ -58,12 +58,17 @@ class IfHandle(HandlesBase.Mixin):
|
||||
trans._funcs, trans._func_count, trans)
|
||||
|
||||
# 2. 转换为 i1 条件
|
||||
# Compare/Not 表达式已返回 i1,直接使用;其他类型与 0 比较
|
||||
# Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较
|
||||
if cond_val is None:
|
||||
cond_val = llvmlite.const_int32(pool, 0)
|
||||
cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
|
||||
if cond_bits == 1:
|
||||
cond_i1: llvmlite.Value | t.CPtr = cond_val
|
||||
elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0:
|
||||
# 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type")
|
||||
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
|
||||
cond_i1 = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_NE, cond_val, null_cond)
|
||||
else:
|
||||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
cond_i1 = llvmlite.build_icmp(
|
||||
|
||||
@@ -559,21 +559,6 @@ class ImportsHandle(HandlesBase.Mixin):
|
||||
resolved: str = _resolve_relative_module(
|
||||
self.Trans.Pool, self.Trans.CurrentPackage,
|
||||
impf.level, impf.module)
|
||||
# [IFD] 诊断 from-import 名称处理
|
||||
mod_dbg: str = "(null)"
|
||||
if impf.module is not None:
|
||||
mod_dbg = impf.module
|
||||
pkg_dbg: str = "(null)"
|
||||
if self.Trans.CurrentPackage is not None:
|
||||
pkg_dbg = self.Trans.CurrentPackage
|
||||
res_dbg: str = "(null)"
|
||||
if resolved is not None:
|
||||
res_dbg = resolved
|
||||
names_dbg: t.CSizeT = 0
|
||||
if impf.names is not None:
|
||||
names_dbg = impf.names.__len__()
|
||||
stdio.printf("[IFD] level=%d module=%s pkg=%s resolved=%s names=%d\n",
|
||||
impf.level, mod_dbg, pkg_dbg, res_dbg, names_dbg)
|
||||
if resolved is not None:
|
||||
names: list[ast.AST | t.CPtr] | t.CPtr = impf.names
|
||||
if names is not None:
|
||||
|
||||
@@ -129,11 +129,136 @@ def translate_children(trans: HT.Translator | t.CPtr,
|
||||
return added_total
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _build_array_initializer_text - 从 AST List 生成 LLVM IR 数组初始化器
|
||||
#
|
||||
# 生成格式: [N x elem_ty] [elem_ty val0, elem_ty val1, ...]
|
||||
# 用于模块级 t.CArray 列表字面量初始化(不依赖 builder,直接生成常量初始化器)
|
||||
# ============================================================
|
||||
def _build_array_initializer_text(pool: memhub.MemBuddy | t.CPtr,
|
||||
var_ty: llvmlite.LLVMType | t.CPtr,
|
||||
list_node: ast.List | t.CPtr) -> str:
|
||||
"""从 AST List 节点生成 LLVM IR 数组初始化器文本,None 失败"""
|
||||
if var_ty is None or list_node is None:
|
||||
return None
|
||||
|
||||
# 重新进行类型转换,确保 TPV 编译器正确识别 ast.List 类型
|
||||
# (避免 init_list_node 初始值为 None 时类型推断退化为 t.CPtr)
|
||||
ln: ast.List | t.CPtr = (ast.List | t.CPtr)(list_node)
|
||||
if ln is None:
|
||||
return None
|
||||
|
||||
# 从 var_ty 提取数组元素类型和数量
|
||||
elem_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
arr_count: t.CInt = 0
|
||||
match var_ty:
|
||||
case llvmlite.LLVMType.Array(et, cnt):
|
||||
elem_ty = et
|
||||
arr_count = cnt
|
||||
|
||||
if elem_ty is None or arr_count <= 0:
|
||||
return None
|
||||
|
||||
# 确定元素类型的 IR 表示
|
||||
elem_ir_ty: str = "i8"
|
||||
match elem_ty:
|
||||
case llvmlite.LLVMType.Int(bits):
|
||||
if bits == 8:
|
||||
elem_ir_ty = "i8"
|
||||
elif bits == 16:
|
||||
elem_ir_ty = "i16"
|
||||
elif bits == 32:
|
||||
elem_ir_ty = "i32"
|
||||
elif bits == 64:
|
||||
elem_ir_ty = "i64"
|
||||
|
||||
# 获取列表元素(通过 ln.elts 而非 list_node.elts,确保类型正确识别)
|
||||
elts: list[ast.AST | t.CPtr] | t.CPtr = ln.elts
|
||||
if elts is None:
|
||||
return None
|
||||
elts_count: t.CSizeT = elts.__len__()
|
||||
|
||||
# 计算缓冲区大小: 每个元素最多 "i32 -9223372036854775808, " 约 26 字符
|
||||
buf_size: t.CSizeT = 64 + elts_count * 32
|
||||
buf: t.CChar | t.CPtr = pool.alloc(buf_size)
|
||||
if buf is None:
|
||||
return None
|
||||
|
||||
# 写入前缀: "["(类型前缀由 _print_global 输出,初始化器只需元素列表)
|
||||
written: t.CInt = viperlib.snprintf(buf, buf_size, "[")
|
||||
pos: t.CSizeT = t.CSizeT(written)
|
||||
|
||||
# 逐个元素写入
|
||||
i: t.CSizeT = 0
|
||||
while i < elts_count:
|
||||
elem_node: ast.AST | t.CPtr = elts.get(i)
|
||||
# 直接从 Constant 节点提取 int_val(避免 extract_cdefine_int_value 的 t.CInt 截断)
|
||||
elem_val: t.CInt64T = 0
|
||||
if elem_node is not None:
|
||||
ek: int = elem_node.kind()
|
||||
if ek == ast.ASTKind.Constant:
|
||||
ec: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(elem_node)
|
||||
if ec is not None and ec.const_kind == ast.CONST_INT:
|
||||
elem_val = ec.int_val
|
||||
elif ek == ast.ASTKind.UnaryOp:
|
||||
# 负数: UnaryOp(USub, Constant)
|
||||
elem_val = HandlesAnnAssign.extract_cdefine_int_value(elem_node)
|
||||
if i > 0:
|
||||
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
|
||||
pos += t.CSizeT(written)
|
||||
written = viperlib.snprintf(buf + pos, buf_size - pos, "%s %lld", elem_ir_ty, elem_val)
|
||||
pos += t.CSizeT(written)
|
||||
i += 1
|
||||
|
||||
# 写入后缀 "]"
|
||||
viperlib.snprintf(buf + pos, buf_size - pos, "]")
|
||||
return buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _build_string_ptr_initializer - 为字符串字面量初始化指针类型全局变量
|
||||
#
|
||||
# 创建内部字符串常量全局 @.str.{var_name} = private constant [len+1 x i8] c"...\00"
|
||||
# 返回 GEP 初始化器文本: getelementptr inbounds ([len+1 x i8], [len+1 x i8]* @.str.{var_name}, i32 0, i32 0)
|
||||
# 用于 b64_tab: t.CArray[t.CChar, None] = "ABC..." 等字符串初始化指针类型
|
||||
# ============================================================
|
||||
def _build_string_ptr_initializer(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
var_name: str,
|
||||
str_val: str) -> str:
|
||||
"""为字符串字面量创建内部常量全局并返回 GEP 初始化器文本,None 失败"""
|
||||
if str_val is None or var_name is None:
|
||||
return None
|
||||
|
||||
# 构造内部字符串常量名称: .str.{var_name}
|
||||
str_name_buf: t.CChar | t.CPtr = pool.alloc(64)
|
||||
if str_name_buf is None:
|
||||
return None
|
||||
viperlib.snprintf(str_name_buf, 64, ".str.%s", var_name)
|
||||
|
||||
# 调用 llvmlite.create_global_string 创建字符串常量全局
|
||||
str_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.create_global_string(
|
||||
pool, mod, str_name_buf, str_val)
|
||||
if str_gv is None:
|
||||
return None
|
||||
|
||||
# 生成 GEP 初始化器文本
|
||||
str_len: t.CSizeT = string.strlen(str_val)
|
||||
init_buf: t.CChar | t.CPtr = pool.alloc(128)
|
||||
if init_buf is None:
|
||||
return None
|
||||
viperlib.snprintf(init_buf, 128,
|
||||
"getelementptr inbounds ([%d x i8], [%d x i8]* @%s, i32 0, i32 0)",
|
||||
str_len + 1, str_len + 1, str_name_buf)
|
||||
return init_buf
|
||||
|
||||
|
||||
# ============================================================
|
||||
# handle_module_level_var - 模块级变量声明 → 创建 LLVM 全局变量
|
||||
#
|
||||
# 当用户已定义 main(无 wrapper main builder)时,模块级
|
||||
# AnnAssign/Assign 创建全局变量 @var_name 并注册到 SymTab 模块作用域
|
||||
# 支持整数、列表字面量、字符串字面量初始值
|
||||
# ============================================================
|
||||
def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> int:
|
||||
@@ -163,7 +288,9 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
var_name: str = None
|
||||
var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
init_val: t.CInt64T = 0
|
||||
has_init: int = 0
|
||||
init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str
|
||||
init_str_val: str = None
|
||||
init_list_node: ast.List | t.CPtr = None
|
||||
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node)
|
||||
@@ -179,12 +306,20 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
|
||||
if resolved is not None:
|
||||
var_ty = resolved
|
||||
# 解析初始值
|
||||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Constant:
|
||||
# 解析初始值(支持整数、列表字面量、字符串字面量)
|
||||
if aa.value is not None:
|
||||
val_kind: int = aa.value.kind()
|
||||
if val_kind == ast.ASTKind.Constant:
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(aa.value)
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
init_val = cn.int_val
|
||||
has_init = 1
|
||||
init_kind = 1
|
||||
elif cn.const_kind == ast.CONST_STR:
|
||||
init_str_val = cn.str_val
|
||||
init_kind = 3
|
||||
elif val_kind == ast.ASTKind.List:
|
||||
init_list_node = (ast.List | t.CPtr)(aa.value)
|
||||
init_kind = 2
|
||||
elif k == ast.ASTKind.Assign:
|
||||
asgn: ast.Assign | t.CPtr = (ast.Assign | t.CPtr)(node)
|
||||
if asgn is None or asgn.targets is None:
|
||||
@@ -197,11 +332,19 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
return 0
|
||||
nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(t0)
|
||||
var_name = nm2.id
|
||||
if asgn.value is not None and asgn.value.kind() == ast.ASTKind.Constant:
|
||||
if asgn.value is not None:
|
||||
val_kind2: int = asgn.value.kind()
|
||||
if val_kind2 == ast.ASTKind.Constant:
|
||||
cn2: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(asgn.value)
|
||||
if cn2.const_kind == ast.CONST_INT:
|
||||
init_val = cn2.int_val
|
||||
has_init = 1
|
||||
init_kind = 1
|
||||
elif cn2.const_kind == ast.CONST_STR:
|
||||
init_str_val = cn2.str_val
|
||||
init_kind = 3
|
||||
elif val_kind2 == ast.ASTKind.List:
|
||||
init_list_node = (ast.List | t.CPtr)(asgn.value)
|
||||
init_kind = 2
|
||||
|
||||
if var_name is None:
|
||||
return 0
|
||||
@@ -225,11 +368,26 @@ def handle_module_level_var(trans: HT.Translator | t.CPtr,
|
||||
|
||||
# 设置初始值(有初始值时清除 external linkage,因为 LLVM 22+ 不允许 external global 带初始值)
|
||||
gv.Linkage = None
|
||||
if has_init != 0:
|
||||
if init_kind == 1:
|
||||
# 整数初始值
|
||||
init_buf: t.CChar | t.CPtr = pool.alloc(48)
|
||||
if init_buf is not None:
|
||||
viperlib.snprintf(init_buf, 48, "%lld", init_val)
|
||||
gv.Initializer = init_buf
|
||||
elif init_kind == 2:
|
||||
# 列表字面量 → 数组初始化器 [N x elem_ty] [elem_ty val0, ...]
|
||||
arr_init: str = _build_array_initializer_text(pool, var_ty, init_list_node)
|
||||
if arr_init is not None:
|
||||
gv.Initializer = arr_init
|
||||
else:
|
||||
gv.Initializer = "zeroinitializer"
|
||||
elif init_kind == 3:
|
||||
# 字符串字面量 → 创建字符串常量全局 + GEP 引用
|
||||
str_init: str = _build_string_ptr_initializer(pool, mod, var_name, init_str_val)
|
||||
if str_init is not None:
|
||||
gv.Initializer = str_init
|
||||
else:
|
||||
gv.Initializer = "zeroinitializer"
|
||||
else:
|
||||
# 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer,
|
||||
# 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type"
|
||||
|
||||
@@ -39,8 +39,10 @@ class ReturnHandle(HandlesBase.Mixin):
|
||||
|
||||
val: llvmlite.Value | t.CPtr = None
|
||||
if rt.value is not None:
|
||||
# return self: 直接返回指针(self 是 SSA 参数 Ptr(struct_ty))
|
||||
# translate_value 会 load 得到结构体值,但 return self 需要指针本身
|
||||
# return self: 返回 self 指针本身(Ptr(StructTy)),而非结构体值
|
||||
# translate_value 会 load 得到结构体值,但 return self 需要指针
|
||||
# 注意: lookup_var 返回的是存储 self 的 alloca (Ptr(Ptr(StructTy)))
|
||||
# 需要 load 得到实际的 self 指针 (Ptr(StructTy))
|
||||
if rt.value.kind() == ast.ASTKind.Name:
|
||||
ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(rt.value)
|
||||
if ret_nm is not None and ret_nm.id is not None:
|
||||
@@ -48,6 +50,15 @@ class ReturnHandle(HandlesBase.Mixin):
|
||||
self_ptr: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||||
self.Trans.SymTab, "self")
|
||||
if self_ptr is not None:
|
||||
# 检查 self_ptr 是否是 Ptr(Ptr(...))(alloca 存储指针)
|
||||
self_pointee_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
match self_ptr.Ty:
|
||||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||||
self_pointee_ty = inner_ty
|
||||
if self_pointee_ty is not None and HandlesExpr.is_ptr_type(self_pointee_ty) != 0:
|
||||
# Ptr(Ptr(StructTy)) → load 得到 Ptr(StructTy)
|
||||
val = llvmlite.build_load(builder, self_pointee_ty, self_ptr)
|
||||
else:
|
||||
val = self_ptr
|
||||
if val is None:
|
||||
val = HandlesExpr.translate_value(
|
||||
|
||||
@@ -2,6 +2,7 @@ import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import llvmlite
|
||||
import stdio
|
||||
import ast
|
||||
@@ -521,10 +522,28 @@ def lookup_field_by_class(class_name: str,
|
||||
# 回退: 无 SHA1 或 SHA1 匹配失败,按类名查找第一个
|
||||
entry = find_struct(class_name)
|
||||
if entry is None:
|
||||
# 诊断:遍历打印所有已注册结构体名,确认目标类是否在注册表中
|
||||
for diag_i in range(_struct_count):
|
||||
diag_entry: StructEntry | t.CPtr = _get_struct_entry(diag_i)
|
||||
# 诊断:目标类未注册
|
||||
fb_lfbc: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_lfbc is not None:
|
||||
viperlib.snprintf(fb_lfbc, 1024,
|
||||
"lookup_field_by_class: 类 '%s' 未注册 (struct_count=%d)",
|
||||
class_name, _struct_count)
|
||||
VLogger.error(fb_lfbc, "STRUCT")
|
||||
return None
|
||||
# 诊断:找到结构体但字段未找到,打印字段表
|
||||
fb_fields: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_fields is not None:
|
||||
viperlib.snprintf(fb_fields, 1024,
|
||||
"lookup_field_by_class: 类 '%s' 已注册 (FieldCount=%d) 但字段 '%s' 未找到",
|
||||
class_name, entry.FieldCount, field_name)
|
||||
VLogger.error(fb_fields, "STRUCT")
|
||||
for fi in range(entry.FieldCount):
|
||||
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
fb_fn: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_fn is not None:
|
||||
viperlib.snprintf(fb_fn, 1024, " 字段[%d]: %s", fi, fe.Name)
|
||||
VLogger.error(fb_fn, "STRUCT")
|
||||
for fi in range(entry.FieldCount):
|
||||
fe: FieldEntry | t.CPtr = _get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
@@ -534,45 +553,6 @@ def lookup_field_by_class(class_name: str,
|
||||
return None
|
||||
|
||||
|
||||
# ============================================================
|
||||
# find_subclass_with_field — 在基类的所有子类中搜索包含指定字段的结构体
|
||||
#
|
||||
# 用于处理 "注解类型是基类但实际值是派生类" 的场景:
|
||||
# node: AST | t.CPtr = If(...)
|
||||
# node.orelse = orelse ← orelse 在 If 上,不在 AST 上
|
||||
#
|
||||
# 遍历所有已注册结构体,通过 ParentName 链检查继承关系,
|
||||
# 返回第一个包含 field_name 的子类 StructEntry。
|
||||
# ============================================================
|
||||
def find_subclass_with_field(base_class_name: str,
|
||||
field_name: str) -> StructEntry | t.CPtr:
|
||||
"""在 base_class_name 的所有子类中搜索包含 field_name 的结构体
|
||||
|
||||
返回第一个找到的 StructEntry(包含正确的 Ty),None=未找到
|
||||
"""
|
||||
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 类型
|
||||
# ============================================================
|
||||
|
||||
@@ -6,6 +6,7 @@ import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesVar as HandlesVar
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
|
||||
@@ -9,6 +9,8 @@ import sys
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Handles.HandlesTranslator as HT
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
|
||||
@@ -143,6 +145,95 @@ def clear_cdefine_constants() -> None:
|
||||
_g_cdefine_values = None
|
||||
|
||||
|
||||
# 常量整数表达式计算成功标志(供 _eval_const_int_expr 使用)
|
||||
_g_const_eval_ok: int = 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _eval_const_int_expr - 递归计算常量整数表达式
|
||||
#
|
||||
# 支持:
|
||||
# - Constant (CONST_INT): 整数字面量
|
||||
# - Name: CDefine 常量名(通过 lookup_cdefine_constant 查找)
|
||||
# - BinOp (Add/Sub/Mult/FloorDiv/BitOr/BitAnd): 递归计算左右操作数
|
||||
#
|
||||
# 成功: 设置 _g_const_eval_ok=1, 返回计算值
|
||||
# 失败: 设置 _g_const_eval_ok=0, 返回 0
|
||||
#
|
||||
# 用于 t.CArray count 的静态计算,如 2 * ZHUFF_MAX_CODES
|
||||
# ============================================================
|
||||
def _eval_const_int_expr(node: ast.AST | t.CPtr) -> int:
|
||||
"""递归计算常量整数表达式"""
|
||||
global _g_const_eval_ok
|
||||
if node is None:
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
k: int = node.kind()
|
||||
|
||||
# Constant: 整数字面量
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
_g_const_eval_ok = 1
|
||||
return cn.int_val
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
|
||||
# Name: CDefine 常量名
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||||
if nm.id is None:
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
val: int = lookup_cdefine_constant(nm.id)
|
||||
if is_cdefine_found() == 0:
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
_g_const_eval_ok = 1
|
||||
return val
|
||||
|
||||
# BinOp: 算术/位运算
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||||
# 递归计算左操作数
|
||||
lv: int = _eval_const_int_expr(bop.left)
|
||||
if _g_const_eval_ok == 0:
|
||||
return 0
|
||||
# 递归计算右操作数
|
||||
rv: int = _eval_const_int_expr(bop.right)
|
||||
if _g_const_eval_ok == 0:
|
||||
return 0
|
||||
# 根据运算符执行计算
|
||||
if bop.op == ast.OpKind.Add:
|
||||
_g_const_eval_ok = 1
|
||||
return lv + rv
|
||||
if bop.op == ast.OpKind.Sub:
|
||||
_g_const_eval_ok = 1
|
||||
return lv - rv
|
||||
if bop.op == ast.OpKind.Mult:
|
||||
_g_const_eval_ok = 1
|
||||
return lv * rv
|
||||
if bop.op == ast.OpKind.FloorDiv:
|
||||
if rv == 0:
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
_g_const_eval_ok = 1
|
||||
return lv // rv
|
||||
if bop.op == ast.OpKind.BitOr:
|
||||
_g_const_eval_ok = 1
|
||||
return lv | rv
|
||||
if bop.op == ast.OpKind.BitAnd:
|
||||
_g_const_eval_ok = 1
|
||||
return lv & rv
|
||||
# 不支持的运算符
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
|
||||
# 不支持的节点类型
|
||||
_g_const_eval_ok = 0
|
||||
return 0
|
||||
|
||||
|
||||
def set_generic_context(tp_names: list[str] | t.CPtr,
|
||||
type_args: list[str] | t.CPtr):
|
||||
"""设置泛型特化上下文(进入泛型类方法体翻译时调用)"""
|
||||
@@ -850,6 +941,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
mod_name: str = HandlesImports.lookup_from_import(from_imports, nm.id)
|
||||
if mod_name is not None:
|
||||
return map_t_type(pool, nm.id)
|
||||
# 兜底: 尝试 map_t_type 中的硬编码 typedef 映射(VOIDPTR, HANDLE, ULONG 等)
|
||||
# 这些是跨模块通用的 C typedef 名,即使 from-import 查找失败也能解析
|
||||
mapped_ty_fallback: llvmlite.LLVMType | t.CPtr = map_t_type(pool, nm.id)
|
||||
if mapped_ty_fallback is not None:
|
||||
return mapped_ty_fallback
|
||||
return None
|
||||
|
||||
# Attribute 节点: t.CInt, t.CUInt64T, namespace_defs.PlainStruct 等
|
||||
@@ -874,6 +970,13 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
if mod_struct_ty is not None:
|
||||
return mod_struct_ty
|
||||
return None
|
||||
# 嵌套 Attribute: w32.win32base.SECURITY_ATTRIBUTES 等三级及以上限定名
|
||||
# at.value 是 Attribute 而非 Name,递归提取最终类名(at.attr)查结构体表
|
||||
# find_struct 支持模块限定名回退,此处用最外层 attr(简单类名)即可
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
|
||||
nested_struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(at.attr)
|
||||
if nested_struct_ty is not None:
|
||||
return nested_struct_ty
|
||||
return None
|
||||
|
||||
# BinOp 节点: 联合类型注解 A | B
|
||||
@@ -929,7 +1032,11 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
# value 是 Name(泛型类名),slice 是类型实参
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||||
# tuple[...] 类型注解:TransPyV 暂不支持 tuple 返回类型,返回 None 让上层默认处理
|
||||
# 避免走到 list[str](pool, 4) 构造路径崩溃(tuple 未定义为泛型类)
|
||||
if gen_nm.id is not None:
|
||||
if string.strcmp(gen_nm.id, "tuple") == 0:
|
||||
return None
|
||||
# 先收集类型实参名,再分配缓冲区拼接 mangled name
|
||||
gen_arg_names: list[str] | t.CPtr = list[str](pool, 4)
|
||||
if sub.slice.kind() == ast.ASTKind.Tuple:
|
||||
@@ -1031,28 +1138,20 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr,
|
||||
pool, elem_node, imported_modules, from_imports, trans)
|
||||
if elem_ty is None:
|
||||
fatal_type_error(elem_node, "t.CArray 元素类型解析失败")
|
||||
# 解析 count(必须是整数常量、None 或 CDefine 常量名)
|
||||
# 解析 count(支持整数常量、None、CDefine 常量名、常量表达式如 2*MAX)
|
||||
# t.CArray[elem_ty, None] 等价于单参数形式 → Ptr(elem_ty)
|
||||
# t.CArray[elem_ty, NAME] 其中 NAME: t.CDefine = value → [value x elem_ty]
|
||||
# t.CArray[elem_ty, 2 * MAX] 常量表达式静态计算 → [2*MAX x elem_ty]
|
||||
count_val: int = 0
|
||||
if count_node.kind() == ast.ASTKind.Constant:
|
||||
cnt_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node)
|
||||
if cnt_cn.const_kind == ast.CONST_NONE:
|
||||
return llvmlite.Ptr(pool, elem_ty)
|
||||
if cnt_cn.const_kind != ast.CONST_INT:
|
||||
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名")
|
||||
count_val = cnt_cn.int_val
|
||||
elif count_node.kind() == ast.ASTKind.Name:
|
||||
# CDefine 常量名查找
|
||||
cnt_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(count_node)
|
||||
if cnt_nm.id is None:
|
||||
fatal_type_error(count_node, "t.CArray count Name 节点 id 为 None")
|
||||
looked_up: int = lookup_cdefine_constant(cnt_nm.id)
|
||||
if is_cdefine_found() == 0:
|
||||
fatal_type_error(count_node, "t.CArray count 不是已注册的 CDefine 常量")
|
||||
count_val = looked_up
|
||||
else:
|
||||
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None 或 CDefine 常量名")
|
||||
# 统一用 _eval_const_int_expr 计算常量整数表达式
|
||||
# 支持 Constant(INT)、Name(CDefine)、BinOp(2*MAX 等算术/位运算)
|
||||
count_val = _eval_const_int_expr(count_node)
|
||||
if _g_const_eval_ok == 0:
|
||||
fatal_type_error(count_node, "t.CArray count 必须是整数常量、None、CDefine 常量名或常量表达式")
|
||||
if count_val <= 0:
|
||||
fatal_type_error(count_node, "t.CArray count 必须为正数")
|
||||
# 创建 ArrayType(不包装 Ptr)
|
||||
|
||||
@@ -78,10 +78,15 @@ class WhileHandle(HandlesBase.Mixin):
|
||||
if cond_val is None:
|
||||
cond_val = llvmlite.const_int32(pool, 0)
|
||||
|
||||
# Compare/Not 表达式已返回 i1,直接使用;其他类型与 0 比较
|
||||
# Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较
|
||||
cond_bits: int = HandlesExpr.get_llvm_type_bits(cond_val.Ty)
|
||||
if cond_bits == 1:
|
||||
cond_i1: llvmlite.Value | t.CPtr = cond_val
|
||||
elif HandlesExpr.is_ptr_type(cond_val.Ty) != 0:
|
||||
# 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type")
|
||||
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
|
||||
cond_i1 = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_NE, cond_val, null_cond)
|
||||
else:
|
||||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||||
cond_i1 = llvmlite.build_icmp(
|
||||
|
||||
@@ -73,7 +73,9 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
|
||||
sha1_set: str, set_count: int,
|
||||
class_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 文件,收集类继承关系"""
|
||||
if result is None or class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None:
|
||||
return -1
|
||||
@@ -88,6 +90,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy | t.CPtr,
|
||||
continue
|
||||
|
||||
sha1_e: str = entry.Sha1
|
||||
file_has_generic: 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:
|
||||
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:
|
||||
break
|
||||
|
||||
@@ -513,14 +532,15 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
continue
|
||||
string.strcpy(sha1_set + set_count * 17, se_ent.Sha1)
|
||||
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:
|
||||
VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1")
|
||||
stdlib.free(sha1_set)
|
||||
return 0
|
||||
# 写入 _sha1_map.txt(人类可读输出,程序内部不读取;机器分析使用 PopulateSha1MapStore 内存存储器)
|
||||
StubMerger.WriteIncludesSha1Map(mb, temp_dir, result, None, 0)
|
||||
# 填充全局 SHA1 映射存储器(内存中,不依赖 _sha1_map.txt 文件)
|
||||
StubMerger.PopulateSha1MapStore(result)
|
||||
# 构建模块 SHA1 映射(供跨模块函数调用名混淆使用)
|
||||
td_len_p1map: t.CSizeT = string.strlen(temp_dir)
|
||||
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
|
||||
|
||||
# 生成 .deps.txt(记录依赖模块名,供依赖图按需翻译使用)
|
||||
# 同时填充内存依赖存储器(PopulateIncludesDeps),供 _BuildReachableSha1Set
|
||||
# 直接从内存读取,不依赖 .deps.txt 过程文件
|
||||
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")
|
||||
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.close()
|
||||
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 资源
|
||||
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)
|
||||
topo_order: t.CPtr = mb.alloc(4 * MAX_FILE_DEPS)
|
||||
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
|
||||
|
||||
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(topo_order, 0, 4 * MAX_FILE_DEPS)
|
||||
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(
|
||||
mb, result, reachable_set, reachable_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:
|
||||
ret_topo: int = _TopoSortFiles(
|
||||
@@ -726,6 +762,87 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str,
|
||||
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
|
||||
topo_count = 0
|
||||
|
||||
# ============================================================
|
||||
# Phase 1a-pre-generic: 预注册所有泛型类模板
|
||||
#
|
||||
# 在 Phase 1a(struct 注册)之前执行,确保所有泛型类模板
|
||||
#(如 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
|
||||
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)
|
||||
if topo_count_box is not None:
|
||||
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 路径翻译方法体)
|
||||
|
||||
@@ -19,6 +19,7 @@ import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.BuildPipeline as BuildPipeline
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
import lib.core.StubMerger as StubMerger
|
||||
import lib.Projectrans.Utils as Utils
|
||||
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(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(人类可读输出,程序内部不读取此文件)
|
||||
# 格式: {sha1}:{rel_path}\n (保留目录结构,如 lib/core/VLogger.py)
|
||||
# 同步追加到内存存储器(AppendToSha1MapStore,供跨模块 CDefine 查找)
|
||||
@@ -296,10 +310,62 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
|
||||
# === 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 log is not None:
|
||||
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 ===
|
||||
# 解决循环引用问题:circ_a 翻译时需要知道 circ_b.ClassB 的 struct 定义
|
||||
# 仅注册 struct/enum/union(declare_only=1),不翻译方法体
|
||||
@@ -334,6 +400,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
PHASE_A_IR_SIZE: t.CSizeT = 1048576
|
||||
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):
|
||||
ea: t.CUInt64T = t.CUInt64T(entries) + i * entry_size
|
||||
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()
|
||||
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:
|
||||
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
|
||||
if app_deps_buf is not None:
|
||||
stdlib.free(app_deps_buf)
|
||||
stdlib.free(entries)
|
||||
return 0
|
||||
|
||||
@@ -404,8 +495,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
if log is not None:
|
||||
log.banner("Phase B: 编译 .obj")
|
||||
|
||||
# 收集 .obj 路径
|
||||
OBJ_PATHS_SIZE: t.CSizeT = 8192
|
||||
# 收集 .obj 路径(增大到 64KB,避免 includes .obj 路径溢出导致链接丢失符号)
|
||||
# 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)
|
||||
if obj_paths is None:
|
||||
return 1
|
||||
@@ -457,6 +551,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
sys.exit(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)
|
||||
od_len: t.CSizeT = string.strlen(output_dir)
|
||||
is_main_mod: int = 0
|
||||
@@ -497,39 +596,85 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
stdlib.free(entries)
|
||||
return 1
|
||||
|
||||
# === 3.5 编译缺失的 includes 文件 ===
|
||||
# includes.binary 可能缺少某些 includes .obj(如 testcheck.py),
|
||||
# 这些文件被用户项目导入但未被 TransPyV 自身依赖,Projectrans.py 未编译它们。
|
||||
# 检测并编译缺失的 includes 文件到 output_dir,加入链接命令。
|
||||
# 数据来源:StubMerger 全局内存存储器(不读取 _sha1_map.txt 文件)
|
||||
# === 3.5 编译缺失的 includes 文件(按依赖图按需编译)===
|
||||
# 只编译被 App 源文件直接/间接引用的 includes 文件(可达集合),
|
||||
# 而非遍历整个 includes 目录。通过 _BuildReachableSha1Set 构建依赖图。
|
||||
if includes_dir is not None and includes_binary_dir is not None:
|
||||
inc_compiled: int = 0
|
||||
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 解引用问题)
|
||||
store_count_mi: int = StubMerger.GetSha1StoreCount()
|
||||
if store_count_mi > 0:
|
||||
store_sha1_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
|
||||
store_mod_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreModArrPtr()
|
||||
store_rel_arr_mi: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
|
||||
|
||||
for si_mi in range(store_count_mi):
|
||||
# 获取当前条目的 SHA1 和 rel_path
|
||||
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
|
||||
# 检查 .obj 是否已存在于 includes.binary
|
||||
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: bytes = stdlib.malloc(ibd_len_mi + 35)
|
||||
if check_pat_mi is None:
|
||||
# 按需编译过滤:只处理 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
|
||||
viperlib.snprintf(check_pat_mi, ibd_len_mi + 35, "%s/%s*.obj", includes_binary_dir, inc_sha1_mi)
|
||||
# 检查 .obj 是否已存在于 includes.binary(切片子目录)
|
||||
ibd_len_mi: t.CSizeT = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: str = StubMerger._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
|
||||
check_fd_mi: w32.win32file.WIN32_FIND_DATAA | t.CPtr = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
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__())
|
||||
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
|
||||
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
|
||||
w32.win32file.FindClose(check_h_mi)
|
||||
obj_exists_mi = 1
|
||||
if obj_exists_mi != 0:
|
||||
if check_pat_mi is not None:
|
||||
stdlib.free(check_pat_mi)
|
||||
stdlib.free(check_fd_mi)
|
||||
continue
|
||||
|
||||
# .obj 不存在,需要编译
|
||||
@@ -538,6 +683,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
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)
|
||||
if src_fp_mi is None:
|
||||
stdlib.free(check_pat_mi)
|
||||
if check_fd_mi is not None:
|
||||
stdlib.free(check_fd_mi)
|
||||
continue
|
||||
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1,
|
||||
"%s/%s", includes_dir, inc_rel_mi)
|
||||
@@ -558,7 +706,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
tbuf_mi[tbr_mi] = '\0'
|
||||
else:
|
||||
tbuf_mi[StubMerger.STUB_READ_BUF_SIZE - 1] = '\0'
|
||||
# 逐行扫描: 找 define 行中含 @" 的(混淆函数名)
|
||||
# 逐行扫描: 找 define 行即为实现文件
|
||||
# (不再要求行中含 @",因为 memhub 等模块的函数名
|
||||
# 如 @ca40915f11b8b6ad._align_up 不带引号但仍是实现文件)
|
||||
tpos_mi: t.CSizeT = 0
|
||||
while tpos_mi < tbr_mi:
|
||||
tls_mi: t.CSizeT = tpos_mi
|
||||
@@ -570,21 +720,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
if tpos_mi < tbr_mi:
|
||||
tpos_mi += 1
|
||||
if tll_mi >= 7 and string.strncmp(tbuf_mi + tls_mi, "define ", 7) == 0:
|
||||
# 临时在行尾加 \0 供 strstr 使用
|
||||
saved_mi: t.CChar = tbuf_mi[tls_mi + tll_mi]
|
||||
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)
|
||||
tf_mi.close()
|
||||
stdlib.free(tpath_mi)
|
||||
# is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件,可能不被项目导入)
|
||||
# is_decl_mi == -1: text.ll 不存在(Phase1 未翻译此文件)
|
||||
# is_decl_mi == 1: 声明文件(仅 declare 无 define)
|
||||
# 两种情况都跳过:不编译未被 Phase1 翻译的 includes 文件
|
||||
if is_decl_mi != 0:
|
||||
# is_decl_mi == 0: 实现文件(有 define)
|
||||
# 只跳过声明文件(is_decl_mi == 1);text.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
|
||||
|
||||
# 尝试 BuildCombinedIR(stub/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)
|
||||
|
||||
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()
|
||||
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+")
|
||||
if inc_combined_mi is not None:
|
||||
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
|
||||
|
||||
# 编译为 .obj
|
||||
# 编译为 .obj(输出到 includes_binary_dir,与存在性检查和链接收集一致)
|
||||
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)
|
||||
stdlib.free(inc_combined_mi)
|
||||
|
||||
if inc_cret_mi != 0:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
@@ -654,10 +827,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
sys.exit(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:
|
||||
inc_obj_len: t.CSizeT = string.strlen(inc_obj_sliced)
|
||||
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'
|
||||
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()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "编译缺失 includes: %d 个", inc_compiled)
|
||||
VLogger.info(fb, "PhaseB+")
|
||||
|
||||
# 释放 app_deps_buf(Phase B+ 块未执行时此处兜底释放)
|
||||
if app_deps_buf is not None:
|
||||
stdlib.free(app_deps_buf)
|
||||
app_deps_buf = None
|
||||
|
||||
# === 4. Phase C: 链接所有 .obj → .exe ===
|
||||
if log is not None:
|
||||
log.banner("Phase C: 链接")
|
||||
@@ -708,6 +901,18 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr,
|
||||
final_obj_paths[fop_pos] = '\0'
|
||||
|
||||
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_set(Phase 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(
|
||||
final_obj_paths, obj_paths_len,
|
||||
linker_cmd, linker_flags, exe_path,
|
||||
|
||||
@@ -51,6 +51,19 @@ _sha1_store_mod: bytes # 模块名数组(每个 64 字节,MAX_INCLUDES 个
|
||||
_sha1_store_rel: bytes # 相对路径数组(每个 256 字节,MAX_INCLUDES 个)
|
||||
_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 最大长度
|
||||
MAX_REL_PATH_LEN: t.CDefine = 256
|
||||
|
||||
@@ -493,6 +506,85 @@ def GetSha1StoreCount() -> int:
|
||||
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 文件的 SHA1(16字符)
|
||||
# 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 文件的 SHA1(16字符)
|
||||
#
|
||||
# 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:
|
||||
"""返回 SHA1 数组指针(每个条目 17 字节)"""
|
||||
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
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 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
|
||||
#
|
||||
@@ -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,
|
||||
temp_dir: str,
|
||||
reachable_set: t.CChar | t.CPtr) -> int:
|
||||
"""构建可达 SHA1 集合(依赖图按需翻译)"""
|
||||
reachable_set: t.CChar | t.CPtr,
|
||||
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:
|
||||
return -1
|
||||
|
||||
# 重置不完整标志
|
||||
_g_reachable_incomplete = 0
|
||||
|
||||
SRC_BUF_SIZE_R: t.CSizeT = 1048576
|
||||
|
||||
# 1. 构建 SHA1→module_name 映射
|
||||
@@ -1293,12 +1419,32 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
viperlib.snprintf(fb, 1024, "SHA1 映射: %d 个", map_count)
|
||||
VLogger.debug(fb, "Reachable")
|
||||
|
||||
# 2. 扫描 source_dir 下的 .py 文件,收集直接依赖
|
||||
# 工作列表(空格分隔的模块名)
|
||||
worklist: bytes = stdlib.malloc(8192)
|
||||
if worklist is None:
|
||||
stdlib.free(sha1_arr)
|
||||
stdlib.free(mod_arr)
|
||||
return -1
|
||||
worklist[0] = '\0'
|
||||
wl_len: t.CSizeT = 0
|
||||
|
||||
reachable_count: int = 0
|
||||
|
||||
# 2. 如果 app_deps 非空,直接使用它作为初始 worklist(跳过自己的非递归扫描)
|
||||
# 否则,回退到自己的非递归扫描(source_dir/*.py,只扫描顶层)
|
||||
if app_deps is not None and app_deps[0] != '\0':
|
||||
ad_len: t.CSizeT = string.strlen(app_deps)
|
||||
if ad_len > 0 and ad_len < 8192:
|
||||
string.strcpy(worklist, app_deps)
|
||||
wl_len = ad_len
|
||||
else:
|
||||
# 回退到自己的非递归扫描
|
||||
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)
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
viperlib.snprintf(pattern, dir_len + 8, "%s/*.py", source_dir)
|
||||
|
||||
@@ -1308,28 +1454,16 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(sha1_arr)
|
||||
stdlib.free(mod_arr)
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
string.memset(find_data, 0, find_data_size + 16)
|
||||
|
||||
# 工作列表(空格分隔的模块名)
|
||||
worklist: bytes = stdlib.malloc(8192)
|
||||
if worklist is None:
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
stdlib.free(sha1_arr)
|
||||
stdlib.free(mod_arr)
|
||||
return -1
|
||||
worklist[0] = '\0'
|
||||
wl_len: t.CSizeT = 0
|
||||
|
||||
reachable_count: int = 0
|
||||
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32base.INVALID_HANDLE_VALUE:
|
||||
fb: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb is not None:
|
||||
viperlib.snprintf(fb, 1024, "未找到 .py 文件: %s", pattern)
|
||||
VLogger.warning(fb, "Reachable")
|
||||
fb_fd: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if fb_fd is not None:
|
||||
viperlib.snprintf(fb_fd, 1024, "未找到 .py 文件: %s", pattern)
|
||||
VLogger.warning(fb_fd, "Reachable")
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
stdlib.free(sha1_arr)
|
||||
@@ -1383,6 +1517,12 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
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)
|
||||
@@ -1512,36 +1652,55 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str,
|
||||
# 加入 reachable_set
|
||||
reachable_count = _AddSha1ToSet(reachable_set, reachable_count, found_sha1)
|
||||
|
||||
# 读取 .deps.txt 追加到 worklist
|
||||
deps_path: str = _sliced_path(temp_dir, td_len_r, found_sha1, "deps.txt")
|
||||
if deps_path is not None:
|
||||
df: fileio.File | t.CPtr = fileio.File(deps_path, fileio.MODE.R)
|
||||
if not df.closed:
|
||||
deps_buf: bytes = stdlib.malloc(2048)
|
||||
if deps_buf is not None:
|
||||
dbr: t.CInt64T = df.read_all(deps_buf, 2048)
|
||||
df.close()
|
||||
if dbr > 0:
|
||||
if dbr < 2048:
|
||||
deps_buf[dbr] = '\0'
|
||||
else:
|
||||
deps_buf[2047] = '\0'
|
||||
# 追加到 worklist(嵌套 if 拆分 + 用 = 代替 += 绕过 TPC AUGASGN bug)
|
||||
dl: t.CSizeT = string.strlen(deps_buf)
|
||||
if worklist is not None:
|
||||
if dl > 0 and wl_len + dl + 2 < 8192:
|
||||
# 从内存查找依赖(不读 deps.txt 过程文件)
|
||||
# Phase B+ 前用 declare_only 翻译所有 includes 文件,提取 _imported_modules 存入内存
|
||||
deps_str: str = LookupIncludesDeps(found_sha1)
|
||||
if deps_str is not None:
|
||||
dl: t.CSizeT = string.strlen(deps_str)
|
||||
if dl > 0:
|
||||
if wl_len + dl + 2 < 8192:
|
||||
if wl_len > 0 and worklist[wl_len - 1] != ' ':
|
||||
worklist[wl_len] = ' '
|
||||
wl_len = wl_len + 1
|
||||
string.strcpy(worklist + wl_len, deps_buf)
|
||||
string.strcpy(worklist + wl_len, deps_str)
|
||||
wl_len = wl_len + dl
|
||||
worklist[wl_len] = ' '
|
||||
wl_len = wl_len + 1
|
||||
worklist[wl_len] = '\0'
|
||||
stdlib.free(deps_buf)
|
||||
# deps_str 为空字符串视为无依赖(合法情况,如叶子模块)
|
||||
else:
|
||||
df.close()
|
||||
stdlib.free(deps_path)
|
||||
# 依赖未在内存中找到
|
||||
# 按需过滤: 检查是否是 App 源文件(source_dir 下的 .py 文件)
|
||||
# App 源文件不需要加入 reachable_set(只用于 includes 过滤),跳过
|
||||
sd_len_ff: t.CSizeT = string.strlen(source_dir)
|
||||
mb_len_ff: t.CSizeT = string.strlen(mod_buf)
|
||||
app_mod_path: bytes = stdlib.malloc(sd_len_ff + mb_len_ff + 6)
|
||||
is_app_src: int = 0
|
||||
if app_mod_path is not None:
|
||||
viperlib.snprintf(app_mod_path, sd_len_ff + mb_len_ff + 6, "%s/%s.py", source_dir, mod_buf)
|
||||
af: fileio.File | t.CPtr = fileio.File(app_mod_path, fileio.MODE.R)
|
||||
if not af.closed:
|
||||
is_app_src = 1
|
||||
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(sha1_arr)
|
||||
stdlib.free(mod_arr)
|
||||
|
||||
11
App/main.py
11
App/main.py
@@ -227,7 +227,7 @@ def main() -> int:
|
||||
Config.resolve_paths(proj_dir)
|
||||
Config.print_config()
|
||||
|
||||
# === --clean: 清理 temp 和 output 目录 ===
|
||||
# === --clean: 清理 temp、output 和 includes.binary 目录 ===
|
||||
if _gb_clean:
|
||||
if log is not None:
|
||||
log.info("清理临时目录...", "clean")
|
||||
@@ -245,6 +245,15 @@ def main() -> int:
|
||||
if fb2 is not None:
|
||||
viperlib.snprintf(fb2, 1024, "%s: 删除 %d 个文件", Config.OutputDir, n2)
|
||||
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=1: 仅 stub 分离(生成 .stub.ll/.text.ll),不编译
|
||||
|
||||
Reference in New Issue
Block a user