fix(callgenbase): _ensure_method_declare 定义侧参数宽度优先,根治字面量实参固化 i32
declare 此前用首个调用点实参类型建参数,字面量 0=i32 把定义 t.CSizeT(i64) 的参数固化窄(SlotsBugTest _translate_struct_ctor can=i32 vs 定义 i64 → 调用侧 trunc、callee 按 i64 读栈残留垃圾高位 → can=巨值读未写槽位崩溃根因)。
修复:从 call_name 提取 {sha1}.{cls}.{method} 查 vpi 参数注解缓存(SetTempDir 已在 Phase1 设置,按需加载可用),仅当实参与定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型,避免字符串路径把 list*/Struct* 降级 i8*)。
This commit is contained in:
1 parent
4d14eeff67
commit
d7ed91e3e5
1 file changed
+103
-2
@@ -546,16 +546,80 @@ class CallGenBase:
|
||||
if func is None:
|
||||
return
|
||||
func.IsDeclared = 1
|
||||
# 定义侧参数类型优先(根治实参字面量固化错误宽度):
|
||||
# declare 此前用调用点实参类型(param_head)建参数,字面量 0=i32 会把
|
||||
# 定义 t.CSizeT(i64) 的参数固化窄(SlotsBugTest _translate_struct_ctor
|
||||
# can=i32 vs 定义 i64 → 调用侧 trunc i64→i32、callee 按 i64 读栈残留
|
||||
# 垃圾高位 → can=巨值读未写槽位崩溃根因)。从 call_name 提取
|
||||
# {sha1}.{cls}.{method} 查 vpi 参数注解缓存取定义侧类型:仅当实参与
|
||||
# 定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型,避免
|
||||
# 字符串路径把 list*/Struct* 降级为 i8*)。
|
||||
sha1_d: str = None
|
||||
cls_d: str = None
|
||||
meth_d: str = None
|
||||
if call_name is not None:
|
||||
cl_len_d: t.CSizeT = string.strlen(call_name)
|
||||
if cl_len_d >= 17 and call_name[16] == '.':
|
||||
sha1_ok_d: int = 1
|
||||
si_d: t.CSizeT
|
||||
for si_d in range(16):
|
||||
cc_d: int = call_name[si_d]
|
||||
if not ((cc_d >= 48 and cc_d <= 57) or (cc_d >= 97 and cc_d <= 102)):
|
||||
sha1_ok_d = 0
|
||||
break
|
||||
if sha1_ok_d != 0:
|
||||
sha1_buf_d: bytes = stdlib.malloc(17)
|
||||
if sha1_buf_d is not None:
|
||||
string.strncpy(sha1_buf_d, call_name, 16)
|
||||
sha1_buf_d[16] = '\0'
|
||||
sha1_d = sha1_buf_d
|
||||
rest_d: str = call_name + 17
|
||||
dot2_d: str = string.strchr(rest_d, '.')
|
||||
if dot2_d is not None:
|
||||
cls_len_d: t.CSizeT = t.CUInt64T(dot2_d) - t.CUInt64T(rest_d)
|
||||
if cls_len_d > 0:
|
||||
cls_buf_d: bytes = stdlib.malloc(cls_len_d + 1)
|
||||
if cls_buf_d is not None:
|
||||
string.strncpy(cls_buf_d, rest_d, cls_len_d)
|
||||
cls_buf_d[cls_len_d] = '\0'
|
||||
cls_d = cls_buf_d
|
||||
meth_d = dot2_d + 1
|
||||
else:
|
||||
meth_d = rest_d
|
||||
prm_annots_d: str = None
|
||||
if sha1_d is not None and meth_d is not None:
|
||||
prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d)
|
||||
i32_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
i8_ptr_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
|
||||
pnode: llvmlite.ParamNode | t.CPtr = param_head
|
||||
pi: int = 0
|
||||
while pnode is not None and pi < param_count:
|
||||
if pnode.Ty is not None:
|
||||
param: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, pnode.Ty, None)
|
||||
param_ty_d: llvmlite.LLVMType | t.CPtr = pnode.Ty
|
||||
if prm_annots_d is not None and prm_annots_d[0] != '\0' and pnode.Ty is not None:
|
||||
seg_ty_d: llvmlite.LLVMType | t.CPtr = VpiReader._resolve_vpi_param_annot_seg(
|
||||
pool, sha1_d, cls_d, meth_d, pi, i32_ty_d, i8_ptr_d)
|
||||
if seg_ty_d is not None:
|
||||
match pnode.Ty:
|
||||
case llvmlite.LLVMType.Int(bits_a):
|
||||
match seg_ty_d:
|
||||
case llvmlite.LLVMType.Int(bits_b):
|
||||
if bits_b > bits_a:
|
||||
param_ty_d = seg_ty_d
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
pass
|
||||
if param_ty_d is not None:
|
||||
param: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, param_ty_d, None)
|
||||
if param is not None:
|
||||
llvmlite.function_add_param(func, param)
|
||||
pnode = pnode.Next
|
||||
pi += 1
|
||||
if sha1_d is not None:
|
||||
stdlib.free(sha1_d)
|
||||
if cls_d is not None:
|
||||
stdlib.free(cls_d)
|
||||
llvmlite.module_add_function(mod, func)
|
||||
|
||||
# ============================================================
|
||||
@@ -587,6 +651,43 @@ class CallGenBase:
|
||||
return None
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
|
||||
|
||||
# ============================================================
|
||||
# _vpi_method_param_annot - 从 vpi 参数注解缓存获取方法参数注解串
|
||||
#
|
||||
# 跨模块方法调用在 stub 未注入(依赖收集不加载被调模块 vpi,如
|
||||
# self.Gen.ExprGenCdef.method 经 Generator 属性访问)时,函数表无
|
||||
# ParamCount/默认参数信息 → 调用点缺参 → 被调函数读栈残留。此函数
|
||||
# 按需加载并查询 vpi 参数注解缓存('@' 分隔,含首参 self 空串占位),
|
||||
# 调用方据此补全 declare/call 缺失参数(默认参数零值兜底)。
|
||||
# 未命中返回 None。
|
||||
# ============================================================
|
||||
def _vpi_method_param_annot(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
cls_sha1: str,
|
||||
cls_name: str,
|
||||
method_name: str) -> str:
|
||||
"""按需加载并查询 vpi 参数注解串,未命中返回 None"""
|
||||
if pool is None or cls_sha1 is None or cls_name is None or method_name is None:
|
||||
return None
|
||||
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
|
||||
VpiReader._ensure_vpi_param_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
|
||||
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
|
||||
|
||||
# ============================================================
|
||||
# _vpi_resolve_param_annot_ty - 解析 vpi 参数注解段为 LLVMType
|
||||
#
|
||||
# 与 _lookup_method_ret_ty_via_vpi 的返回注解解析同源;解析失败返回
|
||||
# None,调用方以 i8* 兜底(ABI 兼容,参数槽位宽度一致)。
|
||||
# ============================================================
|
||||
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
seg: str) -> llvmlite.LLVMType | t.CPtr:
|
||||
"""解析单个 vpi 参数注解段为 LLVMType,失败返回 None"""
|
||||
if pool is None or seg is None:
|
||||
return None
|
||||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty)
|
||||
|
||||
# ============================================================
|
||||
# _special_method_ret_ty - 编译器内置特殊方法的固定返回类型
|
||||
#
|
||||
|
||||
Reference in new issue
Block a user