Files
TransPyC/lib/core/Handles/HandlesExprBuiltin.py
T
TermiNexus c86ecb0c89 refactor(LLVMCG/Handles): 直建 ir.Function 点全部收敛到幂等入口,3 处不再吞 fail-fast
类别:a 编译器缺陷(同一根因的其它变体收口:函数声明创建的唯一入口 + fail-fast 不被吞)

背景(前几轮已修两处):llvmlite 的 `Function.__init__` 不是事务性的(名字在 `_set_name`
就进 module.scope,之后建 Argument/ReturnValue 才断言类型,最后才 add_global),中途失败会
留下悬空名字;本仓已加两道防线:全局守卫 `InstallFuncCreateGuard()`(回滚名字 + 带调用点的
FailFastError)与唯一幂等入口 `_CreateOrReuseFuncDecl`(创建前自检、同名同类型复用、异类型
带两侧签名 fail-fast)。但仍有 20+ 处**绕过入口直建**(同名时抛裸 DuplicatedNameError、且
名字被占/半注册时只能看到没有上下文的名字),以及若干处 `except Exception` 会把这颗
FailFastError 吞掉(声明静默缺失、真因丢失)。

改动:
1. 创建点收敛(21 处,行为等价或更严)——`X = ir.Function(<gen>.module, <fty>, name=<N>)`
   → `X = <gen>._CreateOrReuseFuncDecl(<N>, <fty>)`:
   · Handles:HandlesAnnAssign[llvm.memcpy]、HandlesAssign[llvm.memcpy.p0i8.p0i8.i64 ×2]、
     HandlesBase[GetOrCreateFuncDecl / GetOrCreateStubFuncDecl]、
     HandlesExpr[malloc]、HandlesExprBuiltin[strlen / llvm.memcpy]、
     HandlesExprCall[llvm.* 与普通 / win32 API / strlen / llvm.memcpy]、
     HandlesAsyncFunctions[malloc / create_* / resume_* / CRT 声明 / free]、
     HandlesExprLambda[lambda 包装]、HandlesFunctions[_replace_function_decl 的 old_func=None 分支]、
     HandlesImports[output stub 按需加载]
   · LLVMCG:BaseGen[_DeclareExternalFunc 与 6 处 --hotspot-sites 内部符号]、
     ExprGen[llvm.pow.f64]、VaArg[llvm.va_start / llvm.va_end]
   原有 globals/functions 预检查与随后的 `Gen.functions[...] = X` 赋值一律保留。
   注:`_DeclareExternalFunc` 收敛后外部声明会同时登记进 `self.functions`(幂等入口固有行为,
   与既有 `get_or_declare_c_func` 一致;该路径仅 --hotspot-sites 调试特性使用)。
2. fail-fast 不再被吞(3 处):在包裹创建的 `except Exception` 首部加
   `isinstance(_e, FailFastError) → raise`:
   · HandlesExpr[malloc 的 null 回退分支]、HandlesExprBuiltin[strlen]、HandlesExprCall[strlen]
   (其余异常维持原行为,本轮不扩大 fatal 面。)
   另有 HandlesFunctions / HandlesImports 的同类 handler 前几轮已具备 FFE 重抛,本次未重复改。

验证:13 个改动文件 `py_compile` 全部 EXIT=0;冒烟 4 例通过——同名同类型复用同一对象、
异签名 → FailFastError(报文含两侧签名 i64/i32)、名字被非函数全局占用 → FailFastError、
`llvm.va_start` 幂等。另:`ir.Function(` 在 lib 内仅剩 `BaseGen._CreateOrReuseFuncDecl` 内部
这一处(豁免点);`DecoratorPass.py` 的 `module` 为外部传入(非 Gen 的模块)故其两处保持原样。
2026-09-30 09:53:41 +08:00

1257 lines
74 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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")
offset = Gen._get_member_offset(len_member, bare_cls)
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")
offset = Gen._get_member_offset(attr_name, bare_cls)
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:
raise TypeError(
f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:"
f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};"
f"(成员偏移与 struct 布局不一致)原错误: {_e_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)