fix(CodeGen): 裸结构体形参按 C ABI 按值、修正 |t.CPtr 指针层级,对齐 TPC 并消除签名发散

- FuncGenDef/VpiReader._AggregateParamTy: C ABI 外部声明(t.CExtern/t.State)的裸结构体形参按值 %Struct(可取完整定义时),真 opaque 降级 i64;TPV 内部函数/方法维持 Ptr(struct)。三处(定义侧/函数注入侧/方法注入侧)同口径

- TypeGenResolve/VpiReader: BinOp(A|t.CPtr) 的指针别名守卫改为 pointee 为已注册 struct 才吸收,否则追加一层 => VOIDPTR|t.CPtr 由 i8* 修正为 i8**(对齐 TPC)

- 效果: SetConsoleScreenBufferSize 等 6 个符号由 i64/%COORD* 双形态收敛为按值,同符号多形态数 193->186
This commit is contained in:
TermiNexus committed 2026-09-15 20:47:14 +08:00
1 parent 31df298341
commit 8312e653e8
4 files changed
+140 -23

No files matched your search

+93 -15
View File
@@ -1400,13 +1400,29 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy],
# ⇒ i64*(见 TypeGenResolve.vp BinOp(A | t.CPtr) 的 typedef 别名保真判定),
# 本侧经同一 GenApi.resolve_typedef_or_map 命中后亦 ⇒ Ptr(i64) = i64*;
# **两侧同型即收敛**,不再需要把 declare 侧坍缩成 i8* 去迁就定义侧。
# 仍坍缩 i8* 的三类与定义侧对称:解析失败兜底;裸枚举名(定义侧
# get_struct_type miss 同样落 i8*);指针别名(first_td 不置位,对应定义侧
# td_res_is_ptr != 0 的守卫)。
# 仍坍缩 i8* 的两类与定义侧对称:解析失败兜底;裸枚举名(定义侧
# get_struct_type miss 同样落 i8*)。指针别名(first_td 不置位)
# 已按下段"追加 1 层"处理(与定义侧 td_res_is_ptr 口径同步)。
# 指针别名(first_td 不置位,如 VOIDPTR = CVoid|CPtr → i8*、
# HANDLE = VOIDPTR → i8*):`| t.CPtr` **追加 1 层** → i8**(不吸收)。
# 判据与定义侧 TypeGenResolve.vp BinOp(A | t.CPtr) 的指针层数口径
# 同源(TPC HandlesTypeMerge._MergeAllComponentTypes L940-946:
# 仅 IsStruct 时吸收 1 层):pointee 命中 find_struct_by_type
# ⇒ 类引用语义 ⇒ 吸收(返回 first_ty 即 Ptr(struct));
# 否则 ⇒ Ptr(first_ty)。此前无条件坍缩 i8* → 定义侧/注入侧虽同为
# i8* 但比 TPC 短一层(TPC `VOIDPTR | t.CPtr` = i8**)。
if first_qual != 0 or first_wl != 0 or first_td != 0:
return llvmlite.Ptr(pool, first_ty)
if first_ty.kind() == llvmlite.LLVMType.Struct:
return llvmlite.Ptr(pool, first_ty)
if first_ty.kind() == llvmlite.LLVMType.Ptr:
match first_ty:
case llvmlite.LLVMType.Ptr(pointee_v):
if GenApi.find_struct_by_type(pointee_v) is not None:
return first_ty
case _:
pass
return llvmlite.Ptr(pool, first_ty)
return i8_ptr_ty
return i8_ptr_ty
if has_cvoid != 0:
@@ -2646,6 +2662,61 @@ def _ensure_full_value_types(mod: llvmlite.LLVMModule | t.CPtr,
pass
# ============================================================
# _AggregateParamTy - 裸结构体(聚合)形参类型统一判据
#
# 「裸结构体形参」(注解是结构体/类名、无 `| t.CPtr`)的 LLVM 形参类型判据,
# 三处必须同步(同一文字、同一结果):
# 1) 定义侧 FuncGenDef.forward_declare_functions L26x-28x 与
# translate_function_def 的 extern 声明分支 L54x-56x;
# 2) declare 注入侧 _inject_function_declare / _inject_method_declare(本函数);
# 3) 调用侧:per-函数 found_func 走 ExprGenBase.coerce_to_type L491-495
# (指针 → 非指针值 build_load 解引用);跨模块名走
# CallGenLib._coerce_args_to_def_params(按本判据得出的形参类型 coerce)。
# 规则(对齐 TPC:TransPyC/lib/core/Handles/HandlesTypeMerge.py L940-946
# 「仅 IsStruct 吸收 1 层 CPtr」+ LLVMCG/TypeConvert.py L137-143
# 「CStruct → 具名结构体值」):
# * C ABI 外部声明(t.CExtern/t.State,本模块无 define 侧)→ 按值 %Struct:
# 真实被调方在 DLL/CRT,形参按 C ABI 传值(TPC 定义侧 stub 同为按值,见
# TransPyV/output/db613a1bd6a3ca26.stub.ll:346 `%COORD`)。
# 值参数要求具名类型有**完整定义**(llvm-as 实测:opaque/未定义具名类型
# 作值参数 ⇒ "invalid type for function argument";指针形式亦要求具名类型
# 存在 ⇒ "use of undefined type")。判据 = 结构体在注册表可查且非 opaque
# (find_struct_by_type 命中,先例 _ensure_full_value_types L2633;
# opaque 哨兵 fcount<0 见 includes/llvmlite/__module.vp L466-491):
# 命中 → 先注册完整定义(module_add_named_type,同一先例)再按值;
# 真 opaque / 未注册 → 降级 i64:不引用任何具名类型(恒合法),Windows x64
# ABI 小结构体(≤8 字节,如 COORD)按整数寄存器传、位兼容(同原 L2905-2908
# 依据),且与定义侧同样降级 ⇒ 同符号签名仍逐字一致。
# * TPV 内部函数/方法(存在 define 侧)→ Ptr(struct):与定义侧逐字一致
# (FuncGenDef L28x/L64x、ClassGenVTable L744-750「对象即指针」),
# 指针目标只需 opaque 声明(module_ensure_opaque_for_type 先例 L2850)。
# ============================================================
def _AggregateParamTy(mod: llvmlite.LLVMModule | t.CPtr,
pool: rowptr[memhub.MemBuddy],
ty: llvmlite.LLVMType | t.CPtr,
is_c_abi: int) -> llvmlite.LLVMType | t.CPtr:
"""裸结构体形参类型(C ABI 按值 / 内部 Ptr(struct),见上方注释)"""
if mod is None or pool is None or ty is None:
return ty
if is_c_abi != 0:
# 真 opaque 哨兵:fcount < 0(includes/llvmlite/__module.vp L491)
is_opaque_ap: int = 0
match ty:
case llvmlite.LLVMType.Struct(_, fcount_ap, _):
if fcount_ap < 0:
is_opaque_ap = 1
case _:
pass
if is_opaque_ap == 0 and GenApi.find_struct_by_type(ty) is not None:
_ensure_full_value_types(mod, pool, ty)
return ty
return llvmlite.Int64(pool)
ptr_ty_ap: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, ty)
llvmlite.module_ensure_opaque_for_type(mod, pool, ptr_ty_ap)
return ptr_ty_ap
# ============================================================
# _inject_global_variable_declare - 从 vpi AnnAssign 创建模块级全局变量 declare
#
@@ -2779,6 +2850,13 @@ def _inject_function_declare(pool: rowptr[memhub.MemBuddy],
has_extern = GenApi.has_decorator_marker(fd.returns, "CExtern")
has_state = GenApi.has_decorator_marker(fd.returns, "State")
has_export = GenApi.has_decorator_marker(fd.returns, "CExport")
# 裸结构体形参判据(见 _AggregateParamTy):C ABI 外部声明(t.CExtern/
# t.State,本模块无 define 侧)按值;其余(含 t.CExport,其 define 侧在
# 本树内)保持 Ptr(struct)。与定义侧 FuncGenDef is_extern_decl 同源,
# 保证注入侧 declare 与定义侧 define 同口径(三处必须同步)。
agg_is_c_abi: int = 0
if has_extern != 0 or has_state != 0:
agg_is_c_abi = 1
# 计算 mangled name
# CExport/CExtern/State/main 函数用裸名,其他函数用源模块 SHA1 前缀
@@ -2893,7 +2971,9 @@ def _inject_function_declare(pool: rowptr[memhub.MemBuddy],
if pseg_ty is not None:
match pseg_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
# 裸结构体形参:C ABI 按值 / 内部 Ptr(struct)
# (判据与降级见 _AggregateParamTy,三处必须同步)
param_ty = _AggregateParamTy(mod, pool, pseg_ty, agg_is_c_abi)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
@@ -2902,14 +2982,11 @@ def _inject_function_declare(pool: rowptr[memhub.MemBuddy],
resolved_ty: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
pool, ag.annotation, imported_modules, from_imports, trans)
if resolved_ty is not None:
# 结构体按值参数 → i64:跨模块结构体在本模块是 opaque
# (未定义字段),LLVM 不允许 opaque struct 作为函数参数
# (llc: invalid type for function argument)。Windows ABI
# 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
# 裸结构体参数 → _AggregateParamTy(同一判据)。
match resolved_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
param_ty = _AggregateParamTy(mod, pool, resolved_ty, agg_is_c_abi)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
@@ -3087,7 +3164,11 @@ def _inject_method_declare(pool: rowptr[memhub.MemBuddy],
if pseg_ty is not None:
match pseg_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
# 裸结构体形参:方法恒为 TPV 内部符号(mangled =
# {sha1}.{Class}.{method},无 C ABI 外部 define),
# 故 is_c_abi=0 → Ptr(struct),与定义侧
# ClassGenVTable L744-750 逐字一致(三处必须同步)。
param_ty = _AggregateParamTy(mod, pool, pseg_ty, 0)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
@@ -3096,14 +3177,11 @@ def _inject_method_declare(pool: rowptr[memhub.MemBuddy],
resolved_ty: llvmlite.LLVMType | t.CPtr = GenApi.resolve_annotation_type(
pool, ag.annotation, imported_modules, from_imports, trans)
if resolved_ty is not None:
# 结构体按值参数 → i64:跨模块结构体在本模块是 opaque
# (未定义字段),LLVM 不允许 opaque struct 作为函数参数
# (llc: invalid type for function argument)。Windows ABI
# 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。
# CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。
# 裸结构体参数 → _AggregateParamTy(同一判据,is_c_abi=0)。
match resolved_ty:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Int64(pool)
param_ty = _AggregateParamTy(mod, pool, resolved_ty, 0)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
+3 -1
View File
@@ -461,7 +461,9 @@ class ExprGenBase:
return llvmlite.build_fp2si(builder, val, target_ty)
# 聚合值 → 指针: 落栈 materialize 临时对象后传其地址
# FuncGenDef.vp:263-271 已确认「裸类/数组形参一律 lowering 为
# Ptr(struct/array)」——Viper 不存在"按值传结构体"的形参。故调用点的
# Ptr(struct/array)」——TPV 内部函数/方法不存在"按值传结构体"的形参
# (唯一例外:C ABI 外部声明(t.CExtern/t.State)的裸结构体形参按值,
# 见 VpiReader._AggregateParamTy「三处必须同步」)。故调用点的
# rvalue 聚合(函数调用返回值、下标元素 load 等)必须落栈取址,否则
# 实参类型与指针形参失配(gfx.vp `eye.Sub(target).Norm()`、
# `Mat4_Mul(m, Mat4_Translate(...))`、`DrawTriangle(.., v[i], ..)` 等
+27 -2
View File
@@ -267,9 +267,26 @@ class FuncGenDef(CallGenBase.CallGenBase):
return 0
# 裸类形参 → Ptr(struct)(对象即指针,与方法形参一致)
# CArray 形参 → 元素指针 ty*(C 数组衰减,与 add_param 一致)
# 例外(三处必须同步:本处 / translate_function_def 的 extern
# 声明分支 / VpiReader 注入侧 _AggregateParamTy):
# C ABI 外部声明(t.CExtern/t.State,本模块无 define 侧)的
# 裸结构体形参按值 %Struct —— 真实被调方在 DLL/CRT,形参 ABI
# 由 C 头决定。TPC 定义侧同为按值(TransPyC/lib/core/Handles/
# HandlesTypeMerge.py L940-946「仅 IsStruct 吸收 1 层 CPtr」
# + LLVMCG/TypeConvert.py L137-143「CStruct → 具名结构体值」)。
# 值参数要求具名类型有**完整定义**(llvm-as 实测:opaque/未定义
# 具名类型作值参数 ⇒ "invalid type for function argument"),
# 故判据 = 该结构体在注册表可查(find_struct_by_type 命中,
# 先例 VpiReader._ensure_full_value_types L2633),命中则先
# 注册完整定义再按值(ensure_struct_def_in_module,先例
# CallGenCtor.vp L478);不命中(真 opaque)保持 Ptr(struct)。
match resolved:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty = llvmlite.Ptr(pool, resolved)
if is_extern_decl != 0 and self.Gen.StructGen.find_struct_by_type(resolved) is not None:
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, resolved)
param_ty = resolved
else:
param_ty = llvmlite.Ptr(pool, resolved)
case llvmlite.LLVMType.Array(aelem_ty, _):
param_ty = llvmlite.Ptr(pool, aelem_ty)
case _:
@@ -529,9 +546,17 @@ class FuncGenDef(CallGenBase.CallGenBase):
if resolved_e is not None:
# 裸类形参 → Ptr(struct)(对象即指针,与方法形参一致)
# CArray 形参 → 元素指针 ty*(C 数组衰减)
# 本分支恒为 C ABI 外部声明(is_extern_decl != 0,见
# 上方 if):裸结构体形参按值 %Struct(判据与降级
# 同 forward_declare_functions / VpiReader
# _AggregateParamTy,三处必须同步)。
match resolved_e:
case llvmlite.LLVMType.Struct(_, _, _):
param_ty_e = llvmlite.Ptr(pool, resolved_e)
if self.Gen.StructGen.find_struct_by_type(resolved_e) is not None:
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, resolved_e)
param_ty_e = resolved_e
else:
param_ty_e = llvmlite.Ptr(pool, resolved_e)
case llvmlite.LLVMType.Array(aelem_ty_e, _):
param_ty_e = llvmlite.Ptr(pool, aelem_ty_e)
case _:
+17 -5
View File
@@ -2514,16 +2514,28 @@ class TypeGenResolve(TypeGenBase.TypeGenBase):
# 的类型,参与“基类型 + 指针层数”合并(对齐 TPC
# HandlesTypeMerge._MergeAllComponentTypes L887-946:
# sizet | t.CPtr ⇒ 基类型 i64 + 1 层指针 = i64*)。
# 仅保留非指针结果:Ptr(...)(类 __new__ 的 Ptr(struct)、
# 指针别名)继续走下方 get_struct_type 回退与解析失败兜底,
# 该两条既有路径行为逐字节不变。
# 指针层数口径(本处 / VpiReader 注入侧 / 调用侧三处必须同步):
# `| t.CPtr` 恒为"追加 1 层指针",唯一例外是 struct 引用语义
# (struct 变量本身即指针)时吸收这 1 层:
# VOIDPTR(= CVoid|CPtr → i8*)| t.CPtr ⇒ i8**(不吸收)
# CVoid|CPtr 裸用 ⇒ i8*(见上方 CVoid 分支)
# CChar|t.CPtr ⇒ i8*、CUInt64T|t.CPtr ⇒ i64*(标量 + 1 层)
# 类(__new__ → Ptr(struct))| t.CPtr ⇒ Ptr(struct)(吸收 1 层)
# 依据 TPC TransPyC/lib/core/Handles/HandlesTypeMerge.py
# L940-946:Result.PtrCount += cptr_count,仅
# `IsStruct and base_ptr_count > 0` 时再减 1(吸收)。
# 判据 = Ptr 的 pointee 是否为**已注册 struct**
# (find_struct_by_type 命中 ⇒ 类引用语义 ⇒ 吸收)。
if keep_other_ty == 0 and other_node is not None and other_node.kind() == ast.ASTKind.Name:
other_nm_td: ast.Name | t.CPtr = (ast.Name | t.CPtr)(other_node)
if other_nm_td.id is not None:
td_res_is_ptr: int = 0
match other_ty:
case llvmlite.LLVMType.Ptr(_):
td_res_is_ptr = 1
case llvmlite.LLVMType.Ptr(pointee_td):
if self.Gen.StructGen.find_struct_by_type(pointee_td) is not None:
td_res_is_ptr = 1
else:
td_res_is_ptr = 0
case _:
td_res_is_ptr = 0
if td_res_is_ptr == 0: