From a83d183abb983a4bc5017859a3ba6b7a88f518d5 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 15 Sep 2026 23:58:40 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen/call):=20=E8=B7=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E8=B0=83=E7=94=A8=20SSA=20=E6=95=B4=E5=9E=8B=E5=AE=9E=E5=8F=82?= =?UTF-8?q?=E6=8C=89=E5=AE=9A=E4=B9=89=E4=BE=A7=E5=BD=A2=E5=8F=82=E6=89=A9?= =?UTF-8?q?=E5=AE=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit declare 由调用点实参链建立,而 _coerce_arg_value_to_ty 对非常量(SSA)整型此前原样返回,导致 declare/call 停在调用点窄宽度(string.strncpy 第三参 i32),与定义侧(vpi 注解 n: t.CSizeT => i64)不一致,并经 .stub.ll 传播到未调用该函数的消费方模块 现按源符号性 zext/sext 扩宽(真源 ExprGenBase._zext_or_sext_val,先例 CallGenCtor._translate_t_type_cast),实参 AST/trans 下传;self_off 模块级 0、方法 1 效果: WebTest 危险级签名不一致 1 => 0 --- App/lib/core/Gen/Call/CallGenCall.vp | 2 +- App/lib/core/Gen/Call/CallGenDispatch.vp | 3 +- App/lib/core/Gen/Call/CallGenLib.vp | 66 ++++++++++++++++++++---- App/lib/core/Gen/Call/CallGenMixin.vp | 2 +- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index 5a64256..fbcd3dd 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -1716,7 +1716,7 @@ class CallGenCall(CallGenBase.CallGenBase): # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value # 先按 vpi 定义侧形参 coerce 实参(宽度对齐),再建 declare(ABI 通用化) head = self.Gen.CallGenLib._coerce_args_to_def_params( - pool, builder, call_name, head, total_count) + pool, builder, call_name, head, total_count, cargs, 1, trans) self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) # 检查变长函数 + C ABI 提升(cargs 是实参 AST,self_offset=1 跳过 self) is_variadic_3441: int = self.Gen.CallGenVa._check_and_promote_variadic_call( diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index e99d955..5170800 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -1430,7 +1430,8 @@ class CallGenDispatch(CallGenBase.CallGenBase): ret_ty3 = llvmlite.Int32(pool) # 先按 vpi 定义侧形参 coerce 实参(宽度对齐),再建 declare head3 = self.Gen.CallGenLib._coerce_args_to_def_params( - pool, builder, mangled3, head3, total3) + pool, builder, mangled3, head3, total3, + cargs, 1, trans) self._ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) else: diff --git a/App/lib/core/Gen/Call/CallGenLib.vp b/App/lib/core/Gen/Call/CallGenLib.vp index e6868cb..0424398 100644 --- a/App/lib/core/Gen/Call/CallGenLib.vp +++ b/App/lib/core/Gen/Call/CallGenLib.vp @@ -214,8 +214,17 @@ class CallGenLib(CallGenBase.CallGenBase): builder: llvmlite.IRBuilder | t.CPtr, call_name: str, head: llvmlite.Value | t.CPtr, - count: int) -> llvmlite.Value | t.CPtr: - """把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链""" + count: int, + cargs: list[ast.AST | t.CPtr] | t.CPtr = None, + self_off: int = 0, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """把跨模块调用实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链 + + cargs/self_off/trans:实参 AST 列表与其在实参链中的偏移(模块级函数 0、 + 方法 1——链首元素是 self,而 vpi 段 0 即 self 占位段)。仅用于非常量 + (SSA)整型扩宽时的**源符号性**判定(与 ExprGenBase._zext_or_sext_val + 同源);缺省时该判定退化为有符号(sext),与 coerce_to_type 默认一致。 + """ if pool is None or builder is None or call_name is None or head is None: return head if self._is_sha1_mangled_call(call_name) == 0: @@ -270,7 +279,13 @@ class CallGenLib(CallGenBase.CallGenBase): tgt_ty: llvmlite.LLVMType | t.CPtr = VpiReader._resolve_vpi_param_annot_seg( pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty) if tgt_ty is not None and cur.Ty is not None: - node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty) + # 实参 AST(源符号性判定用):链下标 idx 减 self 偏移即 AST 下标 + arg_idx: int = idx - self_off + arg_node: ast.AST | t.CPtr = None + if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs): + arg_node = cargs.get(arg_idx) + node_v = self._coerce_arg_value_to_ty( + pool, builder, cur, tgt_ty, arg_node, trans) if new_head is None: new_head = node_v else: @@ -290,8 +305,11 @@ class CallGenLib(CallGenBase.CallGenBase): # - 同宽 → 原样; # - 窄化(目标更窄,本簇主场景 i32→i8/i16)→ 常量按目标宽度重建常量, # SSA 用 trunc(trunc 与符号无关,窄化安全); - # - 扩宽交由 _ensure_method_declare 既有"定义侧更宽才采用"逻辑 + 下遍 - # 收敛时按形参 coerce(需 AST 判定符号性,本函数无 AST,不越权处理)。 + # - 扩宽(定义侧形参更宽)→ 常量按目标宽度重建常量;SSA 按**源**符号性 + # 扩宽(zext/sext,真源 ExprGenBase._zext_or_sext_val;同规则先例 + # ExprGenBase.coerce_to_type / CallGenCtor._translate_t_type_cast / + # CallGenLib._promote_clib_call_args)→ declare 由扩宽后的实参链建立, + # declare/call/define 三者同宽(此前 SSA 原样返回 → 与定义侧宽度不一致)。 # 指针:实参与目标皆指针且"泛型 i8* 性"不同时 bitcast(如 i32*→i8*), # 使 declare 指针类型与 define 一致;同为 i8* 或同为具体 struct 指针 # 则原样保留(避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声)。 @@ -300,8 +318,14 @@ class CallGenLib(CallGenBase.CallGenBase): def _coerce_arg_value_to_ty(self, pool: rowptr[memhub.MemBuddy], builder: llvmlite.IRBuilder | t.CPtr, val: llvmlite.Value | t.CPtr, - tgt_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr: - """把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回""" + tgt_ty: llvmlite.LLVMType | t.CPtr, + node: ast.AST | t.CPtr = None, + trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + """把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回 + + node/trans:实参 AST 与其翻译器(仅 SSA 整型扩宽的源符号性判定用, + 委托 ExprGenBase._zext_or_sext_val;缺省退化为有符号 sext)。 + """ if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None: return val match val.Ty: @@ -327,13 +351,37 @@ class CallGenLib(CallGenBase.CallGenBase): # 仅编译期常量在此重建:const_int 按十进制值打印, # i32 -1 → i64 -1(sext 语义)、i32 4294967295 → # i64 4294967295(zext 语义),各自保持原类型数值, - # 无需 AST 判符号性;SSA 值(非常量)判符号性需 AST, - # 交由上游 coerce_to_type(本函数无 AST,不越权处理)。 + # 无需 AST 判符号性。 if val.IsConst != 0: nv_w: llvmlite.Value | t.CPtr = llvmlite.const_int( pool, bits_b, val.IntVal) if nv_w is not None: return nv_w + return val + # 非常量(SSA)扩宽:此前原样返回,declare 与 call 保持 + # 调用点窄宽度,而定义侧(vpi 形参注解)更宽 → 同一 + # 符号 declare/call 与 define 宽度不一致(includes/ + # requests/__init__.vp `string.strncpy(..., host_len)` + # 3 参 i32 而 string.vp 定义 `n: t.CSizeT`=i64;同模块/ + # 同依赖的 strncpy 常量实参分支已扩宽成 i64,SSA 实参 + # 分支未扩宽 → 同一模块两条 declare 宽度不同)。 + # 扩宽方向由**源**符号性决定(与 coerce_to_type / + # CallGenCtor 同一真源 ExprGenBase._zext_or_sext_val: + # i1/i8、无符号源 → zext;否则 sext),与目标类型无关。 + # 扩宽后 declare 由本函数返回的新实参链建立(param_head + # → _ensure_method_declare),declare/call/define 三者同宽。 + if bits_b > bits_a: + if self.Gen.ExprGenBase._zext_or_sext_val( + pool, val, node, trans) != 0: + nv_zw: llvmlite.Value | t.CPtr = llvmlite.build_zext( + builder, val, tgt_ty) + if nv_zw is not None: + return nv_zw + else: + nv_sw: llvmlite.Value | t.CPtr = llvmlite.build_sext( + builder, val, tgt_ty) + if nv_sw is not None: + return nv_sw return val case _: pass diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 8b4f2a3..040634c 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -1637,7 +1637,7 @@ class CallGenMixin(CallGenBase.CallGenBase): # call 实参 / declare / define 三者宽度一致(B3 同族通用化;反例: # su8250.putchar 定义 (i16,i8) 而调用点字面量 1016=i32 固化宽)。 head = self.Gen.CallGenLib._coerce_args_to_def_params( - pool, builder, call_name, head, can) + pool, builder, call_name, head, can, cargs, 0, trans) # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can) # 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升