fix(tpv): 外部函数返回类型不可收敛时 fail-fast,豁免 from-import 与多层模块限定调用
- _infer_external_func_ret_ty 白名单未命中改为 fatal_error(Python 风格 traceback),移除默认 i32 的 WARN 输出 - 裸名调用 fail-fast 前查 from_imports 可解析性:from-import 函数走 mangled 精确解析收敛(SSAValue 场景),纯外部 C 函数才 fail-fast - mod_ret_sha1 解析支持多层模块路径(w32.win32file.CreatePipe 场景), 避免嵌套模块限定调用被误判为裸名外部函数 - 移除 FuncGenBase/StructGenMixin 遗留调试输出
This commit is contained in:
1 parent
5551db3432
commit
4748915472
6 files changed
+68
-40
No files matched your search
@@ -4,7 +4,6 @@ import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
from . import CallGenBase
|
||||
@@ -111,13 +110,21 @@ class CallGenLib(CallGenBase.CallGenBase):
|
||||
llvmlite.add_param(pool, fn, i64_ty, "n")
|
||||
return fn
|
||||
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
func_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
func_name: str,
|
||||
node: ast.AST | t.CPtr,
|
||||
fatal_if_unknown: int) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""根据函数名推断外部 includes 函数的返回类型
|
||||
|
||||
支持三种函数名形式:
|
||||
1. 裸名: "parse", "strlen"
|
||||
2. 别名: "json_parse"(from X import parse as json_parse)
|
||||
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name)
|
||||
|
||||
fatal_if_unknown: 白名单全部未命中时的行为。
|
||||
=1(纯裸名外部函数,无法收敛)→ fatal_error fail-fast(默认 i32
|
||||
会截断指针/i64,静默生成导致运行时数据损坏,不兜底原则)。
|
||||
=0(Phase A 中间遍,_deps_pending 下遍重译可收敛)→ 返回默认
|
||||
i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。
|
||||
"""
|
||||
if pool is None or func_name is None:
|
||||
return llvmlite.Int32(pool)
|
||||
@@ -296,16 +303,16 @@ class CallGenLib(CallGenBase.CallGenBase):
|
||||
string.strcmp(func_name, "function_get_param_head") == 0 or string.strcmp(func_name, "param_get_next") == 0:
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
|
||||
# 默认 i32(整数):与本模块函数定义的默认返回类型一致(HandlesFunctions.py line 324)
|
||||
# 已知返回指针的函数在上方显式列出(返回 i8*),已知返回 i64 的函数也显式列出
|
||||
# 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配
|
||||
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
|
||||
# WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断
|
||||
# --nowarn-register 可抑制此 WARN(自举编译中大量出现,刷屏影响日志收敛)
|
||||
if VLogger._nowarn_register == 0:
|
||||
VLogger.warning(
|
||||
f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}",
|
||||
"HandlesExprCall")
|
||||
# 默认 i32:无登记、无模块归属、白名单全部未命中的外部函数。
|
||||
# fatal_if_unknown=1(纯裸名外部调用,无法收敛)→ fail-fast 报错:
|
||||
# 默认 i32 会截断指针/i64,静默生成导致运行时数据损坏(不兜底原则)。
|
||||
# fatal_if_unknown=0(Phase A 中间遍,_deps_pending 下遍重译收敛)→
|
||||
# 保持默认 i32 占位,不输出 WARN(下遍依赖就绪后走 text.ll/vpi 精确解析)。
|
||||
if fatal_if_unknown != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
node,
|
||||
f"外部函数返回类型未登记且无法收敛推断(默认 i32 会截断指针/i64,需提供 stub 声明或加入白名单): {func_name}",
|
||||
"TypeError")
|
||||
return llvmlite.Int32(pool)
|
||||
def _get_external_func_param_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
func_name: str,
|
||||
@@ -566,7 +573,9 @@ class CallGenLib(CallGenBase.CallGenBase):
|
||||
# 返回类型(使用访问器)
|
||||
call_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(found_func)
|
||||
if call_ret_ty is None:
|
||||
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name)
|
||||
# found_func 已有 declare 却读不到返回类型(访问器异常,罕见防御):
|
||||
# 白名单未命中也不 fail-fast,保持默认 i32(有声明即有类型来源)。
|
||||
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name, None, 0)
|
||||
|
||||
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
|
||||
if string.strcmp(func_name, "memcpy") == 0 and actual_count >= 3:
|
||||
|
||||
@@ -729,21 +729,29 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
mod_ret_sha1: str = None
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||||
# 解析调用模块路径:单层(stdlib.free,value 是 Name)与多层
|
||||
# (w32.win32file.CreatePipe,value 是嵌套 Attribute)都支持。
|
||||
# 缺失多层路径 → mod_ret_sha1=None → 误走裸名外部函数路径,
|
||||
# fail-fast 误杀可收敛的模块限定调用(CreatePipe 根因)。
|
||||
mod_path_ret: str = None
|
||||
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
|
||||
mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value)
|
||||
if mod_ret_nm.id is not None:
|
||||
mod_ret_sha1 = self._lookup_module_sha1(mod_ret_nm.id)
|
||||
if mod_ret_sha1 is not None:
|
||||
# 函数内懒 import:VpiReader imports HandlesExprCall,
|
||||
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
|
||||
ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot(
|
||||
mod_ret_sha1, None, func_name)
|
||||
if ret_annot_fc is not None:
|
||||
vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str(
|
||||
pool, ret_annot_fc,
|
||||
llvmlite.Int32(pool),
|
||||
llvmlite.Ptr(pool, llvmlite.Int8(pool)))
|
||||
mod_path_ret = mod_ret_nm.id
|
||||
elif at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Attribute:
|
||||
mod_path_ret = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_ret.value)
|
||||
if mod_path_ret is not None:
|
||||
mod_ret_sha1 = self._lookup_module_sha1(mod_path_ret)
|
||||
if mod_ret_sha1 is not None:
|
||||
# 函数内懒 import:VpiReader imports HandlesExprCall,
|
||||
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
|
||||
ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot(
|
||||
mod_ret_sha1, None, func_name)
|
||||
if ret_annot_fc is not None:
|
||||
vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str(
|
||||
pool, ret_annot_fc,
|
||||
llvmlite.Int32(pool),
|
||||
llvmlite.Ptr(pool, llvmlite.Int8(pool)))
|
||||
if vpi_ret_ty is not None:
|
||||
call_ret_ty = vpi_ret_ty
|
||||
elif mod_ret_sha1 is not None:
|
||||
@@ -766,9 +774,23 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if trans is not None and trans._declare_only == 0:
|
||||
if self._dep_text_ll_exists(pool, mod_ret_sha1) == 0:
|
||||
trans._deps_pending = 1
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name)
|
||||
# 已标记 _deps_pending,下遍重译会收敛(依赖 text.ll 就绪后
|
||||
# 走精确解析)→ fatal_if_unknown=0,本遍仅临时占位。
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0)
|
||||
else:
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name)
|
||||
# 裸名调用(cl.func 是 Name,mod_ret_sha1=None)。两类来源:
|
||||
# 1. from-import 的函数(如 SSAValue from .__values)→ 后续
|
||||
# L1193-1251 解析模块归属构造 mangled call_name,L1272 经
|
||||
# text.ll/vpi 精确解析返回类型(可收敛)→ fatal_if_unknown=0
|
||||
# 仅临时占位,不 fail-fast。
|
||||
# 2. 纯外部 C 函数(无登记、无模块归属)→ 白名单未命中无法收敛
|
||||
# → fail-fast(fatal_if_unknown=1)。
|
||||
bare_resolvable: int = 0
|
||||
if trans is not None and trans._from_imports is not None:
|
||||
if self._lookup_mod_sha1(pool, trans._from_imports, func_name, 0) is not None:
|
||||
bare_resolvable = 1
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(
|
||||
pool, func_name, node, 1 - bare_resolvable)
|
||||
|
||||
if found_func is None:
|
||||
fp_alloca: llvmlite.Value | t.CPtr = None
|
||||
@@ -1255,7 +1277,10 @@ class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 用 orig_func_name="parse" 重新推断,能正确匹配返回 i8*(JsonValue*)
|
||||
# 避免指针被截断为 i32 后 inttoptr 丢失高 32 位导致崩溃
|
||||
if orig_func_name is not None and orig_func_name != func_name:
|
||||
re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name)
|
||||
# 别名附加尝试:仅作为 mangled 精确解析前的临时推断(fatal_if_unknown=0)。
|
||||
# 若 call_name 是 mangled,L1272 起的 text.ll/vpi 精确解析会覆盖并 fail-fast;
|
||||
# 未命中时保持白名单/默认 i32 由后续路径兜底。
|
||||
re_inferred_ty: llvmlite.LLVMType | t.CPtr = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, orig_func_name, node, 0)
|
||||
if re_inferred_ty is not None:
|
||||
call_ret_ty = re_inferred_ty
|
||||
# ============================================================
|
||||
|
||||
@@ -50,7 +50,7 @@ class FuncGenBase(CallGenBase.CallGenBase):
|
||||
cur_file: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if cur_file is None:
|
||||
cur_file = "?"
|
||||
stdio.printf("[ATTR] extract_func_attrs: file='%s' dlist_len=%d\n", cur_file, dn)
|
||||
|
||||
for di in range(dn):
|
||||
deco: ast.AST | t.CPtr = decorator_list.get(di)
|
||||
if deco is None:
|
||||
|
||||
@@ -401,12 +401,6 @@ class StructGenMixin:
|
||||
fe.DefaultVal = default_val
|
||||
|
||||
struct_entry.FieldCount = idx + 1
|
||||
# [ADF-DIAG] 确认字段表实际注册内容(定位 Thread.arg 缺失/索引错位)
|
||||
dbg_adf: str = fname_pool.alloc(192)
|
||||
if dbg_adf is not None:
|
||||
viperlib.snprintf(dbg_adf, 192, "[ADF] struct=%s field=%s idx=%d\n",
|
||||
struct_entry.Name, field_name, idx)
|
||||
stdio.printf("%s", dbg_adf)
|
||||
return idx
|
||||
|
||||
def _deep_copy_default_ast(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
|
||||
@@ -208,9 +208,9 @@ def init_logger():
|
||||
|
||||
# ============================================================
|
||||
# --nowarn-register 开关(追加):抑制 Phase A 依赖类 WARN 输出——
|
||||
# ①"外部函数返回类型未登记"(HandlesExprCall)②"Phase A 依赖未就绪,
|
||||
# 下遍重试"(PhaseA-DBG)。自举编译中大量出现,刷屏影响日志收敛。
|
||||
# 仅抑制输出,不影响内部逻辑(默认 i32 推断、标记重试等照常执行)。
|
||||
# ①"跨模块函数依赖模块 text.ll 尚未生成"(HandlesExprCall,Phase A 多遍
|
||||
# 收敛的中间遍,下遍重译后精确解析)。自举编译中大量出现,刷屏影响日志收敛。
|
||||
# 仅抑制输出,不影响内部逻辑(标记重试等照常执行)。
|
||||
# ============================================================
|
||||
_nowarn_register: int = 0
|
||||
|
||||
|
||||
+1
-1
@@ -236,7 +236,7 @@ Target: %s
|
||||
BuildPipeline._phase2_cmd_path = p2c
|
||||
log.info(f"--phase2command: 不执行 llc/链接,命令写入 {p2c}", "phase2cmd")
|
||||
|
||||
# --nowarn-register:抑制"外部函数返回类型未登记"类 WARN 输出
|
||||
# --nowarn-register:抑制 Phase A 依赖类 WARN 输出(跨模块 text.ll 未生成,下遍收敛)
|
||||
if args.get_bool("nowarn-register"):
|
||||
VLogger._nowarn_register = 1
|
||||
log.info("--nowarn-register 已启用:抑制注册类 WARN 输出", "vlogger")
|
||||
|
||||
Reference in new issue
Block a user