类别:b(机制漏豁免)/诊断增强,不改变任何成功路径。 现场:编译 StmtGenDeclMixin.vp:242 `len(mcc_pre.children)` 报 [fast-fail] len(mcc_pre.children) 分派 'list[10f3f43ddd447eec.AST*].__len__' 失败: 类='MatchCase' 偏移=6 接收者类型=i32 静态可查到的**正确**布局(temp/8c9751aec486f41c.stub.ll 实测 10 槽): [0]i8*(vtable) [1]AST*(parent) [2]MemManager*(pool) [3..6]i32(lineno/col_offset/ end_lineno/end_col_offset) [7]list[AST*]*(children) [8]AST*(pattern) [9]AST*(guard) 声明侧(.vpi):`class AST` 7 字段 + `class MatchCase(AST)` 2 字段 ⇒ 表 9 项、 children 表内下标 6 ⇒ 正确 base=1 ⇒ 正确偏移 7。而失败现场是 6 ⇒ **base 被算成 0** (表 10 项触发 `_Delta==0 ⇒ base=0`,或取值处实例与类键实例不同源)—— 只看 `偏移=6` 无法区分,故把两侧读数打进报文。 - `AssignHandle._DescribeMemberSlot` 增可选 `StructHint`:base 与失败现场**同源** (不传时行为与改前逐字一致)。 - `HandlesExprBuiltin._HandleLenLlvm` 的 len 分派失败路径(仅该路径)追加上述明细: `class_members[类] 项数/base/下标/声明槽类型`、**完整名序**、 `布局 struct name+元素数+elements[offset]`、`structs[类] 与布局是否同一对象`。 明细生成失败只追加一行说明,绝不掩盖原始 TypeError。 静态核验:py_compile 通过;全仓 symtable 未定义名扫描 0 条。
1277 lines
75 KiB
Python
1277 lines
75 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
|
||
import vast as ast
|
||
import llvmlite.ir as ir
|
||
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
|
||
from lib.core.VLogger import get_logger as _vlog
|
||
from lib.constants.config import mode as _config_mode
|
||
from lib.includes.t import CTypeRegistry
|
||
from lib.includes import t as _t
|
||
from lib.core.SymbolUtils import IsTModule
|
||
|
||
|
||
# len() 参数形态不支持时的共享提示语(与 TransPyV App/lib/core/Gen/Call/
|
||
# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。
|
||
_LEN_SUPPORTED_HINT: str = (
|
||
"len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;"
|
||
"非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度,"
|
||
"或改用 t.CPtr[元素] 并显式维护元素计数"
|
||
)
|
||
|
||
|
||
class ExprBuiltinHandle(BaseHandle):
|
||
_C_LIB_FUNCS: dict[str, object] = {
|
||
'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
|
||
'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
|
||
'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]),
|
||
'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
|
||
'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
|
||
'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
|
||
'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
|
||
'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
|
||
'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
|
||
'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]),
|
||
'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]),
|
||
'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]),
|
||
'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]),
|
||
'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
|
||
'abs': lambda: (ir.IntType(32), [ir.IntType(32)]),
|
||
'labs': lambda: (ir.IntType(64), [ir.IntType(64)]),
|
||
}
|
||
|
||
def _HandleBuiltinCallLlvm(self, Node: ast.Call) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if not isinstance(Node.func, ast.Name):
|
||
return None
|
||
FuncName: str = Node.func.id
|
||
|
||
if FuncName == 'print':
|
||
return self._HandlePrintLlvm(Node)
|
||
elif FuncName == 'len':
|
||
if Node.args:
|
||
return self._HandleLenLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'sizeof':
|
||
if Node.args:
|
||
return self._HandleSizeofLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'abs':
|
||
if Node.args:
|
||
return self._HandleAbsLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'arg':
|
||
return self._HandleArgLlvm(Node.args)
|
||
elif FuncName == 'bool':
|
||
if Node.args:
|
||
Val = self.HandleExprLlvm(Node.args[0])
|
||
if Val:
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type.width == 1:
|
||
return Val
|
||
return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result")
|
||
elif isinstance(Val.type, ir.PointerType):
|
||
return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result")
|
||
return Val
|
||
return ir.Constant(ir.IntType(1), 0)
|
||
elif FuncName == 'int':
|
||
if Node.args:
|
||
Val = self.HandleExprLlvm(Node.args[0])
|
||
if Val:
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type.width < 32:
|
||
return Gen.builder.zext(Val, ir.IntType(32), name="cast_int")
|
||
elif Val.type.width > 32:
|
||
return Gen.builder.trunc(Val, ir.IntType(32), name="trunc_int")
|
||
return Val
|
||
elif isinstance(Val.type, ir.PointerType):
|
||
if isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8:
|
||
result = self._HandleAtoiLlvm(Val)
|
||
if result:
|
||
return result
|
||
elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)):
|
||
return Gen.builder.fptosi(Val, ir.IntType(32), name="float_to_int")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'char':
|
||
# char(x) → i8(与 int()/bool()/float()/double() 并列的内建标量强转;
|
||
# char = t.CChar = i8)
|
||
if Node.args:
|
||
Val = self.HandleExprLlvm(Node.args[0])
|
||
if Val:
|
||
t8_char: ir.Type = ir.IntType(8)
|
||
if isinstance(Val.type, ir.IntType):
|
||
if Val.type.width < 8:
|
||
return Gen.builder.zext(Val, t8_char, name="zext_char")
|
||
elif Val.type.width > 8:
|
||
return Gen.builder.trunc(Val, t8_char, name="trunc_char")
|
||
return Val
|
||
elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)):
|
||
return Gen.builder.fptosi(Val, t8_char, name="float_to_char")
|
||
return ir.Constant(ir.IntType(8), 0)
|
||
elif FuncName == 'float' or FuncName == 'double':
|
||
if Node.args:
|
||
Val = self.HandleExprLlvm(Node.args[0])
|
||
if Val:
|
||
target_type = ir.DoubleType() if FuncName == 'double' else ir.FloatType()
|
||
if isinstance(Val.type, (ir.FloatType, ir.DoubleType)):
|
||
if Val.type != target_type:
|
||
if isinstance(target_type, ir.DoubleType):
|
||
return Gen.builder.fpext(Val, target_type, name="fpext_double")
|
||
return Gen.builder.fptrunc(Val, target_type, name="fptrunc_float")
|
||
return Val
|
||
elif isinstance(Val.type, ir.IntType):
|
||
if Val.type.width == 1:
|
||
return Gen.builder.uitofp(Val, target_type, name="uitofp")
|
||
return Gen.builder.sitofp(Val, target_type, name="sitofp")
|
||
return ir.Constant(ir.DoubleType() if FuncName == 'double' else ir.FloatType(), 0.0)
|
||
elif FuncName == 'min':
|
||
if len(Node.args) >= 2:
|
||
a = self.HandleExprLlvm(Node.args[0])
|
||
b = self.HandleExprLlvm(Node.args[1])
|
||
if a and b:
|
||
is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType))
|
||
if is_float:
|
||
fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="min_int2float")
|
||
fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="min_int2float")
|
||
cmp = Gen.builder.fcmp_ordered('olt', fa, fb, name="min_cmp")
|
||
return Gen.builder.select(cmp, fa, fb, name="min_val")
|
||
else:
|
||
cmp = Gen.builder.icmp_signed('<', a, b, name="min_cmp")
|
||
return Gen.builder.select(cmp, a, b, name="min_val")
|
||
elif len(Node.args) == 1:
|
||
return self.HandleExprLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'max':
|
||
if len(Node.args) >= 2:
|
||
a = self.HandleExprLlvm(Node.args[0])
|
||
b = self.HandleExprLlvm(Node.args[1])
|
||
if a and b:
|
||
is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType))
|
||
if is_float:
|
||
fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="max_int2float")
|
||
fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="max_int2float")
|
||
cmp = Gen.builder.fcmp_ordered('ogt', fa, fb, name="max_cmp")
|
||
return Gen.builder.select(cmp, fa, fb, name="max_val")
|
||
else:
|
||
cmp = Gen.builder.icmp_signed('>', a, b, name="max_cmp")
|
||
return Gen.builder.select(cmp, a, b, name="max_val")
|
||
elif len(Node.args) == 1:
|
||
return self.HandleExprLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'str':
|
||
if Node.args:
|
||
arg_node = Node.args[0]
|
||
arg_val = self.HandleExprLlvm(arg_node)
|
||
if arg_val:
|
||
if isinstance(arg_val.type, ir.PointerType):
|
||
ClassName = self.Trans.ExprHandler._get_var_class(arg_node, Gen)
|
||
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
|
||
if isinstance(arg_val.type, ir.PointerType) and isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8:
|
||
if ClassName in Gen.structs:
|
||
arg_val = Gen.builder.bitcast(arg_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_str")
|
||
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [arg_val], name=f"call_{ClassName}.__str__")
|
||
return str_val
|
||
return arg_val
|
||
# str 是类型名(= t.CChar* = i8*),str(x) ≡ (str)(x) 类型转换:
|
||
# 整型(u64/sizet 地址、char 码)→ inttoptr,与 TPV
|
||
# CallGenDispatch._tc_h3_stdarg_cast 的 str 分支同语义
|
||
# (CallGenCtor._translate_t_type_cast 的「整数 → 指针: inttoptr」)。
|
||
# 曾用 _EmitStringFromValue 把整数 snprintf 进栈缓冲并返回栈地址
|
||
# → free/写入即 bad-free(StubMergerBuild.vp 崩溃根因)。
|
||
if isinstance(arg_val.type, ir.IntType):
|
||
return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="str_int2ptr")
|
||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||
elif FuncName == 'bytes':
|
||
# bytes() — 类型转换为 i8* 指针 (等同于 t.CPtr / str)
|
||
# bytes(int_addr) → inttoptr; bytes(ptr) → bitcast
|
||
if Node.args:
|
||
arg_val = self.HandleExprLlvm(Node.args[0])
|
||
if arg_val:
|
||
if isinstance(arg_val.type, ir.PointerType):
|
||
if isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8:
|
||
return arg_val
|
||
return Gen.builder.bitcast(arg_val, ir.IntType(8).as_pointer(), name="bytes_cast")
|
||
if isinstance(arg_val.type, ir.IntType):
|
||
return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="bytes_int2ptr")
|
||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||
elif FuncName == 'input':
|
||
return self._HandleInputLlvm(Node)
|
||
elif FuncName == 'isinstance':
|
||
# isinstance(obj, Type) — 运行时 vtable 继承链比较(2026-08-10)
|
||
# CVTable 对象头部 vtable 指针唯一标识类;isinstance = 对象 vtable
|
||
# ∈ 目标类及其 MRO 祖先的 vtable 集合(运行时真多态判断)。
|
||
# NoVTable/基础类型/无法解析时回退编译时静态判断。
|
||
return self._HandleIsinstanceLlvm(Node)
|
||
elif FuncName == 'ord':
|
||
if Node.args:
|
||
return self._HandleOrdLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
elif FuncName == 'chr':
|
||
if Node.args:
|
||
return self._HandleChrLlvm(Node.args[0])
|
||
return ir.Constant(ir.IntType(8), 0)
|
||
elif FuncName == 'malloc':
|
||
return self._HandleMallocLlvm(Node.args)
|
||
elif FuncName == 'realloc':
|
||
return self._HandleReallocLlvm(Node.args)
|
||
elif FuncName == 'free':
|
||
return self._HandleFreeLlvm(Node.args)
|
||
elif FuncName == 'memcpy':
|
||
return self._HandleMemcpyLlvm(Node.args)
|
||
elif FuncName == 'memset':
|
||
return self._HandleMemsetLlvm(Node.args)
|
||
elif FuncName == 'va_start':
|
||
return self._HandleVaStartLlvm(Node.args)
|
||
elif FuncName == 'va_end':
|
||
return self._HandleVaEndLlvm(Node.args)
|
||
elif FuncName == 'va_arg':
|
||
return self._HandleVaArgLlvm(Node.args)
|
||
elif FuncName == 'dir':
|
||
return self._HandleDirLlvm(Node)
|
||
elif FuncName == 'type':
|
||
return self._HandleTypeLlvm(Node)
|
||
return None
|
||
|
||
# ============================================================
|
||
# isinstance - 运行时 vtable 子类集合比较(2026-08-10)
|
||
#
|
||
# CVTable 对象头部 vtable 指针唯一标识对象**实际类**(创建对象时写入
|
||
# 实际类的 vtable)。isinstance(obj, Cls) 语义 = Cls 是实际类的祖先
|
||
# (实际类 == Cls 或继承自 Cls)。运行时只能拿到实际类 vtable 地址,
|
||
# 无法向上推导祖先链,因此在编译期沿 class_parents 反向 BFS 枚举
|
||
# Cls 的全部已知子类(含自身),比较 obj->vtable ∈ {Cls 子类 vtable}。
|
||
# class_parents 由 Phase1 声明阶段对全部可达文件填充,子类集合完整。
|
||
# NoVTable / 基础类型 / 无 vtable 可解析 → 回退编译时静态判断。
|
||
# ============================================================
|
||
def _HandleIsinstanceLlvm(self, Node: ast.Call) -> ir.Value:
|
||
"""isinstance(obj, Cls):运行时 vtable 继承链比较(元组 Cls 支持 or 合并)"""
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if len(Node.args) < 2:
|
||
return ir.Constant(ir.IntType(1), 0)
|
||
ObjVal: ir.Value | None = self.HandleExprLlvm(Node.args[0])
|
||
if ObjVal is None:
|
||
return ir.Constant(ir.IntType(1), 0)
|
||
ClsNames: list[str] = self._ExtractIsinstanceClassNames(Node.args[1])
|
||
if not ClsNames:
|
||
return ir.Constant(ir.IntType(1), 0)
|
||
Res: ir.Value | None = None
|
||
for CN in ClsNames:
|
||
R: ir.Value = self._IsinstanceOne(Gen, ObjVal, CN)
|
||
if Res is None:
|
||
Res = R
|
||
else:
|
||
Res = Gen.builder.or_(Res, R, name="isinstance_or")
|
||
return Res if Res is not None else ir.Constant(ir.IntType(1), 0)
|
||
|
||
def _ExtractIsinstanceClassNames(self, TypeNode: ast.expr) -> list[str]:
|
||
"""从 isinstance 第二参数提取类名列表(支持单类 / (A, B) 元组 / 泛型特化)"""
|
||
if isinstance(TypeNode, ast.Tuple):
|
||
names: list[str] = []
|
||
for elt in TypeNode.elts:
|
||
names.extend(self._ExtractIsinstanceClassNames(elt))
|
||
return names
|
||
if isinstance(TypeNode, ast.Name):
|
||
return [TypeNode.id]
|
||
if isinstance(TypeNode, ast.Attribute):
|
||
return [TypeNode.attr]
|
||
if isinstance(TypeNode, ast.Subscript):
|
||
spec = self.Trans.ExprCallHandle._ResolveGenericSlice(TypeNode)
|
||
if spec:
|
||
return [spec]
|
||
return []
|
||
|
||
def _IsinstanceOne(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value:
|
||
"""单类判断:优先运行时 vtable 比较,失败回退编译时静态判断"""
|
||
VtableAddrs: list = self._CollectSubclassVtables(Gen, ClassName)
|
||
if VtableAddrs:
|
||
try:
|
||
return self._EmitVtableCompare(Gen, ObjVal, VtableAddrs)
|
||
except Exception:
|
||
pass
|
||
return self._IsinstanceCompileTime(Gen, ObjVal, ClassName)
|
||
|
||
def _CollectSubclassVtables(self, Gen, ClassName: str) -> list:
|
||
"""收集目标类及**所有已知子类**的 vtable 全局地址(沿 class_parents 反向 BFS)。
|
||
|
||
isinstance(obj, Cls) 的正确语义:obj 头部 vtable 标识对象实际类 C,
|
||
判断 Cls 是否 ∈ C 的祖先链。运行时无法从 C_vtable 向上推导祖先,
|
||
因此在编译期反向枚举 Cls 的子树(含 Cls 自身)的 vtable 地址,
|
||
运行时比较 obj->vtable ∈ 该集合(C 是 Cls 子类 → 命中)。
|
||
"""
|
||
addrs: list = []
|
||
# 反向子类表:父类名 -> [子类名...](class_parents 由 Phase1 全量填充)
|
||
children: dict[str, list[str]] = {}
|
||
for cn, parents in getattr(Gen, 'class_parents', {}).items():
|
||
for p in parents:
|
||
children.setdefault(p, []).append(cn)
|
||
queue: list[str] = [ClassName]
|
||
seen: set[str] = set()
|
||
while queue:
|
||
cn: str = queue.pop(0)
|
||
if cn in seen:
|
||
continue
|
||
seen.add(cn)
|
||
vt = self._ResolveVtableAddr(Gen, cn)
|
||
if vt is not None:
|
||
addrs.append(vt)
|
||
for ch in children.get(cn, []):
|
||
if ch not in seen:
|
||
queue.append(ch)
|
||
return addrs
|
||
|
||
def _ResolveVtableAddr(self, Gen, ClassName: str):
|
||
"""解析类 ClassName 的 vtable 全局地址(本地 Vtables / 跨模块外部引用)。
|
||
|
||
仅对 CVTable 类返回地址;NoVTable 类(class_vtable 与
|
||
_cross_module_vtable_classes 均不含)没有 vtable 全局变量,
|
||
返回 None 让调用方回退编译时静态判断(避免生成对不存在的
|
||
sha1.Class_Vtable 符号的引用导致链接失败)。
|
||
"""
|
||
has_vtable: bool = (ClassName in Gen.class_vtable
|
||
or ClassName in Gen._cross_module_vtable_classes)
|
||
if not has_vtable:
|
||
return None
|
||
vt = Gen.Vtables.get(ClassName) if hasattr(Gen, 'Vtables') else None
|
||
if vt is not None:
|
||
return vt
|
||
sha1_map: dict = getattr(Gen, 'class_sha1_map', {})
|
||
sha1 = sha1_map.get(ClassName)
|
||
if not sha1:
|
||
return None
|
||
vname: str = f"{sha1}.{ClassName}_Vtable"
|
||
if vname in Gen.module.globals:
|
||
return Gen.module.globals[vname]
|
||
try:
|
||
# 外部引用声明为 i8*(而非 [N x i8*]),避免与定义模块
|
||
# 不同长度的 array 类型在链接时 type mismatch
|
||
ext = ir.GlobalVariable(Gen.module, ir.PointerType(ir.IntType(8)), name=vname)
|
||
ext.linkage = 'external'
|
||
return ext
|
||
except Exception:
|
||
return None
|
||
|
||
def _EmitVtableCompare(self, Gen, ObjVal: ir.Value, VtableAddrs: list) -> ir.Value:
|
||
"""运行时:读对象头部 vtable 指针,与集合中每个 vtable 地址比较"""
|
||
ObjPtr: ir.Value = ObjVal
|
||
if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType):
|
||
# 二级指针(变量 alloca / 联合 Class|t.CPtr)→ 先 load 出对象指针
|
||
ObjPtr = Gen._load(ObjVal, name="isinstance_load")
|
||
if not isinstance(ObjPtr.type, ir.PointerType):
|
||
return self._IsinstanceCompileTime(Gen, ObjVal, '')
|
||
ObjAsVt: ir.Value = Gen.builder.bitcast(ObjPtr, ir.PointerType(ir.PointerType(ir.IntType(8))), name="isinstance_cast")
|
||
VtPtr: ir.Value = Gen._load(ObjAsVt, name="isinstance_vt")
|
||
Res: ir.Value | None = None
|
||
for addr in VtableAddrs:
|
||
if addr.type == ir.PointerType(ir.IntType(8)):
|
||
AddrI8: ir.Value = addr
|
||
else:
|
||
AddrI8 = Gen.builder.bitcast(addr, ir.PointerType(ir.IntType(8)), name="vt_addr")
|
||
Cmp: ir.Value = Gen.builder.icmp_unsigned('==', VtPtr, AddrI8, name="isinstance_cmp")
|
||
if Res is None:
|
||
Res = Cmp
|
||
else:
|
||
Res = Gen.builder.or_(Res, Cmp, name="isinstance_or")
|
||
return Res if Res is not None else ir.Constant(ir.IntType(1), 0)
|
||
|
||
def _IsinstanceCompileTime(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value:
|
||
"""编译时静态判断:obj 静态类型 == ClassName 或其 MRO 后代 → 1,否则 0"""
|
||
if ClassName:
|
||
ObjType: ir.Type = ObjVal.type
|
||
if isinstance(ObjType, ir.PointerType):
|
||
ObjType = ObjType.pointee
|
||
if isinstance(ObjType, ir.IdentifiedStructType):
|
||
ShortName: str = Gen._extract_short_name(ObjType.name)
|
||
mro: list = Gen._get_mro_chain(ClassName) if hasattr(Gen, '_get_mro_chain') else [ClassName]
|
||
if ShortName == ClassName or ShortName in mro:
|
||
return ir.Constant(ir.IntType(1), 1)
|
||
# obj 是 ClassName 的子类(obj 的 MRO 含 ClassName)
|
||
obj_mro: list = Gen._get_mro_chain(ShortName) if hasattr(Gen, '_get_mro_chain') else []
|
||
if ClassName in obj_mro:
|
||
return ir.Constant(ir.IntType(1), 1)
|
||
# 无法判断 → 保守 false
|
||
return ir.Constant(ir.IntType(1), 0)
|
||
|
||
def _HandlePrintLlvm(self, Node: ast.Call) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
|
||
args: list[ast.expr] = Node.args
|
||
end_arg: ast.expr | None = None
|
||
sep_arg: ast.expr | None = None
|
||
if Node.keywords:
|
||
for kw in Node.keywords:
|
||
if kw.arg == 'end':
|
||
end_arg = kw.value
|
||
elif kw.arg == 'sep':
|
||
sep_arg = kw.value
|
||
sep_str: str = ' '
|
||
if sep_arg and isinstance(sep_arg, ast.Constant) and isinstance(sep_arg.value, str):
|
||
sep_str = sep_arg.value
|
||
end_str: str = '\n'
|
||
if end_arg:
|
||
if isinstance(end_arg, ast.Constant) and isinstance(end_arg.value, str):
|
||
end_str = end_arg.value
|
||
elif isinstance(end_arg, ast.Constant) and end_arg.value is None:
|
||
end_str = ''
|
||
if not args:
|
||
if end_str:
|
||
nl_fmt = Gen.emit_constant(end_str, 'string')
|
||
Gen.builder.call(printf_func, [nl_fmt], name="print_nl")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
has_fstring = any(isinstance(arg, ast.JoinedStr) for arg in args)
|
||
if has_fstring:
|
||
return self._HandlePrintWithFString(Node, printf_func, args, sep_str, end_str)
|
||
fmt_parts = []
|
||
fmt_args = []
|
||
for i, arg in enumerate(args):
|
||
if i > 0:
|
||
fmt_parts.append(sep_str)
|
||
val = self.HandleExprLlvm(arg)
|
||
if not val:
|
||
fmt_parts.append('(null)')
|
||
continue
|
||
if isinstance(val, ir.Constant) and isinstance(val.type, ir.PointerType) and val.constant is None:
|
||
fmt_parts.append('nil')
|
||
continue
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee = val.type.pointee
|
||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||
fmt_parts.append('%s')
|
||
fmt_args.append(val)
|
||
elif isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
|
||
elem_ptr = Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="print_arr_to_ptr")
|
||
fmt_parts.append('%s')
|
||
fmt_args.append(elem_ptr)
|
||
elif isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen)
|
||
if not ClassName and isinstance(arg, ast.Name):
|
||
ClassName = Gen.var_struct_class.get(arg.id)
|
||
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
|
||
if isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8:
|
||
if ClassName in Gen.structs:
|
||
val = Gen.builder.bitcast(val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
||
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [val], name=f"call_{ClassName}.__str__")
|
||
fmt_parts.append('%s')
|
||
fmt_args.append(str_val)
|
||
else:
|
||
int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int")
|
||
fmt_parts.append('0x%llx')
|
||
fmt_args.append(int_val)
|
||
else:
|
||
int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int")
|
||
fmt_parts.append('%lld')
|
||
fmt_args.append(int_val)
|
||
elif isinstance(val.type, ir.IntType):
|
||
is_unsigned = self._is_val_unsigned(arg, val)
|
||
if val.type.width == 1:
|
||
fmt_parts.append('%d')
|
||
val = Gen.builder.zext(val, ir.IntType(32), name="zext_bool_print")
|
||
elif val.type.width == 8:
|
||
if self._is_char_type(arg):
|
||
char_var = Gen._allocaEntry(ir.IntType(8), name="print_char_buf")
|
||
Gen.builder.store(val, char_var)
|
||
fmt_parts.append('%c')
|
||
fmt_args.append(char_var)
|
||
continue
|
||
fmt_parts.append('%d')
|
||
if is_unsigned:
|
||
val = Gen.builder.zext(val, ir.IntType(32), name="zext_i8_print")
|
||
else:
|
||
val = Gen.builder.sext(val, ir.IntType(32), name="sext_i8_print")
|
||
elif val.type.width == 16:
|
||
fmt_parts.append('%u' if is_unsigned else '%d')
|
||
if is_unsigned:
|
||
val = Gen.builder.zext(val, ir.IntType(32), name="zext_i16_print")
|
||
else:
|
||
val = Gen.builder.sext(val, ir.IntType(32), name="sext_i16_print")
|
||
elif val.type.width == 32:
|
||
fmt_parts.append('%u' if is_unsigned else '%d')
|
||
elif val.type.width == 64:
|
||
fmt_parts.append('%llu' if is_unsigned else '%lld')
|
||
else:
|
||
fmt_parts.append('%d')
|
||
val = Gen.builder.zext(val, ir.IntType(32), name="zext_int_print")
|
||
fmt_args.append(val)
|
||
elif isinstance(val.type, ir.FloatType):
|
||
fmt_parts.append('%f')
|
||
fmt_args.append(Gen.builder.fpext(val, ir.DoubleType(), name="fpext_printf_float"))
|
||
elif isinstance(val.type, ir.DoubleType):
|
||
fmt_parts.append('%f')
|
||
fmt_args.append(val)
|
||
else:
|
||
fmt_parts.append('(unknown)')
|
||
fmt_parts.append(end_str)
|
||
fmt_str = ''.join(fmt_parts)
|
||
fmt_ptr = Gen._create_string_global(fmt_str)
|
||
call_args = [fmt_ptr] + fmt_args
|
||
return Gen.builder.call(printf_func, call_args, name="print_call")
|
||
|
||
def _HandlePrintWithFString(self, Node: ast.Call, printf_func: ir.Function, args: list[ast.expr], sep_str: str, end_str: str) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
for i, arg in enumerate(args):
|
||
if i > 0:
|
||
sep_const = Gen.emit_constant(sep_str, 'string')
|
||
Gen.builder.call(printf_func, [sep_const], name="print_sep")
|
||
if isinstance(arg, ast.JoinedStr):
|
||
str_val = self.HandleExprLlvm(arg)
|
||
if str_val:
|
||
Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None)
|
||
else:
|
||
ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen)
|
||
if ClassName and Gen._has_function(f'{ClassName}.__str__'):
|
||
obj_val = self.HandleExprLlvm(arg)
|
||
if obj_val:
|
||
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
|
||
if ClassName in Gen.structs:
|
||
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
|
||
str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [obj_val], name=f"call_{ClassName}.__str__")
|
||
Gen._RegisterTempPtr(str_val)
|
||
Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None)
|
||
else:
|
||
val = self.HandleExprLlvm(arg)
|
||
if val:
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee = val.type.pointee
|
||
if not (isinstance(pointee, ir.IntType) and pointee.width == 8):
|
||
try:
|
||
val = Gen._load(val, name="print_Load")
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||
is_u = Gen._check_node_unsigned(arg)
|
||
if not is_u and id(val) in Gen._unsigned_results:
|
||
is_u = True
|
||
Gen._emit_printf([val], unsigned_flags=[is_u], sep=None, end=None)
|
||
if end_str:
|
||
end_const = Gen.emit_constant(end_str, 'string')
|
||
Gen.builder.call(printf_func, [end_const], name="print_end")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _is_char_type(self, arg_node: ast.AST) -> bool:
|
||
if isinstance(arg_node, ast.Call):
|
||
if isinstance(arg_node.func, ast.Name):
|
||
return arg_node.func.id in ('CChar', 'CUInt8T', 'CInt8T')
|
||
if isinstance(arg_node.func, ast.Attribute):
|
||
return arg_node.func.attr in ('CChar', 'CUInt8T', 'CInt8T')
|
||
return False
|
||
|
||
def _is_val_unsigned(self, arg_node: ast.AST, val: ir.Value) -> bool:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if isinstance(arg_node, ast.Name):
|
||
var_name: str = arg_node.id
|
||
signedness: object = Gen.var_signedness.get(var_name)
|
||
if signedness:
|
||
if isinstance(signedness, bool):
|
||
return signedness
|
||
return 'unsigned' in signedness
|
||
if isinstance(val.type, ir.IntType):
|
||
return False
|
||
return False
|
||
|
||
def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None:
|
||
if func_name not in self._C_LIB_FUNCS:
|
||
return None
|
||
ret_type: ir.Type
|
||
param_types: list[ir.Type]
|
||
ret_type, param_types = self._C_LIB_FUNCS[func_name]()
|
||
func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types)
|
||
# C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号:
|
||
# 裸建会在第二次抛 llvmlite DuplicatedNameError。
|
||
return Gen._CreateOrReuseFuncDecl(func_name, func_type)
|
||
|
||
def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin,
|
||
val: ir.Value | None, reason: str) -> None:
|
||
"""len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 TransPyV 同口径)。
|
||
|
||
规则(对齐 TransPyV App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支):
|
||
定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen;
|
||
带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。
|
||
非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会
|
||
静默返回常量 0(或回落 strlen)从而掩盖错误,这里改为响亮失败。
|
||
报文含 行:列 + 源码片段 + 期望形态 + 正确做法提示。
|
||
"""
|
||
raw_lineno: int = getattr(arg_node, 'lineno', 0) or 0
|
||
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||
col: int = getattr(arg_node, 'col_offset', -1)
|
||
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||
if _line_offset > 0 and lineno > _line_offset:
|
||
lineno -= _line_offset
|
||
code_line: str = ''
|
||
if lineno > 0 and hasattr(Gen, '_get_source_line'):
|
||
try:
|
||
code_line = (Gen._get_source_line(lineno) or '').strip()
|
||
except Exception:
|
||
code_line = ''
|
||
loc: str = src_file
|
||
if lineno > 0:
|
||
loc += f":{lineno}"
|
||
if col >= 0:
|
||
loc += f":{col}"
|
||
actual: str = str(val.type) if val is not None else '<未知>'
|
||
msg: str = (f"[fast-fail] {reason}。{_LEN_SUPPORTED_HINT}。"
|
||
f"实际类型 '{actual}'。位置 {loc}"
|
||
+ (f",源码: {code_line}" if code_line else ''))
|
||
raise TypeError(msg)
|
||
|
||
def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
# len(obj.attr):obj 可为 self 或任意局部变量(VpiReader 的
|
||
# len(simp.names) 即非 self 场景)。字段静态类型若退化为 i8*
|
||
# (跨模块 stub 的 rawptr[list[...]],stub 不注入字段类型),通用
|
||
# strlen 分支会把 list 容器头当字符串扫 → 垃圾长度 → 越界 get →
|
||
# 读任意地址(VpiReader.vp _ParseVpiIntoModuleImpl 读 -1 根因)。
|
||
# 此处按字段的 class_member_element_class 侧信道(记录 list 特化名)
|
||
# 分派到 list.__len__;attr__len 计数成员特例(TypeGenCType)保留。
|
||
if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name):
|
||
obj_name = arg_node.value.id
|
||
attr_name = arg_node.attr
|
||
len_member = f'{attr_name}__len'
|
||
current_class = self.Trans._CurrentCpythonObjectClass if obj_name == 'self' else None
|
||
if current_class is None:
|
||
_vsc: dict = Gen.var_struct_class if Gen.var_struct_class else {}
|
||
_gsc: dict = getattr(Gen, 'global_struct_class', {}) or {}
|
||
current_class = _vsc.get(obj_name) or _gsc.get(obj_name)
|
||
# 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如
|
||
# 'a351387236c6ce1c.TypeGenCType'),而 class_members /
|
||
# class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离
|
||
# 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss,
|
||
# 整个 obj.attr 分支(含 list.__len__ 修复)被跳过,fall through
|
||
# 到 strlen(stdint.vp 0xC0000005 崩溃根因)。
|
||
bare_cls: str | None = current_class
|
||
if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.':
|
||
bare_cls = bare_cls[17:]
|
||
if bare_cls and bare_cls in Gen.class_members:
|
||
for m_name, m_type in Gen.class_members[bare_cls]:
|
||
if m_name == len_member:
|
||
self_val = Gen._get_var_ptr(obj_name)
|
||
if self_val:
|
||
self_ptr = Gen._load(self_val, name="obj_for_len")
|
||
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):接收者的
|
||
# pointee 可能是**另一份 opaque 同名实例**,GEP 需要在编译期算出
|
||
# 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。
|
||
_STl: Any
|
||
self_ptr, _STl = self.Trans.ExprAttrHandle._ConcretizeGepBase(
|
||
Gen, self_ptr, bare_cls, f"cast_{bare_cls}")
|
||
offset = Gen._get_member_offset(len_member, bare_cls, _STl)
|
||
len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{len_member}_ptr")
|
||
return Gen._load(len_ptr, name=f"{obj_name}_{len_member}")
|
||
# 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段
|
||
# 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through
|
||
# 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get
|
||
# → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class
|
||
# 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。
|
||
field_cls: str | None = None
|
||
_elem_map: dict = getattr(Gen, 'class_member_element_class', {})
|
||
for _mro_cls in Gen._get_mro_chain(bare_cls):
|
||
# class_member_element_class 的 key 是裸类名(如 'TypeGenBase'),
|
||
# _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。
|
||
_mro_key: str = _mro_cls
|
||
if len(_mro_cls) > 17 and _mro_cls[16] == '.':
|
||
_mro_key = _mro_cls[17:]
|
||
_sub: dict = _elem_map.get(_mro_key, {})
|
||
if attr_name in _sub:
|
||
field_cls = _sub[attr_name]
|
||
break
|
||
if field_cls and (field_cls == 'list' or field_cls.startswith('list[')):
|
||
self_val = Gen._get_var_ptr(obj_name)
|
||
if self_val:
|
||
self_ptr = Gen._load(self_val, name="obj_for_len")
|
||
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase)
|
||
_STl2: Any
|
||
self_ptr, _STl2 = self.Trans.ExprAttrHandle._ConcretizeGepBase(
|
||
Gen, self_ptr, bare_cls, f"cast_{bare_cls}")
|
||
offset = Gen._get_member_offset(attr_name, bare_cls, _STl2)
|
||
if offset is not None:
|
||
list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr")
|
||
list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}")
|
||
# 诊断增强(不改变任何成功路径):字段身份(class_member_element_class)
|
||
# 说是 list 容器,但按 _get_member_offset 取出的字段静态类型不是
|
||
# list 特化接收者(典型为 i32,即偏移落在 lineno/col_offset 等 i32 字段)
|
||
# 时,llvmlite 只会抛出无上下文的
|
||
# "Type of #1 arg mismatch: %"…list[…]"* != i32"。
|
||
# 此处仅在"该调用本就会抛 TypeError 终止"时生效,把类/字段/偏移/
|
||
# 接收者类型一并报出,用于判定 class_members 偏移与 struct 布局不一致。
|
||
try:
|
||
result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen)
|
||
except TypeError as _e_len:
|
||
# 来源明细(声明侧 class_members 名序/项数 vs 布局侧 elements)
|
||
# 追加进报文:本处失败的本质是"偏移算错",只看 偏移=6 无法区分
|
||
# 是"表多一项(→ base 被 delta==0 判 0)"还是"取值处实例与类键实例
|
||
# 不同源"。判据/读数见 AssignHandle._DescribeMemberSlot。
|
||
_extra_len: str = ''
|
||
try:
|
||
_extra_len = self.Trans.AssignHandler._DescribeMemberSlot(
|
||
Gen, bare_cls, attr_name, offset, self_ptr, _STl2)
|
||
except Exception as _ea:
|
||
_extra_len = f"\n (来源明细生成失败: {type(_ea).__name__}: {_ea})"
|
||
raise TypeError(
|
||
f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:"
|
||
f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};"
|
||
f"(成员偏移与 struct 布局不一致)原错误: {_e_len}{_extra_len}")
|
||
if result is not None:
|
||
return result
|
||
val = self.HandleExprLlvm(arg_node)
|
||
if not val:
|
||
return None
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee = val.type.pointee
|
||
if isinstance(pointee, ir.PointerType):
|
||
val = Gen._load(val, name="len_deref")
|
||
pointee = val.type.pointee
|
||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
# struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变);
|
||
# 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 TPV 同口径)。
|
||
_matched_cn: str | None = None
|
||
for CN, ST in Gen.structs.items():
|
||
if pointee == ST:
|
||
_matched_cn = CN
|
||
break
|
||
if _matched_cn is not None:
|
||
result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen)
|
||
if result is not None:
|
||
return result
|
||
self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法")
|
||
else:
|
||
# 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。
|
||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||
if isinstance(pointee, ir.ArrayType):
|
||
return ir.Constant(ir.IntType(64), pointee.count)
|
||
# i8*(含 str/bytes 指针形态)→ strlen(保持不变)。
|
||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||
if val.type != ir.PointerType(ir.IntType(8)):
|
||
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
|
||
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
|
||
strlen_func = None
|
||
if 'strlen' in Gen.functions:
|
||
existing = Gen.functions['strlen']
|
||
if existing.ftype == correct_strlen_type:
|
||
strlen_func = existing
|
||
if not strlen_func:
|
||
try:
|
||
strlen_func = Gen._CreateOrReuseFuncDecl('strlen', correct_strlen_type)
|
||
Gen.functions['strlen'] = strlen_func
|
||
except Exception as _e:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
|
||
if isinstance(_e, _FFE):
|
||
raise
|
||
if _config_mode == "strict":
|
||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||
if strlen_func:
|
||
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
|
||
return result
|
||
return None
|
||
# 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast
|
||
# (旧实现静默返回常量 0 或回落 strlen,与 TPV 口径不符)。
|
||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||
elif isinstance(val.type, ir.ArrayType):
|
||
return ir.Constant(ir.IntType(64), val.type.count)
|
||
# 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。
|
||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||
|
||
def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None:
|
||
"""递归解析嵌套的 opaque struct,从 stub 文件中加载其定义"""
|
||
if visited is None:
|
||
visited = set()
|
||
if isinstance(struct_type, ir.PointerType):
|
||
self._resolve_nested_opaque_structs(struct_type.pointee, Gen, visited)
|
||
return
|
||
if not isinstance(struct_type, ir.IdentifiedStructType):
|
||
return
|
||
if struct_type in visited:
|
||
return
|
||
visited.add(struct_type)
|
||
if struct_type.elements is None or len(struct_type.elements) == 0:
|
||
# 尝试从 stub 加载 opaque struct
|
||
try:
|
||
st_name = struct_type.name
|
||
except Exception:
|
||
st_name = None
|
||
if st_name:
|
||
# 提取短名称(去掉模块前缀)
|
||
if '.' in st_name:
|
||
short_name = st_name.split('.', 1)[1]
|
||
else:
|
||
short_name = st_name
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(short_name, Gen)
|
||
return
|
||
# 递归解析嵌套的 struct 成员
|
||
for elem in struct_type.elements:
|
||
self._resolve_nested_opaque_structs(elem, Gen, visited)
|
||
|
||
def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
type_name: str | None
|
||
if isinstance(Node, ast.Name):
|
||
type_name = Node.id
|
||
elif isinstance(Node, ast.Attribute):
|
||
type_name = Node.attr
|
||
else:
|
||
# 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])):
|
||
# 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。
|
||
raise ValueError(
|
||
f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}"
|
||
f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0"
|
||
)
|
||
|
||
if type_name not in Gen.structs:
|
||
# str/bytes 是指针类型 (char*/void*),sizeof = 指针大小
|
||
if type_name in ('str', 'bytes'):
|
||
return ir.Constant(ir.IntType(64), 8)
|
||
ctype_cls = CTypeRegistry.GetClassByName(type_name)
|
||
if ctype_cls is not None:
|
||
try:
|
||
ctype_inst = ctype_cls()
|
||
size_bits = getattr(ctype_inst, 'Size', 0) or 0
|
||
size_bytes = size_bits // 8
|
||
return ir.Constant(ir.IntType(64), size_bytes)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
||
resolved = CTypeRegistry.ResolveName(type_name)
|
||
if resolved:
|
||
ctype_cls, ptr_level = resolved
|
||
try:
|
||
ctype_inst = ctype_cls()
|
||
size_bits = getattr(ctype_inst, 'Size', 0) or 0
|
||
size_bytes = size_bits // 8
|
||
if ptr_level > 0:
|
||
size_bytes = 8
|
||
return ir.Constant(ir.IntType(64), size_bytes)
|
||
except Exception as _e:
|
||
if _config_mode == "strict":
|
||
raise
|
||
_vlog().warning(f"解析类型失败: {_e}", "Exception")
|
||
# 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。
|
||
raise ValueError(
|
||
f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析"
|
||
f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0"
|
||
)
|
||
|
||
struct_type = Gen.structs[type_name]
|
||
if isinstance(struct_type, ir.PointerType):
|
||
struct_type = struct_type.pointee
|
||
if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0):
|
||
self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen)
|
||
struct_type = Gen.structs.get(type_name, struct_type)
|
||
# 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确
|
||
self._resolve_nested_opaque_structs(struct_type, Gen)
|
||
# _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。
|
||
return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type))
|
||
|
||
def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
val: ir.Value | None = self.HandleExprLlvm(arg_node)
|
||
if not val:
|
||
return None
|
||
if isinstance(val.type, ir.PointerType):
|
||
pointee = val.type.pointee
|
||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
for CN, ST in Gen.structs.items():
|
||
if pointee == ST:
|
||
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen)
|
||
if result is not None:
|
||
return result
|
||
break
|
||
if isinstance(val.type, ir.IntType):
|
||
neg_val = Gen.builder.neg(val, name="abs_neg")
|
||
is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg")
|
||
return Gen.builder.select(is_neg, neg_val, val, name="abs_result")
|
||
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
|
||
zero = ir.Constant(val.type, 0.0)
|
||
neg_val = Gen.builder.fneg(val, name="fabs_neg")
|
||
is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg")
|
||
return Gen.builder.select(is_neg, neg_val, val, name="fabs_result")
|
||
return None
|
||
|
||
def _HandleDirLlvm(self, Node: ast.Call) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||
|
||
def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
type_str: str
|
||
if not Node.args:
|
||
type_str = "<class 'unknown' size=0 signed=false, ptr=false>"
|
||
else:
|
||
arg = Node.args[0]
|
||
enum_type_name = None
|
||
|
||
def _get_attr_path(node: ast.AST) -> str | None:
|
||
if isinstance(node, ast.Name):
|
||
return node.id
|
||
elif isinstance(node, ast.Attribute):
|
||
return f"{_get_attr_path(node.value)}.{node.attr}"
|
||
return None
|
||
|
||
if isinstance(arg, ast.Name):
|
||
arg_name = arg.id
|
||
Gen = self.Trans.LlvmGen
|
||
if arg_name in Gen.var_type_info:
|
||
type_info = Gen.var_type_info[arg_name]
|
||
if isinstance(type_info, dict) and type_info.get('type') == 'enum':
|
||
enum_type_name = type_info.get('name')
|
||
if not enum_type_name and self.Trans.SymbolTable.has(arg_name):
|
||
TypeInfo = self.Trans.SymbolTable[arg_name]
|
||
if TypeInfo.IsEnum:
|
||
enum_type_name = arg_name
|
||
elif isinstance(arg, ast.Attribute):
|
||
LastPart = None
|
||
enum_class_name = None
|
||
if isinstance(arg.value, ast.Name):
|
||
LastPart = arg.attr
|
||
if self.Trans.SymbolTable.has(LastPart):
|
||
AttrInfo = self.Trans.SymbolTable[LastPart]
|
||
if AttrInfo.IsEnumMember and AttrInfo.EnumName:
|
||
enum_type_name = AttrInfo.EnumName
|
||
elif isinstance(arg.value, ast.Attribute):
|
||
attr_path = _get_attr_path(arg)
|
||
if attr_path:
|
||
parts = attr_path.split('.')
|
||
if len(parts) >= 2:
|
||
enum_class_name = parts[-2]
|
||
LastPart = parts[-1]
|
||
qualified_name_dot = f"{enum_class_name}.{LastPart}"
|
||
qualified_name_under = f"{enum_class_name}_{LastPart}"
|
||
enum_member_found = None
|
||
for qname in (qualified_name_dot, qualified_name_under):
|
||
if self.Trans.SymbolTable.has(qname):
|
||
info = self.Trans.SymbolTable[qname]
|
||
if info.IsEnumMember and info.EnumName == enum_class_name:
|
||
enum_member_found = info
|
||
break
|
||
if not enum_member_found:
|
||
for key in self.Trans.SymbolTable:
|
||
if key == LastPart:
|
||
info = self.Trans.SymbolTable[key]
|
||
if info.IsEnumMember and info.EnumName == enum_class_name:
|
||
enum_member_found = info
|
||
break
|
||
if enum_member_found:
|
||
enum_type_name = enum_class_name
|
||
if enum_type_name:
|
||
_enum_bits: int = Gen._enum_int_type(enum_type_name).width
|
||
type_str = f"<enum '{enum_type_name}' size={_enum_bits // 8} signed=true, ptr=false>"
|
||
else:
|
||
llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg)
|
||
if isinstance(llvm_type, ir.PointerType):
|
||
pointee = llvm_type.pointee
|
||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||
llvm_type = pointee
|
||
type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type)
|
||
type_str = f"<class '{type_info['name']}' size={type_info['size']} signed={str(type_info['signed']).lower()}, ptr={str(type_info['ptr']).lower()}>"
|
||
return Gen.emit_constant(type_str, 'string')
|
||
|
||
def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str):
|
||
prompt = Node.args[0].value
|
||
prompt_ptr = Gen.emit_constant(prompt, 'string')
|
||
printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
|
||
Gen.builder.call(printf_func, [prompt_ptr])
|
||
scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
|
||
input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer")
|
||
result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer])
|
||
return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result")
|
||
|
||
def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
atoi_func: ir.Function = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))]))
|
||
return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result")
|
||
|
||
def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
val: ir.Value | None = self.HandleExprLlvm(expr_node)
|
||
if val and isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8:
|
||
return Gen._load(val, name="ord_result")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
val: ir.Value | None = self.HandleExprLlvm(expr_node)
|
||
if val:
|
||
char_ptr = Gen._alloca(ir.IntType(8), name="chr_char")
|
||
Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr)
|
||
return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result")
|
||
return ir.Constant(ir.IntType(8).as_pointer(), None)
|
||
|
||
def _HandleMallocLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
|
||
malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type)
|
||
param_type: ir.Type = malloc_func.type.pointee.args[0]
|
||
size_val: ir.Value | None = None
|
||
if args:
|
||
size_val = self.HandleExprLlvm(args[0])
|
||
if size_val and isinstance(size_val.type, ir.IntType):
|
||
if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width:
|
||
if size_val.type.width < param_type.width:
|
||
size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size")
|
||
else:
|
||
size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size")
|
||
if not size_val:
|
||
size_val = ir.Constant(param_type, 1)
|
||
result: ir.Value = Gen.builder.call(malloc_func, [size_val], name="malloc_result")
|
||
return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast")
|
||
|
||
def _HandleReallocLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
realloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)])
|
||
realloc_func: ir.Function = Gen._get_or_declare_func('realloc', realloc_type)
|
||
size_param_type: ir.Type = realloc_func.type.pointee.args[1]
|
||
ptr_val: ir.Value | None = None
|
||
size_val: ir.Value | None = None
|
||
if len(args) >= 2:
|
||
ptr_val = self.HandleExprLlvm(args[0])
|
||
size_val = self.HandleExprLlvm(args[1])
|
||
elif len(args) == 1:
|
||
size_val = self.HandleExprLlvm(args[0])
|
||
if not ptr_val:
|
||
ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None)
|
||
if isinstance(ptr_val.type, ir.PointerType) and not (isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8):
|
||
ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr")
|
||
if size_val and isinstance(size_val.type, ir.IntType):
|
||
if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width:
|
||
if size_val.type.width < size_param_type.width:
|
||
size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size")
|
||
else:
|
||
size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size")
|
||
if not size_val:
|
||
size_val = ir.Constant(size_param_type, 0)
|
||
result: ir.Value = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result")
|
||
return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast")
|
||
|
||
def _HandleFreeLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
|
||
free_func: ir.Function = Gen._get_or_declare_func('free', free_type)
|
||
if args:
|
||
var_name: str | None = None
|
||
if isinstance(args[0], ast.Name):
|
||
var_name = args[0].id
|
||
ptr_val: ir.Value | None = self.HandleExprLlvm(args[0])
|
||
if ptr_val is not None:
|
||
if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr:
|
||
registered_val = Gen._var_to_heap_ptr[var_name]
|
||
Gen._unregister_local_heap_ptr(registered_val)
|
||
del Gen._var_to_heap_ptr[var_name]
|
||
if isinstance(ptr_val.type, ir.PointerType):
|
||
if isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8:
|
||
pass
|
||
else:
|
||
ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr")
|
||
elif isinstance(ptr_val.type, ir.IntType):
|
||
ptr_val = Gen.builder.inttoptr(ptr_val, ir.PointerType(ir.IntType(8)), name="free_inttoptr")
|
||
Gen.builder.call(free_func, [ptr_val], name="free_call")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleMemcpyLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if 'llvm.memcpy' not in Gen.functions:
|
||
memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)])
|
||
Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type)
|
||
if len(args) >= 3:
|
||
dst: ir.Value | None = self.HandleExprLlvm(args[0])
|
||
src: ir.Value | None = self.HandleExprLlvm(args[1])
|
||
size: ir.Value | None = self.HandleExprLlvm(args[2])
|
||
if dst and src and size:
|
||
i8ptr: ir.PointerType = ir.PointerType(ir.IntType(8))
|
||
if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr:
|
||
dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast")
|
||
elif isinstance(dst.type, ir.IntType):
|
||
if dst.type.width < 64:
|
||
dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr")
|
||
dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr")
|
||
if isinstance(src.type, ir.PointerType) and src.type != i8ptr:
|
||
src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast")
|
||
elif isinstance(src.type, ir.IntType):
|
||
if src.type.width < 64:
|
||
src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr")
|
||
src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr")
|
||
if isinstance(size.type, ir.IntType) and size.type.width < 64:
|
||
size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size")
|
||
isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0)
|
||
Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleMemsetLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
memset_func: ir.Function = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]))
|
||
if len(args) >= 3:
|
||
dst: ir.Value | None = self.HandleExprLlvm(args[0])
|
||
val: ir.Value | None = self.HandleExprLlvm(args[1])
|
||
size: ir.Value | None = self.HandleExprLlvm(args[2])
|
||
if dst and val is not None and size:
|
||
if isinstance(dst.type, ir.PointerType) and not (isinstance(dst.type.pointee, ir.IntType) and dst.type.pointee.width == 8):
|
||
dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast")
|
||
elif isinstance(dst.type, ir.IntType):
|
||
if dst.type.width < 64:
|
||
dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_memset_dst")
|
||
elif dst.type.width > 64:
|
||
dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst")
|
||
dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr")
|
||
if isinstance(val.type, ir.PointerType):
|
||
# 0 字面量被推断为 null 指针(i8*),memset 第二参数需要 i32
|
||
val = ir.Constant(ir.IntType(32), 0)
|
||
elif isinstance(val.type, ir.IntType) and val.type.width != 32:
|
||
if val.type.width < 32:
|
||
val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val")
|
||
else:
|
||
val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val")
|
||
if isinstance(size.type, ir.IntType) and size.type.width < 64:
|
||
size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size")
|
||
elif isinstance(size.type, ir.IntType) and size.type.width > 64:
|
||
size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size")
|
||
Gen.builder.call(memset_func, [dst, val, size], name="memset_call")
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleVaStartLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleVaEndLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
|
||
def _HandleArgLlvm(self, args: list[ast.expr], CallNode: ast.Call | None = None) -> ir.Value | None:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
va_list_ptr: ir.Value | None = None
|
||
variadic_info: dict | None = getattr(Gen, '_va_arg_info', None)
|
||
if not variadic_info:
|
||
variadic_info = getattr(Gen, '_variadic_info', None)
|
||
if variadic_info:
|
||
va_list_ptr = variadic_info.get('va_list_ptr')
|
||
if not va_list_ptr:
|
||
vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args'
|
||
if vararg_name in Gen._direct_values:
|
||
va_list_ptr = Gen._direct_values[vararg_name]
|
||
elif vararg_name in Gen.variables:
|
||
va_list_ptr = Gen.variables[vararg_name]
|
||
if not va_list_ptr:
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
# 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type
|
||
# (typedef/T*/struct 同一规则源,解析不出即 fail-fast),无参数走
|
||
# _infer_vararg_type_from_context(无注解可推断时保持 i32 默认)。
|
||
target_type: ir.Type = self._resolve_vararg_type(args[0], Gen) if args \
|
||
else self._infer_vararg_type_from_context(Gen)
|
||
if not getattr(Gen, '_va_arg_counter', None):
|
||
Gen._va_arg_counter = 0
|
||
Gen._va_arg_counter += 1
|
||
result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type)
|
||
if result is not None and getattr(result, 'name', None):
|
||
result.name = f"va_arg_result_{Gen._va_arg_counter}"
|
||
return result
|
||
|
||
def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type:
|
||
"""将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。
|
||
|
||
解析顺序(与注解解析同一规则源,避免各处手抄一份而漏项):
|
||
1) str/bytes/CPtr → i8*;类名(Gen.structs)→ 该类指针
|
||
2) CTypeRegistry.GetClassByName/ResolveName(t.* 类型类与 C 基本类型名)
|
||
3) CTypeInfo.FromNode:builtins/库/符号表 typedef(uint64/int64/sizet/
|
||
size_t…)、`T*` 指针后缀(parser 已展开为 `T | t.CPtr`)等
|
||
|
||
fail-fast:以上均解析不出 LLVM 类型时直接报错终止,**绝不回退 i32**。
|
||
回退 i32 的后果不是"精度差一点"而是**读错变参槽**:Win64 变参槽固定 8 字节,
|
||
emit_va_arg 对 <64 位整数会生成 "va_arg i64 → trunc to i32",于是
|
||
`va_arg(ap, uint64)` 只保留低 32 位、高 32 位被丢弃。viperlib vsnprintf 的
|
||
`%016llx` 分支(`u_num_l: uint64 = va_arg(ap, uint64)`)因此输出
|
||
0x00000000XXXXXXXX —— llvmlite.ConstFloat 用该文本作"浮点常量位模式名",
|
||
所有 double 字面量遂退化为「真值低 32 位零扩展」(1.0/2.5/3.0 的低 32 位
|
||
全 0 ⇒ 全变成 0.0,除法/比较结果错乱)。
|
||
"""
|
||
target_type: ir.Type | None = None
|
||
if isinstance(type_arg, ast.Name):
|
||
type_name: str = type_arg.id
|
||
if type_name in ('str', 'bytes'):
|
||
target_type = ir.PointerType(ir.IntType(8))
|
||
elif type_name == 'CPtr':
|
||
target_type = ir.PointerType(ir.IntType(8))
|
||
elif type_name in Gen.structs:
|
||
target_type = ir.PointerType(Gen.structs[type_name])
|
||
else:
|
||
ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name)
|
||
if ctype_cls is not None:
|
||
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
||
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
|
||
if resolved:
|
||
target_type = resolved
|
||
else:
|
||
resolved_tuple: tuple | None = CTypeRegistry.ResolveName(type_name)
|
||
if resolved_tuple is not None:
|
||
ctype_cls, ptr_level = resolved_tuple
|
||
llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
||
if llvm_str:
|
||
base: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
|
||
if base is not None:
|
||
for _ in range(ptr_level):
|
||
if isinstance(base, ir.VoidType):
|
||
base = ir.IntType(8).as_pointer()
|
||
else:
|
||
base = ir.PointerType(base)
|
||
target_type = base
|
||
elif isinstance(type_arg, ast.Attribute):
|
||
if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable):
|
||
attr_name: str = type_arg.attr
|
||
ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name)
|
||
if ctype_cls is not None:
|
||
llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls)
|
||
resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str)
|
||
if resolved:
|
||
target_type = resolved
|
||
# 常规分支未命中:走注解解析同一规则源(typedef / `T*` 指针后缀 / t.* 组合写法)。
|
||
# 该规则源对未收录的名字会退化成 `%"{mod}.名"*` 假结构体(CStruct),故显式判掉。
|
||
if target_type is None:
|
||
type_info: CTypeInfo | None = CTypeInfo.FromNode(type_arg, self.Trans.SymbolTable)
|
||
if type_info is not None and type_info.BaseType is not None \
|
||
and not isinstance(type_info.BaseType, _t.CStruct):
|
||
target_type = Gen._ctype_to_llvm(type_info)
|
||
if target_type is None:
|
||
type_name_dbg: str = type(type_arg).__name__
|
||
if isinstance(type_arg, ast.Name):
|
||
type_name_dbg = type_arg.id
|
||
elif isinstance(type_arg, ast.Attribute):
|
||
type_name_dbg = type_arg.attr
|
||
raise TypeError(
|
||
f"[fast-fail] va_arg/arg 的类型参数 '{type_name_dbg}' 解析不出 LLVM 类型:"
|
||
f"拒绝回退 i32(Win64 变参槽 8 字节,i32 会 trunc 掉高 32 位,"
|
||
f"如 va_arg(ap, uint64) 只留低 32 位)。请检查该类型名是否已声明/import。")
|
||
return target_type
|
||
|
||
def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type:
|
||
"""从当前赋值上下文推断 va_arg/arg 的目标类型(无显式类型参数时用)
|
||
|
||
有赋值注解时与显式类型参数走同一解析入口(含 typedef/T*/struct;解析不出即
|
||
fail-fast,避免 i32 回退读错 8 字节变参槽);无注解可推断时保持 i32 默认
|
||
(`va_arg(ap)` 读 i32 的既有语义,见 viperlib vsnprintf)。
|
||
"""
|
||
assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None)
|
||
if assign_node:
|
||
ann: ast.AST | None = getattr(assign_node, 'annotation', None)
|
||
if ann is not None:
|
||
return self._resolve_vararg_type(ann, Gen)
|
||
return ir.IntType(32)
|
||
|
||
def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value:
|
||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||
if not args:
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
# 求值 va_list 指针(args[0])
|
||
va_list_ptr: ir.Value = self.HandleExprLlvm(args[0])
|
||
if va_list_ptr is None:
|
||
return ir.Constant(ir.IntType(32), 0)
|
||
# 解析目标类型
|
||
if len(args) >= 2:
|
||
target_type: ir.Type = self._resolve_vararg_type(args[1], Gen)
|
||
else:
|
||
target_type: ir.Type = self._infer_vararg_type_from_context(Gen)
|
||
if not getattr(Gen, '_va_arg_counter', None):
|
||
Gen._va_arg_counter = 0
|
||
Gen._va_arg_counter += 1
|
||
result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type)
|
||
if result is not None and getattr(result, 'name', None):
|
||
result.name = f"va_arg_explicit_{Gen._va_arg_counter}"
|
||
return result if result is not None else ir.Constant(ir.IntType(32), 0)
|