diff --git a/App/lib/core/Gen/CallGenBase.vp b/App/lib/core/Gen/CallGenBase.vp index be007ca..694696a 100644 --- a/App/lib/core/Gen/CallGenBase.vp +++ b/App/lib/core/Gen/CallGenBase.vp @@ -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 - 编译器内置特殊方法的固定返回类型 #