fix(tpv): 模块限定调用优先用 vpi 定义侧形参类型,Win32 白名单降为兜底

mlir-opt 报:
  error: 'llvm.call' op operand type mismatch for operand 2: 'i64' != 'i32'
      %5 = llvm.call @VirtualAlloc(%c6, %2, %3, %4) : (!llvm.ptr, i64, i64, i64) -> !llvm.ptr

声明(真值):extern def VirtualAlloc(VOIDPTR, sizet, ULONG, ULONG) -> VOIDPTR
           ⇒ llvm.func @VirtualAlloc(!llvm.ptr, i64, i32, i32)

根因:调用点 found_func 为 None 时用 `_get_external_func_param_ty` 硬编码白名单,
它把 Win32 API 的**所有**非指针参数一律当 i64(注释依据"DWORD/ULONG 在 stubs 里
被映射为 i64"的旧口径),而现实中 ULONG → i32 ⇒ 字面量被 sext 到 i64,与声明
i32 不匹配。

修复:CallGenMixin 的参数 coerce 分支(found_func is None)改为**模块限定调用
先用定义侧 vpi 形参注解**(`_resolve_vpi_param_annot_seg`,模块级函数段下标 ==
形参下标)取真实类型;拿不到注解才回退白名单。CallGenLib 的该函数补注释,
标明它是**兜底专用**且 Win32 口径已过时。
This commit is contained in:
TermiNexus committed 2026-10-04 18:17:51 +08:00
1 parent b7e4861e6c
commit 6a6cae2a44
2 files changed
+40 -3

No files matched your search

+9 -2
View File
@@ -565,8 +565,15 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
param_idx: int) -> mlirlite.LLVMType*:
"""获取外部函数第 param_idx 个参数的期望类型,None 表示用字面量原类型
对 Win32 API 函数的所有非指针参数返回 i64,匹配 declare 中
ULONG/SIZE_T 被统一翻译为 i64 的情况。
⚠ **兜底专用**:调用方应先按**定义侧 vpi 形参注解**取真实类型
(CallGenMixin 的模块限定调用分支,见 _resolve_vpi_param_annot_seg),
只有注解不可得时才落到本白名单。
本白名单把 Win32 API 的**所有**非指针参数一律当 i64,其依据是旧口径
"DWORD/ULONG/SIZE_T 在 stubs 翻译时都被映射为 i64";该口径**已过时**
(现实:SIZE_T→i64、ULONG/DWORD→i32),若据此提升实参会让 call(i64)
与 declare(i32) 不匹配(mlir-opt: "llvm.call op operand type mismatch",
现场 FuncTest VirtualAlloc)。故仅在拿不到定义侧注解时才用。
"""
if pool is None or func_name is None:
return None
+31 -1
View File
@@ -1120,7 +1120,37 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
kvc_ty: mlirlite.LLVMType* = self.Gen.CallGenVa._get_known_vararg_cfunc_param_ty(pool, func_name, ai)
if kvc_ty is not None:
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, kvc_ty, arg)
ext_param_ty: mlirlite.LLVMType* = self.Gen.CallGenLib._get_external_func_param_ty(pool, func_name, ai)
# ① 模块限定调用的**定义侧 vpi 形参注解优先**(真实类型)。
# 白名单 _get_external_func_param_ty 把 Win32 API 的**所有**
# 非指针参数一律当 i64(其注释基于"DWORD/ULONG 在 stubs 里
# 都被映射为 i64"的旧口径),而现状 ULONG → i32 ⇒ 实参被
# sext 到 i64、声明却是 i32,call 与 declare 不匹配:
# mlir-opt 报 "llvm.call op operand type mismatch for
# operand N: 'i64' != 'i32'"(现场 FuncTest VirtualAlloc)。
# 模块级函数:vpi 段下标 == 形参下标(cls=None,无 self 占位段)。
ext_param_ty: mlirlite.LLVMType* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_pt: ast.Attribute* = ast.Attribute*(cl.func)
mpath_pt: str = None
if at_pt.value is not None and at_pt.value.kind() == ast.ASTKind.Name:
nm_pt: ast.Name* = ast.Name*(at_pt.value)
mpath_pt = nm_pt.id
elif at_pt.value is not None and at_pt.value.kind() == ast.ASTKind.Attribute:
mpath_pt = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_pt.value)
if mpath_pt is not None:
fi_pt: str = None
if trans is not None:
fi_pt = trans._from_imports
sha_pt: str = self.Gen.StructGen._resolve_module_sha1(mpath_pt, fi_pt)
if sha_pt is not None:
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_param_annot_loaded(sha_pt, self.Gen.CallGenBase.TempDir)
ext_param_ty = VpiReader._resolve_vpi_param_annot_seg(
pool, sha_pt, None, func_name, ai,
mlirlite.Int32(pool), mlirlite.Ptr(pool, mlirlite.Int8(pool)))
# ② 白名单仅作**兜底**(定义侧注解不可得时)
if ext_param_ty is None:
ext_param_ty = self.Gen.CallGenLib._get_external_func_param_ty(pool, func_name, ai)
if ext_param_ty is not None:
val_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
tgt_bits_ap: int = self.Gen.ExprGenBase.get_llvm_type_bits(ext_param_ty)