From 8312e653e8cd26d887a05f029c1a4aa93d1dcd05 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 15 Sep 2026 20:47:14 +0800 Subject: [PATCH] =?UTF-8?q?fix(CodeGen):=20=E8=A3=B8=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93=E5=BD=A2=E5=8F=82=E6=8C=89=20C=20ABI=20=E6=8C=89?= =?UTF-8?q?=E5=80=BC=E3=80=81=E4=BF=AE=E6=AD=A3=20|t.CPtr=20=E6=8C=87?= =?UTF-8?q?=E9=92=88=E5=B1=82=E7=BA=A7=EF=BC=8C=E5=AF=B9=E9=BD=90=20TPC=20?= =?UTF-8?q?=E5=B9=B6=E6=B6=88=E9=99=A4=E7=AD=BE=E5=90=8D=E5=8F=91=E6=95=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- App/lib/Stub/StubGen/VpiReader.vp | 108 ++++++++++++++++++++---- App/lib/core/Gen/Expr/ExprGenBase.vp | 4 +- App/lib/core/Gen/Func/FuncGenDef.vp | 29 ++++++- App/lib/core/Gen/Type/TypeGenResolve.vp | 22 +++-- 4 files changed, 140 insertions(+), 23 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 9798098..d05de4a 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -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 _: diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index 07e688c..211d29e 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -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], ..)` 等 diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index cdcb09c..e0af937 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -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 _: diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 3d7feba..dd2aa9d 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -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: