fix(gen): TPV isinstance 运行链路修复(构造 vtable 槽/this_adjust/虚分派/NoVTable)
- 构造展开跳过 __vtable__ 字段,修复 Dog("dog") 覆盖 vtable 导致 isinstance 全失败
- this_adjust 改字节偏移法(delta=子类偏移-父类偏移),覆盖 vtable 父类
- _do_virtual_call 新增 vtable_base:读 vtable 用对象头,调用用调整后 self
- NoVTable 类无参构造分配内存而非返回 ConstZero
- ClassGenMixin:类体 CDefine 兜底注册 + vtable linkage 默认 external
This commit is contained in:
1 parent
6677da99f7
commit
45ba3a4b5a
3 files changed
+476
-20
No files matched your search
@@ -19,6 +19,7 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||||
import lib.core.Handles.HandlesFunctions as HandlesFunctions
|
||||
import lib.core.StubMerger as StubMerger
|
||||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||||
|
||||
@@ -1977,7 +1978,25 @@ class CallGenMixin:
|
||||
continue
|
||||
fe: HandlesStruct.FieldEntry | t.CPtr = None
|
||||
if ctor_entry is not None:
|
||||
fe = HandlesStruct._get_field_entry(ctor_entry, ai)
|
||||
# OOP 结构体字段表第 0 个字段是 __vtable__(vtable 指针占位),
|
||||
# 构造参数按"用户字段顺序"存储:必须跳过 __vtable__ 字段,
|
||||
# 否则参数会覆盖 vtable 槽(对象头读到垃圾指针,
|
||||
# isinstance 的 icmp 全部不匹配 + 方法分派错乱)。
|
||||
# 原实现直接 _get_field_entry(ctor_entry, ai) 取到 __vtable__
|
||||
# → GEP Index 0 → 覆盖 vtable(Dog("dog") 场景崩溃漂移根因)。
|
||||
target_idx: int = 0
|
||||
for fi in range(ctor_entry.FieldCount):
|
||||
cand_fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry(
|
||||
ctor_entry, fi)
|
||||
if cand_fe is None:
|
||||
continue
|
||||
cand_name: str = HandlesStruct.get_field_name_ptr(cand_fe)
|
||||
if cand_name is not None and string.strcmp(cand_name, "__vtable__") == 0:
|
||||
continue
|
||||
if target_idx == ai:
|
||||
fe = cand_fe
|
||||
break
|
||||
target_idx += 1
|
||||
else:
|
||||
fe = HandlesStruct.get_field_by_index(class_name, ai)
|
||||
if fe is None:
|
||||
@@ -2187,12 +2206,21 @@ class CallGenMixin:
|
||||
args_head: llvmlite.Value | t.CPtr,
|
||||
args_count: int,
|
||||
found_func: llvmlite.Function | t.CPtr,
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""通过虚表间接调用虚方法,返回 call 结果(void 方法返回 None)"""
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr,
|
||||
vtable_base: llvmlite.Value | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""通过虚表间接调用虚方法,返回 call 结果(void 方法返回 None)
|
||||
|
||||
vtable_base: 对象头指针(vtable 所在位置,即未调整的 obj_ptr)。
|
||||
多继承 this 调整后 self_ptr 可能前移(如 obj+4 指向父类字段区),
|
||||
从调整后位置读 vtable 会读到字段值 → 垃圾指针崩溃。默认 None 时
|
||||
vtable_base = self_ptr(未调整场景,读对象头)。
|
||||
"""
|
||||
if pool is None or builder is None or class_name is None or method_name is None:
|
||||
return None
|
||||
if self_ptr is None or args_head is None:
|
||||
return None
|
||||
if vtable_base is None:
|
||||
vtable_base = self_ptr
|
||||
|
||||
# 1. 获取结构体类型(用 find_struct_by_type 规避跨模块同名 find_struct 找错)
|
||||
vc_entry: HandlesStruct.StructEntry | t.CPtr = None
|
||||
@@ -2234,8 +2262,10 @@ class CallGenMixin:
|
||||
# 4. bitcast 到 i8** 访问 __vtable__ 字段(索引 0)
|
||||
# vtable 指针总是在对象第一个字段,无需 GEP,直接 bitcast 到 i8** 即可。
|
||||
# 这样即使 struct_ty 是 opaque(跨模块未加载完整定义)也能正常工作。
|
||||
# 注意用 vtable_base(对象头)而非 self_ptr:多继承 this 调整后
|
||||
# self_ptr 已前移,其头部不是 vtable 而是父类字段值。
|
||||
vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, self_ptr, i8_ptr_ptr_ty)
|
||||
builder, vtable_base, i8_ptr_ptr_ty)
|
||||
if vtable_slot is None:
|
||||
return None
|
||||
|
||||
@@ -2588,7 +2618,6 @@ class CallGenMixin:
|
||||
if cmop_has_vt == 1 and cmop_method_idx >= 0:
|
||||
return self._do_virtual_call(pool, builder, mod, class_name, method_name,
|
||||
self_ptr, head, total_count, found_func, call_ret_ty)
|
||||
|
||||
# 跨模块调用(found_func 为 None):创建 declare 声明,避免 llc 报 undefined value
|
||||
if found_func is None:
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count)
|
||||
@@ -3471,15 +3500,21 @@ class CallGenMixin:
|
||||
if kw_calls is not None:
|
||||
kwc = kw_calls.__len__()
|
||||
|
||||
# 多继承 this 调整:继承方法定义在"无 vtable 的父类"且该父类字段区在子类中
|
||||
# 偏移 > 0 时(如 C(A,B).get_b,B 的字段区在 C 中从 b 开始),父类方法按自身
|
||||
# 布局读 self.field@0 会读到前面父类的字段。调整:GEP 到父类第一个用户字段
|
||||
# 在子类中的位置,bitcast 成父类指针作为 self。
|
||||
# 有 vtable 的父类布局是子类布局前缀(vptr 共享),self 无需调整。
|
||||
# 多继承 this 调整(字节偏移法,2026-08-10,与 TPC HandlesExprCall._adjust_this_for_inherited_method 一致):
|
||||
# 继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内 GEP(self_B, [0,1])
|
||||
# 的字节偏移是 B 布局中 b 的 offset 12)。父类字段区在子类中的字节偏移可能
|
||||
# 不同(如 AB={vtable,a@8,b@12} 中 b 偏移 16),self 需按字节差前移,使父类
|
||||
# 方法内二次 GEP 恰好落到子类字段。
|
||||
# delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移
|
||||
# delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节 GEP 到
|
||||
# 父类字段区在子类中的等效基址,再 bitcast 成父类指针。
|
||||
# 原实现仅覆盖"无 vtable 的父类"(其首字段偏移 0,GEP 到首字段即父类起点),
|
||||
# 且用 GEP 字段法——对 vtable 父类(首字段偏移非 0,如 B)被直接跳过不调整
|
||||
# → AB 场景 B.get_b 按 B 布局读 self.b@12 落到 a → AB 字段/方法 FAILED。
|
||||
self_arg: llvmlite.Value | t.CPtr = obj_ptr
|
||||
if mc_anc is not None and string.strcmp(mc_anc, class_name) != 0:
|
||||
anc_entry_th: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(mc_anc)
|
||||
if anc_entry_th is not None and anc_entry_th.HasVTable == 0 and anc_entry_th.FieldCount > 0:
|
||||
if anc_entry_th is not None and anc_entry_th.FieldCount > 0:
|
||||
anc_first_name_th: str = None
|
||||
afi_th: int = 0
|
||||
while afi_th < anc_entry_th.FieldCount:
|
||||
@@ -3495,13 +3530,27 @@ class CallGenMixin:
|
||||
sub_entry_th: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(
|
||||
class_name)
|
||||
if sub_entry_th is not None:
|
||||
anc_field_th: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
|
||||
anc_entry_th.Ty, anc_first_name_th)
|
||||
sub_field_th: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
|
||||
sub_entry_th.Ty, anc_first_name_th)
|
||||
if sub_field_th is not None and sub_field_th.Index > 0:
|
||||
anc_ptr_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, anc_entry_th.Ty)
|
||||
adj_th: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||||
builder, sub_entry_th.Ty, sub_field_th.Ty, obj_ptr, sub_field_th.Index)
|
||||
self_arg = llvmlite.build_bitcast(builder, adj_th, anc_ptr_ty_th)
|
||||
if anc_field_th is not None and sub_field_th is not None:
|
||||
anc_off_th: int = self._field_byte_offset(
|
||||
pool, anc_entry_th, anc_field_th.Index)
|
||||
sub_off_th: int = self._field_byte_offset(
|
||||
pool, sub_entry_th, sub_field_th.Index)
|
||||
delta_th: int = sub_off_th - anc_off_th
|
||||
if delta_th > 0:
|
||||
i8_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_th)
|
||||
obj_i8_th: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, obj_ptr, i8_ptr_th)
|
||||
delta_val_th: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, delta_th)
|
||||
adj_th: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||||
builder, i8_ty_th, obj_i8_th, delta_val_th)
|
||||
if adj_th is not None:
|
||||
anc_ptr_ty_th: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, anc_entry_th.Ty)
|
||||
self_arg = llvmlite.build_bitcast(builder, adj_th, anc_ptr_ty_th)
|
||||
# 构建参数链表: self → arg1 → arg2 → ...
|
||||
llvmlite.value_set_next(self_arg, None)
|
||||
head: llvmlite.Value | t.CPtr = self_arg
|
||||
@@ -3666,8 +3715,12 @@ class CallGenMixin:
|
||||
mc_method_idx = mc_mi
|
||||
break
|
||||
if mc_has_vt == 1 and mc_method_idx >= 0:
|
||||
# 虚分派 self 必须用调整后的 self_arg(多继承 this 调整),
|
||||
# 否则 B.get_b 收到未调整的 AB*,方法体内按 B 布局读字段错位。
|
||||
# vtable 读取必须用对象头 obj_ptr(调整后 self 头部是父类字段值)。
|
||||
return self._do_virtual_call(pool, builder, mod, class_name, method_name,
|
||||
obj_ptr, head, total_count, found_func, call_ret_ty)
|
||||
self_arg, head, total_count, found_func, call_ret_ty,
|
||||
obj_ptr)
|
||||
|
||||
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count)
|
||||
@@ -3675,6 +3728,48 @@ class CallGenMixin:
|
||||
is_variadic_3441: int = HandlesExprCall._check_and_promote_variadic_call(pool, builder, mod, call_name, head, total_count)
|
||||
return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, is_variadic_3441)
|
||||
|
||||
# ============================================================
|
||||
# _field_byte_offset — 计算字段在结构体布局中的字节偏移
|
||||
#
|
||||
# 按 LLVM 布局遍历 struct elements 带对齐累加(与 ClassGenMixin._get_type_size
|
||||
# 的 padding 规则一致)。field_index 是 FieldEntry.Index(OOP 类 __vtable__ 占 0)。
|
||||
# 供多继承 this 调整(字节偏移法)计算 delta。
|
||||
# ============================================================
|
||||
def _field_byte_offset(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
entry: HandlesStruct.StructEntry | t.CPtr,
|
||||
field_index: int) -> int:
|
||||
"""计算字段在结构体中的字节偏移(含对齐 padding),无法计算返回 0"""
|
||||
if pool is None or entry is None or entry.Ty is None or field_index < 0:
|
||||
return 0
|
||||
match entry.Ty:
|
||||
case llvmlite.LLVMType.Struct(fields, fcount, name):
|
||||
offset_fb: int = 0
|
||||
cur_fb: llvmlite.ParamNode | t.CPtr = fields
|
||||
i_fb: int = 0
|
||||
while cur_fb is not None and i_fb < field_index:
|
||||
if cur_fb.Ty is not None:
|
||||
fty_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty)
|
||||
fsize_fb: int = HandlesClassDef._get_type_size(fty_fb)
|
||||
falign_fb: int = HandlesClassDef._get_type_align(fty_fb)
|
||||
if falign_fb > 1:
|
||||
rem_fb: int = offset_fb % falign_fb
|
||||
if rem_fb != 0:
|
||||
offset_fb += falign_fb - rem_fb
|
||||
offset_fb += fsize_fb
|
||||
cur_fb = cur_fb.Next
|
||||
i_fb += 1
|
||||
# 字段自身对齐
|
||||
if cur_fb is not None and cur_fb.Ty is not None:
|
||||
fty2_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty)
|
||||
falign2_fb: int = HandlesClassDef._get_type_align(fty2_fb)
|
||||
if falign2_fb > 1:
|
||||
rem2_fb: int = offset_fb % falign2_fb
|
||||
if rem2_fb != 0:
|
||||
offset_fb += falign2_fb - rem2_fb
|
||||
return offset_fb
|
||||
case _:
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _kwarg_name_matches — 检查关键字参数名是否匹配函数参数名
|
||||
#
|
||||
@@ -4759,6 +4854,17 @@ class CallGenMixin:
|
||||
i64_ty_ln2: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||
return llvmlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
|
||||
|
||||
# 检测 isinstance() 内置函数调用 — 运行时 vtable 子类集合比较
|
||||
# isinstance(obj, Cls):obj 头部 vtable 指针 ∈ {Cls 及全部子类 vtable};
|
||||
# NoVTable 目标类回退编译时静态判断(详见 _translate_isinstance)。
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
if func_name is not None and string.strcmp(func_name, "isinstance") == 0:
|
||||
if can >= 2:
|
||||
is_res: llvmlite.Value | t.CPtr = self._translate_isinstance(
|
||||
pool, builder, mod, cargs.get(0), cargs.get(1), trans)
|
||||
if is_res is not None:
|
||||
return is_res
|
||||
|
||||
# 检测类名静态/类方法调用: ClassName.method(args)
|
||||
# 当 cl.func 是 Attribute、value 是已注册结构体类名(非普通变量)时,
|
||||
# 且方法带 @staticmethod/@classmethod 元数据,走静态调用路径
|
||||
@@ -5184,6 +5290,16 @@ class CallGenMixin:
|
||||
if ctor_is_oop != 0:
|
||||
return self._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
|
||||
# @t.NoVTable 类(无方法、类语义/指针对象):无参构造也应分配内存
|
||||
# 返回指针。否则按值结构体返回 ConstZero(struct_ty),LHS 联合注解
|
||||
# (如 Plain|CPtr)存 0 指针 → p.v 解引用 NULL 段错误
|
||||
# (TpvIsinstanceTest Plain 场景崩溃根因)。
|
||||
ctor_is_novt: int = 0
|
||||
if gen_ctor_entry is not None:
|
||||
ctor_is_novt = gen_ctor_entry.IsNoVTable
|
||||
if ctor_is_novt != 0:
|
||||
return self._translate_struct_ctor(
|
||||
pool, builder, mod, struct_ty, func_name, cargs, 0, trans)
|
||||
# 普通结构体:检查是否有默认值(用 entry 直接访问)
|
||||
gen_has_defaults: int = 0
|
||||
if gen_ctor_entry is not None:
|
||||
@@ -5681,6 +5797,332 @@ class CallGenMixin:
|
||||
is_variadic_call: int = HandlesExprCall._check_and_promote_variadic_call(pool, builder, mod, call_name, head, can)
|
||||
return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, is_variadic_call)
|
||||
|
||||
# ============================================================
|
||||
# isinstance - 运行时 vtable 子类集合比较(2026-08-10)
|
||||
#
|
||||
# CVTable 对象头部 vtable 指针唯一标识对象实际类。isinstance(obj, Cls)
|
||||
# 语义 = Cls 是实际类的祖先(实际类 == Cls 或继承自 Cls)。运行时只能
|
||||
# 拿到实际类 vtable 地址,无法向上推导祖先链,因此在编译期沿
|
||||
# StructEntry.ParentNames 反向 BFS 枚举 Cls 的全部已知子类(含自身),
|
||||
# 运行时比较 obj->vtable ∈ {Cls 子类 vtable 地址集合}。
|
||||
# NoVTable / 未知类回退编译时静态判断(obj 静态类型 MRO 判断)。
|
||||
# ============================================================
|
||||
|
||||
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
|
||||
ISINSTANCE_CLASS_MAX: t.CDefine = 64
|
||||
|
||||
def _translate_isinstance(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
obj_node: ast.AST | t.CPtr,
|
||||
cls_node: ast.AST | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""isinstance(obj, Cls):运行时 vtable 子类集合比较,返回 i1"""
|
||||
if obj_node is None or cls_node is None:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
dbg_enter: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_enter is not None:
|
||||
viperlib.snprintf(dbg_enter, 1024, "[ISINSTANCE] ENTER kind=%d", cls_node.kind())
|
||||
VLogger.info(dbg_enter, "ISINSTANCE")
|
||||
obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, obj_node, None, 0, trans)
|
||||
dbg_a: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_a is not None:
|
||||
viperlib.snprintf(dbg_a, 1024, "[ISINSTANCE] A obj_set=%d",
|
||||
0 if obj_val is None else 1)
|
||||
VLogger.info(dbg_a, "ISINSTANCE")
|
||||
if obj_val is None or obj_val.Ty is None:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
# 对象指针规整:二级指针(变量 alloca / 联合 Class|t.CPtr)先 load
|
||||
obj_ptr: llvmlite.Value | t.CPtr = obj_val
|
||||
match obj_val.Ty:
|
||||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||||
match inner_ty:
|
||||
case llvmlite.LLVMType.Ptr(_):
|
||||
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, inner_ty, obj_val)
|
||||
if loaded is not None:
|
||||
obj_ptr = loaded
|
||||
case _:
|
||||
pass
|
||||
obj_is_ptr: int = HandlesExpr.is_ptr_type(obj_ptr.Ty)
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
i8_ptr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty)
|
||||
# 对象 vtable 指针惰性读取(首个 CVTable 类比较时执行一次)
|
||||
vtable_ptr: llvmlite.Value | t.CPtr = None
|
||||
vtable_ready: int = 0
|
||||
res: llvmlite.Value | t.CPtr = None
|
||||
cls_names: t.CSizeT | t.CPtr = self._extract_isinstance_class_names(pool, cls_node)
|
||||
dbg_b: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_b is not None:
|
||||
viperlib.snprintf(dbg_b, 1024, "[ISINSTANCE] B cls_names=%d cls0=%lld",
|
||||
0 if cls_names is None else 1,
|
||||
cls_names[0] if cls_names is not None else -1)
|
||||
VLogger.info(dbg_b, "ISINSTANCE")
|
||||
if cls_names is None:
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
dbg0: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg0 is not None:
|
||||
viperlib.snprintf(dbg0, 1024, "[ISINSTANCE] C kind=%d ptr=%d cls0=%lld",
|
||||
cls_node.kind(), obj_is_ptr, cls_names[0])
|
||||
VLogger.info(dbg0, "ISINSTANCE")
|
||||
ci: int = 0
|
||||
while cls_names[ci] != 0:
|
||||
cn: str = (str | t.CPtr)(t.CVoid(cls_names[ci], t.CPtr))
|
||||
if cn is not None:
|
||||
dbg1: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg1 is not None:
|
||||
viperlib.snprintf(dbg1, 1024, "[ISINSTANCE] cn_ptr=%lld nv=%d vt=%d",
|
||||
cls_names[ci],
|
||||
HandlesStruct.is_novtable_by_name(cn),
|
||||
HandlesStruct.has_vtable_by_name(cn))
|
||||
VLogger.info(dbg1, "ISINSTANCE")
|
||||
if HandlesStruct.is_novtable_by_name(cn) != 0 \
|
||||
or HandlesStruct.has_vtable_by_name(cn) == 0:
|
||||
# NoVTable / 未知类 → 编译时静态判断(obj 静态类型 MRO)
|
||||
st: int = self._isinstance_compile_time(pool, obj_ptr, cn)
|
||||
c1: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, 1, 1 if st > 0 else 0)
|
||||
if res is None:
|
||||
res = c1
|
||||
else:
|
||||
res = llvmlite.build_or(builder, res, c1)
|
||||
elif obj_is_ptr != 0:
|
||||
# CVTable → 运行时 vtable 比较(读对象头部 vtable 指针)
|
||||
if vtable_ready == 0:
|
||||
vtable_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, obj_ptr, i8_ptr_ptr_ty)
|
||||
if vtable_slot is not None:
|
||||
vtable_ptr = llvmlite.build_load(builder, i8_ptr_ty, vtable_slot)
|
||||
vtable_ready = 1
|
||||
if vtable_ptr is not None:
|
||||
subs: t.CSizeT | t.CPtr = self._collect_subclass_vtable_classes(pool, cn)
|
||||
dbg2: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg2 is not None:
|
||||
viperlib.snprintf(dbg2, 1024, "[ISINSTANCE] cn_ptr=%lld subs0=%lld subs1=%lld",
|
||||
cls_names[ci],
|
||||
subs[0] if subs is not None else -1,
|
||||
subs[1] if subs is not None else -1)
|
||||
VLogger.info(dbg2, "ISINSTANCE")
|
||||
if subs is not None:
|
||||
k: int = 0
|
||||
while subs[k] != 0:
|
||||
scn: str = (str | t.CPtr)(t.CVoid(subs[k], t.CPtr))
|
||||
if scn is not None:
|
||||
sub_i8: llvmlite.Value | t.CPtr = self._make_vtable_ref_i8(
|
||||
pool, builder, mod, scn, trans)
|
||||
if sub_i8 is not None:
|
||||
cmp: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
|
||||
builder, llvmlite.ICMP_EQ, vtable_ptr, sub_i8)
|
||||
if cmp is not None:
|
||||
if res is None:
|
||||
res = cmp
|
||||
else:
|
||||
res = llvmlite.build_or(builder, res, cmp)
|
||||
else:
|
||||
dbg_ci: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_ci is not None:
|
||||
viperlib.snprintf(dbg_ci, 1024, "[ISINSTANCE] CMP-NONE subs_k=%lld", subs[k])
|
||||
VLogger.info(dbg_ci, "ISINSTANCE")
|
||||
else:
|
||||
dbg_ri: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_ri is not None:
|
||||
viperlib.snprintf(dbg_ri, 1024, "[ISINSTANCE] REF-NONE subs_k=%lld", subs[k])
|
||||
VLogger.info(dbg_ri, "ISINSTANCE")
|
||||
k += 1
|
||||
else:
|
||||
dbg_sn: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_sn is not None:
|
||||
viperlib.snprintf(dbg_sn, 1024, "[ISINSTANCE] SUBS-NONE cn=%lld", cls_names[ci])
|
||||
VLogger.info(dbg_sn, "ISINSTANCE")
|
||||
ci += 1
|
||||
if res is None:
|
||||
dbg_rn: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if dbg_rn is not None:
|
||||
viperlib.snprintf(dbg_rn, 1024, "[ISINSTANCE] RES-NONE")
|
||||
VLogger.info(dbg_rn, "ISINSTANCE")
|
||||
return llvmlite.const_int(pool, 1, 0)
|
||||
return res
|
||||
|
||||
def _extract_isinstance_class_names(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
node: ast.AST | t.CPtr) -> t.CSizeT | t.CPtr:
|
||||
"""从 isinstance 第二参数提取类名数组(Name→id / Attribute→attr / Tuple→遍历)"""
|
||||
arr: t.CSizeT | t.CPtr = pool.alloc(8 * 16)
|
||||
if arr is None:
|
||||
return None
|
||||
# 全数组清零:pool 分配内存非零时,未初始化槽位会被 while cls_names[ci]!=0
|
||||
# 误当类名指针解引用 → 段错误
|
||||
string.memset(arr, 0, 8 * 16)
|
||||
arr[0] = 0
|
||||
if node is None:
|
||||
return arr
|
||||
count: int = 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||||
if nm.id is not None:
|
||||
arr[0] = t.CSizeT(nm.id)
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||||
if at.attr is not None:
|
||||
arr[0] = t.CSizeT(at.attr)
|
||||
elif k == ast.ASTKind.Tuple:
|
||||
tp: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(node)
|
||||
elts: list[ast.AST | t.CPtr] | t.CPtr = tp.elts
|
||||
if elts is not None:
|
||||
n: t.CSizeT = elts.__len__()
|
||||
for i in range(n):
|
||||
if count >= 15:
|
||||
break
|
||||
elt: ast.AST | t.CPtr = elts.get(i)
|
||||
if elt is None:
|
||||
continue
|
||||
ek: int = elt.kind()
|
||||
if ek == ast.ASTKind.Name:
|
||||
enm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(elt)
|
||||
if enm.id is not None:
|
||||
arr[count] = t.CSizeT(enm.id)
|
||||
count += 1
|
||||
elif ek == ast.ASTKind.Attribute:
|
||||
eat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(elt)
|
||||
if eat.attr is not None:
|
||||
arr[count] = t.CSizeT(eat.attr)
|
||||
count += 1
|
||||
arr[count] = 0
|
||||
return arr
|
||||
|
||||
def _collect_subclass_vtable_classes(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
class_name: str) -> t.CSizeT | t.CPtr:
|
||||
"""反向 BFS 收集目标类及全部子类(含自身,仅 HasVTable==1),返回 0 结尾数组"""
|
||||
if class_name is None:
|
||||
return None
|
||||
names: t.CSizeT | t.CPtr = pool.alloc(8 * (ISINSTANCE_CLASS_MAX + 1))
|
||||
queue: t.CSizeT | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
|
||||
visited: t.CSizeT | t.CPtr = pool.alloc(8 * ISINSTANCE_CLASS_MAX)
|
||||
if names is None or queue is None or visited is None:
|
||||
return None
|
||||
string.memset(names, 0, 8 * (ISINSTANCE_CLASS_MAX + 1))
|
||||
string.memset(queue, 0, 8 * ISINSTANCE_CLASS_MAX)
|
||||
string.memset(visited, 0, 8 * ISINSTANCE_CLASS_MAX)
|
||||
qcount: int = 0
|
||||
outcount: int = 0
|
||||
vcount: int = 0
|
||||
queue[0] = t.CSizeT(class_name)
|
||||
qcount = 1
|
||||
while qcount > 0:
|
||||
qcount -= 1
|
||||
cur_addr: t.CSizeT = queue[qcount]
|
||||
cur: str = (str | t.CPtr)(t.CVoid(cur_addr, t.CPtr))
|
||||
if cur is None:
|
||||
continue
|
||||
dup: int = 0
|
||||
for vi in range(vcount):
|
||||
va: t.CSizeT = visited[vi]
|
||||
if va != 0:
|
||||
vn: str = (str | t.CPtr)(t.CVoid(va, t.CPtr))
|
||||
if vn is not None and string.strcmp(vn, cur) == 0:
|
||||
dup = 1
|
||||
break
|
||||
if dup != 0:
|
||||
continue
|
||||
if vcount < ISINSTANCE_CLASS_MAX:
|
||||
visited[vcount] = t.CSizeT(cur)
|
||||
vcount += 1
|
||||
centry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(cur)
|
||||
if centry is not None and centry.HasVTable != 0:
|
||||
if outcount < ISINSTANCE_CLASS_MAX:
|
||||
names[outcount] = t.CSizeT(cur)
|
||||
outcount += 1
|
||||
# 子类入队:遍历全部已注册 struct,ParentNames 含 cur 者
|
||||
scnt: int = HandlesStruct.get_struct_count()
|
||||
for i in range(scnt):
|
||||
e2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct._get_struct_entry(i)
|
||||
if e2 is None or e2.Name is None:
|
||||
continue
|
||||
if e2.ParentCount > 0 and e2.ParentNames is not None:
|
||||
parr: t.CSizeT | t.CPtr = (t.CSizeT | t.CPtr)(t.CVoid(e2.ParentNames, t.CPtr))
|
||||
for j in range(e2.ParentCount):
|
||||
pa: t.CSizeT = parr[j]
|
||||
if pa != 0:
|
||||
pn: str = (str | t.CPtr)(t.CVoid(pa, t.CPtr))
|
||||
if pn is not None and string.strcmp(pn, cur) == 0:
|
||||
if qcount < ISINSTANCE_CLASS_MAX:
|
||||
queue[qcount] = t.CSizeT(e2.Name)
|
||||
qcount += 1
|
||||
break
|
||||
names[outcount] = 0
|
||||
return names
|
||||
|
||||
def _make_vtable_ref_i8(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
class_name: str,
|
||||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""构造类 vtable 地址引用(i8*):本模块已定义直接引用,跨模块声明 external"""
|
||||
if class_name is None:
|
||||
return None
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
buf: t.CChar | t.CPtr = pool.alloc(256)
|
||||
if buf is None:
|
||||
return None
|
||||
viperlib.snprintf(buf, 256, "%s.vtable", class_name)
|
||||
vn: str = (str | t.CPtr)(buf)
|
||||
sha1: str = HandlesStruct.get_struct_sha1(class_name)
|
||||
if sha1 is None:
|
||||
if trans is None:
|
||||
return None
|
||||
sha1 = trans.ModuleSha1
|
||||
if sha1 is None:
|
||||
return None
|
||||
mangled: str = HandlesFunctions._mangle_name_with_sha1(sha1, vn)
|
||||
ref_name: t.CChar | t.CPtr = pool.alloc(256)
|
||||
if ref_name is None:
|
||||
return None
|
||||
# vtable 名含 sha1 点号前缀,一律 @"..." 引号形式
|
||||
viperlib.snprintf(ref_name, 256, "@\"%s\"", mangled)
|
||||
gv: llvmlite.GlobalVariable | t.CPtr = HandlesExpr.find_global_in_module(mod, mangled)
|
||||
ref_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if gv is not None:
|
||||
# 本模块已定义 vtable(constant {i8* x N})→ 引用类型 Ptr(struct)
|
||||
ref_ty = llvmlite.Ptr(pool, gv.Ty)
|
||||
else:
|
||||
# 跨模块:声明 @mangled = external global i8*,引用类型 i8**
|
||||
gv2: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(
|
||||
pool, mangled, i8_ptr_ty)
|
||||
if gv2 is None:
|
||||
return None
|
||||
llvmlite.module_add_global(mod, gv2)
|
||||
ref_ty = llvmlite.Ptr(pool, i8_ptr_ty)
|
||||
ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, ref_ty, ref_name)
|
||||
if ref is None:
|
||||
return None
|
||||
return llvmlite.build_bitcast(builder, ref, i8_ptr_ty)
|
||||
|
||||
def _isinstance_compile_time(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
obj_ptr: llvmlite.Value | t.CPtr,
|
||||
class_name: str) -> int:
|
||||
"""编译时静态判断:obj 静态类型 == class_name 或 class_name 在其 MRO 中"""
|
||||
if obj_ptr is None or obj_ptr.Ty is None or class_name is None:
|
||||
return 0
|
||||
match obj_ptr.Ty:
|
||||
case llvmlite.LLVMType.Ptr(obj_struct_ty):
|
||||
sname: str = HandlesStruct.get_class_name_by_type(pool, obj_struct_ty)
|
||||
if sname is not None:
|
||||
if string.strcmp(sname, class_name) == 0:
|
||||
return 1
|
||||
mro: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, sname)
|
||||
if mro is not None:
|
||||
k: int = 0
|
||||
while mro[k] != 0:
|
||||
mn: str = (str | t.CPtr)(t.CVoid(mro[k], t.CPtr))
|
||||
if mn is not None and string.strcmp(mn, class_name) == 0:
|
||||
return 1
|
||||
k += 1
|
||||
case _:
|
||||
pass
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# P4 簇 5+6 迁移方法(函数体来自 HandlesExprCall.vp:
|
||||
# 闭包调用/函数指针/printf/print/stdarg 糖/变长参数提升/函数表/全局注册表)
|
||||
|
||||
@@ -2424,6 +2424,15 @@ class ClassGenMixin:
|
||||
if tgt.id is None:
|
||||
continue
|
||||
|
||||
# 类体 CDefine 常量(任意位置,2026-08-10):注册到全局 CDefine 表。
|
||||
# 方法内裸名(如 ISINSTANCE_CLASS_MAX)经 translate_name_value 查
|
||||
# CDefine 表解析为常量值;类体开头的常量(MRO_MAX 等)由早期预注册
|
||||
# 路径注册,但方法之后的 AnnAssign 不经过那些路径 → 此处统一兜底注册。
|
||||
# 不 continue:CDefine 字段仍按普通字段收集(i32,与 MRO_MAX 一致)。
|
||||
if HandlesAnnAssign.is_cdefine_annotation(aa.annotation) != 0:
|
||||
if aa.value is not None:
|
||||
HandlesAnnAssign._register_cdefine_from_value(pool, tgt.id, aa.value)
|
||||
|
||||
# 跳过编译期元数据字段(__provides__/__requires__/__require_must__)
|
||||
# 这些字段只用于 with 上下文的静态可达性检查,不生成运行时代码
|
||||
# 避免类体中 list 字面量翻译失败(类体无 pool 变量上下文)
|
||||
@@ -3117,7 +3126,11 @@ class ClassGenMixin:
|
||||
return 0
|
||||
gv.Initializer = init_buf
|
||||
gv.IsConstant = 1
|
||||
gv.Linkage = "internal"
|
||||
# 默认 linkage(external,2026-08-10):isinstance 内建函数需要跨模块
|
||||
# 引用其他模块类的 vtable 符号({模块sha1}.Class.vtable),internal 不对外
|
||||
# 导出会导致链接失败。vtable 名含模块 sha1 前缀全局唯一,外部可见不冲突。
|
||||
# llvmlite 打印规则:Initializer 非空 + external 时不打印 external 关键字,
|
||||
# 输出 @name = constant {...}(合法外部强定义)。
|
||||
|
||||
# 添加到模块
|
||||
llvmlite.module_add_global(mod, gv)
|
||||
|
||||
@@ -647,13 +647,14 @@ def _do_virtual_call(pool: memhub.MemBuddy | t.CPtr,
|
||||
args_head: llvmlite.Value | t.CPtr,
|
||||
args_count: int,
|
||||
found_func: llvmlite.Function | t.CPtr,
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
ret_ty: llvmlite.LLVMType | t.CPtr,
|
||||
vtable_base: llvmlite.Value | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||||
"""通过虚表间接调用虚方法,返回 call 结果(void 方法返回 None)(委托到 gen._do_virtual_call)"""
|
||||
import lib.core.Gen.Generator as Generator
|
||||
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
|
||||
if g is not None:
|
||||
return g._do_virtual_call(pool, builder, mod, class_name, method_name,
|
||||
self_ptr, args_head, args_count, found_func, ret_ty)
|
||||
self_ptr, args_head, args_count, found_func, ret_ty, vtable_base)
|
||||
return None
|
||||
|
||||
# ============================================================
|
||||
|
||||
Reference in new issue
Block a user