fix(CallGen): 跨模块调用的 declare 形参与实参统一以定义侧为准

问题一(declare 形参被首个调用点冻结):CallGenBase._ensure_method_declare 新建跨模块方法
declare 时形参类型取"首个调用点的实参类型",而这条 declare 被同模块所有调用点共用 ⇒ 后续
调用传入另一种具体指针类型时,被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"。
现场:VarGenMixin.repl_ensure_entry 的 node: ast.AST*,先被 translate_annassign 以 ast.AnnAssign*
建 declare,后 translate_assign 传 ast.AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*"。
⇒ 指针形参与整数同口径,一律采用定义侧(.vpi 注解段)类型;解析不出指针时保留实参类型。

问题二(实参未对齐 declare):declare 改用定义侧类型后实参也必须对齐,否则首个调用点
(由它建立 declare、不走 coerce_to_type)原样传具体指针会与 declare 类型不一致(llc 报
argument type mismatch)。⇒ CallGenLib._coerce_arg_value_to_ty 的同层指针分支改为"类型内容
不一致即 bitcast 到定义侧类型"(原判据只覆盖一侧为 i8*、或模板注解来源两种情形)。
This commit is contained in:
TermiNexus committed 2026-10-01 20:29:15 +08:00
1 parent 93e983f74e
commit 8e3811c490
2 files changed
+24 -23

No files matched your search

+13 -1
View File
@@ -703,7 +703,12 @@ novirtual class CallGenBase:
func: llvmlite.Function* = llvmlite.new_function(pool, call_name, ret_ty)
if func is None: return
func.IsDeclared = 1
# 定义侧参数类型优先:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解取定义侧类型,仅当实参与定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型)。反方向(定义更窄)由 CallGenLib._coerce_args_to_def_params 在调用点先窄化实参
# 定义侧参数类型优先:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解取定义侧类型。
# · 整数:仅当定义宽度更宽时采用定义宽度(反方向由 CallGenLib._coerce_args_to_def_params 在调用点先窄化实参)
# · 指针:**一律采用定义侧类型**——declare 被同模块所有调用点共用,若沿用"首个调用点的实参类型",
# 后续调用传入别的具体指针类型就会被这条被冻住的形参类型 coerce,报"非基本类型指针间隐式转换"
# (现场 VarGenMixin.repl_ensure_entry 的 node: ast.AST*:先被 translate_annassign 以 AnnAssign*
# 建 declare,后 translate_assign 传 AST* 即报"源类型 …AST* → 目标类型 …AnnAssign*")
sha1_d: str = None
cls_d: str = None
meth_d: str = None
@@ -752,6 +757,13 @@ novirtual class CallGenBase:
param_ty_d = seg_ty_d
case _:
pass
case llvmlite.LLVMType.Ptr(_):
# 指针形参:定义侧为准(见上方注释)。定义侧解析不出指针时保留实参类型
match seg_ty_d:
case llvmlite.LLVMType.Ptr(_):
param_ty_d = seg_ty_d
case _:
pass
case _:
pass
if param_ty_d is not None:
+11 -22
View File
@@ -274,7 +274,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
stdlib.free(cls_nm)
return new_head
# _coerce_arg_value_to_ty:单个实参 Value 转换到目标形参类型——整型同宽原样、窄化 trunc/按宽度重建常量、扩宽按源符号性 zext/sext;指针 i8* 性不同则 bitcast;其余原样返回(不可转换亦不改型)
# _coerce_arg_value_to_ty:单个实参 Value 转换到目标形参类型——整型同宽原样、窄化 trunc/按宽度重建常量、扩宽按源符号性 zext/sext;同层指针类型不一致则 bitcast 到目标类型;其余原样返回(不可转换亦不改型)
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
val: llvmlite.Value*,
@@ -322,30 +322,19 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
return val
case _:
pass
case llvmlite.LLVMType.Ptr(pointee_a):
case llvmlite.LLVMType.Ptr(_):
match tgt_ty:
case llvmlite.LLVMType.Ptr(pointee_b):
ia: int = 0
match pointee_a:
case llvmlite.LLVMType.Int(pa_bits):
if pa_bits == 8:
ia = 1
case _:
pass
ib: int = 0
match pointee_b:
case llvmlite.LLVMType.Int(pb_bits):
if pb_bits == 8:
ib = 1
case _:
pass
if ia != ib:
case llvmlite.LLVMType.Ptr(_):
# 同层指针:实参类型与定义侧形参类型不一致就 bitcast 对齐(一致则不比,
# 避免生成非法 `bitcast T* to T*`)。跨模块调用的 call/declare 都以
# 定义侧为准(declare 见 CallGenBase._ensure_method_declare),故这里
# 必须对齐:首个调用点由它建立 declare、不走 coerce_to_type,原样传具体
# 指针就会与 declare 的类型不一致(llc 报 argument type mismatch)。
# 原判据只覆盖"一侧为 i8*"与"模板注解来源",漏了两侧同为具体结构体指针
# 但基类型不同的情形(实参 MemBuddy* vs 定义侧形参 MemManager*)。
if self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, tgt_ty) == 0:
nv_p: llvmlite.Value* = llvmlite.build_bitcast(builder, val, tgt_ty)
if nv_p is not None: return nv_p
elif tpl_src != 0:
# 模板注解来源:两侧同为具体结构体指针但基类型不同(实参 MemBuddy* vs 定义侧形参 MemManager*)⇒ bitcast 使 declare 指针口径与 define 一致
nv_p2: llvmlite.Value* = llvmlite.build_bitcast(builder, val, tgt_ty)
if nv_p2 is not None: return nv_p2
case _:
pass
case _: