diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index e75a703..bd179be 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -709,6 +709,116 @@ def _lookup_vpi_param_annots(src_sha1: str, cls_name: str, method_name: str) -> return r +# ============================================================ +# _resolve_vpi_param_annot_seg_template - 泛型特化类的参数注解段解析 +# +# 真源:_cache_vpi_param_annots_text 的类名键段取自 vpi 的 class 行文本 +# (取到 ':' / 空格 / '(' 为止),`class list[T]:` ⇒ 键段 "list[T]";因此特化 +# 类名(list[str]/list[int]/list[rowptr[AST]])的精确键必然遗漏,定义侧形参 +# 注解取不到 → 调用点 declare 停在调用点实参宽度(elem_size 字面量 i32 把 +# 定义侧 t.CSizeT(i64) 固化窄)。 +# 与 _lookup_vpi_ret_annot 的"泛型特化归一化匹配"同源:按 {sha1}.{base}[T]. +# {method} 形态在缓存中定位模板条目,把段内独立的类型形参 token 用特化实参 +# (cls_name 的 [...] 内容)替换后再解析(先例 _subst_template_arg)。 +# 未命中/多参模板(键段含空格被截断,如 "Pair[A,")返回 None,不做兜底推断。 +# ============================================================ +def _resolve_vpi_param_annot_seg_template(pool: rowptr[memhub.MemBuddy], + src_sha1: str, + cls_name: str, + method_name: str, + seg_idx: int, + i32_ty: llvmlite.LLVMType | t.CPtr, + i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr: + """特化类 X[arg] 的参数注解段解析(模板 X[T] 注解 + T→arg 替换);未命中 None""" + if pool is None or src_sha1 is None or cls_name is None or method_name is None \ + or _g_vpi_param_keys is None: + return None + lbkt_t: str = string.strchr(cls_name, 91) # '[' = 91 + if lbkt_t is None: + return None + rbkt_t: str = string.strrchr(cls_name, 93) # ']' = 93 + if rbkt_t is None or rbkt_t <= lbkt_t: + return None + base_len_t: sizet = uint64(lbkt_t) - uint64(cls_name) + arg_len_t: sizet = uint64(rbkt_t) - uint64(lbkt_t) - 1 + if base_len_t <= 0 or arg_len_t <= 0: + return None + base_t: bytes = stdlib.malloc(base_len_t + 1) + if base_t is None: + return None + arg_t: bytes = stdlib.malloc(arg_len_t + 1) + if arg_t is None: + stdlib.free(base_t) + return None + string.strncpy(base_t, cls_name, base_len_t) + base_t[base_len_t] = '\0' + string.strncpy(arg_t, lbkt_t + 1, arg_len_t) + arg_t[arg_len_t] = '\0' + sha1_len_t: sizet = string.strlen(src_sha1) + ty_t: llvmlite.LLVMType | t.CPtr = None + ki_t: int + for ki_t in range(_g_vpi_param_count): + gk_t: bytes = _g_vpi_param_keys + ki_t * VPI_PARAM_KEY_LEN + if string.strncmp(gk_t, src_sha1, sha1_len_t) != 0 or gk_t[sha1_len_t] != '.': + continue + if string.strncmp(gk_t + sha1_len_t + 1, base_t, base_len_t) != 0: + continue + if gk_t[sha1_len_t + 1 + base_len_t] != 91: + continue + gklb_t: str = string.strchr(gk_t + sha1_len_t + 1 + base_len_t, 93) + if gklb_t is None: + continue + tplen_t: sizet = uint64(gklb_t) - uint64(gk_t + sha1_len_t + 1 + base_len_t) - 1 + # 方法名须整体匹配(gklb_t 后即 ']' 与 '.';mangled 形态保证无同名歧义) + if tplen_t <= 0 or string.strcmp(gklb_t + 2, method_name) != 0: + continue + annots_t: str = _g_vpi_param_strs + ki_t * VPI_PARAM_LEN + if annots_t is None or annots_t[0] == '\0': + break + # 段定位(与 _resolve_vpi_param_annot_seg 同口径:'@' 分隔,段 0 = self 空段) + cur_t: sizet = 0 + cur_seg_t: int = 0 + while cur_seg_t < seg_idx and annots_t[cur_t] != '\0': + while annots_t[cur_t] != '\0' and annots_t[cur_t] != '@': + cur_t += 1 + if annots_t[cur_t] == '@': + cur_t += 1 + cur_seg_t += 1 + seg_start_t: sizet = cur_t + seg_len_t: sizet = 0 + while annots_t[seg_start_t + seg_len_t] != '\0' \ + and annots_t[seg_start_t + seg_len_t] != '@': + seg_len_t += 1 + if seg_len_t == 0: + break + seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1) + if seg_buf_t is None: + break + string.strncpy(seg_buf_t, annots_t + seg_start_t, seg_len_t) + seg_buf_t[seg_len_t] = '\0' + # 类型形参 token(如 T)→ 特化实参(如 str / rowptr[AST] / AST|t.CPtr): + # 模板注解按源码原样缓存,不替换则 `item: T` 解析不出类型(None 跳过) + tparam_t: bytes = stdlib.malloc(tplen_t + 1) + if tparam_t is not None: + string.strncpy(tparam_t, gk_t + sha1_len_t + 1 + base_len_t + 1, tplen_t) + tparam_t[tplen_t] = '\0' + subst_t: str = _subst_template_arg(seg_buf_t, tparam_t, arg_t) + if subst_t is not None: + # _subst_template_arg 早退分支(tplen==0 或 tplen>alen)返回入参 + # 本身 ⇒ subst_t 与 seg_buf_t 同指针时并非新分配,此处不得 free + # (否则与末尾 free(seg_buf_t) 重复释放) + if uint64(subst_t) != uint64(seg_buf_t): + stdlib.free(seg_buf_t) + seg_buf_t = subst_t + stdlib.free(tparam_t) + ty_t = _resolve_vpi_ret_annot_str(pool, seg_buf_t, i32_ty, i8_ptr_ty) + stdlib.free(seg_buf_t) + break + stdlib.free(base_t) + stdlib.free(arg_t) + return ty_t + + # ============================================================ # _repair_declare_params_from_text - 用文本缓存补全 declare 参数 # diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 4d2b58e..8dbdc54 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -745,7 +745,8 @@ class CallGenBase: sha1_buf_d[pfx_len_d - 1] = '\0' sha1_d = sha1_buf_d rest_d: str = call_name + pfx_len_d - dot2_d: str = string.strchr(rest_d, '.') + # 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断 + dot2_d: str = string.strrchr(rest_d, '.') if dot2_d is not None: cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d) if cls_len_d > 0: diff --git a/App/lib/core/Gen/Call/CallGenCall.vp b/App/lib/core/Gen/Call/CallGenCall.vp index fbcd3dd..66fdc46 100644 --- a/App/lib/core/Gen/Call/CallGenCall.vp +++ b/App/lib/core/Gen/Call/CallGenCall.vp @@ -652,6 +652,16 @@ class CallGenCall(CallGenBase.CallGenBase): call_ret_ty = self._fail_method_ret_ty(method_name, node) # 跨模块调用(found_func 为 None):创建 declare 声明,避免 llc 报 undefined value if found_func is None: + # 先按定义侧形参 coerce 实参(宽度/指针口径对齐),再建 declare(先例 + # a83d183 的三处调用点:_translate_method_call / _translate_call_with_kwargs + # / CallGenDispatch 特化分支)。本路径此前直接用调用点实参类型建 + # declare:泛型特化构造 `list[T](pool, 8)` 的 elem_size 字面量 i32 会把 + # 定义侧 t.CSizeT(i64) 固化窄,且池实参(MemBuddy*/i8*)与定义侧 + # MemManager* 口径不一致 → 跨模块 declare/define 不一致并经 .stub.ll + # 传播到未调用该函数的消费方模块。泛型特化名({sha}.list[str].__new__) + # 的定义侧注解在模板类上,由 _coerce_args_to_def_params 的模板退查解析。 + head = self.Gen.CallGenLib._coerce_args_to_def_params( + pool, builder, call_name, head, total_count, None, 1, trans) self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, 0) diff --git a/App/lib/core/Gen/Call/CallGenLib.vp b/App/lib/core/Gen/Call/CallGenLib.vp index 0424398..6fd0cf9 100644 --- a/App/lib/core/Gen/Call/CallGenLib.vp +++ b/App/lib/core/Gen/Call/CallGenLib.vp @@ -233,7 +233,8 @@ class CallGenLib(CallGenBase.CallGenBase): rest: str = call_name + 17 cls_nm: str = None meth_nm: str = rest - dot_r: str = string.strchr(rest, 46) + # 取末个 '.':method 不含 '.',首个 '.' 会把含模块限定名的类型实参截断 + dot_r: str = string.strrchr(rest, 46) if dot_r is not None: cls_len: sizet = uint64(dot_r) - uint64(rest) if cls_len <= 0: @@ -263,11 +264,21 @@ class CallGenLib(CallGenBase.CallGenBase): if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir) prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm) + # 泛型特化类({sha}.list[str].__new__):定义侧注解写在**模板类**上 + # (vpi 键段为 "list[T]",见 VpiReader._cache_vpi_param_annots_text 的 + # 类名取值),特化名的精确键必然遗漏 ⇒ 逐段退查模板注解并把类型形参 + # 替换为特化实参(VpiReader._resolve_vpi_param_annot_seg_template, + # 与 _lookup_vpi_ret_annot 的泛型特化归一化匹配同源)。tpl_src=1 使 + # 指针口径也对齐定义侧(MemBuddy* → MemManager*)。 + tpl_src: int = 0 if prm_annots is None: - stdlib.free(sha1_buf) - if cls_nm is not None: - stdlib.free(cls_nm) - return head + if cls_nm is not None and string.strchr(cls_nm, 91) is not None: + tpl_src = 1 + else: + stdlib.free(sha1_buf) + if cls_nm is not None: + stdlib.free(cls_nm) + return head i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool)) new_head: llvmlite.Value | t.CPtr = None @@ -276,16 +287,21 @@ class CallGenLib(CallGenBase.CallGenBase): idx: int = 0 while cur is not None and idx < count: node_v: llvmlite.Value | t.CPtr = cur - 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: + tgt_ty: llvmlite.LLVMType | t.CPtr = None + if tpl_src != 0: + tgt_ty = VpiReader._resolve_vpi_param_annot_seg_template( + pool, sha1_buf, cls_nm, meth_nm, idx, i32_ty, i8_ptr_ty) + else: + tgt_ty = 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: # 实参 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) + pool, builder, cur, tgt_ty, arg_node, trans, tpl_src) if new_head is None: new_head = node_v else: @@ -320,11 +336,15 @@ class CallGenLib(CallGenBase.CallGenBase): val: llvmlite.Value | t.CPtr, tgt_ty: llvmlite.LLVMType | t.CPtr, node: ast.AST | t.CPtr = None, - trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr: + trans: HT.Translator | t.CPtr = None, + tpl_src: int = 0) -> llvmlite.Value | t.CPtr: """把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回 node/trans:实参 AST 与其翻译器(仅 SSA 整型扩宽的源符号性判定用, 委托 ExprGenBase._zext_or_sext_val;缺省退化为有符号 sext)。 + tpl_src:目标类型来自**泛型模板类**注解(特化名精确键遗漏 → 退查模板 + + 类型形参替换,见 VpiReader._resolve_vpi_param_annot_seg_template)⇒ + 指针口径亦可对齐定义侧(同为具体结构体指针但基类型不同时 bitcast)。 """ if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None: return val @@ -407,6 +427,17 @@ class CallGenLib(CallGenBase.CallGenBase): builder, val, tgt_ty) if nv_p is not None: return nv_p + elif tpl_src != 0: + # 模板注解来源:两侧同为具体结构体指针但基类型不同 + # (实参 MemBuddy* vs 定义侧形参 MemManager*,注解 + # `memhub.MemManager | t.CPtr`)→ bitcast 使 declare + # 指针口径与 define 一致(位不变、ABI 无影响)。 + # 非模板来源保持既有"具体指针原样保留"策略, + # 避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声。 + nv_p2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, val, tgt_ty) + if nv_p2 is not None: + return nv_p2 case _: pass case _: