- TypeGenMixin 新增 CDefineStrFlags/CDefineIsStr 字段与 register_cdefine_string/lookup_cdefine_string/is_cdefine_str 方法 - AssignGenMixin._register_cdefine_from_value 新增 CONST_STR 分支 - ExprGenMixin.translate_name_value CDefine 本地/跨模块均支持字符串 - Generator.InitGen / HandlesType 委托同步初始化新字段
5334 lines
282 KiB
Plaintext
5334 lines
282 KiB
Plaintext
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import memhub
|
||
import string
|
||
import llvmlite
|
||
import stdlib
|
||
import stdio
|
||
import viperlib
|
||
import w32.fileio as fileio
|
||
import lib.core.Handles.HandlesBase as HandlesBase
|
||
import lib.core.Handles.HandlesTranslator as HT
|
||
import lib.core.Handles.HandlesVar as HandlesVar
|
||
import lib.core.Handles.HandlesExpr as HandlesExpr
|
||
import lib.core.Handles.HandlesExprOps as HandlesExprOps
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||
import lib.core.Handles.HandlesType as HandlesType
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
import lib.core.Handles.HandlesEnum as HandlesEnum
|
||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||
import lib.core.Handles.HandlesImports as HandlesImports
|
||
import lib.core.StubMerger as StubMerger
|
||
import lib.core.VLogger as VLogger
|
||
import lib.Projectrans.Config as Config
|
||
import lib.Projectrans.Utils as Utils
|
||
|
||
|
||
# ============================================================
|
||
# docstring 登记表容量(__doc__ 魔法属性支持)
|
||
#
|
||
# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的
|
||
# 函数/类/方法 docstring,幂等查重)
|
||
# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度
|
||
# DOC_VAL_SZ: docstring 内容缓冲宽度
|
||
# ============================================================
|
||
DOC_MAX: t.CDefine = 512
|
||
DOC_KEY_SZ: t.CDefine = 192
|
||
DOC_VAL_SZ: t.CDefine = 4096
|
||
|
||
# ============================================================
|
||
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
|
||
#
|
||
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
|
||
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
|
||
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
|
||
# ============================================================
|
||
FSTR_BUF_SIZE: t.CDefine = 1024
|
||
FSTR_SPEC_MAX: t.CDefine = 64
|
||
FSTR_FMT_MAX: t.CDefine = 64
|
||
|
||
|
||
# ============================================================
|
||
# ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md)
|
||
#
|
||
# 从 HandlesExpr.vp(48 函数,无全局状态)与 HandlesExprOps.vp
|
||
# 迁移:模块级纯函数 → 类方法(self 替代,参数保留含 builder/pool)。
|
||
#
|
||
# 迁移原则(与 P2 一致,行为零变化):
|
||
# 1. 方法保留原函数签名(含 builder/pool 首参),仅函数体内
|
||
# 本模块函数调用改为 self.xxx(...)
|
||
# 2. 外部模块调用(HandlesExprCall.xxx / HandlesType.xxx 等)保留模块前缀
|
||
# 3. HandlesExpr.vp / HandlesExprOps.vp 原函数保留为"委托":本地
|
||
# 模块级单例 _g_gen(BuildPipeline/main 注入)cast 成 Generator 类型
|
||
# 4. ExprHandle 类保留在 HandlesExpr.vp(translator handler 模式)
|
||
#
|
||
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整)。
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# ExprGenMixin - 表达式/常量/下标/属性访问/运算符
|
||
# ============================================================
|
||
@t.NoVTable
|
||
class ExprGenMixin:
|
||
"""表达式处理 Mixin(纯方法化迁移,无状态字段)"""
|
||
|
||
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
|
||
DocKeys: bytes
|
||
DocValues: bytes
|
||
DocCount: int
|
||
|
||
# ============================================================
|
||
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
|
||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# 从 AST 节点提取函数名
|
||
# ============================================================
|
||
def get_func_name(self, func_node: ast.AST | t.CPtr) -> str:
|
||
"""从函数节点提取函数名"""
|
||
if func_node is None:
|
||
return None
|
||
k: int = func_node.kind()
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
|
||
return nm.id
|
||
elif k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
|
||
return at.attr
|
||
return None
|
||
|
||
# ============================================================
|
||
# 转义 LLVM IR 字符串字面量
|
||
# ============================================================
|
||
def escape_llvm_string(self, pool: memhub.MemBuddy | t.CPtr,
|
||
src: str) -> str:
|
||
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
|
||
if src is None:
|
||
return None
|
||
slen: t.CSizeT = string.strlen(src)
|
||
alloc_size: t.CSizeT = 4 * slen + 8
|
||
buf: str = pool.alloc(alloc_size)
|
||
if buf is None:
|
||
return None
|
||
pos: t.CSizeT = 0
|
||
buf[pos] = 'c'
|
||
pos += 1
|
||
buf[pos] = '"'
|
||
pos += 1
|
||
for i in range(slen):
|
||
ch: t.CChar = src[i]
|
||
if ch == '\0':
|
||
break
|
||
if ch == '\"' or ch == '\\' or ch < 0x20:
|
||
buf[pos] = '\\'
|
||
pos += 1
|
||
hi: int = (ch >> 4) & 0xF
|
||
lo: int = ch & 0xF
|
||
buf[pos] = Utils.nibble_to_hex_char(hi, 1)
|
||
pos += 1
|
||
buf[pos] = Utils.nibble_to_hex_char(lo, 1)
|
||
pos += 1
|
||
else:
|
||
buf[pos] = ch
|
||
pos += 1
|
||
buf[pos] = '\\'
|
||
pos += 1
|
||
buf[pos] = '0'
|
||
pos += 1
|
||
buf[pos] = '0'
|
||
pos += 1
|
||
buf[pos] = '"'
|
||
pos += 1
|
||
buf[pos] = '\0'
|
||
return buf
|
||
|
||
# ============================================================
|
||
# 获取 LLVM 整数类型的位宽
|
||
# ============================================================
|
||
def get_llvm_type_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
|
||
if ty is None:
|
||
return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Int(bits):
|
||
return bits
|
||
case _:
|
||
return 0
|
||
|
||
# ============================================================
|
||
# 获取 LLVM 浮点类型的位宽
|
||
# ============================================================
|
||
def get_llvm_float_bits(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
|
||
if ty is None:
|
||
return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Float(bits):
|
||
return bits
|
||
case _:
|
||
return 0
|
||
|
||
# ============================================================
|
||
# is_ptr_type - 检查类型是否为 Ptr
|
||
#
|
||
# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。
|
||
# ============================================================
|
||
def is_ptr_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
|
||
if ty is None:
|
||
return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
return 1
|
||
case _:
|
||
return 0
|
||
|
||
# ============================================================
|
||
# get_ptr_level - 计算指针层级
|
||
#
|
||
# 返回值:
|
||
# 0 = 非指针(数值/结构体等)
|
||
# 1 = 一级指针(如 i8*, Struct*)
|
||
# 2 = 二级指针(如 i8**, Struct**)
|
||
# 3+ = 更高级指针
|
||
#
|
||
# 用于比较运算的类型安全检查:
|
||
# - 数值(0) vs 数值(0) → 允许
|
||
# - 一级指针(1) vs 一级指针(1) → 允许
|
||
# - 数值(0) vs 指针(1+) → 禁止(fast-fail)
|
||
# - 不同级指针(1 vs 2) → 禁止(fast-fail)
|
||
# ============================================================
|
||
def get_ptr_level(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针..."""
|
||
if ty is None:
|
||
return 0
|
||
level: int = 0
|
||
cur: llvmlite.LLVMType | t.CPtr = ty
|
||
while cur is not None:
|
||
match cur:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
level += 1
|
||
cur = pointee
|
||
case _:
|
||
break
|
||
return level
|
||
|
||
# ============================================================
|
||
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
|
||
#
|
||
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
|
||
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
|
||
# ============================================================
|
||
def _is_zero_int_literal(self, node: ast.AST | t.CPtr) -> int:
|
||
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
|
||
if node is None:
|
||
return 0
|
||
if node.kind() != ast.ASTKind.Constant:
|
||
return 0
|
||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||
if cn is None or cn.const_kind != ast.CONST_INT:
|
||
return 0
|
||
if cn.int_val == 0:
|
||
return 1
|
||
return 0
|
||
|
||
# ============================================================
|
||
# is_array_type - 检查类型是否为 Array
|
||
#
|
||
# 返回 1=是, 0=否。类型检查工具函数。
|
||
# ============================================================
|
||
def is_array_type(self, ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||
"""检查 ty 是否是 Array 类型,返回 1=是, 0=否"""
|
||
if ty is None:
|
||
return 0
|
||
match ty:
|
||
case llvmlite.LLVMType.Array(_, _):
|
||
return 1
|
||
case _:
|
||
return 0
|
||
|
||
# ============================================================
|
||
# _zext_index_to_i32 - 数组下标索引零扩展
|
||
#
|
||
# LLVM GEP 对窄整数索引默认符号扩展:i8 0xE2(226) 会被当成 -30,
|
||
# 导致数组下标越界读(字节 >= 128 的乱码根因)。
|
||
# 数组下标语义为非负,<32 位整数索引一律 zext 到 i32。
|
||
# 仅用于数组 GEP;指针负偏移(p[-1])不经过此路径,不受影响。
|
||
# ============================================================
|
||
def _zext_index_to_i32(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""将 <32 位整数下标零扩展到 i32(数组下标非负语义)"""
|
||
if idx_val is None or idx_val.Ty is None or builder is None:
|
||
return idx_val
|
||
idx_bits: int = self.get_llvm_type_bits(idx_val.Ty)
|
||
if idx_bits != 0 and idx_bits < 32:
|
||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(builder.Pool)
|
||
return llvmlite.build_zext(builder, idx_val, i32_ty)
|
||
return idx_val
|
||
|
||
# ============================================================
|
||
# _get_custom_struct_cls_nm - 检查类型是否为已注册的自定义结构体
|
||
#
|
||
# 处理两种情况:
|
||
# - ty 直接是 Struct → 检查是否注册
|
||
# - ty 是 Ptr(Struct) → 检查 pointee 是否注册
|
||
#
|
||
# 返回类名字符串或 None。用于 subscript 操作中判断是否需要
|
||
# 转发到 __getitem__/__setitem__ 而非生成 GEP。
|
||
# ============================================================
|
||
def _get_custom_struct_cls_nm(self, pool: memhub.MemBuddy | t.CPtr,
|
||
ty: llvmlite.LLVMType | t.CPtr) -> str:
|
||
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
|
||
if ty is None:
|
||
return None
|
||
# 直接检查 ty 是否是注册的结构体
|
||
cls_nm_direct: str = HandlesStruct.get_class_name_by_type(pool, ty)
|
||
if cls_nm_direct is not None:
|
||
return cls_nm_direct
|
||
# 如果 ty 是 Ptr,检查 pointee
|
||
if self.is_ptr_type(ty) != 0:
|
||
pointee_cs: llvmlite.LLVMType | t.CPtr = ty.Pointee
|
||
if pointee_cs is not None:
|
||
return HandlesStruct.get_class_name_by_type(pool, pointee_cs)
|
||
return None
|
||
|
||
# ============================================================
|
||
# _is_list_subscript_base - 判断下标基座是否为 list[T] 变量
|
||
#
|
||
# 属性访问的 Subscript 基座(如 lst[i].x)需要区分:
|
||
# - list[T] 变量:translate_subscript 走 __getitem__ 内联返回元素地址
|
||
# - 普通结构体指针数组(如 pts: Point | t.CPtr):get_subscript_ptr 返回元素地址
|
||
# 路由错误会让 list[T] 被当作结构体数组 GEP,访问错位崩溃。
|
||
# ============================================================
|
||
def _is_list_subscript_base(self, pool: memhub.MemBuddy | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
sub_node: ast.AST | t.CPtr) -> int:
|
||
"""判断下标基座是否为 list[T] 变量(1=是)"""
|
||
if sub_node is None or trans is None:
|
||
return 0
|
||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
|
||
return 0
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||
if nm.id is None:
|
||
return 0
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is None or alloca.Ty is None:
|
||
return 0
|
||
if self.is_ptr_type(alloca.Ty) == 0:
|
||
return 0
|
||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||
if pointee is None:
|
||
return 0
|
||
match pointee:
|
||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||
lsn_ls: str = None
|
||
match inner_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, lsn3):
|
||
lsn_ls = lsn3
|
||
case _:
|
||
pass
|
||
if lsn_ls is not None and string.strstr(lsn_ls, "list[") is not None:
|
||
return 1
|
||
case _:
|
||
pass
|
||
return 0
|
||
|
||
# ============================================================
|
||
# _sub_base_class_name - 解析下标基座变量的结构体类名
|
||
# ============================================================
|
||
def _sub_base_class_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
sub_node: ast.AST | t.CPtr) -> str:
|
||
"""解析下标基座变量的结构体类名(无则 None)"""
|
||
if sub_node is None or trans is None:
|
||
return None
|
||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name:
|
||
return None
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||
if nm.id is None:
|
||
return None
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is None or alloca.Ty is None:
|
||
return None
|
||
if self.is_ptr_type(alloca.Ty) == 0:
|
||
return None
|
||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||
if pointee is None:
|
||
return None
|
||
if self.is_ptr_type(pointee) != 0:
|
||
inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee
|
||
if inner is not None:
|
||
return HandlesStruct.get_class_name_by_type(pool, inner)
|
||
return HandlesStruct.get_class_name_by_type(pool, pointee)
|
||
|
||
# ============================================================
|
||
# _sub_base_uses_getitem - 下标基座是否应走 __getitem__ 语义
|
||
#
|
||
# 属性访问的 Subscript 基座路由依据:
|
||
# - list[T] / OOP 定义 __getitem__ 的类(如 dict[key])→ translate_value
|
||
# (translate_subscript 走 __getitem__ 内联/方法调用返回元素值或地址)
|
||
# - 普通结构体指针数组(如 pts: Point | t.CPtr)→ get_subscript_ptr
|
||
# (GEP 元素地址,避免结构体值 base=非指针 报错)
|
||
# ============================================================
|
||
def _sub_base_uses_getitem(self, pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
sub_node: ast.AST | t.CPtr) -> int:
|
||
"""下标基座应走 __getitem__ 语义时返回 1,否则 0(应走 GEP 地址语义)"""
|
||
if self._is_list_subscript_base(pool, trans, sub_node) != 0:
|
||
return 1
|
||
cls_ugi: str = self._sub_base_class_name(pool, trans, sub_node)
|
||
if cls_ugi is None:
|
||
return 0
|
||
lookup_ugi: str = pool.alloc(128)
|
||
if lookup_ugi is None:
|
||
return 0
|
||
viperlib.snprintf(lookup_ugi, 128, "%s.__getitem__", cls_ugi)
|
||
if HandlesExprCall.find_func_in_module(mod, lookup_ugi) is not None:
|
||
return 1
|
||
return 0
|
||
|
||
# ============================================================
|
||
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||
#
|
||
# 用于支持 X|t.CPtr 类型变量的属性访问:
|
||
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
|
||
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
|
||
# ============================================================
|
||
def _deref_if_ptr_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""如果 obj_ptr 是 Ptr(Ptr(...)),load 解引用获取内层指针"""
|
||
if obj_ptr is None or obj_ptr.Ty is None:
|
||
return obj_ptr
|
||
if self.is_ptr_type(obj_ptr.Ty) == 0:
|
||
return obj_ptr
|
||
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
|
||
if pointee is None:
|
||
return obj_ptr
|
||
if self.is_ptr_type(pointee) == 0:
|
||
return obj_ptr
|
||
# obj_ptr 是 Ptr(Ptr(...)),load 解引用
|
||
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
|
||
if loaded is None:
|
||
return obj_ptr
|
||
return loaded
|
||
|
||
# ============================================================
|
||
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
|
||
# ============================================================
|
||
def coerce_to_type(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
val: llvmlite.Value | t.CPtr,
|
||
target_ty: llvmlite.LLVMType | t.CPtr,
|
||
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""将 val 强制转换为 target_ty
|
||
|
||
整数: sext(扩展)/ trunc(截断)
|
||
浮点: fpext(扩展)/ fptrunc(截断)
|
||
int→float: sitofp
|
||
float→int: fptosi
|
||
|
||
node: AST 节点(用于错误定位),可为 None
|
||
"""
|
||
if val is None or target_ty is None:
|
||
return val
|
||
# 函数 → 指针: bitcast(如 CreateThread(..., _thread_alloc_free, ...) 传函数指针)
|
||
# 函数 Value 的 Ty 是 Func,既不是整数也不是指针,需在整数/浮点转换之前处理
|
||
match val.Ty:
|
||
case llvmlite.LLVMType.Func(_, _, _):
|
||
if self.is_ptr_type(target_ty) != 0:
|
||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||
return val
|
||
case _:
|
||
pass
|
||
# 整数转换
|
||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||
target_bits: int = self.get_llvm_type_bits(target_ty)
|
||
# 整数 → 指针: inttoptr(必须在纯整数转换之前检查,因为 ptr 的 bits 非零会误入 sext/trunc 路径)
|
||
# 适用于: 跨模块方法返回 i64/i32(默认推断),目标变量是指针类型
|
||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||
return llvmlite.build_inttoptr(builder, val, target_ty)
|
||
# 指针 → 整数: ptrtoint(当目标明确是整数而非指针时)
|
||
# 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符
|
||
# 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针,
|
||
# ptrtoint 会截断地址低 8 位(非 0),应 load 解引用取首字符 0
|
||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and self.is_ptr_type(target_ty) == 0:
|
||
return llvmlite.build_ptrtoint(builder, val, target_ty)
|
||
if val_bits != 0 and target_bits != 0:
|
||
if val_bits == target_bits:
|
||
return val
|
||
if val_bits < target_bits:
|
||
# i1 (bool) → 更宽整数用 zext(bool 语义:1 保持 1,而非 sext 的 0xFF...)
|
||
# 与 HandlesReturn 中 return 语句的 i1 扩展规则保持一致
|
||
if val_bits == 1:
|
||
return llvmlite.build_zext(builder, val, target_ty)
|
||
return llvmlite.build_sext(builder, val, target_ty)
|
||
return llvmlite.build_trunc(builder, val, target_ty)
|
||
# 浮点转换
|
||
val_fbits: int = self.get_llvm_float_bits(val.Ty)
|
||
target_fbits: int = self.get_llvm_float_bits(target_ty)
|
||
# float ↔ ptr: 禁止(无意义的转换,fast-fail 防止误走入 build_load 路径)
|
||
if (val_fbits != 0 and self.is_ptr_type(target_ty) != 0) or \
|
||
(self.is_ptr_type(val.Ty) != 0 and target_fbits != 0):
|
||
HandlesType.fatal_error(node, "unsupported type conversion: float <-> ptr", "CastError")
|
||
return val
|
||
if val_fbits != 0 and target_fbits != 0:
|
||
if val_fbits == target_fbits:
|
||
return val
|
||
if val_fbits < target_fbits:
|
||
return llvmlite.build_fpext(builder, val, target_ty)
|
||
return llvmlite.build_fptrunc(builder, val, target_ty)
|
||
# int → float
|
||
if val_bits != 0 and target_fbits != 0:
|
||
return llvmlite.build_si2fp(builder, val, target_ty)
|
||
# float → int
|
||
if val_fbits != 0 and target_bits != 0:
|
||
return llvmlite.build_fp2si(builder, val, target_ty)
|
||
# 指针 → 非指针值: build_load 解引用
|
||
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
|
||
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
|
||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||
return llvmlite.build_load(builder, target_ty, val)
|
||
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
|
||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||
return val
|
||
|
||
# ============================================================
|
||
# translate_ptr_arith - 指针算术: ptr + int / ptr - int
|
||
#
|
||
# 用 getelementptr(按 pointee 元素大小缩放索引),对齐 TPC
|
||
# ExprGen.emit_binary_op / HandlesAugAssign.py。
|
||
# 旧实现用 ptrtoint + add + inttoptr(按字节加):i8* 元素大小 1
|
||
# 恰好正确,但 i32*/struct* 等指针 `p + 3` 只前进 3 字节(应 12),
|
||
# 导致 StringIterTest Test 4/8 失败(指针偏移/指针算术)。
|
||
#
|
||
# 数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),
|
||
# 避免以整个数组为步长(对齐 TPC ExprGen L53-55)。
|
||
# ============================================================
|
||
def translate_ptr_arith(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
ptr_val: llvmlite.Value | t.CPtr,
|
||
int_val: llvmlite.Value | t.CPtr,
|
||
op: int) -> llvmlite.Value | t.CPtr:
|
||
"""指针算术: ptr + int / ptr - int → GEP(按元素大小缩放索引)"""
|
||
if ptr_val is None or int_val is None or ptr_val.Ty is None:
|
||
return None
|
||
# 提取 pointee 类型(非指针 → 不处理返回 None)
|
||
elem_ty: llvmlite.LLVMType | t.CPtr = None
|
||
match ptr_val.Ty:
|
||
case llvmlite.LLVMType.Ptr(pointee):
|
||
elem_ty = pointee
|
||
case _:
|
||
return None
|
||
if elem_ty is None:
|
||
return None
|
||
# 索引统一到 i64(对齐 TPC zext 到 i64)
|
||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||
idx: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, int_val, i64_ty, None)
|
||
if idx is None:
|
||
return None
|
||
# ptr - int → 负索引(LLVM GEP 索引为加法语义)
|
||
if op == ast.OpKind.Sub:
|
||
neg_idx: llvmlite.Value | t.CPtr = llvmlite.build_sub(
|
||
builder, llvmlite.const_int64(pool, 0), idx)
|
||
if neg_idx is None:
|
||
return None
|
||
idx = neg_idx
|
||
# 数组 pointee → 先 decay 为元素指针(getelementptr [N x ty], [N x ty]* %p, i32 0, i32 0)
|
||
if self.is_array_type(elem_ty) != 0:
|
||
inner_ty: llvmlite.LLVMType | t.CPtr = None
|
||
match elem_ty:
|
||
case llvmlite.LLVMType.Array(inner, _):
|
||
inner_ty = inner
|
||
case _:
|
||
return None
|
||
if inner_ty is None:
|
||
return None
|
||
decayed: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||
builder, elem_ty, inner_ty, ptr_val, llvmlite.const_int64(pool, 0))
|
||
if decayed is None:
|
||
return None
|
||
return llvmlite.build_gep(builder, inner_ty, decayed, idx)
|
||
return llvmlite.build_gep(builder, elem_ty, ptr_val, idx)
|
||
|
||
# ============================================================
|
||
# 创建全局字符串常量
|
||
# ============================================================
|
||
def create_global_string(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
str_val: str,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""创建全局字符串常量并返回 i8* bitcast"""
|
||
escaped: str = self.escape_llvm_string(pool, str_val)
|
||
if escaped is None:
|
||
return None
|
||
|
||
slen: t.CSizeT = string.strlen(str_val)
|
||
count: int = slen + 1
|
||
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
|
||
|
||
# 使用模块级计数器(trans._label_counter)避免跨函数命名冲突
|
||
str_idx: int = trans._label_counter
|
||
trans._label_counter += 1
|
||
|
||
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
|
||
gv_name: str = pool.alloc(48)
|
||
if gv_name is None:
|
||
return None
|
||
if trans.ModuleSha1 is not None:
|
||
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
|
||
else:
|
||
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
|
||
|
||
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
|
||
if gv is None:
|
||
return None
|
||
llvmlite.module_add_global(mod, gv)
|
||
|
||
# 去掉 private 链接,和函数一致:stub 中 declare,text 中 define
|
||
llvmlite.global_set_unnamed_addr(gv, 1)
|
||
llvmlite.global_set_constant(gv, 1)
|
||
# LLVM 22+ 不允许 external linkage 全局变量带初始值,设为 internal
|
||
gv.Linkage = "internal"
|
||
gv.Initializer = escaped
|
||
|
||
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
|
||
gv_ref_name: str = pool.alloc(64)
|
||
if gv_ref_name is None:
|
||
return None
|
||
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
|
||
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
|
||
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||
|
||
bc: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||
return bc
|
||
|
||
# ============================================================
|
||
# 翻译常量表达式
|
||
# ============================================================
|
||
def translate_constant(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译常量(int/str/bool)"""
|
||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
|
||
if cn is None:
|
||
return None
|
||
ck: int = cn.const_kind
|
||
if cn.const_kind == ast.CONST_INT:
|
||
# 超出 i32 范围则用 i64(避免常量创建时被截断)
|
||
iv: t.CInt64T = cn.int_val
|
||
if iv > 2147483647 or iv < -2147483648:
|
||
return llvmlite.const_int64(pool, iv)
|
||
return llvmlite.const_int32(pool, iv)
|
||
elif cn.const_kind == ast.CONST_FLOAT:
|
||
# 浮点常量默认创建为 double(64 位),赋值时由 coerce_to_type 自动 fptrunc
|
||
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
|
||
elif cn.const_kind == ast.CONST_CHAR:
|
||
# 单引号单字符 → i32 值(字符 ASCII 码),coerce_to_type 会 trunc 为 i8
|
||
cv: t.CInt64T = cn.int_val
|
||
return llvmlite.const_int32(pool, cv)
|
||
elif cn.const_kind == ast.CONST_STR:
|
||
sv: str = cn.str_val
|
||
if sv is None:
|
||
return None
|
||
r: llvmlite.Value | t.CPtr = self.create_global_string(builder, pool, mod, sv, trans)
|
||
return r
|
||
elif cn.const_kind == ast.CONST_BOOL:
|
||
if cn.int_val != 0:
|
||
return llvmlite.const_int32(pool, 1)
|
||
return llvmlite.const_int32(pool, 0)
|
||
elif cn.const_kind == ast.CONST_NONE:
|
||
# None → i8* null(空指针常量),用于 `p is None` / `p is not None` 比较
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
if i8_ty is None:
|
||
return None
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||
if i8_ptr_ty is None:
|
||
return None
|
||
rv_none: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty, "null")
|
||
return rv_none
|
||
return None
|
||
|
||
# ============================================================
|
||
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
|
||
#
|
||
# 用于 list 字面量 [a, b, c] 的元素类型推断
|
||
# ============================================================
|
||
def _infer_elem_type_from_value(self, pool: memhub.MemBuddy | t.CPtr,
|
||
value: llvmlite.Value | t.CPtr) -> str:
|
||
"""从 LLVM Value 的类型推断 list 元素类型名
|
||
|
||
fail-fast:值类型缺失/无法映射时返回 None(调用方携带节点报错终止),
|
||
绝不静默回退 "str"(掩盖上游 Value 类型缺失根因)。
|
||
"""
|
||
if value is None or value.Ty is None:
|
||
return None
|
||
ty: llvmlite.LLVMType | t.CPtr = value.Ty
|
||
bits: int = self.get_llvm_type_bits(ty)
|
||
if bits != 0:
|
||
if bits == 32:
|
||
return "int"
|
||
if bits == 64:
|
||
return "CSizeT"
|
||
if bits == 8:
|
||
return "CInt8T"
|
||
if bits == 16:
|
||
return "CInt16T"
|
||
return None
|
||
if self.is_ptr_type(ty) != 0:
|
||
return "str"
|
||
fbits: int = self.get_llvm_float_bits(ty)
|
||
if fbits != 0:
|
||
if fbits == 64:
|
||
return "CDouble"
|
||
if fbits == 32:
|
||
return "CFloat"
|
||
return None
|
||
|
||
# ============================================================
|
||
# _find_pool_var — 查找上下文中的 pool 变量
|
||
#
|
||
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
|
||
# ============================================================
|
||
def _find_pool_var(self, trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""查找上下文中的 pool 变量,返回 alloca 或 None
|
||
|
||
查找顺序: pool → _mbuddy → mbuddy → mb
|
||
兜底: 若都未找到,注册外部全局 @_mbuddy (i8*) 供函数内动态 list 创建使用。
|
||
注意: 模块级 t.CArray 初始化不走此路径(由 _init_global_array_from_list 处理),
|
||
此兜底仅用于函数内的动态 list 字面量(如 op=[...])。
|
||
@_mbuddy 由入口模块(如 Phase2.py)定义并初始化,其他模块以 external 引用。
|
||
"""
|
||
if trans is None:
|
||
return None
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
|
||
if alloca is not None:
|
||
return alloca
|
||
alloca = HandlesVar.lookup_var(trans.SymTab, "_mbuddy")
|
||
if alloca is not None:
|
||
return alloca
|
||
alloca = HandlesVar.lookup_var(trans.SymTab, "mbuddy")
|
||
if alloca is not None:
|
||
return alloca
|
||
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
|
||
if alloca is not None:
|
||
return alloca
|
||
# 兜底: 创建外部全局 @_mbuddy 引用(i8** 类型)
|
||
# 仅用于函数内动态 list 字面量,@_mbuddy 由入口模块定义并初始化
|
||
pool: memhub.MemBuddy | t.CPtr = trans.Pool
|
||
if pool is None or trans.Module is None or trans.SymTab is None:
|
||
return None
|
||
# 先检查是否已注册到模块作用域(避免重复创建全局变量)
|
||
existing: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(trans.SymTab, "_mbuddy")
|
||
if existing is not None:
|
||
return existing
|
||
# 创建 i8* 类型(t.CVoid | t.CPtr = i8*)
|
||
i8_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_fb)
|
||
# 创建外部全局 @_mbuddy(external linkage,无初始值)
|
||
gv_ext: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, "_mbuddy", i8_ptr_ty_fb)
|
||
if gv_ext is not None:
|
||
gv_ext.Linkage = "external"
|
||
llvmlite.module_add_global(trans.Module, gv_ext)
|
||
# 创建 Value 引用(@_mbuddy, 类型为 i8**)
|
||
i8_ptr_ptr_ty_fb: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ptr_ty_fb)
|
||
ref_name_fb: str = pool.alloc(16)
|
||
if ref_name_fb is not None:
|
||
string.strcpy(ref_name_fb, "@_mbuddy")
|
||
gv_ref_fb: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, i8_ptr_ptr_ty_fb, ref_name_fb)
|
||
# 注册到模块作用域,避免重复创建
|
||
HandlesVar.define_module_var(trans.SymTab, "_mbuddy", gv_ref_fb)
|
||
return gv_ref_fb
|
||
|
||
# ============================================================
|
||
# translate_list_literal — 翻译 list 字面量 [a, b, c]
|
||
#
|
||
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
|
||
#
|
||
# Args:
|
||
# builder: IRBuilder
|
||
# pool: 编译器内存池(MemBuddy)
|
||
# mod: LLVMModule
|
||
# node: ast.List 节点
|
||
# elem_type_name: 元素类型名(如 "str", "int"),None 表示从元素推断
|
||
# trans: Translator 对象
|
||
#
|
||
# Returns:
|
||
# list 对象指针(Ptr(list[T] struct)),None 失败
|
||
# ============================================================
|
||
def translate_list_literal(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
elem_type_name: str,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
|
||
if node is None or trans is None:
|
||
return None
|
||
|
||
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
|
||
if list_node is None:
|
||
return None
|
||
|
||
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
|
||
elts_count: t.CSizeT = 0
|
||
if elts is not None:
|
||
elts_count = len(elts)
|
||
|
||
# 推断元素类型
|
||
inferred_type_name: str = elem_type_name
|
||
first_elem_val: llvmlite.Value | t.CPtr = None
|
||
if inferred_type_name is None:
|
||
if elts_count > 0:
|
||
first_elem_node: ast.AST | t.CPtr = elts.get(0)
|
||
first_elem_val = self.translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
|
||
if first_elem_val is None or first_elem_val.Ty is None:
|
||
# fail-fast:首元素翻译失败/Value 类型缺失,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(
|
||
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||
inferred_type_name = self._infer_elem_type_from_value(pool, first_elem_val)
|
||
if inferred_type_name is None:
|
||
# fail-fast:首元素 Value 类型无法映射为 list 元素类型,立即终止编译
|
||
HandlesType.fatal_error(
|
||
first_elem_node, "list 字面量元素类型推断失败(Value 类型缺失)", "TypeError")
|
||
else:
|
||
# 空 list 无元素可推断,保持默认 str(list[str])
|
||
inferred_type_name = "str"
|
||
|
||
# 查找 pool 变量
|
||
pool_alloca: llvmlite.Value | t.CPtr = self._find_pool_var(trans)
|
||
if pool_alloca is None:
|
||
HandlesType.fatal_error(node, "list literal requires pool variable in context")
|
||
return None
|
||
|
||
# load pool 变量值(i8*)
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
|
||
if pool_val is None:
|
||
return None
|
||
|
||
# 特化 list[T] 类(elem_size=8:str 是指针,4 会导致双元素重叠损坏)
|
||
type_args: list[str] | t.CPtr = list[str](pool, 8)
|
||
type_args.append(inferred_type_name)
|
||
spec_name: str = HandlesClassDef._specialize_generic_class(trans, "list", type_args)
|
||
if spec_name is None:
|
||
HandlesType.fatal_error(node, "list literal generic specialization failed")
|
||
return None
|
||
|
||
# 查找 struct entry
|
||
struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name)
|
||
if struct_entry is None:
|
||
HandlesType.fatal_error(node, "list literal specialized class not registered")
|
||
return None
|
||
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
|
||
if struct_ty is None:
|
||
return None
|
||
|
||
# alloca 临时 list 空间
|
||
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
|
||
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
|
||
if tmp is None:
|
||
return None
|
||
|
||
# 存储指针:默认用 alloca,如果有 __new__ 则用 __new__ 返回的指针
|
||
# __new__ 是可选的,仅当泛型类模板定义了 __new__ 方法时才调用
|
||
# (与 _translate_struct_ctor/_translate_generic_ctor_call 保持一致,
|
||
# 避免为无 __new__ 的类生成过度声明)
|
||
storage_ptr: llvmlite.Value | t.CPtr = tmp
|
||
has_new_flag: int = 0
|
||
if struct_entry is not None:
|
||
has_new_flag = struct_entry.HasNew
|
||
if has_new_flag != 0:
|
||
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||
if new_args is None:
|
||
return None
|
||
string.memset(new_args, 0, 8 * 32)
|
||
new_args[0] = t.CSizeT(pool_val)
|
||
new_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
|
||
if new_ptr is not None:
|
||
storage_ptr = new_ptr
|
||
|
||
# 调用 __before_init__(storage_ptr)
|
||
HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
|
||
|
||
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
|
||
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||
if init_args is None:
|
||
return None
|
||
string.memset(init_args, 0, 8 * 32)
|
||
init_args[0] = t.CSizeT(pool_val)
|
||
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
|
||
init_args[1] = t.CSizeT(elem_size_val)
|
||
HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
|
||
|
||
# 对每个元素调用 append(storage_ptr, elem_val)
|
||
if elts_count > 0:
|
||
# 第一个元素可能已经翻译过
|
||
if first_elem_val is not None:
|
||
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||
if append_args is not None:
|
||
string.memset(append_args, 0, 8 * 32)
|
||
append_args[0] = t.CSizeT(first_elem_val)
|
||
HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||
|
||
# 翻译剩余元素
|
||
i: t.CSizeT = 1
|
||
while i < elts_count:
|
||
elem_node_i: ast.AST | t.CPtr = elts.get(i)
|
||
elem_val_i: llvmlite.Value | t.CPtr = self.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||
if elem_val_i is not None:
|
||
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
|
||
if ap_args is not None:
|
||
string.memset(ap_args, 0, 8 * 32)
|
||
ap_args[0] = t.CSizeT(elem_val_i)
|
||
HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
|
||
i += 1
|
||
|
||
return storage_ptr
|
||
|
||
# ============================================================
|
||
# 翻译值表达式(RHS 分派)— 模块级版本
|
||
# ============================================================
|
||
def translate_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
funcs_ptr: t.CPtr = None,
|
||
func_count: int = 0,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译值表达式为 LLVM Value"""
|
||
if node is None:
|
||
return None
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Constant:
|
||
rv: llvmlite.Value | t.CPtr = self.translate_constant(builder, pool, mod, node, trans)
|
||
return rv
|
||
elif k == ast.ASTKind.Name:
|
||
return self.translate_name_value(builder, pool, node, trans)
|
||
elif k == ast.ASTKind.BinOp:
|
||
return self.translate_binop(pool, builder, mod, node, trans)
|
||
elif k == ast.ASTKind.Call:
|
||
return HandlesExprCall.translate_call(pool, builder, mod, node,
|
||
funcs_ptr, func_count, trans)
|
||
elif k == ast.ASTKind.Compare:
|
||
return self.translate_compare(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.UnaryOp:
|
||
return self.translate_unaryop(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.BoolOp:
|
||
return self.translate_boolop(builder, pool, mod, node,
|
||
funcs_ptr, func_count, trans)
|
||
elif k == ast.ASTKind.Subscript:
|
||
return self.translate_subscript(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.Attribute:
|
||
return self.translate_attribute(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.List:
|
||
return self.translate_list_literal(builder, pool, mod, node, None, trans)
|
||
elif k == ast.ASTKind.IfExp:
|
||
return self.translate_ifexp(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.Lambda:
|
||
# 模块级委托:translate_lambda 定义在 FuncGenMixin(跨 Mixin
|
||
# 方法不能 self. 直接调用,见 HandlesExpr.translate_lambda)
|
||
return HandlesExpr.translate_lambda(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.JoinedStr:
|
||
return self.translate_joined_str(builder, pool, mod, node, trans)
|
||
elif k == ast.ASTKind.FormattedValue:
|
||
# 解析器对 f"{expr:spec}"(单 FormattedValue 无字面量)返回裸 FormattedValue
|
||
# 而非 JoinedStr(见 ast/parser.vp _parse_fstring),需单独翻译
|
||
return self.translate_formatted_value(builder, pool, mod, node, trans)
|
||
return None
|
||
|
||
# ============================================================
|
||
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
|
||
#
|
||
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
|
||
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
|
||
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
|
||
# ============================================================
|
||
def translate_joined_str(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""f-string → snprintf 生成 i8* 栈缓冲"""
|
||
if node is None or builder is None or trans is None:
|
||
return None
|
||
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
|
||
if js is None or js.values is None:
|
||
return None
|
||
|
||
# 编译期格式串缓冲(pool 分配)
|
||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||
if fmt_buf is None:
|
||
return None
|
||
fmt_buf[0] = '\0'
|
||
|
||
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
|
||
arg_head: llvmlite.Value | t.CPtr = None
|
||
arg_tail: llvmlite.Value | t.CPtr = None
|
||
arg_count: int = 0
|
||
|
||
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
|
||
pn: t.CSizeT = len(parts)
|
||
for pi in range(pn):
|
||
part: ast.AST | t.CPtr = parts.get(pi)
|
||
if part is None:
|
||
continue
|
||
if part.kind() == ast.ASTKind.Constant:
|
||
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
|
||
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
||
self._fstr_append_literal(fmt_buf, pcn.str_val)
|
||
continue
|
||
if part.kind() != ast.ASTKind.FormattedValue:
|
||
continue
|
||
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
|
||
if fv.value is None:
|
||
continue
|
||
val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, fv.value, None, 0, trans)
|
||
if val is None:
|
||
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
|
||
# 格式串写 "(null)" 兜底(静默输出掩盖表达式翻译失败根因)。
|
||
HandlesType.fatal_error(
|
||
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||
continue
|
||
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
|
||
c_fmt: str = None
|
||
if fv.format_spec is not None:
|
||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||
if c_fmt is None:
|
||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||
if c_fmt is None:
|
||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||
HandlesType.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||
# 追加格式说明符
|
||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
||
string.strcat(fmt_buf, c_fmt)
|
||
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
|
||
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
|
||
builder, pool, val, c_fmt, fv.value, trans)
|
||
if cast_val is None:
|
||
cast_val = val
|
||
cast_val.Next = None
|
||
if arg_head is None:
|
||
arg_head = cast_val
|
||
else:
|
||
arg_tail.Next = cast_val
|
||
arg_tail = cast_val
|
||
arg_count += 1
|
||
|
||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||
|
||
def translate_formatted_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
|
||
if node is None or builder is None or trans is None:
|
||
return None
|
||
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
|
||
if fv is None or fv.value is None:
|
||
return None
|
||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||
if fmt_buf is None:
|
||
return None
|
||
fmt_buf[0] = '\0'
|
||
arg_head: llvmlite.Value | t.CPtr = None
|
||
arg_tail: llvmlite.Value | t.CPtr = None
|
||
arg_count: int = 0
|
||
val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, fv.value, None, 0, trans)
|
||
if val is None:
|
||
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
|
||
# 格式串写 "(null)" 兜底(静默输出掩盖表达式翻译失败根因)。
|
||
HandlesType.fatal_error(
|
||
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
|
||
else:
|
||
c_fmt: str = None
|
||
if fv.format_spec is not None:
|
||
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
|
||
if c_fmt is None:
|
||
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
|
||
if c_fmt is None:
|
||
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
|
||
HandlesType.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
|
||
string.strcat(fmt_buf, c_fmt)
|
||
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
|
||
builder, pool, val, c_fmt, fv.value, trans)
|
||
if cast_val is None:
|
||
cast_val = val
|
||
cast_val.Next = None
|
||
arg_head = cast_val
|
||
arg_tail = cast_val
|
||
arg_count = 1
|
||
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
|
||
|
||
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
fmt_buf: str,
|
||
arg_head: llvmlite.Value | t.CPtr,
|
||
arg_tail: llvmlite.Value | t.CPtr,
|
||
arg_count: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
|
||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
if fmt_buf is None or fmt_buf[0] == '\0':
|
||
# 空 f-string(f"")→ 空字符串常量
|
||
return HandlesExpr.create_global_string(builder, pool, mod, "", trans)
|
||
# 格式串 → 全局字符串常量(i8*)
|
||
fmt_ptr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string(
|
||
builder, pool, mod, fmt_buf, trans)
|
||
if fmt_ptr is None:
|
||
return None
|
||
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
|
||
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
|
||
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
|
||
if buf_alloca is None:
|
||
return None
|
||
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
|
||
if buf_ptr is None:
|
||
return None
|
||
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
|
||
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
|
||
buf_ptr.Next = size_val
|
||
size_val.Next = fmt_ptr
|
||
fmt_ptr.Next = arg_head
|
||
if arg_tail is not None:
|
||
arg_tail.Next = None
|
||
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||
return buf_ptr
|
||
|
||
def _fstr_append_literal(self, fmt_buf: str,
|
||
lit: str) -> None:
|
||
"""追加字面量到格式串(% 转义为 %%)"""
|
||
if fmt_buf is None or lit is None:
|
||
return
|
||
cur_len: t.CSizeT = string.strlen(fmt_buf)
|
||
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
|
||
if rem == 0:
|
||
return
|
||
src_len: t.CSizeT = string.strlen(lit)
|
||
si: t.CSizeT = 0
|
||
while si < src_len and rem > 0:
|
||
ch: t.CChar = lit[si]
|
||
if ch == '%':
|
||
if rem < 2:
|
||
break
|
||
fmt_buf[cur_len] = '%'
|
||
fmt_buf[cur_len + 1] = '%'
|
||
cur_len += 2
|
||
rem -= 2
|
||
else:
|
||
fmt_buf[cur_len] = ch
|
||
cur_len += 1
|
||
rem -= 1
|
||
si += 1
|
||
fmt_buf[cur_len] = '\0'
|
||
|
||
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
|
||
"""追加原始字符串(不做 % 转义)"""
|
||
if fmt_buf is None or s is None:
|
||
return
|
||
cur_len: t.CSizeT = string.strlen(fmt_buf)
|
||
src_len: t.CSizeT = string.strlen(s)
|
||
if cur_len + src_len < FSTR_BUF_SIZE:
|
||
string.strcpy(fmt_buf + cur_len, s)
|
||
|
||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
|
||
spec_node: ast.AST | t.CPtr,
|
||
val: llvmlite.Value | t.CPtr,
|
||
arg_node: ast.AST | t.CPtr) -> str:
|
||
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
|
||
if spec_node is None or pool is None or val is None:
|
||
return None
|
||
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
|
||
spec: str = pool.alloc(FSTR_SPEC_MAX)
|
||
if spec is None:
|
||
return None
|
||
spec[0] = '\0'
|
||
sk: int = spec_node.kind()
|
||
if sk == ast.ASTKind.JoinedStr:
|
||
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
|
||
if jss.values is None:
|
||
return None
|
||
vn: t.CSizeT = len(jss.values)
|
||
for vi in range(vn):
|
||
vpart: ast.AST | t.CPtr = jss.values.get(vi)
|
||
if vpart is None:
|
||
continue
|
||
if vpart.kind() != ast.ASTKind.Constant:
|
||
return None
|
||
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
|
||
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
|
||
return None
|
||
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
|
||
string.strcat(spec, vcn.str_val)
|
||
elif sk == ast.ASTKind.Constant:
|
||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
|
||
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
|
||
return None
|
||
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
||
else:
|
||
return None
|
||
slen: t.CSizeT = string.strlen(spec)
|
||
if slen == 0:
|
||
return None
|
||
|
||
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
|
||
pos: t.CSizeT = 0
|
||
align_char: t.CChar = 0
|
||
if pos < slen:
|
||
ch0: t.CChar = spec[pos]
|
||
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
|
||
align_char = ch0
|
||
pos += 1
|
||
elif pos + 1 < slen:
|
||
ch1: t.CChar = spec[pos + 1]
|
||
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
|
||
align_char = ch1
|
||
pos += 2
|
||
c_flags: str = pool.alloc(FSTR_FMT_MAX)
|
||
c_width: str = pool.alloc(32)
|
||
c_prec: str = pool.alloc(32)
|
||
if c_flags is None or c_width is None or c_prec is None:
|
||
return None
|
||
c_flags[0] = '\0'
|
||
c_width[0] = '\0'
|
||
c_prec[0] = '\0'
|
||
# sign
|
||
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
|
||
if spec[pos] == '+':
|
||
string.strcat(c_flags, "+")
|
||
elif spec[pos] == ' ':
|
||
string.strcat(c_flags, " ")
|
||
pos += 1
|
||
# alt '#'
|
||
if pos < slen and spec[pos] == '#':
|
||
string.strcat(c_flags, "#")
|
||
pos += 1
|
||
# zero '0'
|
||
if pos < slen and spec[pos] == '0':
|
||
string.strcat(c_flags, "0")
|
||
pos += 1
|
||
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
|
||
if align_char == '<' or align_char == '^':
|
||
string.strcat(c_flags, "-")
|
||
# width digits
|
||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||
wl: t.CSizeT = string.strlen(c_width)
|
||
if wl < 30:
|
||
c_width[wl] = spec[pos]
|
||
c_width[wl + 1] = '\0'
|
||
pos += 1
|
||
# comma ',' → C printf 不支持千分位,忽略
|
||
if pos < slen and spec[pos] == ',':
|
||
pos += 1
|
||
# .precision
|
||
if pos < slen and spec[pos] == '.':
|
||
string.strcat(c_prec, ".")
|
||
pos += 1
|
||
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
|
||
pl: t.CSizeT = string.strlen(c_prec)
|
||
if pl < 30:
|
||
c_prec[pl] = spec[pos]
|
||
c_prec[pl + 1] = '\0'
|
||
pos += 1
|
||
if string.strlen(c_prec) == 1:
|
||
return None # 只有 '.' 无效
|
||
# type char
|
||
fmt_type: t.CChar = 0
|
||
if pos < slen:
|
||
fmt_type = spec[pos]
|
||
pos += 1
|
||
if pos != slen:
|
||
return None # 多余字符无法解析
|
||
|
||
# 3. 长度修饰(按值类型位宽)
|
||
len_mod: str = pool.alloc(4)
|
||
if len_mod is None:
|
||
return None
|
||
len_mod[0] = '\0'
|
||
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||
if ibits == 64:
|
||
string.strcpy(len_mod, "ll")
|
||
elif ibits == 32:
|
||
string.strcpy(len_mod, "l")
|
||
elif ibits == 16:
|
||
string.strcpy(len_mod, "h")
|
||
elif ibits == 8:
|
||
string.strcpy(len_mod, "hh")
|
||
|
||
# 4. 组装 C 格式串
|
||
out: str = pool.alloc(FSTR_FMT_MAX)
|
||
if out is None:
|
||
return None
|
||
out[0] = '\0'
|
||
string.strcat(out, "%")
|
||
string.strcat(out, c_flags)
|
||
string.strcat(out, c_width)
|
||
string.strcat(out, c_prec)
|
||
if fmt_type == 0:
|
||
# 无 type:按值类型推断
|
||
fbits2: int = HandlesExpr.get_llvm_float_bits(val.Ty)
|
||
if ibits != 0:
|
||
if ibits == 8:
|
||
string.strcat(out, "c")
|
||
elif ibits == 1:
|
||
string.strcat(out, "d")
|
||
else:
|
||
string.strcat(out, len_mod)
|
||
string.strcat(out, "d")
|
||
elif fbits2 != 0:
|
||
string.strcat(out, "f")
|
||
elif HandlesExpr.is_ptr_type(val.Ty) != 0:
|
||
string.strcat(out, "s")
|
||
else:
|
||
string.strcat(out, "d")
|
||
elif fmt_type == 'c':
|
||
string.strcat(out, "c")
|
||
elif fmt_type == 'f' or fmt_type == 'F':
|
||
string.strcat(out, "f")
|
||
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
|
||
tmp_ch: str = pool.alloc(2)
|
||
if tmp_ch is None:
|
||
return None
|
||
tmp_ch[0] = fmt_type
|
||
tmp_ch[1] = '\0'
|
||
string.strcat(out, tmp_ch)
|
||
elif fmt_type == 's':
|
||
string.strcat(out, "s")
|
||
elif fmt_type == '%':
|
||
string.strcat(out, "%")
|
||
elif fmt_type == 'b':
|
||
return None # 二进制不支持
|
||
elif fmt_type == 'd' or fmt_type == 'i':
|
||
string.strcat(out, len_mod)
|
||
string.strcat(out, "d")
|
||
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
|
||
string.strcat(out, len_mod)
|
||
tmp_ch2: str = pool.alloc(2)
|
||
if tmp_ch2 is None:
|
||
return None
|
||
tmp_ch2[0] = fmt_type
|
||
tmp_ch2[1] = '\0'
|
||
string.strcat(out, tmp_ch2)
|
||
else:
|
||
return None
|
||
return out
|
||
|
||
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
|
||
val: llvmlite.Value | t.CPtr,
|
||
arg_node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr) -> str:
|
||
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
|
||
if val is None or pool is None:
|
||
return None
|
||
out: str = pool.alloc(16)
|
||
if out is None:
|
||
return None
|
||
out[0] = '\0'
|
||
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
|
||
if ibits == 1:
|
||
return "%d" # bool
|
||
if ibits == 8:
|
||
return "%c" # 字符
|
||
if ibits != 0:
|
||
if self._node_type_unsigned(pool, arg_node, trans) != 0:
|
||
if ibits == 64:
|
||
string.strcpy(out, "%llu")
|
||
else:
|
||
string.strcpy(out, "%u")
|
||
else:
|
||
if ibits == 64:
|
||
string.strcpy(out, "%lld")
|
||
else:
|
||
string.strcpy(out, "%d")
|
||
return out
|
||
if fbits != 0:
|
||
return "%f"
|
||
if HandlesExpr.is_ptr_type(val.Ty) != 0:
|
||
return "%s"
|
||
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
|
||
HandlesType.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
|
||
return None
|
||
|
||
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
val: llvmlite.Value | t.CPtr,
|
||
c_fmt: str,
|
||
arg_node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
|
||
if val is None or c_fmt is None:
|
||
return None
|
||
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
|
||
fmt_char: t.CChar = 0
|
||
flen: t.CSizeT = string.strlen(c_fmt)
|
||
fi: t.CSizeT = flen
|
||
while fi > 0:
|
||
fi -= 1
|
||
ch: t.CChar = c_fmt[fi]
|
||
if ch == '%':
|
||
break
|
||
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
|
||
if ch != 'l' and ch != 'h':
|
||
fmt_char = ch
|
||
break
|
||
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||
if fmt_char == 's':
|
||
return val
|
||
if fmt_char == 'c':
|
||
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||
if ibits != 0 and ibits < 32:
|
||
return llvmlite.build_zext(builder, val, i32_ty)
|
||
return val
|
||
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
|
||
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
|
||
ibits = HandlesExpr.get_llvm_type_bits(val.Ty)
|
||
if ibits == 1:
|
||
return llvmlite.build_zext(builder, val, i32_ty)
|
||
if ibits == 8 or ibits == 16:
|
||
if self._node_type_unsigned(pool, arg_node, trans) != 0:
|
||
return llvmlite.build_zext(builder, val, i32_ty)
|
||
return llvmlite.build_sext(builder, val, i32_ty)
|
||
return val
|
||
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
|
||
or fmt_char == 'g' or fmt_char == 'G':
|
||
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
|
||
if fbits == 32:
|
||
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
|
||
return val
|
||
return val
|
||
|
||
# ============================================================
|
||
# 翻译三元表达式 IfExp(test, body, orelse)
|
||
#
|
||
# 生成: cond ? body : orelse
|
||
# then_blk: 翻译 body → br merge
|
||
# else_blk: 翻译 orelse → br merge
|
||
# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
|
||
# ============================================================
|
||
def translate_ifexp(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value"""
|
||
if node is None or builder is None or trans is None:
|
||
return None
|
||
ie: ast.IfExp | t.CPtr = (ast.IfExp | t.CPtr)(node)
|
||
if ie is None:
|
||
return None
|
||
|
||
func: llvmlite.Function | t.CPtr = trans._cur_func
|
||
if func is None:
|
||
return None
|
||
|
||
# 1. 求值条件
|
||
cond_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, ie.test, None, 0, trans)
|
||
if cond_val is None:
|
||
return None
|
||
# 转换为 i1
|
||
cond_bits: int = self.get_llvm_type_bits(cond_val.Ty)
|
||
if cond_bits == 1:
|
||
cond_i1: llvmlite.Value | t.CPtr = cond_val
|
||
else:
|
||
if self.is_ptr_type(cond_val.Ty) != 0:
|
||
# 指针类型:与 null 比较
|
||
null_cond: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cond_val.Ty, "null")
|
||
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
|
||
else:
|
||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
|
||
|
||
# 2. 创建三个基本块
|
||
cnt: int = trans._label_counter
|
||
trans._label_counter = cnt + 1
|
||
name_buf: str = pool.alloc(32)
|
||
if name_buf is None:
|
||
return None
|
||
viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
|
||
then_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||
viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
|
||
else_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||
viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
|
||
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
if then_bb is None or else_bb is None or merge_bb is None:
|
||
return None
|
||
|
||
# 3. 条件跳转
|
||
llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)
|
||
|
||
# 4. then 分支
|
||
llvmlite.position_at_end(builder, then_bb)
|
||
body_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, ie.body, None, 0, trans)
|
||
then_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
|
||
if body_val is None:
|
||
# fail-fast:三元表达式 then 分支翻译失败,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(ie.body, "三元表达式分支翻译失败", "TypeError")
|
||
# 暂不 br,等类型对齐后再 br
|
||
|
||
# 5. else 分支
|
||
llvmlite.position_at_end(builder, else_bb)
|
||
orelse_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, ie.orelse, None, 0, trans)
|
||
else_blk_for_phi: llvmlite.BasicBlock | t.CPtr = trans._cur_builder.CurBlock
|
||
if orelse_val is None:
|
||
# fail-fast:三元表达式 else 分支翻译失败,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(ie.orelse, "三元表达式分支翻译失败", "TypeError")
|
||
# 暂不 br,等类型对齐后再 br
|
||
|
||
# 类型对齐 (两分支类型可能不同)
|
||
# 必须在各自分支中执行 coerce_to_type,确保 merge 块中 PHI 是第一条指令
|
||
# (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则)
|
||
body_bits: int = self.get_llvm_type_bits(body_val.Ty)
|
||
orelse_bits: int = self.get_llvm_type_bits(orelse_val.Ty)
|
||
body_is_ptr: int = self.is_ptr_type(body_val.Ty)
|
||
orelse_is_ptr: int = self.is_ptr_type(orelse_val.Ty)
|
||
# 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null)
|
||
# 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
|
||
# 选择 body_val.Ty 作为 phi_ty(then 分支优先),orelse_val 对齐到它
|
||
# coerce_to_type 内部会调用 ptrtoint(指针→整数)或 inttoptr(整数→指针)
|
||
if body_is_ptr != orelse_is_ptr:
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
elif body_bits > orelse_bits and orelse_bits > 0:
|
||
# 在 else 块中转换 orelse_val
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
elif orelse_bits > body_bits and body_bits > 0:
|
||
# 回到 then 块转换 body_val
|
||
llvmlite.position_at_end(builder, then_blk_for_phi)
|
||
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
|
||
elif body_bits == orelse_bits and body_bits > 0:
|
||
# bits 相同但类型不同(如 i64 vs Ptr):null 不能用于 i64 PHI
|
||
# 此分支理论上不会触发(已被情况0覆盖),保留作为防御
|
||
if self.is_ptr_type(body_val.Ty) != self.is_ptr_type(orelse_val.Ty):
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
|
||
# 现在分别在 then/else 块中 br 到 merge(在类型转换之后)
|
||
llvmlite.position_at_end(builder, then_blk_for_phi)
|
||
llvmlite.build_br(builder, merge_bb)
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
llvmlite.build_br(builder, merge_bb)
|
||
|
||
# 6. merge 块: phi(PHI 是 merge 块的第一条指令)
|
||
llvmlite.position_at_end(builder, merge_bb)
|
||
|
||
phi_ty: llvmlite.LLVMType | t.CPtr = body_val.Ty
|
||
if phi_ty is None:
|
||
# fail-fast:三元表达式分支类型缺失,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(node, "三元表达式分支类型缺失", "TypeError")
|
||
|
||
inc1: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
|
||
pool, body_val, then_blk_for_phi)
|
||
inc2: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(
|
||
pool, orelse_val, else_blk_for_phi)
|
||
if inc1 is None or inc2 is None:
|
||
return None
|
||
# 链表顺序: inc1 (head) -> inc2 (tail)
|
||
# 之前错误写成 inc2.Next = inc1,导致 build_phi 从 inc1 遍历时
|
||
# inc1.Next 为 None,只输出一个 phi 条目,引发 LLVM 验证错误:
|
||
# "PHINode should have one entry for each predecessor"
|
||
inc1.Next = inc2
|
||
return llvmlite.build_phi(builder, phi_ty, inc1, 2)
|
||
|
||
# ============================================================
|
||
# 翻译比较表达式 Compare(left, ops, comparators)
|
||
# ============================================================
|
||
def should_zext_widen(self, pool: memhub.MemBuddy | t.CPtr,
|
||
narrow_val: llvmlite.Value | t.CPtr,
|
||
wide_val: llvmlite.Value | t.CPtr,
|
||
narrow_node: ast.AST | t.CPtr = None) -> int:
|
||
"""判断整数拓宽方向:1=zext(无符号语义),0=sext
|
||
|
||
对齐 TPC 的 _check_node_unsigned / HandlesExprOps 拓宽规则:
|
||
1. i1 (bool) → zext(1 保持 1,而非 sext 的 0xFF...)
|
||
2. i8 → zext(系统编程中 i8 几乎总是无符号字节)
|
||
3. 宽侧是常量且其值超出窄侧位宽的有符号范围 → zext
|
||
(常量"想要"无符号解释,如 0xAABBCCDD > INT32_MAX 与
|
||
t.CUInt32T 值比较,sext 会把 0xAABBCCDD 变成
|
||
0xFFFFFFFFAABBCCDD 导致恒不等)
|
||
"""
|
||
if narrow_val is None or narrow_val.Ty is None:
|
||
return 0
|
||
narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty)
|
||
if narrow_bits == 1:
|
||
return 1
|
||
if narrow_bits == 8:
|
||
return 1
|
||
# 宽侧常量启发式
|
||
if wide_val is not None and wide_val.IsConst == 1:
|
||
wide_bits: int = self.get_llvm_type_bits(wide_val.Ty)
|
||
if wide_bits > narrow_bits and narrow_bits < 64:
|
||
signed_max: t.CInt64T = (t.CInt64T(1) << (narrow_bits - 1)) - 1
|
||
if wide_val.IntVal > signed_max:
|
||
return 1
|
||
# 窄侧 AST 节点解析为无符号类型(如 t.CUIntXXT 字面量/强转)
|
||
if narrow_node is not None:
|
||
if self._node_type_unsigned(pool, narrow_node) != 0:
|
||
return 1
|
||
return 0
|
||
|
||
def _node_type_unsigned(self, pool: memhub.MemBuddy | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> int:
|
||
"""轻量判断 AST 节点是否解析为无符号整数类型(1=是,0=否/未知)
|
||
|
||
覆盖常见形态(对齐 TPC _check_node_unsigned 的 Call/Subscript 分支):
|
||
- t.CUInt8T/CUInt16T/CUInt32T/CUInt64T/CUnsigned* 等显式无符号类型强转调用
|
||
- 无符号类型名的 Name 引用(如 UInt32T 别名)
|
||
- 变量名(trans 可用时):查询 SymTab 中变量的 AnnotClassName(如 v: t.CUInt32T)
|
||
"""
|
||
if node is None:
|
||
return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
|
||
and at.attr is not None:
|
||
if self._type_name_unsigned(at.attr) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||
if nm.id is not None:
|
||
# 1) 名字本身是无符号类型名(如 UInt32T 别名)
|
||
if self._type_name_unsigned(nm.id) != 0:
|
||
return 1
|
||
# 2) 变量注解类型为无符号类型(trans 可用时查 SymTab)
|
||
if trans is not None:
|
||
ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, nm.id)
|
||
if ve is not None and ve.AnnotClassName is not None:
|
||
if self._type_name_unsigned(ve.AnnotClassName) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.BinOp:
|
||
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||
if self._node_type_unsigned(pool, bop.left, trans) != 0:
|
||
return 1
|
||
if self._node_type_unsigned(pool, bop.right, trans) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.UnaryOp:
|
||
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
|
||
if self._node_type_unsigned(pool, uop.operand, trans) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Subscript:
|
||
# 数组/指针下标: md5_buf[i](t.CArray[t.CUInt8T, N])或 buf[i]
|
||
# (形参 t.CUInt8T | t.CPtr)元素是无符号整型 → printf("%02x", ...) zext。
|
||
sub_su: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||
if sub_su.value is not None and sub_su.value.kind() == ast.ASTKind.Name:
|
||
snm_su: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_su.value)
|
||
if snm_su.id is not None and trans is not None:
|
||
ve_su: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, snm_su.id)
|
||
if ve_su is not None and ve_su.AnnotClassName is not None:
|
||
if self._annot_str_unsigned(ve_su.AnnotClassName) != 0:
|
||
return 1
|
||
return 0
|
||
return 0
|
||
|
||
def _type_name_unsigned(self, type_name: str) -> int:
|
||
"""类型名是否为无符号整数类型(精确匹配,避免子串误伤如 nUIntCount)"""
|
||
if type_name is None:
|
||
return 0
|
||
# t.CUIntXXT 形式
|
||
if string.strcmp(type_name, "CUInt8T") == 0: return 1
|
||
if string.strcmp(type_name, "CUInt16T") == 0: return 1
|
||
if string.strcmp(type_name, "CUInt32T") == 0: return 1
|
||
if string.strcmp(type_name, "CUInt64T") == 0: return 1
|
||
# stdint 裸别名(from stdint import *)
|
||
if string.strcmp(type_name, "UInt8T") == 0: return 1
|
||
if string.strcmp(type_name, "UInt16T") == 0: return 1
|
||
if string.strcmp(type_name, "UInt32T") == 0: return 1
|
||
if string.strcmp(type_name, "UInt64T") == 0: return 1
|
||
# CUnsigned* 形式
|
||
if string.strcmp(type_name, "CUnsignedChar") == 0: return 1
|
||
if string.strcmp(type_name, "CUnsignedShort") == 0: return 1
|
||
if string.strcmp(type_name, "CUnsignedInt") == 0: return 1
|
||
if string.strcmp(type_name, "CUnsignedLong") == 0: return 1
|
||
if string.strcmp(type_name, "CUnsignedLongLong") == 0: return 1
|
||
# 常见无符号别名
|
||
if string.strcmp(type_name, "BYTE") == 0: return 1
|
||
if string.strcmp(type_name, "UCHAR") == 0: return 1
|
||
if string.strcmp(type_name, "USHORT") == 0: return 1
|
||
if string.strcmp(type_name, "UINT") == 0: return 1
|
||
if string.strcmp(type_name, "ULONG") == 0: return 1
|
||
return 0
|
||
|
||
def _annot_str_unsigned(self, annot: str) -> int:
|
||
"""检查类型注解字符串是否引用无符号整型(子串扫描)
|
||
|
||
用于复合注解(t.CArray[t.CUInt8T, 16] / t.CUInt8T | t.CPtr),
|
||
_type_name_unsigned 的精确匹配对这类串不适用。
|
||
"""
|
||
if annot is None:
|
||
return 0
|
||
if string.strstr(annot, "CUInt8T") is not None: return 1
|
||
if string.strstr(annot, "CUInt16T") is not None: return 1
|
||
if string.strstr(annot, "CUInt32T") is not None: return 1
|
||
if string.strstr(annot, "CUInt64T") is not None: return 1
|
||
if string.strstr(annot, "CUnsigned") is not None: return 1
|
||
if string.strstr(annot, "BYTE") is not None: return 1
|
||
if string.strstr(annot, "UCHAR") is not None: return 1
|
||
if string.strstr(annot, "USHORT") is not None: return 1
|
||
if string.strstr(annot, "UINT") is not None: return 1
|
||
if string.strstr(annot, "ULONG") is not None: return 1
|
||
return 0
|
||
|
||
def translate_compare(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译比较表达式,返回 i1 结果"""
|
||
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
|
||
if cmp is None:
|
||
return None
|
||
|
||
ops_list: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(cmp.ops)
|
||
if ops_list is None or len(ops_list) == 0:
|
||
return None
|
||
op: int = ops_list.get(0)
|
||
|
||
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
|
||
if comparators is None or len(comparators) == 0:
|
||
return None
|
||
rhs_node: ast.AST | t.CPtr = comparators.get(0)
|
||
rhs: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, rhs_node, None, 0, trans)
|
||
if rhs is None:
|
||
return None
|
||
|
||
# === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)===
|
||
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
|
||
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
|
||
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
|
||
lhs_node: ast.AST | t.CPtr = cmp.left
|
||
cmp_with_none: int = 0
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne or op == ast.OpKind.Is or op == ast.OpKind.IsNot:
|
||
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
|
||
rhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(rhs_node)
|
||
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
|
||
cmp_with_none = 1
|
||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
|
||
lhs_cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(lhs_node)
|
||
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
|
||
cmp_with_none = 1
|
||
|
||
# === 指针判空模式: ptr != 0 / ptr == 0 ===
|
||
# 跳过运算符重载走身份比较(icmp null)。跨模块类(如 cpython.PyObject)
|
||
# 未定义 __eq__/__ne__ 时,try_operator_overload 无法判定方法不存在
|
||
# (类在依赖模块,cls_sha1 != trans.ModuleSha1),会误发幽灵
|
||
# `call void @...__ne__` → 链接 undefined reference(CPythonTest main.vp
|
||
# `py_num != 0` 根因)。C 语义:指针与字面量 0 比较 = 判空。
|
||
cmp_zero: int = 0
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
|
||
if rhs_node is not None and self._is_zero_int_literal(rhs_node) != 0:
|
||
cmp_zero = 1
|
||
if cmp_zero == 0 and lhs_node is not None and self._is_zero_int_literal(lhs_node) != 0:
|
||
cmp_zero = 1
|
||
|
||
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
|
||
# None 比较跳过重载(走身份比较)
|
||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
|
||
if nm is not None and nm.id is not None:
|
||
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||
trans.SymTab, nm.id)
|
||
if lhs_alloca is not None:
|
||
if cmp_zero == 0 or self.get_ptr_level(lhs_alloca.Ty) == 0:
|
||
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||
pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
|
||
if ovl_result is not None:
|
||
return ovl_result
|
||
|
||
# 正常翻译 lhs
|
||
lhs: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, cmp.left, None, 0, trans)
|
||
if lhs is None:
|
||
return None
|
||
|
||
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
|
||
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
|
||
# None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过
|
||
if cmp_with_none == 0 and (cmp_zero == 0 or self.get_ptr_level(lhs.Ty) == 0):
|
||
ovl_result2: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||
pool, builder, mod, lhs, rhs, op, trans, 1)
|
||
if ovl_result2 is not None:
|
||
return ovl_result2
|
||
|
||
predicate: int = llvmlite.ICMP_SLT
|
||
if op == ast.OpKind.Lt:
|
||
predicate = llvmlite.ICMP_SLT
|
||
elif op == ast.OpKind.Le:
|
||
predicate = llvmlite.ICMP_SLE
|
||
elif op == ast.OpKind.Gt:
|
||
predicate = llvmlite.ICMP_SGT
|
||
elif op == ast.OpKind.Ge:
|
||
predicate = llvmlite.ICMP_SGE
|
||
elif op == ast.OpKind.Eq:
|
||
predicate = llvmlite.ICMP_EQ
|
||
elif op == ast.OpKind.Ne:
|
||
predicate = llvmlite.ICMP_NE
|
||
elif op == ast.OpKind.Is:
|
||
# `x is y` → 指针/值相等比较
|
||
predicate = llvmlite.ICMP_EQ
|
||
elif op == ast.OpKind.IsNot:
|
||
# `x is not y` → 指针/值不等比较
|
||
predicate = llvmlite.ICMP_NE
|
||
|
||
# === 类型安全检查(BUG2 修复)===
|
||
# 规则:
|
||
# - 数值(0) vs 数值(0) → 允许(无关类型,按位宽 sext 对齐)
|
||
# - 一级指针(1) vs 一级指针(1) → 允许(无关 pointee 类型,地址比较)
|
||
# - 数值(0) vs 指针(1+) → 禁止(fast-fail,除 None 比较外)
|
||
# - 不同级指针(1 vs 2 等) → 禁止(fast-fail)
|
||
#
|
||
# None 比较跳过此检查(None 被翻译为 null 指针或 0,与任意指针/数值比较合法)
|
||
if cmp_with_none == 0:
|
||
lhs_ptr_level: int = self.get_ptr_level(lhs.Ty)
|
||
rhs_ptr_level: int = self.get_ptr_level(rhs.Ty)
|
||
if lhs_ptr_level != rhs_ptr_level:
|
||
# 指针 vs 整数字面量 0(判空模式 ptr != 0 / ptr == 0):
|
||
# 0 视为 NULL,转成与指针侧同类型的 null 指针(icmp 需要同类型),
|
||
# 不报类型不匹配(ClassArrayTest 的 if ptr != 0 等常见模式)。
|
||
zero_is_rhs: int = 0
|
||
zero_is_lhs: int = 0
|
||
if rhs_ptr_level == 0 and self._is_zero_int_literal(rhs_node) != 0:
|
||
zero_is_rhs = 1
|
||
if lhs_ptr_level == 0 and self._is_zero_int_literal(lhs_node) != 0:
|
||
zero_is_lhs = 1
|
||
if zero_is_rhs != 0 or zero_is_lhs != 0:
|
||
if zero_is_rhs != 0:
|
||
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
|
||
else:
|
||
lhs = llvmlite.ConstNull(pool, rhs.Ty, "null")
|
||
else:
|
||
# 类型不匹配:数值vs指针 或 不同级指针
|
||
# fail-fast:比较类型不匹配立即终止编译(暴露上游根因),
|
||
# 不再 VLogger.error + 返回 None 静默跳过
|
||
HandlesType.fatal_error(
|
||
cmp,
|
||
f"比较类型不匹配: 数值与指针/不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})",
|
||
"TypeError")
|
||
else:
|
||
# None 比较: None 被翻译为 i8* null(一级指针)
|
||
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
|
||
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
|
||
# 如果 lhs 是结构体值类型,结构体永远不等于 None,返回常量 i1
|
||
lhs_ptr_level_none: int = self.get_ptr_level(lhs.Ty)
|
||
rhs_ptr_level_none: int = self.get_ptr_level(rhs.Ty)
|
||
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
|
||
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
|
||
# 由下方 bitcast 对齐处理,此处不处理
|
||
pass
|
||
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
|
||
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
|
||
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
|
||
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
|
||
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
|
||
lhs_is_struct_none: int = 0
|
||
match lhs.Ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
lhs_is_struct_none = 1
|
||
case _:
|
||
lhs_is_struct_none = 0
|
||
if lhs_is_struct_none != 0:
|
||
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||
return llvmlite.const_int(pool, 1, 0)
|
||
else: # Ne, IsNot
|
||
return llvmlite.const_int(pool, 1, 1)
|
||
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
|
||
rhs = llvmlite.const_int32(pool, 0)
|
||
|
||
# 类型对齐:icmp 要求两边类型相同
|
||
# 仅对数值类型做对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
|
||
# i1 (bool) 侧用 zext 对齐(1 保持 1,而非 sext 的 0xFF...)
|
||
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
|
||
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
|
||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
|
||
if lhs_bits < rhs_bits:
|
||
if self.should_zext_widen(pool, lhs, rhs, cmp.left) != 0:
|
||
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
|
||
else:
|
||
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
|
||
else:
|
||
if self.should_zext_widen(pool, rhs, lhs, rhs_node) != 0:
|
||
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
|
||
else:
|
||
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
|
||
|
||
# 指针类型对齐:同层级但不同 pointee 类型的指针需要 bitcast 统一类型
|
||
# 例如 i8* vs Struct* → 将 rhs bitcast 为 lhs 的类型
|
||
if lhs_bits == 0 and rhs_bits == 0:
|
||
lhs_is_ptr_now: int = self.is_ptr_type(lhs.Ty)
|
||
rhs_is_ptr_now: int = self.is_ptr_type(rhs.Ty)
|
||
if lhs_is_ptr_now != 0 and rhs_is_ptr_now != 0:
|
||
# 两边都是指针,用 TypePrint 比较类型字符串
|
||
lhs_ty_buf: str = pool.alloc(128)
|
||
rhs_ty_buf: str = pool.alloc(128)
|
||
if lhs_ty_buf is not None and rhs_ty_buf is not None:
|
||
llvmlite.TypePrint(lhs_ty_buf, 128, lhs.Ty, pool)
|
||
llvmlite.TypePrint(rhs_ty_buf, 128, rhs.Ty, pool)
|
||
if string.strcmp(lhs_ty_buf, rhs_ty_buf) != 0:
|
||
rhs = llvmlite.build_bitcast(builder, rhs, lhs.Ty)
|
||
|
||
# 浮点比较:任一操作数为浮点类型时使用 fcmp 而非 icmp
|
||
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
|
||
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
|
||
if lhs_fbits != 0 or rhs_fbits != 0:
|
||
# 浮点类型对齐(fpext/fptrunc)
|
||
if lhs_fbits != 0 and rhs_fbits != 0 and lhs_fbits != rhs_fbits:
|
||
if lhs_fbits < rhs_fbits:
|
||
lhs = llvmlite.build_fpext(builder, lhs, rhs.Ty)
|
||
else:
|
||
rhs = llvmlite.build_fptrunc(builder, rhs, lhs.Ty)
|
||
elif lhs_fbits != 0 and rhs_fbits == 0:
|
||
# int → float
|
||
rhs = llvmlite.build_si2fp(builder, rhs, lhs.Ty)
|
||
elif rhs_fbits != 0 and lhs_fbits == 0:
|
||
lhs = llvmlite.build_si2fp(builder, lhs, rhs.Ty)
|
||
# 映射比较谓词到 fcmp
|
||
fpredicate: int = llvmlite.FCMP_OEQ
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||
fpredicate = llvmlite.FCMP_OEQ
|
||
elif op == ast.OpKind.Ne or op == ast.OpKind.IsNot:
|
||
fpredicate = llvmlite.FCMP_ONE
|
||
elif op == ast.OpKind.Lt:
|
||
fpredicate = llvmlite.FCMP_OLT
|
||
elif op == ast.OpKind.Le:
|
||
fpredicate = llvmlite.FCMP_OLE
|
||
elif op == ast.OpKind.Gt:
|
||
fpredicate = llvmlite.FCMP_OGT
|
||
elif op == ast.OpKind.Ge:
|
||
fpredicate = llvmlite.FCMP_OGE
|
||
return llvmlite.build_fcmp(builder, fpredicate, lhs, rhs)
|
||
|
||
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
|
||
|
||
# ============================================================
|
||
# 翻译一元运算表达式 UnaryOp(op, operand)
|
||
# ============================================================
|
||
def translate_unaryop(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译一元运算(-x, +x, not x, ~x)"""
|
||
uo: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
|
||
if uo is None:
|
||
return None
|
||
|
||
operand: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, uo.operand, None, 0, trans)
|
||
if operand is None:
|
||
return None
|
||
|
||
# === 一元运算符重载: -obj/+obj/~obj 调用 __neg__/__pos__/__invert__ ===
|
||
# 仅 USub/UAdd/Invert 有重载语义(not 无 dunder 方法)
|
||
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
|
||
ovl_u: llvmlite.Value | t.CPtr = self.try_unary_operator_overload(
|
||
pool, builder, mod, operand, uo.op, trans)
|
||
if ovl_u is not None:
|
||
return ovl_u
|
||
|
||
if uo.op == ast.OpKind.USub:
|
||
# -x
|
||
# 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
|
||
operand_fbits: int = self.get_llvm_float_bits(operand.Ty)
|
||
if operand_fbits > 0:
|
||
return llvmlite.build_fneg(builder, operand)
|
||
# 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 i32 与 i64 类型不匹配)
|
||
operand_bits: int = self.get_llvm_type_bits(operand.Ty)
|
||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||
if operand_bits == 64:
|
||
zero = llvmlite.const_int64(pool, 0)
|
||
elif operand_bits > 0 and operand_bits != 32:
|
||
# 其他位宽(i8/i16 等):用 i32 零值,operand 会被 coerce
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
return llvmlite.build_sub(builder, zero, operand)
|
||
elif uo.op == ast.OpKind.UAdd:
|
||
# +x = x
|
||
return operand
|
||
elif uo.op == ast.OpKind.Not:
|
||
# not x = (x == 0/null),返回 i1
|
||
if self.is_ptr_type(operand.Ty) != 0:
|
||
# 指针类型:与 null 比较
|
||
null_op: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, operand.Ty, "null")
|
||
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
|
||
# 结构体值类型:永远为真(非空),not 返回 false
|
||
operand_is_struct_not: int = 0
|
||
match operand.Ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
operand_is_struct_not = 1
|
||
case _:
|
||
operand_is_struct_not = 0
|
||
if operand_is_struct_not != 0:
|
||
return llvmlite.const_int(pool, 1, 0)
|
||
operand_bits_not: int = self.get_llvm_type_bits(operand.Ty)
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
if operand_bits_not == 64:
|
||
zero = llvmlite.const_int64(pool, 0)
|
||
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
|
||
elif uo.op == ast.OpKind.Invert:
|
||
# ~x = x ^ -1
|
||
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
|
||
neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1)
|
||
if operand_bits_inv == 64:
|
||
neg1 = llvmlite.const_int64(pool, -1)
|
||
return llvmlite.build_xor(builder, operand, neg1)
|
||
return operand
|
||
|
||
# ============================================================
|
||
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
|
||
#
|
||
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1),否则求 b,
|
||
# 最后一个值直接求值并跳 merge
|
||
# or: a or b or c → 若 a 为真短路到 merge(返回 a 的 i1),否则求 b,
|
||
# 最后一个值直接求值并跳 merge
|
||
# merge 块用 phi 合并所有入边的 i1
|
||
# ============================================================
|
||
def translate_boolop(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
funcs_ptr: t.CPtr = None,
|
||
func_count: int = 0,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译布尔运算(and/or)的短路求值,返回 i1"""
|
||
bo: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
|
||
if bo is None:
|
||
return None
|
||
|
||
op: int = bo.op
|
||
values: list[ast.AST | t.CPtr] | t.CPtr = bo.values
|
||
if values is None:
|
||
return None
|
||
count: t.CSizeT = len(values)
|
||
if count == 0:
|
||
return None
|
||
if count == 1:
|
||
return self.translate_value(builder, pool, mod, values.get(0),
|
||
funcs_ptr, func_count, trans)
|
||
|
||
func: llvmlite.Function | t.CPtr = builder.Func
|
||
if func is None:
|
||
return None
|
||
|
||
# 创建 merge BB(用 builder.Counter 生成唯一标签名,position_at_end 的
|
||
# move_to_end 保证 BB 文本顺序与控制流顺序一致,标签名用 counter 安全)
|
||
cnt: int = builder.Counter
|
||
builder.Counter = cnt + 1
|
||
name_buf: str = pool.alloc(32)
|
||
if name_buf is None:
|
||
return None
|
||
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
|
||
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
# phi 入边链表头/尾
|
||
phi_head: llvmlite.PhiIncoming | t.CPtr = None
|
||
phi_tail: llvmlite.PhiIncoming | t.CPtr = None
|
||
phi_count: int = count - 1 # 循环次数
|
||
|
||
# 对每个值(除最后一个)求值并短路
|
||
for i in range(count - 1):
|
||
val_node: ast.AST | t.CPtr = values.get(i)
|
||
val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, val_node,
|
||
funcs_ptr, func_count, trans)
|
||
if val is None:
|
||
return None
|
||
|
||
# 转为 i1(已经是 i1 的直接用,否则与 0/null 比较)
|
||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||
val_i1: llvmlite.Value | t.CPtr = val
|
||
if val_bits != 1:
|
||
if self.is_ptr_type(val.Ty) != 0:
|
||
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
|
||
null_val: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, val.Ty, "null")
|
||
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
|
||
else:
|
||
zero: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
|
||
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
|
||
|
||
# 创建 next BB(求值下一个值)
|
||
cnt = builder.Counter
|
||
builder.Counter = cnt + 1
|
||
viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
|
||
next_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
# and: 短路假→merge,真→next;or: 短路真→merge,假→next
|
||
if op == ast.OpKind.And:
|
||
llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
|
||
else:
|
||
llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)
|
||
|
||
# 添加 phi 入边:(val_i1, cond_br 所在 BB)
|
||
cur_bb: llvmlite.BasicBlock | t.CPtr = builder.CurBlock
|
||
inc: llvmlite.PhiIncoming | t.CPtr = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
|
||
if phi_head is None:
|
||
phi_head = inc
|
||
phi_tail = inc
|
||
else:
|
||
phi_tail.Next = inc
|
||
phi_tail = inc
|
||
|
||
# 定位到 next BB,求值下一个值
|
||
llvmlite.position_at_end(builder, next_bb)
|
||
|
||
# 最后一个值:求值后直接跳 merge
|
||
last_node: ast.AST | t.CPtr = values.get(count - 1)
|
||
last_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, last_node,
|
||
funcs_ptr, func_count, trans)
|
||
if last_val is None:
|
||
return None
|
||
last_bits: int = self.get_llvm_type_bits(last_val.Ty)
|
||
last_i1: llvmlite.Value | t.CPtr = last_val
|
||
if last_bits != 1:
|
||
if self.is_ptr_type(last_val.Ty) != 0:
|
||
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
|
||
null_val2: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, last_val.Ty, "null")
|
||
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
|
||
else:
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
|
||
|
||
llvmlite.build_br(builder, merge_bb)
|
||
|
||
cur_bb = builder.CurBlock
|
||
inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
|
||
if phi_head is None:
|
||
phi_head = inc
|
||
else:
|
||
phi_tail.Next = inc
|
||
phi_count += 1
|
||
|
||
# 定位到 merge,创建 phi 合并所有入边
|
||
llvmlite.position_at_end(builder, merge_bb)
|
||
i1_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int1(pool)
|
||
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
|
||
|
||
# ============================================================
|
||
# _build_source_path_from_sha1 - 通过 SHA1 从存储器构建源 .vp/.py 文件路径
|
||
#
|
||
# .vpi 文件可能未生成(Phase1 未输出 vpi),此时回退读取源 .py 文件。
|
||
# 从 StubMerger 全局存储器查找 SHA1 对应的相对路径,
|
||
# 根据 rel_path 前缀选择基础目录:
|
||
# - rel_path 以 "lib/" 开头 → App 源文件,用 {Config.SourceDir}/{rel_path}
|
||
# - 其他 → includes 文件,用 {Config.IncludesDir}/{rel_path}
|
||
#
|
||
# Returns:
|
||
# stdlib.malloc 分配的路径字符串(调用者负责 free),None 失败
|
||
# ============================================================
|
||
def _build_source_path_from_sha1(self, sha1: str) -> str:
|
||
"""通过 SHA1 从全局存储器构建源 .py 文件路径"""
|
||
if sha1 is None:
|
||
return None
|
||
if Config.IncludesDir is None:
|
||
return None
|
||
# 从 StubMerger 全局存储器获取相对路径数组
|
||
rel_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreRelArrPtr()
|
||
count: int = StubMerger.GetSha1StoreCount()
|
||
if rel_arr_ptr is None or count <= 0:
|
||
return None
|
||
sha1_arr_ptr: bytes | t.CPtr = StubMerger.GetSha1StoreArrPtr()
|
||
if sha1_arr_ptr is None:
|
||
return None
|
||
# 遍历查找匹配的 SHA1
|
||
idx_s: int = -1
|
||
for i in range(count):
|
||
sidx: t.CSizeT = t.CSizeT(i) * 17
|
||
cur_sha1: str = sha1_arr_ptr + sidx
|
||
if cur_sha1[0] == '\0':
|
||
continue
|
||
if string.strcmp(cur_sha1, sha1) == 0:
|
||
idx_s = i
|
||
break
|
||
if idx_s < 0:
|
||
return None
|
||
# 获取相对路径(MAX_REL_PATH_LEN=256,避免跨模块 CDefine 解析问题)
|
||
MAX_RPL: t.CSizeT = 256
|
||
ridx: t.CSizeT = t.CSizeT(idx_s) * MAX_RPL
|
||
rel_path: str = rel_arr_ptr + ridx
|
||
if rel_path[0] == '\0':
|
||
return None
|
||
# 选择基础目录:rel_path 以 "lib/" 开头 → App 源文件(SourceDir);
|
||
# 以 "includes/" 开头 → includes 文件(去掉 "includes/" 前缀,
|
||
# 因 IncludesDir 已包含 includes 目录本身,避免双前缀导致文件不存在)
|
||
base_dir: str = Config.IncludesDir
|
||
is_app: int = 0
|
||
if string.strlen(rel_path) >= 4:
|
||
if (rel_path[0] == 'l' and rel_path[1] == 'i' and
|
||
rel_path[2] == 'b' and rel_path[3] == '/'):
|
||
is_app = 1
|
||
if is_app != 0 and Config.SourceDir is not None:
|
||
base_dir = Config.SourceDir
|
||
elif string.strlen(rel_path) >= 9:
|
||
if (rel_path[0] == 'i' and rel_path[1] == 'n' and rel_path[2] == 'c' and
|
||
rel_path[3] == 'l' and rel_path[4] == 'u' and rel_path[5] == 'd' and
|
||
rel_path[6] == 'e' and rel_path[7] == 's' and rel_path[8] == '/'):
|
||
rel_path = rel_path + 9
|
||
# 构建完整路径: {base_dir}/{rel_path}
|
||
base_len: t.CSizeT = string.strlen(base_dir)
|
||
rel_len: t.CSizeT = string.strlen(rel_path)
|
||
path_len: t.CSizeT = base_len + rel_len + 2
|
||
buf: str = stdlib.malloc(path_len)
|
||
if buf is None:
|
||
return None
|
||
viperlib.snprintf(buf, path_len, "%s/%s", base_dir, rel_path)
|
||
return buf
|
||
|
||
# ============================================================
|
||
# _lookup_cross_module_cdefine - 跨模块查找 CDefine 常量
|
||
#
|
||
# 当当前模块的 CDefine 表中找不到某个 Name 时,从 from_imports
|
||
# 解析出源模块,再从该模块的 pyi 文件中解析 CDefine 常量值。
|
||
# 找到后缓存到当前模块的 CDefine 表中,避免重复查找。
|
||
#
|
||
# pyi 文件格式: "NAME: t.CDefine = value"
|
||
# ============================================================
|
||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy | t.CPtr,
|
||
name: str,
|
||
from_imports: str,
|
||
imported_modules: str = None) -> int:
|
||
"""跨模块查找 CDefine 常量,返回值或 -1(未找到)
|
||
|
||
查找顺序:
|
||
1. from_imports 精确匹配(from X import NAME)
|
||
2. from_imports star 回退(from X import *)
|
||
3. imported_modules 遍历(import X):当通过 import X 导入模块时,
|
||
模块中定义的 CDefine 常量可通过此路径找到(如方法签名默认参数引用)
|
||
"""
|
||
# 入口处清零标志,确保所有早退路径(return -1)都返回"未找到"状态
|
||
# 避免递归调用时前一次成功的标志残留导致误判
|
||
HandlesType.set_cdefine_found(0)
|
||
if name is None:
|
||
return -1
|
||
if from_imports is None:
|
||
from_imports = ""
|
||
|
||
# 1. 从 from_imports 查找名称对应的模块名
|
||
# 先尝试精确匹配(allow_star_fallback=0),避免 CDefine 常量被 star import 误导
|
||
mod_name_raw: str = HandlesImports.lookup_from_import(from_imports, name, 0)
|
||
if mod_name_raw is None:
|
||
# 精确匹配失败:尝试 star import 回退
|
||
mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1)
|
||
|
||
# 当前模块名(去前导点),用于构建子模块全名 base_mod.X(如 "hashlib" + "." +
|
||
# "__md5")。此前 base_mod 未赋值(未初始化变量)→ 子模块递归 strlen(NULL)
|
||
# 崩溃(ASan access-violation,Attribute count 新路径触发 _lookup_cross_module_cdefine)。
|
||
base_mod: str = mod_name_raw
|
||
if base_mod is not None:
|
||
while base_mod[0] == '.':
|
||
base_mod = base_mod + 1
|
||
|
||
# 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6)
|
||
# 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中,
|
||
# 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules
|
||
if mod_name_raw is None:
|
||
# from_imports 完全没有匹配(精确和 star 都失败)
|
||
# 直接进入 imported_modules 遍历(步骤 9.6)
|
||
if imported_modules is None:
|
||
return -1
|
||
# 跳过 from_imports 模块读取,直接走步骤 9.6 的 imported_modules 遍历
|
||
# 设置 result_found=0 跳过步骤 3-9,直接到 9.6
|
||
HandlesType.set_cdefine_found(0)
|
||
# 直接遍历 imported_modules
|
||
im_len: t.CSizeT = string.strlen(imported_modules)
|
||
im_ci: t.CSizeT = 0
|
||
while im_ci < im_len:
|
||
while im_ci < im_len and imported_modules[im_ci] == ' ':
|
||
im_ci += 1
|
||
if im_ci >= im_len:
|
||
break
|
||
im_start: t.CSizeT = im_ci
|
||
while im_ci < im_len and imported_modules[im_ci] != ' ':
|
||
im_ci += 1
|
||
im_end: t.CSizeT = im_ci
|
||
im_nlen: t.CSizeT = im_end - im_start
|
||
if im_nlen > 0 and im_nlen < 256:
|
||
im_buf: str = pool.alloc(im_nlen + 1)
|
||
if im_buf is not None:
|
||
mk: t.CSizeT = 0
|
||
while mk < im_nlen:
|
||
im_buf[mk] = imported_modules[im_start + mk]
|
||
mk += 1
|
||
im_buf[im_nlen] = '\0'
|
||
sub_fi: str = pool.alloc(im_nlen + 3)
|
||
if sub_fi is not None:
|
||
sub_fi[0] = '*'
|
||
sub_fi[1] = ':'
|
||
mk2: t.CSizeT = 0
|
||
while mk2 < im_nlen:
|
||
sub_fi[2 + mk2] = im_buf[mk2]
|
||
mk2 += 1
|
||
sub_fi[2 + im_nlen] = '\0'
|
||
sub_val: int = self._lookup_cross_module_cdefine(pool, name, sub_fi, None)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
return sub_val
|
||
return -1
|
||
|
||
# 2. 截断模块名 → 去前导点 → 精确+后缀查找 SHA1(复用 HandlesExprCall 公共逻辑)
|
||
sha1: str = HandlesExprCall._lookup_sha1_from_modname_raw(pool, mod_name_raw)
|
||
if sha1 is None:
|
||
return -1
|
||
|
||
# 5. 获取 temp_dir
|
||
temp_dir: str = HandlesType.get_temp_dir()
|
||
if temp_dir is None:
|
||
return -1
|
||
|
||
# 6. 构建 vpi 文件路径
|
||
td_len: t.CSizeT = string.strlen(temp_dir)
|
||
pyi_path: str = StubMerger._sliced_path(temp_dir, td_len, sha1, "vpi")
|
||
if pyi_path is None:
|
||
return -1
|
||
|
||
# 7. 读取 vpi 文件;若不存在或内容过短(空 stub)则回退读取源 .vp/.py 文件
|
||
PYI_READ_BUF_SIZE: t.CSizeT = 65536
|
||
pyi_buf: bytes = stdlib.malloc(PYI_READ_BUF_SIZE)
|
||
if pyi_buf is None:
|
||
stdlib.free(pyi_path)
|
||
return -1
|
||
pf: fileio.File | t.CPtr = fileio.File(pyi_path, fileio.MODE.R)
|
||
need_py_fallback: int = 0
|
||
bytes_read: LONG = 0
|
||
if pf.closed:
|
||
# .vpi 文件不存在:回退读取源 .vp/.py 文件
|
||
need_py_fallback = 1
|
||
else:
|
||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||
pf.close()
|
||
# vpi 内容过短(< 10 字节)视为空 stub,回退 .py
|
||
if bytes_read < 10:
|
||
need_py_fallback = 1
|
||
else:
|
||
if bytes_read < PYI_READ_BUF_SIZE:
|
||
pyi_buf[bytes_read] = 0
|
||
else:
|
||
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
|
||
# vpi(存根签名文件)中的 CDefine 只声明名字不带值
|
||
# (如 "U_M_PI: t.CDefine",无 '='),真正的值定义在源
|
||
# .vp/.py 文件中。若 vpi 中所有 "t.CDefine" 声明后均无
|
||
# '='(即 vpi 不含 CDefine 值),必须回退读源文件才能找到。
|
||
cd_sig: int = 0
|
||
cs_total: t.CSizeT = string.strlen(pyi_buf)
|
||
cs_i: t.CSizeT = 0
|
||
while cs_i + 9 < cs_total:
|
||
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and
|
||
pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and
|
||
pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and
|
||
pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and
|
||
pyi_buf[cs_i + 8] == 'e'):
|
||
# 检查该行剩余部分是否有 '='(带值声明才在 vpi 中匹配)
|
||
cd_eq: int = 0
|
||
cs_j: t.CSizeT = cs_i + 9
|
||
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
|
||
if pyi_buf[cs_j] == '=':
|
||
cd_eq = 1
|
||
break
|
||
cs_j += 1
|
||
if cd_eq != 0:
|
||
cd_sig = 1
|
||
break
|
||
cs_i += 1
|
||
if cd_sig == 0:
|
||
need_py_fallback = 1
|
||
stdlib.free(pyi_path)
|
||
|
||
if need_py_fallback != 0:
|
||
# 从 SHA1 存储器查找相对路径,构建 {base_dir}/{rel_path}
|
||
src_path: str = self._build_source_path_from_sha1(sha1)
|
||
if src_path is None:
|
||
# 存储器中找不到 SHA1 = 编译器 bug(Phase1/Phase2 未填充存储器)
|
||
# 报错终止,不静默返回 -1(避免后续生成错误的 @UNDEFINED 全局变量引用)
|
||
err_buf: str = pool.alloc(512)
|
||
if err_buf is not None:
|
||
viperlib.snprintf(err_buf, 512,
|
||
"跨模块 CDefine 查找失败:SHA1 %s 不在存储器中(base_mod=%s name=%s)。"
|
||
"Phase1/Phase2 未正确填充 PopulateSha1MapStore/AppendToSha1MapStore",
|
||
sha1, base_mod, name)
|
||
VLogger.error(err_buf, "XMOD-CD")
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
pf = fileio.File(src_path, fileio.MODE.R)
|
||
stdlib.free(src_path)
|
||
if pf.closed:
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
bytes_read = pf.read_all(pyi_buf, PYI_READ_BUF_SIZE)
|
||
pf.close()
|
||
if bytes_read <= 0:
|
||
stdlib.free(pyi_buf)
|
||
return -1
|
||
if bytes_read < PYI_READ_BUF_SIZE:
|
||
pyi_buf[bytes_read] = 0
|
||
else:
|
||
pyi_buf[PYI_READ_BUF_SIZE - 1] = 0
|
||
|
||
# 8. 在 pyi_buf 中查找 "name: t.CDefine = value" 行
|
||
# 格式: NAME: t.CDefine = value
|
||
name_len: t.CSizeT = string.strlen(name)
|
||
total_len: t.CSizeT = string.strlen(pyi_buf)
|
||
pos: t.CSizeT = 0
|
||
result_val: int = 0
|
||
result_found: int = 0
|
||
res_is_double: int = 0
|
||
while pos < total_len:
|
||
# 找到行首
|
||
line_start: t.CSizeT = pos
|
||
# 找到行尾
|
||
while pos < total_len and pyi_buf[pos] != '\n':
|
||
pos += 1
|
||
line_len: t.CSizeT = pos - line_start
|
||
if pos < total_len:
|
||
pos += 1 # 跳过 '\n'
|
||
|
||
# 检查行是否以 "name:" 开头
|
||
if line_len < name_len + 1:
|
||
continue
|
||
match_ok: int = 1
|
||
for ki in range(name_len):
|
||
if pyi_buf[line_start + ki] != name[ki]:
|
||
match_ok = 0
|
||
break
|
||
if match_ok == 0:
|
||
continue
|
||
if pyi_buf[line_start + name_len] != ':':
|
||
continue
|
||
|
||
# 检查行是否包含 "t.CDefine"
|
||
# 在行中查找 "t.CDefine"
|
||
has_cdefine: int = 0
|
||
ci: t.CSizeT = line_start + name_len + 1
|
||
while ci + 8 <= line_start + line_len:
|
||
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and
|
||
pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and
|
||
pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and
|
||
pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and
|
||
pyi_buf[ci + 8] == 'e'):
|
||
has_cdefine = 1
|
||
break
|
||
ci += 1
|
||
if has_cdefine == 0:
|
||
continue
|
||
|
||
# 找到 "=" 后面的值
|
||
eq_pos: t.CSizeT = line_start + name_len + 1
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=':
|
||
eq_pos += 1
|
||
if eq_pos >= line_start + line_len:
|
||
continue
|
||
eq_pos += 1 # 跳过 '='
|
||
|
||
# 跳过空格
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# 检查是否是 t.CUnsignedLong(...) / t.CLong(...) / t.CInt(...) 等类型构造函数
|
||
# 格式: t.CUnsignedLong(-11) / t.CUnsignedLong(0xFFFFFFFF)
|
||
# 如果是,跳过 "t.CXxx(" 前缀,解析括号内的值,忽略结尾 ')'
|
||
is_type_ctor: int = 0
|
||
if eq_pos + 2 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
|
||
# 找到 '(' 的位置
|
||
paren_pos: t.CSizeT = eq_pos + 2
|
||
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(':
|
||
paren_pos += 1
|
||
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
|
||
is_type_ctor = 1
|
||
eq_pos = paren_pos + 1 # 跳过 '('
|
||
# 跳过括号内可能的前导空格
|
||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ':
|
||
eq_pos += 1
|
||
|
||
# 解析整数值(支持十六进制 0x 前缀、负号、浮点小数点)
|
||
# 负号标记
|
||
is_neg: int = 0
|
||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '-':
|
||
is_neg = 1
|
||
eq_pos += 1
|
||
val_str_start: t.CSizeT = eq_pos
|
||
val_str_len: t.CSizeT = 0
|
||
is_hex: int = 0
|
||
is_float_val: int = 0
|
||
if eq_pos + 1 < line_start + line_len:
|
||
if pyi_buf[eq_pos] == '0' and (pyi_buf[eq_pos + 1] == 'x' or pyi_buf[eq_pos + 1] == 'X'):
|
||
is_hex = 1
|
||
val_str_len = 2
|
||
eq_pos += 2
|
||
while eq_pos < line_start + line_len:
|
||
ch: t.CChar = pyi_buf[eq_pos]
|
||
if is_hex == 1:
|
||
if ((ch >= '0' and ch <= '9') or
|
||
(ch >= 'a' and ch <= 'f') or
|
||
(ch >= 'A' and ch <= 'F')):
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
else:
|
||
if ch >= '0' and ch <= '9':
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
elif ch == '.' and is_float_val == 0:
|
||
# 浮点值:'3.14159'('.' 只允许一次)
|
||
is_float_val = 1
|
||
val_str_len += 1
|
||
eq_pos += 1
|
||
else:
|
||
break
|
||
|
||
if val_str_len == 0:
|
||
continue
|
||
|
||
# 复制值字符串到新缓冲区并解析
|
||
val_buf: str = pool.alloc(val_str_len + 1)
|
||
if val_buf is None:
|
||
continue
|
||
for vi in range(val_str_len):
|
||
val_buf[vi] = pyi_buf[val_str_start + vi]
|
||
val_buf[val_str_len] = '\0'
|
||
|
||
if is_hex == 1:
|
||
# 手动解析十六进制(跳过 0x 前缀)
|
||
hex_result: int = 0
|
||
hex_buf: str = val_buf + 2 # 跳过 "0x"
|
||
for hi in range(val_str_len - 2):
|
||
hc: t.CChar = hex_buf[hi]
|
||
hd: int = 0
|
||
if hc >= '0' and hc <= '9':
|
||
hd = hc - '0'
|
||
elif hc >= 'a' and hc <= 'f':
|
||
hd = hc - 'a' + 10
|
||
elif hc >= 'A' and hc <= 'F':
|
||
hd = hc - 'A' + 10
|
||
else:
|
||
break
|
||
hex_result = hex_result * 16 + hd
|
||
result_val = hex_result
|
||
# 十六进制负值(如 0xFFFFFFFF)保持原样,由 32 位截断处理
|
||
result_found = 1
|
||
elif is_float_val != 0:
|
||
# 浮点 CDefine(如 U_M_PI = 3.14159...):
|
||
# 值缓存为 double(register_cdefine_double),供调用处生成
|
||
# double 常量;整数部分供 int 路径使用(is_cdefine_double 标志)。
|
||
dval_f: double = string.atof(val_buf)
|
||
if is_neg != 0:
|
||
dval_f = -dval_f
|
||
HandlesType.register_cdefine_double(pool, name, dval_f)
|
||
result_val = string.atoi(val_buf)
|
||
if is_neg != 0:
|
||
result_val = -result_val
|
||
result_found = 1
|
||
res_is_double = 1
|
||
else:
|
||
result_val = string.atoi(val_buf)
|
||
# 应用负号(如 t.CUnsignedLong(-11) → -11)
|
||
if is_neg != 0:
|
||
result_val = -result_val
|
||
result_found = 1
|
||
break
|
||
|
||
# 9.5 如果当前模块文件中没找到,递归查找子模块
|
||
# 处理 from .X import NAME 和 from .X import * 的情况
|
||
# 例如 ast.__init__.vp 中 from .base import CONST_INT, ...
|
||
# 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1
|
||
if result_found == 0:
|
||
# base_mod 已在函数开头计算(mod_name_raw 去前导点),
|
||
# 供子模块递归构建 "base_mod.X" 全名。
|
||
name_len_rc: t.CSizeT = string.strlen(name)
|
||
rc_pos: t.CSizeT = 0
|
||
while rc_pos < total_len and result_found == 0:
|
||
# 找到行首
|
||
rc_line_start: t.CSizeT = rc_pos
|
||
# 找到行尾
|
||
while rc_pos < total_len and pyi_buf[rc_pos] != '\n':
|
||
rc_pos += 1
|
||
rc_line_len: t.CSizeT = rc_pos - rc_line_start
|
||
if rc_pos < total_len:
|
||
rc_pos += 1
|
||
|
||
# 检查行是否以 "from ." 开头(相对导入子模块)
|
||
if rc_line_len < 12:
|
||
continue
|
||
if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \
|
||
pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \
|
||
pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.':
|
||
continue
|
||
|
||
# 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止)
|
||
rc_sub_start: t.CSizeT = rc_line_start + 6
|
||
rc_sub_end: t.CSizeT = rc_sub_start
|
||
while rc_sub_end < rc_line_start + rc_line_len and \
|
||
pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t':
|
||
rc_sub_end += 1
|
||
rc_sub_len: t.CSizeT = rc_sub_end - rc_sub_start
|
||
if rc_sub_len == 0:
|
||
continue
|
||
|
||
# 检查行是否包含 "import"
|
||
rc_has_import: int = 0
|
||
rc_ipos: t.CSizeT = rc_sub_end
|
||
while rc_ipos + 6 <= rc_line_start + rc_line_len:
|
||
if pyi_buf[rc_ipos] == 'i' and pyi_buf[rc_ipos+1] == 'm' and \
|
||
pyi_buf[rc_ipos+2] == 'p' and pyi_buf[rc_ipos+3] == 'o' and \
|
||
pyi_buf[rc_ipos+4] == 'r' and pyi_buf[rc_ipos+5] == 't':
|
||
rc_has_import = 1
|
||
break
|
||
rc_ipos += 1
|
||
if rc_has_import == 0:
|
||
continue
|
||
|
||
# 检查 NAME 是否在导入列表中,或导入是 *
|
||
# 处理多行导入:from .X import (... NAME, ...)
|
||
# 从 "import" 之后扫描,如果有 "(" 则扫描到 ")" 为止
|
||
rc_imp_start: t.CSizeT = rc_ipos + 6
|
||
rc_star: int = 0
|
||
rc_name_match: int = 0
|
||
|
||
# 确定扫描范围:单行或跨行(括号内)
|
||
rc_scan_end: t.CSizeT = rc_line_start + rc_line_len
|
||
rc_has_paren: int = 0
|
||
rc_pp: t.CSizeT = rc_imp_start
|
||
while rc_pp < rc_line_start + rc_line_len:
|
||
if pyi_buf[rc_pp] == '(':
|
||
rc_has_paren = 1
|
||
break
|
||
if pyi_buf[rc_pp] == '*':
|
||
rc_star = 1
|
||
rc_pp += 1
|
||
if rc_has_paren != 0:
|
||
# 多行导入:从 "(" 之后扫描到 ")" 为止
|
||
rc_scan_end = rc_pp + 1
|
||
rc_paren_closed: int = 0
|
||
rc_sp: t.CSizeT = rc_pp + 1
|
||
while rc_sp < total_len and rc_paren_closed == 0:
|
||
if pyi_buf[rc_sp] == ')':
|
||
rc_paren_closed = 1
|
||
rc_scan_end = rc_sp
|
||
break
|
||
rc_sp += 1
|
||
if rc_paren_closed == 0:
|
||
rc_scan_end = total_len
|
||
|
||
# 在 rc_imp_start 到 rc_scan_end 范围内查找 NAME 和 *
|
||
rc_cpos: t.CSizeT = rc_imp_start
|
||
while rc_cpos < rc_scan_end:
|
||
rc_ch: t.CChar = pyi_buf[rc_cpos]
|
||
if rc_ch == '*':
|
||
rc_star = 1
|
||
# 检查 NAME 是否匹配(作为完整单词)
|
||
if rc_cpos + name_len_rc <= rc_scan_end:
|
||
rc_match: int = 1
|
||
rc_ki: t.CSizeT
|
||
for rc_ki in range(name_len_rc):
|
||
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
|
||
rc_match = 0
|
||
break
|
||
if rc_match == 1:
|
||
# 检查前字符是否是非标识符字符
|
||
rc_before_ok: int = 0
|
||
if rc_cpos == rc_imp_start:
|
||
rc_before_ok = 1
|
||
else:
|
||
rc_before_ch: t.CChar = pyi_buf[rc_cpos - 1]
|
||
if rc_before_ch == ' ' or rc_before_ch == '\t' or \
|
||
rc_before_ch == '(' or rc_before_ch == ',' or \
|
||
rc_before_ch == '\n' or rc_before_ch == '\r':
|
||
rc_before_ok = 1
|
||
# 检查后字符是否是非标识符字符
|
||
rc_after_ok: int = 0
|
||
rc_after_pos: t.CSizeT = rc_cpos + name_len_rc
|
||
if rc_after_pos >= rc_scan_end:
|
||
rc_after_ok = 1
|
||
else:
|
||
rc_after_ch: t.CChar = pyi_buf[rc_after_pos]
|
||
if rc_after_ch == ' ' or rc_after_ch == '\t' or \
|
||
rc_after_ch == ')' or rc_after_ch == ',' or \
|
||
rc_after_ch == '\r' or rc_after_ch == '\n':
|
||
rc_after_ok = 1
|
||
if rc_before_ok == 1 and rc_after_ok == 1:
|
||
rc_name_match = 1
|
||
rc_cpos += 1
|
||
|
||
if rc_star == 0 and rc_name_match == 0:
|
||
continue
|
||
|
||
# 复制子模块名到新缓冲区(pyi_buf + rc_sub_start 不是 NUL 结尾)
|
||
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
|
||
if rc_sub_buf is None:
|
||
continue
|
||
rc_si: t.CSizeT
|
||
for rc_si in range(rc_sub_len):
|
||
rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||
rc_sub_buf[rc_sub_len] = '\0'
|
||
|
||
# 构建完整子模块名 base_mod.X
|
||
rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1
|
||
rc_full_mod: str = pool.alloc(rc_full_len)
|
||
if rc_full_mod is None:
|
||
continue
|
||
viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf)
|
||
|
||
# 构建临时 from_imports "name:full_mod"
|
||
rc_fi_len: t.CSizeT = name_len_rc + 1 + string.strlen(rc_full_mod) + 1
|
||
rc_fi: str = pool.alloc(rc_fi_len)
|
||
if rc_fi is None:
|
||
continue
|
||
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
|
||
|
||
# 递归调用查找子模块
|
||
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
result_val = rc_sub_val
|
||
result_found = 1
|
||
|
||
stdlib.free(pyi_buf)
|
||
|
||
# 9.6 from_imports star fallback 模块中未找到:回退到 imported_modules 遍历
|
||
# 当 `from stdint import *` 导致 star fallback 返回 stdint,但目标常量
|
||
# (如 SHARE_READ)实际在 `import w32.fileio` 导入的模块中时,
|
||
# 需要遍历 imported_modules 逐个查找
|
||
if result_found == 0 and imported_modules is not None:
|
||
im2_len: t.CSizeT = string.strlen(imported_modules)
|
||
im2_ci: t.CSizeT = 0
|
||
while im2_ci < im2_len and result_found == 0:
|
||
while im2_ci < im2_len and imported_modules[im2_ci] == ' ':
|
||
im2_ci += 1
|
||
if im2_ci >= im2_len:
|
||
break
|
||
im2_start: t.CSizeT = im2_ci
|
||
while im2_ci < im2_len and imported_modules[im2_ci] != ' ':
|
||
im2_ci += 1
|
||
im2_end: t.CSizeT = im2_ci
|
||
im2_nlen: t.CSizeT = im2_end - im2_start
|
||
if im2_nlen > 0 and im2_nlen < 256:
|
||
im2_buf: str = pool.alloc(im2_nlen + 1)
|
||
if im2_buf is not None:
|
||
mk3: t.CSizeT = 0
|
||
while mk3 < im2_nlen:
|
||
im2_buf[mk3] = imported_modules[im2_start + mk3]
|
||
mk3 += 1
|
||
im2_buf[im2_nlen] = '\0'
|
||
sub_fi2: str = pool.alloc(im2_nlen + 3)
|
||
if sub_fi2 is not None:
|
||
sub_fi2[0] = '*'
|
||
sub_fi2[1] = ':'
|
||
mk4: t.CSizeT = 0
|
||
while mk4 < im2_nlen:
|
||
sub_fi2[2 + mk4] = im2_buf[mk4]
|
||
mk4 += 1
|
||
sub_fi2[2 + im2_nlen] = '\0'
|
||
sub_val2: int = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
result_val = sub_val2
|
||
result_found = 1
|
||
|
||
# 10. 缓存到当前模块的 CDefine 表中
|
||
# 浮点 CDefine 已在匹配循环中 register_cdefine_double 缓存
|
||
# (res_is_double=1),此处只缓存整数 CDefine,避免覆盖 double。
|
||
if result_found != 0:
|
||
if res_is_double == 0:
|
||
HandlesType.register_cdefine_constant(pool, name, result_val)
|
||
HandlesType.set_cdefine_found(1)
|
||
else:
|
||
HandlesType.set_cdefine_found(0)
|
||
|
||
return result_val
|
||
|
||
# ============================================================
|
||
# _is_cdefine_name_pattern - 检查名字是否符合 CDefine 命名约定
|
||
#
|
||
# CDefine 常量(如 FOREGROUND_GREEN, STD_OUTPUT_HANDLE, SCOPE_MODULE)
|
||
# 都遵循 ALL_CAPS 约定:仅含大写字母 A-Z、数字 0-9、下划线 _,
|
||
# 且至少含一个大写字母。
|
||
#
|
||
# Python 关键字(match, if, for)和变量名(self, pool, builder)
|
||
# 都含小写字母,不会匹配,从而避免 fail-fast 误报。
|
||
#
|
||
# 返回: 1=符合 CDefine 命名约定, 0=不符合
|
||
# ============================================================
|
||
def _is_cdefine_name_pattern(self, name: str) -> int:
|
||
"""检查名字是否符合 CDefine 命名约定(全大写+下划线+数字)"""
|
||
if name is None:
|
||
return 0
|
||
has_upper: int = 0
|
||
i: t.CSizeT = 0
|
||
while name[i] != '\0':
|
||
c: t.CChar = name[i]
|
||
if c >= 'a' and c <= 'z':
|
||
return 0
|
||
if c >= 'A' and c <= 'Z':
|
||
has_upper = 1
|
||
i += 1
|
||
return has_upper
|
||
|
||
# ============================================================
|
||
# 翻译变量引用(Name 节点)→ load
|
||
# ============================================================
|
||
def translate_name_value(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译变量引用(Name 节点)→ load"""
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
|
||
if nm is None:
|
||
return None
|
||
nm_id: str = nm.id
|
||
if nm_id is None:
|
||
return None
|
||
|
||
# CDefine 编译期常量: 返回常量值(浮点 CDefine → double 常量,整数 → i32)
|
||
# NAME: t.CDefine = value 形式定义的常量在编译期已注册到本地表
|
||
# 浮点 CDefine(如 vipermath 的 U_M_PI=3.14...)必须返回浮点常量,
|
||
# 否则在 double 函数里 return 生成 ret i32 0(llc 报错)。
|
||
cdef_val_d: double = HandlesType.lookup_cdefine_double(nm_id)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
if HandlesType.is_cdefine_str() != 0:
|
||
# 字符串 CDefine(如 ZLIB_VERSION: t.CDefine = "1.3.2"):
|
||
# 返回 global string(i8*),供 return ZLIB_VERSION 等使用。
|
||
cdef_sv: str = HandlesType.lookup_cdefine_string(nm_id)
|
||
if cdef_sv is not None:
|
||
return self.create_global_string(builder, pool, mod, cdef_sv, trans)
|
||
HandlesType.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
|
||
if HandlesType.is_cdefine_double() != 0:
|
||
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), cdef_val_d)
|
||
cdef_val_i: int = t.CInt(cdef_val_d)
|
||
return llvmlite.const_int32(pool, cdef_val_i)
|
||
|
||
# 本地表未找到:尝试跨模块 .vpi 查找(保证多模块查表存表一致)
|
||
# 仅对符合 CDefine 命名约定(ALL_CAPS)的名字触发跨模块查找,
|
||
# 避免对普通变量名(mb/name/ptr/pool/self 等)误触发,造成严重性能损耗
|
||
if self._is_cdefine_name_pattern(nm_id) != 0:
|
||
if trans is not None:
|
||
cdef_val = self._lookup_cross_module_cdefine(
|
||
pool, nm_id, trans._from_imports, trans._imported_modules)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
if HandlesType.is_cdefine_str() != 0:
|
||
cdef_sv2: str = HandlesType.lookup_cdefine_string(nm_id)
|
||
if cdef_sv2 is not None:
|
||
return self.create_global_string(builder, pool, mod, cdef_sv2, trans)
|
||
HandlesType.fatal_error(node, f"CDefine 字符串值缺失: {nm_id}", "InternalCompilerError")
|
||
# 跨模块浮点 CDefine 已缓存为 double(is_cdefine_double),
|
||
# 需返回浮点常量而非截断的整数
|
||
dchk_nm: double = HandlesType.lookup_cdefine_double(nm_id)
|
||
if HandlesType.is_cdefine_double() != 0:
|
||
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nm)
|
||
return llvmlite.const_int32(pool, cdef_val)
|
||
|
||
# global 变量:从模块作用域查找(global 声明优先级最高)
|
||
if trans is not None:
|
||
if HT.is_global_name(trans, nm_id) != 0:
|
||
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
|
||
trans.SymTab, nm_id)
|
||
if mod_alloca is not None:
|
||
load_ty: llvmlite.LLVMType | t.CPtr = None
|
||
if mod_alloca.Ty is not None:
|
||
load_ty = mod_alloca.Ty.Pointee
|
||
if load_ty is None:
|
||
# fail-fast:global 变量存储类型缺失,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(
|
||
node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
|
||
return llvmlite.build_load(builder, load_ty, mod_alloca)
|
||
|
||
# nonlocal 变量:通过闭包 env 访问
|
||
if HT.is_nonlocal_name(trans, nm_id) != 0:
|
||
return HandlesNonlocal.load_nonlocal_var(trans, nm_id)
|
||
|
||
# *args va_list:变量名直接解析为指向 24 字节 va_list 缓冲区的 i8* 地址
|
||
# (对齐 TPC _direct_values[vararg_name] = va_list_ptr;不 load 缓冲区内容,
|
||
# 转发时按值传该地址即可,如 sprintf → vsnprintf(..., args))
|
||
if trans is not None and trans._vararg_name is not None:
|
||
if nm_id is not None and string.strcmp(nm_id, trans._vararg_name) == 0:
|
||
if trans._va_list_ptr is not None:
|
||
return trans._va_list_ptr
|
||
|
||
# 局部变量查找(必须先于模块别名检查)
|
||
# 否则与模块同名的局部变量(如 `import t, c` 后的局部变量 c)会被误判为模块别名,
|
||
# 导致 SymTab 已注册的局部变量查找不到(rhs_val is None bug 根因)
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id)
|
||
if alloca is not None:
|
||
load_ty: llvmlite.LLVMType | t.CPtr = None
|
||
if alloca.Ty is not None:
|
||
load_ty = alloca.Ty.Pointee
|
||
if load_ty is None:
|
||
# fail-fast:局部变量存储类型缺失,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(
|
||
node, f"变量 '{nm_id}' 存储类型缺失", "TypeError")
|
||
# 结构体值变量(alloca 类型为 Struct,如 `x: Cls | t.CPtr`):
|
||
# 按"对象即指针"语义返回其地址(alloca 指针),而非加载结构体值。
|
||
# 结构体值无法参与 ptrtoint/方法调用等地址相关操作,加载值会导致
|
||
# `ptrtoint %Struct ... to i64` 无效指令(llc invalid cast opcode)。
|
||
match load_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
return alloca
|
||
case llvmlite.LLVMType.Array(_, _):
|
||
# 数组变量按 C 语义退化为指针(返回数组基址),而非整体
|
||
# load 数组值——如 printf("%s", str_arr) 应传数组首地址,
|
||
# load [N x i8] 值传给 %s 会读到错误数据崩溃
|
||
# (ClassArrayTest TestCharArray 运行时崩溃根因)。
|
||
return alloca
|
||
return llvmlite.build_load(builder, load_ty, alloca)
|
||
|
||
# 模块别名检查:SymTab 查找失败后才检查
|
||
# 如果 nm_id 是已导入模块名(如 win32file, fileio)且未被声明为局部变量,
|
||
# 返回 None 让上层处理(用于属性访问 win32file.CreateFileA)
|
||
if trans is not None and trans._imported_modules is not None:
|
||
if HandlesImports.is_module_imported(trans._imported_modules, nm_id) != 0:
|
||
return None
|
||
|
||
# 模块级函数引用:函数名作为值传递(如 CreateThread(..., _thread_alloc_free, ...))。
|
||
# 之前缺失此查找导致函数名参数翻译返回 None 被跳过,后续参数整体前移
|
||
# (lpStartAddress 变成对象地址,线程从数据区执行直接崩溃)。
|
||
# 注意:不能直接返回 Function 对象,也不能直接 build_bitcast(Function)——
|
||
# Function 布局是 {Next, Name, RetTy, Params, ...},value_get_ty 会错位读到
|
||
# Name 字段,TypePrint 输出 "void",生成 `bitcast void ... to i8*` 非法 IR;
|
||
# 且 Function.Next 是模块函数链表指针,参数链表构建对其赋值会截断函数链表,
|
||
# 最终打印出 `declare void @()` 空名函数。正确做法:按 Function 的
|
||
# RetTy/Params 构造真实函数类型,用 new_value 包装成普通 Value 再 bitcast。
|
||
if trans is not None and trans.Module is not None:
|
||
fn_ref: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(
|
||
trans.Module, nm_id)
|
||
if fn_ref is not None:
|
||
fn_name: str = llvmlite.function_get_name(fn_ref)
|
||
if fn_name is None:
|
||
return None
|
||
# 构造函数类型:ret_ty + 参数 ParamNode 链表
|
||
fn_ret_ty: llvmlite.LLVMType | t.CPtr = llvmlite.function_get_ret_ty(fn_ref)
|
||
if fn_ret_ty is None:
|
||
# fail-fast:函数返回类型缺失,无法构造函数指针(暴露上游根因)
|
||
HandlesType.fatal_error(
|
||
node, f"函数 '{nm_id}' 返回类型缺失,无法构造函数指针", "TypeError")
|
||
fn_pp: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(fn_ref)
|
||
fn_first_node: llvmlite.ParamNode | t.CPtr = None
|
||
fn_prev_node: llvmlite.ParamNode | t.CPtr = None
|
||
fn_pcount: int = 0
|
||
while fn_pp is not None:
|
||
fn_pty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(fn_pp)
|
||
fn_pn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, fn_pty)
|
||
if fn_pn is not None:
|
||
if fn_first_node is None:
|
||
fn_first_node = fn_pn
|
||
if fn_prev_node is not None:
|
||
llvmlite.paramnode_set_next(fn_prev_node, fn_pn)
|
||
fn_prev_node = fn_pn
|
||
fn_pcount += 1
|
||
fn_pp = llvmlite.param_get_next(fn_pp)
|
||
fn_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(
|
||
pool, fn_ret_ty, fn_first_node, fn_pcount)
|
||
if fn_ty is None:
|
||
return None
|
||
# 构造函数指针 Value(new_value 包装,避免触碰 Function.Next)
|
||
fn_ref_v: llvmlite.Value | t.CPtr = llvmlite.new_value(pool)
|
||
if fn_ref_v is None:
|
||
return None
|
||
llvmlite.value_set_ty(fn_ref_v, llvmlite.Ptr(pool, fn_ty))
|
||
llvmlite.value_set_isconst(fn_ref_v, 1)
|
||
fn_ref_name: str = pool.alloc(256)
|
||
if fn_ref_name is not None:
|
||
fn_ref_name[0] = '\0'
|
||
if HandlesClassDef._vtable_name_needs_quote(fn_name) != 0:
|
||
viperlib.snprintf(fn_ref_name, 256, "@\"%s\"", fn_name)
|
||
else:
|
||
viperlib.snprintf(fn_ref_name, 256, "@%s", fn_name)
|
||
llvmlite.value_set_name(fn_ref_v, fn_ref_name)
|
||
fn_i8ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
|
||
pool, llvmlite.Int8(pool))
|
||
return llvmlite.build_bitcast(builder, fn_ref_v, fn_i8ptr_ty)
|
||
|
||
# ============================================================
|
||
# 跨模块 from-import 全局变量读取(如 from .tokens import _op_head)
|
||
# 根因:模块级全局(非 CDefine/非函数)from-import 后无本模块 SymTab
|
||
# 条目,所有查找路径未命中 → translate_name_value 返回 None → 赋值被
|
||
# 静默跳过 → 变量未初始化 → 垃圾指针解引用崩溃(AstTest 的
|
||
# _read_operator 里 `cur = _op_head` 丢失导致 while 循环读未初始化 %6)。
|
||
# 语义对齐模块属性全局(sys._mbuddy):external @{全局名} 引用 + load,
|
||
# 定义由源模块提供(COFF 按名链接)。别名导入用源模块内真实名。
|
||
# ============================================================
|
||
if trans is not None and trans._from_imports is not None:
|
||
if trans.Module is not None:
|
||
# 是 from-import 名称(禁用 star 回退,避免误归 star 模块)
|
||
fi_mod: str = HandlesImports.lookup_from_import(
|
||
trans._from_imports, nm_id, 0)
|
||
if fi_mod is not None:
|
||
# 排除已注册类名/枚举名:这些是类型,不作为全局变量值读取
|
||
fi_is_class: int = 0
|
||
if HandlesStruct.get_struct_type(nm_id) is not None:
|
||
fi_is_class = 1
|
||
# 排除 ALL_CAPS CDefine(上方已处理本地+跨模块查找)
|
||
if self._is_cdefine_name_pattern(nm_id) != 0:
|
||
fi_is_class = 1
|
||
if fi_is_class == 0:
|
||
fi_gname: str = nm_id
|
||
fi_orig: str = HandlesImports.lookup_from_import_original(
|
||
trans._from_imports, nm_id)
|
||
if fi_orig is not None:
|
||
og_len_fi: t.CSizeT = 0
|
||
while fi_orig[og_len_fi] != ' ' and fi_orig[og_len_fi] != '\0':
|
||
og_len_fi += 1
|
||
if og_len_fi > 0:
|
||
og_buf_fi: str = pool.alloc(og_len_fi + 1)
|
||
if og_buf_fi is not None:
|
||
string.strncpy(og_buf_fi, fi_orig, og_len_fi)
|
||
og_buf_fi[og_len_fi] = '\0'
|
||
fi_gname = og_buf_fi
|
||
# stub 已注入全局(精确类型)→ 直接引用;未注入 → fail-fast
|
||
# (不再创建 external i8* 声明,否则 llc 报 "use of undefined value",
|
||
# 静默回退掩盖 stub 未注入的上游根因)
|
||
gv_fi: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(
|
||
trans.Module, fi_gname)
|
||
ref_fi: llvmlite.Value | t.CPtr = None
|
||
if gv_fi is not None and gv_fi.Ty is not None:
|
||
ref_fi = self.make_global_ref(pool, fi_gname, gv_fi.Ty)
|
||
else:
|
||
HandlesType.fatal_error(
|
||
node,
|
||
f"跨模块全局变量 '{fi_gname}' 类型未知(stub 未注入)",
|
||
"TypeError")
|
||
if ref_fi is not None and ref_fi.Ty is not None:
|
||
lty_fi: llvmlite.LLVMType | t.CPtr = ref_fi.Ty.Pointee
|
||
if lty_fi is not None:
|
||
return llvmlite.build_load(builder, lty_fi, ref_fi)
|
||
return ref_fi
|
||
|
||
# 所有查找路径均失败
|
||
# fail-fast:未定义名称立即终止编译(NameError,暴露上游根因),
|
||
# 不再静默返回 None(否则下游 store/call 到 None 崩溃,掩盖名称查找失败)。
|
||
# 豁免:关键字/内建名(self/pool/true/false/None/match 上下文关键字、_、
|
||
# CIf 系等特殊名字)——这些名字不注册 SymTab 属正常,调用方有合法语义
|
||
# 处理路径(如 CIf 未定义名特判走 else),保持返回 None 不报错。
|
||
if nm_id is None:
|
||
return None
|
||
if string.strcmp(nm_id, "self") == 0 or string.strcmp(nm_id, "pool") == 0 \
|
||
or string.strcmp(nm_id, "true") == 0 or string.strcmp(nm_id, "false") == 0 \
|
||
or string.strcmp(nm_id, "None") == 0 or string.strcmp(nm_id, "match") == 0 \
|
||
or string.strcmp(nm_id, "_") == 0 \
|
||
or string.strcmp(nm_id, "CIf") == 0 or string.strcmp(nm_id, "CIfdef") == 0 \
|
||
or string.strcmp(nm_id, "CIfndef") == 0:
|
||
return None
|
||
HandlesType.fatal_error(node, f"未定义名称 '{nm_id}'", "NameError")
|
||
return None
|
||
|
||
# ============================================================
|
||
# list_getitem_inline — 内联生成 list[T] __getitem__ 逻辑
|
||
#
|
||
# list 结构布局 (6 字段 × 8 字节):
|
||
# __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40)
|
||
# 返回元素地址 (i8*),调用方根据元素类型 load 正确的值
|
||
# ============================================================
|
||
def list_getitem_inline(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
lm_obj: llvmlite.Value | t.CPtr,
|
||
idx_val: llvmlite.Value | t.CPtr,
|
||
node: ast.AST | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""内联生成 list __getitem__ 逻辑,返回元素地址 (i8*)"""
|
||
if builder is None or pool is None or lm_obj is None or idx_val is None:
|
||
return None
|
||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||
# 加载 __data__ (偏移 0, index 0)
|
||
g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
|
||
g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
|
||
if g_dpp is None:
|
||
return None
|
||
g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
|
||
# 加载 __elem_size__ (偏移 32, index 4)
|
||
g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
|
||
g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
|
||
if g_epp is None:
|
||
return None
|
||
g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
|
||
if g_data is None or g_esize is None:
|
||
return None
|
||
g_idx_i64: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, idx_val, i64_ty, node)
|
||
if g_idx_i64 is None:
|
||
return None
|
||
g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
|
||
if g_offset is None:
|
||
return None
|
||
# 返回元素地址 (i8*),不 load
|
||
return llvmlite.build_gep(builder, i8_ty, g_data, g_offset)
|
||
|
||
# ============================================================
|
||
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
|
||
#
|
||
# 类型名格式: <sha1>.list[<elem_type>]
|
||
# 用 strstr 检查完整模式,避免子串提取
|
||
# 支持: list[int]→i32, list[str]/list[bytes]→i8*, list[CInt]→i32 等
|
||
# 默认: i32
|
||
# ============================================================
|
||
def _list_elem_type_from_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||
struct_name: str) -> llvmlite.LLVMType | t.CPtr:
|
||
"""从 list 类型名提取元素 LLVM 类型"""
|
||
if pool is None or struct_name is None:
|
||
return None
|
||
# 用 strstr 检查完整模式
|
||
if string.strstr(struct_name, "list[int]") is not None:
|
||
return llvmlite.Int32(pool)
|
||
if string.strstr(struct_name, "list[CInt]") is not None:
|
||
return llvmlite.Int32(pool)
|
||
if string.strstr(struct_name, "list[CInt8T]") is not None:
|
||
return llvmlite.Int8(pool)
|
||
if string.strstr(struct_name, "list[CInt16T]") is not None:
|
||
return llvmlite.Int16(pool)
|
||
if string.strstr(struct_name, "list[CInt32T]") is not None:
|
||
return llvmlite.Int32(pool)
|
||
if string.strstr(struct_name, "list[CInt64T]") is not None:
|
||
return llvmlite.Int64(pool)
|
||
if string.strstr(struct_name, "list[CSizeT]") is not None:
|
||
return llvmlite.Int64(pool)
|
||
if string.strstr(struct_name, "list[CDouble]") is not None:
|
||
return llvmlite.Double(pool)
|
||
if string.strstr(struct_name, "list[CFloat]") is not None:
|
||
return llvmlite.Float(pool)
|
||
if string.strstr(struct_name, "list[CChar]") is not None:
|
||
return llvmlite.Int8(pool)
|
||
# str/bytes/CPtr 等已知指针元素类型 → i8*(元素存储为 8 字节地址)
|
||
i8_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
if string.strstr(struct_name, "list[str]") is not None:
|
||
return llvmlite.Ptr(pool, i8_ty_d)
|
||
if string.strstr(struct_name, "list[bytes]") is not None:
|
||
return llvmlite.Ptr(pool, i8_ty_d)
|
||
if string.strstr(struct_name, "list[CPtr]") is not None:
|
||
return llvmlite.Ptr(pool, i8_ty_d)
|
||
# 未知 list[T] 元素类型(如 list[Foo]、list[AST|t.CPtr]):元素存储为
|
||
# 8 字节槽(指针或 8 字节整数),回退 Ptr(i8) 位宽一致(load 8 字节),
|
||
# 由 AnnotClassName/特化恢复机制保证语义。禁止 fail-fast(误杀合法
|
||
# 自定义类元素列表的下标访问)。
|
||
return llvmlite.Ptr(pool, i8_ty_d)
|
||
|
||
# ============================================================
|
||
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
|
||
#
|
||
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
|
||
# ============================================================
|
||
def list_setitem_inline(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
lm_obj: llvmlite.Value | t.CPtr,
|
||
idx_val: llvmlite.Value | t.CPtr,
|
||
rhs_val: llvmlite.Value | t.CPtr,
|
||
node: ast.AST | t.CPtr = None) -> int:
|
||
"""内联生成 list __setitem__ 逻辑,返回 0"""
|
||
if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None:
|
||
return 0
|
||
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||
# 加载 __data__ (偏移 0, index 0)
|
||
s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
|
||
s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
|
||
if s_dpp is None:
|
||
return 0
|
||
s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
|
||
# 加载 __elem_size__ (偏移 32, index 4)
|
||
s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
|
||
s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
|
||
if s_epp is None:
|
||
return 0
|
||
s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
|
||
if s_data is None or s_esize is None:
|
||
return 0
|
||
s_idx_i64: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, idx_val, i64_ty, node)
|
||
if s_idx_i64 is None:
|
||
return 0
|
||
s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
|
||
if s_offset is None:
|
||
return 0
|
||
s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
|
||
if s_addr is None:
|
||
return 0
|
||
s_val_ptr: llvmlite.Value | t.CPtr = self.coerce_to_type(builder, rhs_val, i8_ptr_ty, node)
|
||
if s_val_ptr is None:
|
||
return 0
|
||
llvmlite.build_store(builder, s_val_ptr, s_addr)
|
||
return 0
|
||
|
||
# ============================================================
|
||
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
|
||
#
|
||
# 返回 list 对象指针 (Ptr(list[T])) 或 None
|
||
# ============================================================
|
||
def is_list_subscript(self, node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None"""
|
||
if node is None or trans is None:
|
||
return None
|
||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||
if sub is None or sub.value is None:
|
||
return None
|
||
if sub.value.kind() != ast.ASTKind.Name:
|
||
return None
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||
if nm is None or nm.id is None:
|
||
return None
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is None or alloca.Ty is None:
|
||
return None
|
||
if self.is_ptr_type(alloca.Ty) == 0:
|
||
return None
|
||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||
if pointee is None:
|
||
return None
|
||
# 检查 pointee 是否是 Ptr(Struct)
|
||
inner_ty: llvmlite.LLVMType | t.CPtr = None
|
||
match pointee:
|
||
case llvmlite.LLVMType.Ptr(it):
|
||
inner_ty = it
|
||
case _:
|
||
return None
|
||
if inner_ty is None:
|
||
return None
|
||
# 检查 inner_ty 是否是 Struct 且名称包含 "list["
|
||
struct_name: str = None
|
||
match inner_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, sname):
|
||
struct_name = sname
|
||
case _:
|
||
return None
|
||
if struct_name is None:
|
||
return None
|
||
if string.strstr(struct_name, "list[") is None:
|
||
return None
|
||
# 是 list 类型: load 出 list 对象指针
|
||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||
return llvmlite.build_load(builder, pointee, alloca)
|
||
|
||
# ============================================================
|
||
# _nonlocal_subscript_ptr - nonlocal 数组/指针的下标元素指针
|
||
#
|
||
# 嵌套函数(提升函数)体内访问 nonlocal 变量时,变量的实际存储是
|
||
# 运行时共享堆(env[ni] 存的堆地址),不能 lookup_var 找外层函数
|
||
# alloca(其 SSA 编号属于外层函数,当前函数引用错乱崩溃——
|
||
# ClosureTest2 Test 4 fill() 内 arr[i] 崩溃根因)。
|
||
# 正确路径:从 env 读堆地址(_load_raw_var_ptr)→ bitcast → GEP。
|
||
# ============================================================
|
||
def _nonlocal_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
nm_id: str,
|
||
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""nonlocal 数组/指针下标:从 env 读堆地址 → bitcast → GEP,返回元素指针"""
|
||
if trans is None or nm_id is None or idx_val is None:
|
||
return None
|
||
nl_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, nm_id)
|
||
if nl_ty is None:
|
||
return None
|
||
raw_nl: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(trans, nm_id)
|
||
if raw_nl is None:
|
||
return None
|
||
match nl_ty:
|
||
case llvmlite.LLVMType.Array(elem_ty_nl, _cnt_nl):
|
||
arr_ptr_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||
if arr_ptr_nl is None:
|
||
return None
|
||
return llvmlite.build_gep_array(
|
||
builder, nl_ty, elem_ty_nl, arr_ptr_nl,
|
||
self._zext_index_to_i32(builder, idx_val))
|
||
case llvmlite.LLVMType.Ptr(inner_nl):
|
||
arr_ptr_nl2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||
if arr_ptr_nl2 is None:
|
||
return None
|
||
return llvmlite.build_gep(builder, inner_nl, arr_ptr_nl2, idx_val)
|
||
case _:
|
||
arr_ptr_nl3: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||
if arr_ptr_nl3 is None:
|
||
return None
|
||
return llvmlite.build_gep(builder, nl_ty, arr_ptr_nl3, idx_val)
|
||
return None
|
||
|
||
# ============================================================
|
||
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
|
||
#
|
||
# 指针遍历: ptr 是指针变量,load 出指针值后 GEP + load
|
||
# 数组遍历: arr 是数组变量,用 alloca 指针做双索引 GEP [0, i] + load
|
||
# list[T]: 泛型类不注册 struct,subscript 走 __getitem__ 内联路径
|
||
# ============================================================
|
||
def translate_subscript(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译下标表达式,返回加载的元素值"""
|
||
if node is None or builder is None:
|
||
return None
|
||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||
if sub is None:
|
||
return None
|
||
|
||
# 翻译索引
|
||
idx_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, sub.slice, None, 0, trans)
|
||
if idx_val is None:
|
||
return None
|
||
|
||
# 如果 value 是 Name,尝试从 alloca 类型推断
|
||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||
if nm.id is not None and trans is not None:
|
||
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
|
||
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
|
||
# 读路径:元素指针 load 出元素值
|
||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||
nl_ptr_r: llvmlite.Value | t.CPtr = self._nonlocal_subscript_ptr(
|
||
builder, pool, trans, nm.id, idx_val)
|
||
if nl_ptr_r is not None and nl_ptr_r.Ty is not None:
|
||
nl_elem_ty: llvmlite.LLVMType | t.CPtr = nl_ptr_r.Ty.Pointee
|
||
if nl_elem_ty is not None:
|
||
return llvmlite.build_load(builder, nl_elem_ty, nl_ptr_r)
|
||
return None
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is not None and alloca.Ty is not None:
|
||
if self.is_ptr_type(alloca.Ty) != 0:
|
||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||
if pointee is not None:
|
||
# 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号
|
||
# 用 match 检查 pointee 类型
|
||
match pointee:
|
||
case llvmlite.LLVMType.Array(elem_ty, count):
|
||
# 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
|
||
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||
builder, pointee, elem_ty, alloca,
|
||
self._zext_index_to_i32(builder, idx_val))
|
||
if elem_ptr is not None:
|
||
return llvmlite.build_load(builder, elem_ty, elem_ptr)
|
||
return None
|
||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||
# 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct)
|
||
# list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历
|
||
list_struct_name: str = None
|
||
match inner_ty:
|
||
case llvmlite.LLVMType.Struct(_, _, lsn):
|
||
list_struct_name = lsn
|
||
case _:
|
||
pass
|
||
if list_struct_name is not None:
|
||
if string.strstr(list_struct_name, "list[") is not None:
|
||
# list[T] 类型: 内联生成 __getitem__ 逻辑
|
||
ptr_val_list: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_list is not None:
|
||
elem_addr_list: llvmlite.Value | t.CPtr = self.list_getitem_inline(
|
||
builder, pool, ptr_val_list, idx_val, node)
|
||
if elem_addr_list is not None:
|
||
# 根据类型名提取元素类型,bitcast 后 load
|
||
elem_ty_list: llvmlite.LLVMType | t.CPtr = self._list_elem_type_from_name(
|
||
pool, list_struct_name)
|
||
if elem_ty_list is not None:
|
||
elem_ptr_ty_list: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
|
||
pool, elem_ty_list)
|
||
casted_addr_list: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, elem_addr_list, elem_ptr_ty_list)
|
||
if casted_addr_list is not None:
|
||
return llvmlite.build_load(
|
||
builder, elem_ty_list, casted_addr_list)
|
||
return None
|
||
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针 Ptr(Ptr(Ptr(...))),
|
||
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
|
||
# 此时 [i] 应该是指针解引用 (步长 sizeof(ptr)=8), 而非调用
|
||
# 结构体的 __getitem__ 方法 (会错误生成 AST.__getitem__ 调用)。
|
||
# 对比 HashTable|t.CPtr: alloca.Ty=HashTable**, inner_ty=Struct(HashTable)
|
||
# (非 Ptr), 仍走下面的方法调用转发路径。
|
||
if self.is_ptr_type(inner_ty) != 0:
|
||
ptr_val_pp: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_pp is not None:
|
||
elem_ptr_pp: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, inner_ty, ptr_val_pp, idx_val)
|
||
if elem_ptr_pp is not None:
|
||
return llvmlite.build_load(builder, inner_ty, elem_ptr_pp)
|
||
return None
|
||
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
|
||
# 仅当类定义了 __getitem__(当前模块定义或跨模块 stub declare 注入)
|
||
# 才转发方法调用;否则普通结构体指针按下标做 GEP 数组访问
|
||
# (对齐 TPC _HandleSubscriptLlvm: if ClassName and has_function(__getitem__))
|
||
cls_nm_rd_ptr: str = self._get_custom_struct_cls_nm(pool, inner_ty)
|
||
if not (cls_nm_rd_ptr is None):
|
||
gi_lookup_rd: str = pool.alloc(128)
|
||
if gi_lookup_rd is not None:
|
||
viperlib.snprintf(gi_lookup_rd, 128, "%s.__getitem__", cls_nm_rd_ptr)
|
||
if HandlesExprCall.find_func_in_module(mod, gi_lookup_rd) is not None:
|
||
obj_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if not (obj_ptr_rd is None):
|
||
arg_vals_rd_p: t.CSizeT | t.CPtr = pool.alloc(8)
|
||
if not (arg_vals_rd_p is None):
|
||
arg_vals_rd_p[0] = t.CSizeT(idx_val)
|
||
ret_rd_p: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, cls_nm_rd_ptr, "__getitem__",
|
||
obj_ptr_rd, arg_vals_rd_p, 1, trans, sub)
|
||
if not (ret_rd_p is None):
|
||
return ret_rd_p
|
||
# 无 __getitem__:结构体指针数组访问(GEP + load)
|
||
ptr_val_rd_g: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_rd_g is not None:
|
||
elem_rd_g: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, inner_ty, ptr_val_rd_g, idx_val)
|
||
if elem_rd_g is not None:
|
||
return llvmlite.build_load(builder, inner_ty, elem_rd_g)
|
||
return None
|
||
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**,
|
||
# 直接 GEP 按 i8* 步长(8 字节),load i8*
|
||
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
|
||
if trans is not None:
|
||
ve_pe_ts: HandlesVar.VarEntry | t.CPtr = \
|
||
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve_pe_ts is not None and ve_pe_ts.IsPtrElement == 1:
|
||
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值(i8*),
|
||
# elem[i] 语义 = load [指针值 + i*8](8 字节指针元素)。
|
||
# 必须先 load alloca 得指针值,再 GEP i8*(步长 8)——
|
||
# 否则 GEP base 用 alloca(&elem_ptr)读到栈变量而非数据区,
|
||
# list.get 返回"元素地址"而非"元素值"(dump 虚调用读错 vtable)。
|
||
ptr_val_pe_ts: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_pe_ts is not None:
|
||
elem_ptr_ts: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, pointee, ptr_val_pe_ts, idx_val)
|
||
if elem_ptr_ts is not None:
|
||
return llvmlite.build_load(builder, pointee, elem_ptr_ts)
|
||
return None
|
||
# 普通指针遍历: 先 load 指针值,再 GEP
|
||
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val is not None:
|
||
elem_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, inner_ty, ptr_val, idx_val)
|
||
if elem_ptr2 is not None:
|
||
return llvmlite.build_load(builder, inner_ty, elem_ptr2)
|
||
return None
|
||
case _:
|
||
# 联合类型变量(IsPtrElement=1,如 _list.vp 的
|
||
# elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但
|
||
# 元素是 8 字节指针,须按 i8* 步长 GEP + load i8*——
|
||
# 否则 list.get 只读 1 字节,指针截断 inttoptr 崩溃。
|
||
if trans is not None:
|
||
ve_pe_rd: HandlesVar.VarEntry | t.CPtr = \
|
||
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve_pe_rd is not None and ve_pe_rd.IsPtrElement == 1:
|
||
match pointee:
|
||
case llvmlite.LLVMType.Int8():
|
||
i8_ptr_rd: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
|
||
pool, llvmlite.Int8(pool))
|
||
elem_ptr_rd: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, i8_ptr_rd, alloca, idx_val)
|
||
if elem_ptr_rd is not None:
|
||
return llvmlite.build_load(
|
||
builder, i8_ptr_rd, elem_ptr_rd)
|
||
case _:
|
||
pass
|
||
# 自定义类(结构体类型): 仅当类定义了 __getitem__
|
||
# (当前模块定义或跨模块 stub declare 注入)才转发方法调用;
|
||
# 否则普通结构体指针按下标做 GEP 数组访问
|
||
cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee)
|
||
if not (cls_nm_sub is None):
|
||
gi_lookup_sub: str = pool.alloc(128)
|
||
if gi_lookup_sub is not None:
|
||
viperlib.snprintf(gi_lookup_sub, 128, "%s.__getitem__", cls_nm_sub)
|
||
if HandlesExprCall.find_func_in_module(mod, gi_lookup_sub) is not None:
|
||
obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if not (obj_val_sub is None):
|
||
arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8)
|
||
if not (arg_vals_sub is None):
|
||
arg_vals_sub[0] = t.CSizeT(idx_val)
|
||
ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, cls_nm_sub, "__getitem__",
|
||
obj_val_sub, arg_vals_sub, 1, trans, sub)
|
||
if not (ret_sub is None):
|
||
return ret_sub
|
||
# 无 __getitem__:结构体指针数组访问(GEP + load)
|
||
ptr_val_sub_g: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_sub_g is not None:
|
||
elem_sub_g: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, pointee, ptr_val_sub_g, idx_val)
|
||
if elem_sub_g is not None:
|
||
return llvmlite.build_load(builder, pointee, elem_sub_g)
|
||
return None
|
||
pass
|
||
|
||
# 通用路径:翻译 value 获取指针
|
||
ptr_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, sub.value, None, 0, trans)
|
||
if ptr_val is None or ptr_val.Ty is None:
|
||
return None
|
||
|
||
# 检查是否是指针类型
|
||
if self.is_ptr_type(ptr_val.Ty) != 0:
|
||
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
|
||
if elem_ty2 is not None:
|
||
# 数组类型: 双索引 GEP + load 元素
|
||
match elem_ty2:
|
||
case llvmlite.LLVMType.Array(arr_elem_ty2, arr_count2):
|
||
elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
|
||
builder, elem_ty2, arr_elem_ty2, ptr_val,
|
||
self._zext_index_to_i32(builder, idx_val))
|
||
if elem_ptr3 is not None:
|
||
return llvmlite.build_load(builder, arr_elem_ty2, elem_ptr3)
|
||
return None
|
||
case _:
|
||
pass
|
||
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __getitem__ 才转发方法调用;
|
||
# 否则普通结构体指针按下标做 GEP 数组访问
|
||
cls_nm_rd_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
|
||
if not (cls_nm_rd_gen is None):
|
||
gi_lookup_gen: str = pool.alloc(128)
|
||
if gi_lookup_gen is not None:
|
||
viperlib.snprintf(gi_lookup_gen, 128, "%s.__getitem__", cls_nm_rd_gen)
|
||
if HandlesExprCall.find_func_in_module(mod, gi_lookup_gen) is not None:
|
||
arg_vals_rd_g: t.CSizeT | t.CPtr = pool.alloc(8)
|
||
if not (arg_vals_rd_g is None):
|
||
arg_vals_rd_g[0] = t.CSizeT(idx_val)
|
||
ret_rd_g: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, cls_nm_rd_gen, "__getitem__",
|
||
ptr_val, arg_vals_rd_g, 1, trans, sub)
|
||
if not (ret_rd_g is None):
|
||
return ret_rd_g
|
||
# 无 __getitem__:结构体指针数组访问(GEP + load)
|
||
elem_ptr_gen: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, elem_ty2, ptr_val, idx_val)
|
||
if elem_ptr_gen is not None:
|
||
return llvmlite.build_load(builder, elem_ty2, elem_ptr_gen)
|
||
return None
|
||
# 指针类型: 单索引 GEP + load
|
||
elem_ptr4: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, elem_ty2, ptr_val, idx_val)
|
||
if elem_ptr4 is not None:
|
||
return llvmlite.build_load(builder, elem_ty2, elem_ptr4)
|
||
return None
|
||
|
||
# ============================================================
|
||
# find_global_in_module - 在模块全局变量链表中按名称查找
|
||
#
|
||
# 查找策略:
|
||
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
|
||
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
|
||
# ============================================================
|
||
def find_global_in_module(self, mod: llvmlite.LLVMModule | t.CPtr,
|
||
name: str) -> llvmlite.GlobalVariable | t.CPtr:
|
||
"""在模块全局变量链表中按名称查找全局变量"""
|
||
if mod is None or name is None:
|
||
return None
|
||
name_len: t.CSizeT = string.strlen(name)
|
||
cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
|
||
# 第一遍: 精确匹配
|
||
while cur is not None:
|
||
if cur.Name is not None:
|
||
if string.strcmp(cur.Name, name) == 0:
|
||
return cur
|
||
cur = cur.Next
|
||
# 第二遍: 后缀匹配 (.name)
|
||
cur = mod.GlobalHead
|
||
while cur is not None:
|
||
if cur.Name is not None:
|
||
cur_len: t.CSizeT = string.strlen(cur.Name)
|
||
if cur_len > name_len + 1:
|
||
suffix_start: t.CSizeT = cur_len - name_len
|
||
if cur.Name[suffix_start - 1] == '.':
|
||
match: int = 1
|
||
for i in range(name_len):
|
||
if cur.Name[suffix_start + i] != name[i]:
|
||
match = 0
|
||
break
|
||
if match == 1:
|
||
return cur
|
||
cur = cur.Next
|
||
return None
|
||
|
||
# ============================================================
|
||
# make_global_ref - 创建全局变量引用 (@name,类型为 ty*)
|
||
#
|
||
# 用于跨模块全局变量访问: stub 未注入时,翻译阶段生成 @name 引用,
|
||
# 链接时由 stub 提供 external global 声明。
|
||
# ============================================================
|
||
def make_global_ref(self, pool: memhub.MemBuddy | t.CPtr,
|
||
name: str,
|
||
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""创建全局变量引用 (类型为 ty*,名称为 @name)"""
|
||
if name is None or ty is None:
|
||
return None
|
||
ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, ty)
|
||
ref_name: str = pool.alloc(64)
|
||
if ref_name is None:
|
||
return None
|
||
viperlib.snprintf(ref_name, 64, "@%s", name)
|
||
return llvmlite.SSAValue(pool, ptr_ty, ref_name)
|
||
|
||
# ============================================================
|
||
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
|
||
#
|
||
# 当 obj 是已导入模块名 (如 sys._mbuddy) 时:
|
||
# 1. 在当前模块全局变量链表中查找 (stub 已注入的情况)
|
||
# 2. 未找到则创建前向引用 (@attr_name, 类型为 ty 参数)
|
||
#
|
||
# 参数:
|
||
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
|
||
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
|
||
# ============================================================
|
||
def _is_t_type_object_name(self, name: str) -> int:
|
||
"""检查 name 是否是 t.py 中的类型对象名(CPtr/CInt/CDouble 等)
|
||
|
||
这些类型对象在运行时作为全局变量 @CPtr/@CInt 等被引用,
|
||
但 t.py 不被编译,需要在使用处生成带 null 初值的定义。
|
||
"""
|
||
if name is None:
|
||
return 0
|
||
# t.py 中的类型对象名(PascalCase,首字母大写)
|
||
if string.strcmp(name, "CPtr") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CInt") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CChar") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CShort") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CLong") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CLongLong") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CDouble") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CFloat") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CBool") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CVoid") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CInt8T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CInt16T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CInt32T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CInt64T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUInt8T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUInt16T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUInt32T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUInt64T") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsigned") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsignedInt") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsignedChar") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsignedShort") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsignedLong") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUnsignedLongLong") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CSizeT") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CIntPtrT") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CUIntPtrT") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CPtrDiffT") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CType") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CDefine") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CStatic") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CExport") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CArray") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CTypedef") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CNoVTable") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CNeedPtr") == 0:
|
||
return 1
|
||
if string.strcmp(name, "CAutoPtr") == 0:
|
||
return 1
|
||
return 0
|
||
|
||
def _resolve_module_attribute_global(self, pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
at: ast.Attribute | t.CPtr,
|
||
ty_hint: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)"""
|
||
if at is None or at.value is None or at.attr is None:
|
||
return None
|
||
if at.value.kind() != ast.ASTKind.Name:
|
||
return None
|
||
if trans is None or trans._imported_modules is None:
|
||
return None
|
||
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if mod_nm.id is None:
|
||
return None
|
||
# 检查是否是已导入模块
|
||
if HandlesImports.is_module_imported(trans._imported_modules, mod_nm.id) == 0:
|
||
return None
|
||
# 在当前模块查找全局变量 (stub 可能已注入)
|
||
gv: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, at.attr)
|
||
if gv is not None and gv.Ty is not None:
|
||
return self.make_global_ref(pool, at.attr, gv.Ty)
|
||
# 未找到: 创建 external global 声明并添加到模块
|
||
# 否则只生成 @_argc 引用而无声明,LLVM 报 "use of undefined value '@_argc'"
|
||
use_ty: llvmlite.LLVMType | t.CPtr = ty_hint
|
||
if use_ty is None:
|
||
use_ty = llvmlite.Int8(pool)
|
||
# 确保 use_ty 中的跨模块结构体类型有 opaque 声明
|
||
llvmlite.module_ensure_opaque_for_type(mod, pool, use_ty)
|
||
# 特殊处理: t.py 中的类型对象(CPtr/CInt/CDouble 等)
|
||
# t.py 不被编译,但这些类型对象在运行时被引用(如 va_arg(ap, t.CPtr))。
|
||
# 创建带 null 初值的 linkonce_odr 全局变量定义,避免链接时 undefined reference。
|
||
# LLVM 22+ 不允许 external linkage 全局变量带初值,使用 linkonce_odr 替代。
|
||
is_t_type_obj: int = 0
|
||
if string.strcmp(mod_nm.id, "t") == 0:
|
||
is_t_type_obj = self._is_t_type_object_name(at.attr)
|
||
ext_gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, at.attr, use_ty)
|
||
if ext_gv is not None:
|
||
if is_t_type_obj != 0:
|
||
# t.CPtr 等类型对象: 创建带 null 初值的定义(linkonce_odr)
|
||
ext_gv.Linkage = "linkonce_odr"
|
||
# i8* null 的初值文本
|
||
null_init: bytes = pool.alloc(8)
|
||
if null_init is not None:
|
||
null_init[0] = 'n'
|
||
null_init[1] = 'u'
|
||
null_init[2] = 'l'
|
||
null_init[3] = 'l'
|
||
null_init[4] = '\0'
|
||
ext_gv.Initializer = null_init
|
||
else:
|
||
ext_gv.Initializer = None
|
||
else:
|
||
# 普通模块属性: external 声明(由其他模块提供定义)
|
||
ext_gv.Linkage = "external"
|
||
ext_gv.Initializer = None
|
||
llvmlite.module_add_global(mod, ext_gv)
|
||
return self.make_global_ref(pool, at.attr, use_ty)
|
||
|
||
# ============================================================
|
||
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
|
||
#
|
||
# 优先级:
|
||
# 1. 枚举成员访问 (State.Idle → 常量值)
|
||
# 2. 模块属性访问 (sys._mbuddy → 加载全局变量)
|
||
# 3. 结构体字段访问 (obj.field → GEP + load)
|
||
#
|
||
# 对于 Name 类型的 obj,直接使用 alloca 指针(不 load 结构体)
|
||
# ============================================================
|
||
|
||
|
||
# ============================================================
|
||
# _get_cderef_attr_base - c.Deref(ptr).attr 属性访问的 base 指针
|
||
#
|
||
# c.Deref(ptr) 作为属性访问 base 时(如 c.Deref(p).a),需要 ptr
|
||
# 指针本身而非 load 后的结构体值(*p.a → p->a),与 TPC
|
||
# _handle_attr_call_result 语义一致。否则 c.Deref 返回结构体值,
|
||
# 后续 GEP 在非指针上做属性访问会误报"base=非指针类型"。
|
||
#
|
||
# 返回:
|
||
# ptr 指针值(Ptr(Struct) 或 Ptr(Ptr(Struct)),交由 _deref_if_ptr_ptr 解层级)
|
||
# 非 c.Deref 模式返回 None
|
||
# ============================================================
|
||
def _get_cderef_attr_base(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
at_value: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""检测 at.value 是否为 c.Deref(ptr) 调用,返回 ptr 指针(不做 load)"""
|
||
if at_value is None or at_value.kind() != ast.ASTKind.Call:
|
||
return None
|
||
cl_cd: ast.Call | t.CPtr = (ast.Call | t.CPtr)(at_value)
|
||
if cl_cd.func is None or cl_cd.func.kind() != ast.ASTKind.Attribute:
|
||
return None
|
||
f_cd: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl_cd.func)
|
||
if f_cd.attr is None or string.strcmp(f_cd.attr, "Deref") != 0:
|
||
return None
|
||
if f_cd.value is None or f_cd.value.kind() != ast.ASTKind.Name:
|
||
return None
|
||
nm_cd: ast.Name | t.CPtr = (ast.Name | t.CPtr)(f_cd.value)
|
||
if nm_cd.id is None or string.strcmp(nm_cd.id, "c") != 0:
|
||
return None
|
||
if cl_cd.args is None or len(cl_cd.args) < 1:
|
||
return None
|
||
inner_cd: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, cl_cd.args.get(0), None, 0, trans)
|
||
if inner_cd is not None and self.is_ptr_type(inner_cd.Ty) != 0:
|
||
return inner_cd
|
||
return None
|
||
|
||
# ============================================================
|
||
# docstring 提取/登记/查找(__doc__ 魔法属性支持)
|
||
#
|
||
# 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有
|
||
# App/includes 文件的 AST,提取函数/类/方法的 docstring 内容
|
||
# 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性
|
||
# 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR
|
||
# 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。
|
||
# ============================================================
|
||
|
||
def _extract_docstring_from_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> str:
|
||
"""从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容"""
|
||
if body is None or len(body) == 0:
|
||
return None
|
||
first: ast.AST | t.CPtr = body.get(0)
|
||
if first is None or first.kind() != ast.ASTKind.Expr:
|
||
return None
|
||
ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(first)
|
||
if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant:
|
||
return None
|
||
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(ex.value)
|
||
if cn is None or cn.const_kind != ast.CONST_STR:
|
||
return None
|
||
return cn.str_val
|
||
|
||
def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None:
|
||
"""登记 docstring(key="{module_sha1}.{symbol}"),查重幂等"""
|
||
if module_sha1 is None or symbol is None or doc is None:
|
||
return
|
||
if string.strlen(doc) == 0:
|
||
return
|
||
if self.DocKeys is None:
|
||
self.DocKeys = stdlib.malloc(DOC_MAX * DOC_KEY_SZ)
|
||
self.DocValues = stdlib.malloc(DOC_MAX * DOC_VAL_SZ)
|
||
if self.DocKeys is None or self.DocValues is None:
|
||
return
|
||
string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ)
|
||
string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ)
|
||
if self.DocCount >= DOC_MAX:
|
||
return
|
||
ml: t.CSizeT = string.strlen(module_sha1)
|
||
sl: t.CSizeT = string.strlen(symbol)
|
||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||
if key_buf is None:
|
||
return
|
||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||
i: int
|
||
for i in range(self.DocCount):
|
||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||
stdlib.free(key_buf)
|
||
return
|
||
string.strcpy(self.DocKeys + self.DocCount * DOC_KEY_SZ, key_buf)
|
||
string.strcpy(self.DocValues + self.DocCount * DOC_VAL_SZ, doc)
|
||
self.DocCount += 1
|
||
stdlib.free(key_buf)
|
||
|
||
def lookup_docstring(self, module_sha1: str, symbol: str) -> str:
|
||
"""查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None"""
|
||
if self.DocKeys is None or module_sha1 is None or symbol is None:
|
||
return None
|
||
ml: t.CSizeT = string.strlen(module_sha1)
|
||
sl: t.CSizeT = string.strlen(symbol)
|
||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||
if key_buf is None:
|
||
return None
|
||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||
i: int
|
||
for i in range(self.DocCount):
|
||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||
stdlib.free(key_buf)
|
||
return self.DocValues + i * DOC_VAL_SZ
|
||
stdlib.free(key_buf)
|
||
return None
|
||
|
||
def extract_module_docstrings(self, tree: ast.AST | t.CPtr, module_sha1: str) -> None:
|
||
"""扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)"""
|
||
if tree is None or module_sha1 is None:
|
||
return
|
||
ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children
|
||
if ch is None:
|
||
return
|
||
cn: t.CSizeT = len(ch)
|
||
for ci in range(cn):
|
||
child: ast.AST | t.CPtr = ch.get(ci)
|
||
if child is None:
|
||
continue
|
||
kd: int = child.kind()
|
||
if kd == ast.ASTKind.FunctionDef:
|
||
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child)
|
||
if fd is not None and fd.name is not None:
|
||
fd_body: list[ast.AST | t.CPtr] | t.CPtr = fd.children
|
||
fdoc: str = self._extract_docstring_from_body(fd_body)
|
||
if fdoc is not None:
|
||
self.register_docstring(module_sha1, fd.name, fdoc)
|
||
elif kd == ast.ASTKind.ClassDef:
|
||
cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child)
|
||
if cd is None or cd.name is None:
|
||
continue
|
||
cd_body0: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||
cdoc: str = self._extract_docstring_from_body(cd_body0)
|
||
if cdoc is not None:
|
||
self.register_docstring(module_sha1, cd.name, cdoc)
|
||
# 类方法 docstring(key="{ClassName}.{method}")
|
||
cd_body: list[ast.AST | t.CPtr] | t.CPtr = cd.children
|
||
if cd_body is not None:
|
||
cbn: t.CSizeT = len(cd_body)
|
||
for mi in range(cbn):
|
||
m: ast.AST | t.CPtr = cd_body.get(mi)
|
||
if m is None or m.kind() != ast.ASTKind.FunctionDef:
|
||
continue
|
||
mfd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m)
|
||
if mfd is None or mfd.name is None:
|
||
continue
|
||
mdoc: str = self._extract_docstring_from_body(mfd.children)
|
||
if mdoc is not None:
|
||
cl_nl: t.CSizeT = string.strlen(cd.name)
|
||
mt_nl: t.CSizeT = string.strlen(mfd.name)
|
||
sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2)
|
||
if sym_buf is not None:
|
||
viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name)
|
||
self.register_docstring(module_sha1, sym_buf, mdoc)
|
||
stdlib.free(sym_buf)
|
||
|
||
def _resolve_doc_symbol_name(self, trans: HT.Translator | t.CPtr, name_id: str,
|
||
out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int:
|
||
"""解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1
|
||
|
||
判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。
|
||
"""
|
||
if trans is None or name_id is None or out_mod is None or out_sym is None:
|
||
return 0
|
||
if trans.ModuleSha1 is None:
|
||
return 0
|
||
# 1. 本地变量(函数变量/全局变量)
|
||
if HandlesVar.lookup_var(trans.SymTab, name_id) is not None:
|
||
string.strcpy(out_mod, trans.ModuleSha1)
|
||
string.strcpy(out_sym, name_id)
|
||
return 1
|
||
# 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类)
|
||
se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(name_id)
|
||
if se is not None:
|
||
if se.ModuleSha1 is not None:
|
||
string.strcpy(out_mod, se.ModuleSha1)
|
||
else:
|
||
string.strcpy(out_mod, trans.ModuleSha1)
|
||
string.strcpy(out_sym, name_id)
|
||
return 1
|
||
# 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免
|
||
# 跨 Mixin self 调用生成不存在符号)
|
||
mod_sha1: str = StubMerger.LookupSha1ByModName(name_id)
|
||
if mod_sha1 is None:
|
||
mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(name_id)
|
||
if mod_sha1 is not None:
|
||
string.strcpy(out_mod, mod_sha1)
|
||
string.strcpy(out_sym, name_id)
|
||
return 1
|
||
# 4. 兜底当前模块
|
||
string.strcpy(out_mod, trans.ModuleSha1)
|
||
string.strcpy(out_sym, name_id)
|
||
return 1
|
||
|
||
def _resolve_doc_symbol_attr(self, trans: HT.Translator | t.CPtr,
|
||
at: ast.Attribute | t.CPtr,
|
||
out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int:
|
||
"""解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym
|
||
|
||
支持形态:remote.remote_func / remote.RemoteClass /
|
||
remote.RemoteClass.remote_method / LocalClass.local_method 等。
|
||
最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。
|
||
"""
|
||
if trans is None or at is None or out_mod is None or out_sym is None:
|
||
return 0
|
||
if trans.ModuleSha1 is None:
|
||
return 0
|
||
# 收集 Attribute 链(最外层 attr 在前)
|
||
DOC_CHAIN_MAX: t.CInt = 8
|
||
items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128)
|
||
if items is None:
|
||
return 0
|
||
n_items: int = 0
|
||
cur: ast.AST | t.CPtr = (ast.AST | t.CPtr)(at)
|
||
while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX:
|
||
cat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cur)
|
||
if cat.attr is None:
|
||
break
|
||
string.strcpy(items + n_items * 128, cat.attr)
|
||
n_items += 1
|
||
cur = cat.value
|
||
# 最左应为 Name
|
||
head_id: str = None
|
||
if cur is not None and cur.kind() == ast.ASTKind.Name:
|
||
hnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cur)
|
||
head_id = hnm.id
|
||
if head_id is None:
|
||
stdlib.free(items)
|
||
return 0
|
||
# head 判定:模块名 or 变量/类
|
||
is_mod: int = 0
|
||
mod_sha1: str = None
|
||
if HandlesVar.lookup_var(trans.SymTab, head_id) is None:
|
||
mod_sha1 = StubMerger.LookupSha1ByModName(head_id)
|
||
if mod_sha1 is None:
|
||
mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(head_id)
|
||
if mod_sha1 is not None:
|
||
is_mod = 1
|
||
if is_mod != 0:
|
||
# 模块限定:symbol = items 反转(不含 head_id)
|
||
if n_items == 0:
|
||
string.strcpy(out_mod, mod_sha1)
|
||
string.strcpy(out_sym, head_id)
|
||
stdlib.free(items)
|
||
return 1
|
||
total: t.CSizeT = 0
|
||
j: int
|
||
for j in range(n_items):
|
||
total += string.strlen(items + j * 128) + 1
|
||
sym_buf: bytes = stdlib.malloc(total + 1)
|
||
if sym_buf is None:
|
||
stdlib.free(items)
|
||
return 0
|
||
sym_buf[0] = '\0'
|
||
for j in range(n_items):
|
||
if j > 0:
|
||
string.strcat(sym_buf, ".")
|
||
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
||
string.strcpy(out_mod, mod_sha1)
|
||
string.strcpy(out_sym, sym_buf)
|
||
stdlib.free(sym_buf)
|
||
stdlib.free(items)
|
||
return 1
|
||
# 变量/类:symbol = head_id + '.' + items 反转
|
||
total2: t.CSizeT = string.strlen(head_id)
|
||
for j in range(n_items):
|
||
total2 += string.strlen(items + j * 128) + 1
|
||
sym2: bytes = stdlib.malloc(total2 + 1)
|
||
if sym2 is None:
|
||
stdlib.free(items)
|
||
return 0
|
||
sym2[0] = '\0'
|
||
string.strcat(sym2, head_id)
|
||
for j in range(n_items):
|
||
string.strcat(sym2, ".")
|
||
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
||
se2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(head_id)
|
||
if se2 is not None and se2.ModuleSha1 is not None:
|
||
string.strcpy(out_mod, se2.ModuleSha1)
|
||
else:
|
||
string.strcpy(out_mod, trans.ModuleSha1)
|
||
string.strcpy(out_sym, sym2)
|
||
stdlib.free(sym2)
|
||
stdlib.free(items)
|
||
return 1
|
||
|
||
def translate_attribute(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译属性访问,返回加载的字段值"""
|
||
if node is None or builder is None:
|
||
return None
|
||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||
if at is None or at.value is None or at.attr is None:
|
||
# fast-fail: AST 节点字段缺失属解析器 bug,应报告具体缺失字段
|
||
diag_buf_an: bytes = stdlib.malloc(256)
|
||
if diag_buf_an is not None:
|
||
missing_an: str = "?"
|
||
if at is not None:
|
||
if at.value is None:
|
||
missing_an = "value"
|
||
elif at.attr is None:
|
||
missing_an = "attr"
|
||
viperlib.snprintf(diag_buf_an, 256,
|
||
"属性访问失败: AST 节点字段缺失 (%s sha1=%s:%d)",
|
||
missing_an,
|
||
trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?",
|
||
at.lineno if at is not None and at.lineno is not None else 0)
|
||
VLogger.error(diag_buf_an, "ATTR")
|
||
return None
|
||
|
||
# 魔法属性 __doc__:查询 docstring 登记表(预注册阶段从 AST 提取),
|
||
# 命中则当前模块内联生成字符串常量返回;未命中(无 docstring 的
|
||
# 函数/类/方法)返回 null 指针(i8*)。支持本模块与跨模块符号:
|
||
# local_func / LocalClass / LocalClass.method / remote.remote_func /
|
||
# remote.RemoteClass / remote.RemoteClass.remote_method
|
||
if string.strcmp(at.attr, "__doc__") == 0:
|
||
doc_mod_buf: bytes = pool.alloc(64)
|
||
doc_sym_buf: bytes = pool.alloc(256)
|
||
if doc_mod_buf is None or doc_sym_buf is None:
|
||
return None
|
||
doc_mod_buf[0] = '\0'
|
||
doc_sym_buf[0] = '\0'
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
nm_doc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm_doc.id is not None:
|
||
self._resolve_doc_symbol_name(trans, nm_doc.id, doc_mod_buf, doc_sym_buf)
|
||
elif at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
|
||
self._resolve_doc_symbol_attr(trans, (ast.Attribute | t.CPtr)(at.value), doc_mod_buf, doc_sym_buf)
|
||
doc_val: str = None
|
||
if doc_mod_buf[0] != '\0' and doc_sym_buf[0] != '\0':
|
||
doc_val = self.lookup_docstring(doc_mod_buf, doc_sym_buf)
|
||
if doc_val is None:
|
||
i8_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||
if i8_ty_doc is None:
|
||
return None
|
||
i8_ptr_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_doc)
|
||
if i8_ptr_ty_doc is None:
|
||
return None
|
||
return llvmlite.ConstNull(pool, i8_ptr_ty_doc, "null")
|
||
return self.create_global_string(builder, pool, mod, doc_val, trans)
|
||
|
||
# 枚举成员访问: EnumName.MemberName → 常量值
|
||
# 当 value 是 Name 且 Name.id 是已注册枚举时,查找成员并返回常量
|
||
if at.value.kind() == ast.ASTKind.Name:
|
||
enum_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if enum_nm.id is not None:
|
||
if HandlesEnum.is_enum_class(enum_nm.id) == 1:
|
||
member: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
|
||
enum_nm.id, at.attr)
|
||
if member is not None:
|
||
base_ty: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
|
||
enum_nm.id)
|
||
if base_ty is not None:
|
||
# 生成数字字符串作为常量名(LLVM IR 要求常量输出数字值)
|
||
name_buf: str = pool.alloc(32)
|
||
if name_buf is not None:
|
||
viperlib.snprintf(name_buf, 32, "%lld", member.Value)
|
||
return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf)
|
||
return llvmlite.const_int32(pool, member.Value)
|
||
# fast-fail: 枚举成员未找到
|
||
VLogger.error(
|
||
f"枚举成员未找到: {enum_nm.id}.{at.attr}",
|
||
"ATTR")
|
||
return None
|
||
|
||
# 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值
|
||
# 当 value 是 Attribute(如 fileio.MODE)且 attr 部分是已注册枚举时
|
||
if at.value.kind() == ast.ASTKind.Attribute:
|
||
inner_at_e: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
|
||
if inner_at_e.value is not None and inner_at_e.value.kind() == ast.ASTKind.Name:
|
||
inner_nm_e: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at_e.value)
|
||
if inner_nm_e.id is not None and inner_at_e.attr is not None:
|
||
if HandlesEnum.is_enum_class(inner_at_e.attr) == 1:
|
||
member_e: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
|
||
inner_at_e.attr, at.attr)
|
||
if member_e is not None:
|
||
base_ty_e: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
|
||
inner_at_e.attr)
|
||
if base_ty_e is not None:
|
||
name_buf_e: str = pool.alloc(32)
|
||
if name_buf_e is not None:
|
||
viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value)
|
||
return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e)
|
||
return llvmlite.const_int32(pool, member_e.Value)
|
||
# fast-fail: 嵌套枚举成员未找到
|
||
VLogger.error(
|
||
f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr}",
|
||
"ATTR")
|
||
return None
|
||
|
||
# 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy
|
||
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载
|
||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm_ma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm_ma.id is not None:
|
||
# 先检查是否是普通变量 (优先级高于模块属性)
|
||
ma_is_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||
trans.SymTab, nm_ma.id)
|
||
if ma_is_var is None:
|
||
# 先检查是否是 CDefine 编译期常量 (如 ast.CONST_INT / win32base.INVALID_HANDLE_VALUE)
|
||
# 模块属性访问的 CDefine 常量需要跨模块查找源 .vp / .vpi 文件
|
||
cdef_ma_val: int = HandlesType.lookup_cdefine_constant(at.attr)
|
||
cd_local: int = HandlesType.is_cdefine_found()
|
||
if cd_local == 0:
|
||
# 确定真实模块名:优先从 _from_imports 解析别名
|
||
# from_imports 中条目格式 "local_name:real_module"
|
||
# 如 win32base:w32.win32base → 别名 win32base 真实模块 w32.win32base
|
||
real_mod_name: str = nm_ma.id
|
||
if trans._from_imports is not None:
|
||
resolved_mod: str = HandlesImports.lookup_from_import(
|
||
trans._from_imports, nm_ma.id, 0)
|
||
if resolved_mod is not None:
|
||
# 复制模块名到新缓冲区(截断于空格、null、或 ':')
|
||
rm_len: t.CSizeT = 0
|
||
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':':
|
||
rm_len += 1
|
||
rm_buf: str = pool.alloc(rm_len + 1)
|
||
if rm_buf is not None:
|
||
for rm_i in range(rm_len):
|
||
rm_buf[rm_i] = resolved_mod[rm_i]
|
||
rm_buf[rm_len] = '\0'
|
||
real_mod_name = rm_buf
|
||
# 即使 _from_imports 为 None(如纯 `import ast` 无 from-import),
|
||
# 也必须尝试跨模块 CDefine 查找,否则 ast.CONST_INT 会被
|
||
# 误解析为全局变量引用 @CONST_INT(未定义符号)。
|
||
# 构建临时 from_imports "{attr}:{real_module}" 用于跨模块查找
|
||
if real_mod_name is not None:
|
||
ma_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(real_mod_name) + 2
|
||
ma_fi: str = pool.alloc(ma_fi_len)
|
||
if ma_fi is not None:
|
||
viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name)
|
||
cdef_ma_val = self._lookup_cross_module_cdefine(pool, at.attr, ma_fi)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
# 跨模块浮点 CDefine(如 vipermath.U_M_PI)→ 浮点常量
|
||
dchk_ma: double = HandlesType.lookup_cdefine_double(at.attr)
|
||
if HandlesType.is_cdefine_double() != 0:
|
||
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_ma)
|
||
return llvmlite.const_int32(pool, cdef_ma_val)
|
||
# 检查 nm_ma.id.at.attr 是否是已导入子模块 (如 w32.win32file)
|
||
# 如果是子模块,不应创建全局变量,让嵌套属性访问路径处理
|
||
is_submod: int = 0
|
||
if trans._imported_modules is not None:
|
||
submod_len: t.CSizeT = string.strlen(nm_ma.id) + string.strlen(at.attr) + 2
|
||
submod_buf: str = pool.alloc(submod_len)
|
||
if submod_buf is not None:
|
||
viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
|
||
if HandlesImports.is_module_imported(trans._imported_modules, submod_buf) == 1:
|
||
is_submod = 1
|
||
# also check from_imports: at.attr 别名对应真实模块 w32.win32file
|
||
if is_submod == 0 and trans._from_imports is not None:
|
||
fi_mod_raw: str = HandlesImports.lookup_from_import(
|
||
trans._from_imports, at.attr, 0)
|
||
if fi_mod_raw is not None:
|
||
# 复制模块名到新缓冲区(截断于空格、null、或 ':')
|
||
fm_len: t.CSizeT = 0
|
||
while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':':
|
||
fm_len += 1
|
||
fi_mod: str = pool.alloc(fm_len + 1)
|
||
if fi_mod is not None:
|
||
for fm_i in range(fm_len):
|
||
fi_mod[fm_i] = fi_mod_raw[fm_i]
|
||
fi_mod[fm_len] = '\0'
|
||
# 检查 fi_mod 是否以 "nm_ma.id." 开头 (如 w32.win32file 以 w32. 开头)
|
||
prefix_len: t.CSizeT = string.strlen(nm_ma.id)
|
||
fi_mod_len: t.CSizeT = string.strlen(fi_mod)
|
||
if fi_mod_len > prefix_len + 1:
|
||
match_prefix: int = 1
|
||
for pi in range(prefix_len):
|
||
if fi_mod[pi] != nm_ma.id[pi]:
|
||
match_prefix = 0
|
||
break
|
||
if match_prefix == 1 and fi_mod[prefix_len] == '.':
|
||
is_submod = 1
|
||
if is_submod != 0:
|
||
return None # 是子模块,交给嵌套属性访问路径
|
||
# 特殊处理: t.ASM_DESCR 不是 CDefine,是内联汇编约束命名空间
|
||
# t.ASM_DESCR.XXX 由 HandlesExprCall 的 _asm_resolve_constraint 处理
|
||
# 此处返回 None,让上层嵌套属性访问路径处理
|
||
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0:
|
||
return None
|
||
# CDefine 未找到且属性名符合 ALL_CAPS 约定: fail-fast 终止编译
|
||
# 不再创建未定义的外部全局变量引用(否则 llc 报 "use of undefined value")
|
||
if self._is_cdefine_name_pattern(at.attr) != 0:
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"跨模块 CDefine 未找到: '{nm_ma.id}.{at.attr}'(CDefine cross-module lookup failed)",
|
||
"TypeError")
|
||
# 非 CDefine: 使用 i8* 作为类型提示 (模块级变量通常存储指针)
|
||
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
mod_gv_ref: llvmlite.Value | t.CPtr = self._resolve_module_attribute_global(
|
||
pool, mod, trans, at, i8_ptr_hint)
|
||
if mod_gv_ref is not None and mod_gv_ref.Ty is not None:
|
||
load_ty_mod: llvmlite.LLVMType | t.CPtr = mod_gv_ref.Ty.Pointee
|
||
if load_ty_mod is not None:
|
||
return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref)
|
||
|
||
# 嵌套模块属性访问 (w32.win32file.GENERIC_READ → 查找全局 @GENERIC_READ)
|
||
# 当 at.value 是 Attribute 且解析为已导入子模块时,查找 at.attr 作为全局常量
|
||
if at.value.kind() == ast.ASTKind.Attribute and trans is not None:
|
||
inner_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(at.value)
|
||
if inner_at.value is not None and inner_at.value.kind() == ast.ASTKind.Name:
|
||
inner_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(inner_at.value)
|
||
if inner_nm.id is not None and inner_at.attr is not None:
|
||
if HandlesVar.lookup_var(trans.SymTab, inner_nm.id) is None:
|
||
# 构建限定模块名: "w32.win32file"
|
||
qual_len: t.CSizeT = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2
|
||
qual_buf: str = pool.alloc(qual_len)
|
||
if qual_buf is not None:
|
||
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
|
||
if HandlesImports.is_module_imported(trans._imported_modules, qual_buf) == 1:
|
||
# 查找全局变量 at.attr (stub 已注入的情况)
|
||
gv_nested: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, at.attr)
|
||
if gv_nested is not None and gv_nested.Ty is not None:
|
||
gv_ref_nested: llvmlite.Value | t.CPtr = self.make_global_ref(
|
||
pool, at.attr, gv_nested.Ty)
|
||
if gv_ref_nested is not None and gv_ref_nested.Ty is not None:
|
||
load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee
|
||
if load_ty_nested is not None:
|
||
return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
|
||
# 未找到全局: 尝试 CDefine 常量查找
|
||
# 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问,
|
||
# 先从限定模块的 .vpi 文件中查找 CDefine 常量值并内联
|
||
cdef_val_nested: int = HandlesType.lookup_cdefine_constant(at.attr)
|
||
if HandlesType.is_cdefine_found() == 0:
|
||
# 构建临时 from_imports "attr:module" 用于跨模块查找
|
||
tmp_fi_len: t.CSizeT = string.strlen(at.attr) + string.strlen(qual_buf) + 2
|
||
tmp_fi: str = pool.alloc(tmp_fi_len)
|
||
if tmp_fi is not None:
|
||
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
|
||
cdef_val_nested = self._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
|
||
if HandlesType.is_cdefine_found() != 0:
|
||
# 跨模块浮点 CDefine → 浮点常量
|
||
dchk_nst: double = HandlesType.lookup_cdefine_double(at.attr)
|
||
if HandlesType.is_cdefine_double() != 0:
|
||
return llvmlite.ConstFloat(pool, llvmlite.Double(pool), dchk_nst)
|
||
return llvmlite.const_int32(pool, cdef_val_nested)
|
||
# CDefine 未找到: fail-fast 终止编译(不再创建未定义的全局变量引用)
|
||
# 之前此处创建 @INVALID_HANDLE_VALUE 等前向引用,但全局未定义,
|
||
# 导致 llc 报 "use of undefined value" 错误。
|
||
# 正确做法是报错终止,让开发者修复跨模块 CDefine 查找失败的根本原因。
|
||
if self._is_cdefine_name_pattern(at.attr) != 0:
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"嵌套属性跨模块 CDefine 未找到: '{qual_buf}.{at.attr}'(CDefine cross-module lookup failed)",
|
||
"TypeError")
|
||
return None
|
||
|
||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
|
||
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
|
||
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
|
||
obj_ptr: llvmlite.Value | t.CPtr = None
|
||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm.id is not None:
|
||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
|
||
trans, nm.id)
|
||
if raw_nl_a is not None:
|
||
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
|
||
trans, nm.id)
|
||
if nl_ty_a is not None:
|
||
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
|
||
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
|
||
# 存对象指针,需 load 出对象指针
|
||
match nl_ty_a:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
obj_ptr = llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
case llvmlite.LLVMType.Array(_, _):
|
||
obj_ptr = llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
case _:
|
||
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
|
||
llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
if nl_val_ptr_a is not None:
|
||
obj_ptr = llvmlite.build_load(
|
||
builder, nl_ty_a, nl_val_ptr_a)
|
||
else:
|
||
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
|
||
# 非 Name 路径:翻译对象值(会 load)
|
||
if obj_ptr is None:
|
||
# c.Deref(ptr).attr: 属性访问 base 取指针本身而非 load 后的结构体值
|
||
# (如 c.Deref(p).a 语义是 (*p).a,直接对 p 做 GEP)
|
||
cd_base_rd: llvmlite.Value | t.CPtr = self._get_cderef_attr_base(
|
||
builder, pool, mod, at.value, trans)
|
||
if cd_base_rd is not None:
|
||
obj_ptr = cd_base_rd
|
||
elif at.value.kind() == ast.ASTKind.Subscript:
|
||
# 下标基座属性访问(如 pts[0].x):list[T]/OOP __getitem__ 类走
|
||
# translate_value(返回元素值/地址);普通结构体指针数组走
|
||
# get_subscript_ptr(GEP 元素地址,避免结构体值 base=非指针 报错)
|
||
if self._sub_base_uses_getitem(pool, mod, trans, at.value) != 0:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = self.get_subscript_ptr(builder, pool, mod, at.value, trans)
|
||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||
# 嵌套属性基座(如 r.origin.x):取内层字段地址(get_attribute_ptr 递归,
|
||
# 地址语义),指针字段(X|t.CPtr)由 _deref_if_ptr_ptr load 解引用
|
||
obj_ptr = self.get_attribute_ptr(builder, pool, mod, at.value, trans)
|
||
if obj_ptr is None:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
|
||
if obj_ptr is None or obj_ptr.Ty is None:
|
||
return None
|
||
|
||
# 如果 obj_ptr 是 Ptr(Ptr(Struct))(X|t.CPtr 变量的 alloca),
|
||
# load 解引用获取 Ptr(Struct)
|
||
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
|
||
|
||
# 查找结构体类型信息
|
||
# obj_ptr 类型应该是 Ptr(Struct(...))
|
||
match obj_ptr.Ty:
|
||
case llvmlite.LLVMType.Ptr(struct_ty):
|
||
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
|
||
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||
# 查找字段索引和类型
|
||
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
|
||
struct_ty, at.attr)
|
||
# REnum 变体字段查找: lookup_field 失败时,尝试 REnum 别名
|
||
# REnum 结构体字段名是 _p1/_p2/...,但源码用变体字段名访问(如 ty.Pointee)
|
||
if field_info is None:
|
||
renum_se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(
|
||
struct_ty)
|
||
if renum_se is not None and renum_se.Name is not None:
|
||
is_rn: int = HandlesClassDef._is_renum_class_name(renum_se.Name)
|
||
if is_rn == 1:
|
||
alias_idx: int = HandlesClassDef._lookup_renum_field_alias(
|
||
renum_se.Name, at.attr)
|
||
if alias_idx >= 1:
|
||
alias_fe: HandlesStruct.FieldEntry | t.CPtr = \
|
||
HandlesStruct._get_field_entry(renum_se, alias_idx)
|
||
if alias_fe is not None and alias_fe.Ty is not None:
|
||
alias_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||
builder, struct_ty, alias_fe.Ty, obj_ptr, alias_idx)
|
||
if alias_ptr is not None:
|
||
return llvmlite.build_load(builder, alias_fe.Ty, alias_ptr)
|
||
# property getter 回退: obj.attr 是 @property/@x.getter 方法时生成调用
|
||
# (字段未找到时先尝试,避免报误导性的"字段未找到"错误)
|
||
if field_info is None and at.value is not None and trans is not None:
|
||
prop_cls_fb: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
|
||
prop_val: llvmlite.Value | t.CPtr = HandlesExprCall._try_property_getter_attr(
|
||
pool, builder, mod, at, obj_ptr, trans, prop_cls_fb)
|
||
if prop_val is not None:
|
||
return prop_val
|
||
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
|
||
# 传递 SHA1 以区分跨模块同名类
|
||
# base 注解类名提取:Name → 变量注解类名;Attribute → 属性链递归字段注解类名
|
||
if field_info is None:
|
||
base_class: str = None
|
||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm_fb.id is not None:
|
||
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, nm_fb.id)
|
||
if var_entry is not None:
|
||
base_class = var_entry.AnnotClassName
|
||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||
base_class = self._resolve_attr_field_class(pool, trans, at.value)
|
||
if base_class is not None:
|
||
cur_sha1: str = trans.ModuleSha1
|
||
field_info = HandlesStruct.lookup_field_by_class(
|
||
base_class, at.attr, cur_sha1)
|
||
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
|
||
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
|
||
if field_info is not None:
|
||
annot_se: HandlesStruct.StructEntry | t.CPtr = \
|
||
HandlesStruct.find_struct(base_class)
|
||
if annot_se is not None and annot_se.Ty is not None:
|
||
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
|
||
llvmlite.Ptr(pool, annot_se.Ty)
|
||
casted_ptr: llvmlite.Value | t.CPtr = \
|
||
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
|
||
if casted_ptr is not None:
|
||
obj_ptr = casted_ptr
|
||
struct_ty = annot_se.Ty
|
||
# 按需过滤更新 struct_ty 后,需重新确保
|
||
# 注解类型结构体定义在当前模块可用
|
||
# (原始 struct_ty 可能是 i8,ensure 无效)
|
||
HandlesStruct.ensure_struct_def_in_module(
|
||
pool, mod, annot_se.Ty)
|
||
if field_info is not None:
|
||
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
|
||
# 联合体:bitcast obj_ptr 到 field_ty* 后 load
|
||
if HandlesStruct.is_union_by_type(struct_ty) == 1:
|
||
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_ty)
|
||
casted: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||
builder, obj_ptr, field_ptr_ty)
|
||
if casted is not None:
|
||
return llvmlite.build_load(builder, field_ty, casted)
|
||
return None
|
||
# 普通结构体:GEP + load
|
||
field_idx: int = field_info.Index
|
||
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
|
||
builder, struct_ty, field_ty, obj_ptr, field_idx)
|
||
if field_ptr is not None:
|
||
# 数组类型字段不能 load 为 SSA value,直接返回字段指针
|
||
# 用于后续下标访问: self.state[0] → GEP state 字段 → GEP 数组元素
|
||
if self.is_array_type(field_ty) != 0:
|
||
return field_ptr
|
||
loaded_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, field_ty, field_ptr)
|
||
# 联合类型字段(如 Token | t.CPtr)被编译为 i8*,
|
||
# 若 AnnotClassName 指示了具体结构体类型,bitcast 为正确的结构体指针。
|
||
# 注意:仅当字段类型本身是指针时才 bitcast(指针→指针);
|
||
# 内嵌结构体字段(field_ty=Struct)load 出的是结构体值,
|
||
# bitcast 值→指针是非法 IR(llc: invalid cast opcode)。
|
||
if loaded_val is not None and field_info.AnnotClassName is not None \
|
||
and self.is_ptr_type(field_ty) != 0:
|
||
annot_struct: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(
|
||
field_info.AnnotClassName)
|
||
if annot_struct is not None and annot_struct.Ty is not None:
|
||
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
|
||
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
|
||
return loaded_val
|
||
# fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None)
|
||
if trans is not None:
|
||
diag_annot: str = None
|
||
diag_var_nm: str = None
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
dnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
diag_var_nm = dnm.id
|
||
if dnm.id is not None:
|
||
dve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, dnm.id)
|
||
if dve is not None:
|
||
diag_annot = dve.AnnotClassName
|
||
diag_var_txt: str = "?"
|
||
if diag_var_nm is not None:
|
||
diag_var_txt = diag_var_nm
|
||
diag_annot_txt: str = "None"
|
||
if diag_annot is not None:
|
||
diag_annot_txt = diag_annot
|
||
sha1_attr: str = "?"
|
||
if trans.ModuleSha1 is not None:
|
||
sha1_attr = trans.ModuleSha1
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"属性访问失败: .{at.attr} (var={diag_var_txt} annot={diag_annot_txt} sha1={sha1_attr})",
|
||
"TypeError")
|
||
return None
|
||
case _:
|
||
# fast-fail: 对非指针类型做属性访问(如 i32.x),
|
||
# Python traceback 立即终止,不静默返回 None(否则下游 "store to None" 崩溃,丢失错误现场)
|
||
attr_nm_ap: str = "?"
|
||
if at is not None and at.attr is not None:
|
||
attr_nm_ap = at.attr
|
||
sha1_ap: str = "?"
|
||
if trans is not None and trans.ModuleSha1 is not None:
|
||
sha1_ap = trans.ModuleSha1
|
||
ln_ap: int = 0
|
||
if at is not None and at.lineno is not None:
|
||
ln_ap = at.lineno
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"属性访问失败: .{attr_nm_ap} (base=非指针类型,无法做属性访问 sha1={sha1_ap}:{ln_ap})",
|
||
"TypeError")
|
||
|
||
# ============================================================
|
||
# 获取下标表达式的元素指针(不加载值,用于赋值)
|
||
# ============================================================
|
||
def get_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""获取下标表达式的元素指针(用于赋值 lhs)"""
|
||
if node is None or builder is None:
|
||
return None
|
||
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
|
||
if sub is None:
|
||
return None
|
||
|
||
# 翻译索引
|
||
idx_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, sub.slice, None, 0, trans)
|
||
if idx_val is None:
|
||
return None
|
||
|
||
# 如果 value 是 Name,尝试从 alloca 类型推断
|
||
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
|
||
if nm.id is not None and trans is not None:
|
||
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
|
||
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
|
||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||
return self._nonlocal_subscript_ptr(
|
||
builder, pool, trans, nm.id, idx_val)
|
||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
if alloca is not None and alloca.Ty is not None:
|
||
if self.is_ptr_type(alloca.Ty) != 0:
|
||
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
|
||
if pointee is not None:
|
||
# 用 match 检查 pointee 类型
|
||
match pointee:
|
||
case llvmlite.LLVMType.Array(elem_ty, count):
|
||
# 数组遍历
|
||
return llvmlite.build_gep_array(
|
||
builder, pointee, elem_ty, alloca,
|
||
self._zext_index_to_i32(builder, idx_val))
|
||
case llvmlite.LLVMType.Ptr(inner_ty):
|
||
# inner_ty 是 Ptr 说明 alloca.Ty 是三重指针,
|
||
# 即 X|t.CPtr 当 X 本身是指针类型 (如 T=AST|t.CPtr, T|t.CPtr=AST**)。
|
||
# 此时 [i]=val 应该是指针解引用赋值, 而非调用 __setitem__。
|
||
if self.is_ptr_type(inner_ty) != 0:
|
||
ptr_val_gsp: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_gsp is not None:
|
||
return llvmlite.build_gep(
|
||
builder, inner_ty, ptr_val_gsp, idx_val)
|
||
return None
|
||
# 自定义结构体 (如 HashTable|t.CPtr, JsonValue|t.CPtr):
|
||
# 仅当类定义了 __setitem__(当前模块定义或跨模块 stub declare 注入)
|
||
# 才返回 None 触发 __setitem__ 重载;否则普通结构体指针按下标
|
||
# 做 GEP 数组访问(走下方指针遍历路径)
|
||
cls_nm_ptr_chk: str = self._get_custom_struct_cls_nm(pool, inner_ty)
|
||
if not (cls_nm_ptr_chk is None):
|
||
si_lookup_gsp: str = pool.alloc(128)
|
||
if si_lookup_gsp is not None:
|
||
viperlib.snprintf(si_lookup_gsp, 128, "%s.__setitem__", cls_nm_ptr_chk)
|
||
if HandlesExprCall.find_func_in_module(mod, si_lookup_gsp) is not None:
|
||
return None
|
||
# bytes|t.CPtr / str|t.CPtr: alloca 是 i8**,
|
||
# 直接 GEP 按 i8* 步长(8 字节),不 load
|
||
# 仅当 inner_ty 不是自定义结构体时才走指针遍历
|
||
if trans is not None:
|
||
ve_pe: HandlesVar.VarEntry | t.CPtr = \
|
||
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve_pe is not None and ve_pe.IsPtrElement == 1:
|
||
# bytes|t.CPtr / str|t.CPtr / T|t.CPtr:变量存指针值,
|
||
# 下标语义 = [指针值 + i*8](8 字节指针元素)。必须先 load
|
||
# alloca 得指针值,再 GEP——否则 GEP base 用 alloca(&elem_ptr)
|
||
# 读到栈变量而非数据区,赋值写不进 list 数据
|
||
# (data 全 0xbaadf00d,dump 虚调用读错 vtable 崩溃)。
|
||
ptr_val_pe: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_pe is not None:
|
||
match pointee:
|
||
case llvmlite.LLVMType.Int8():
|
||
i8_ptr_pe: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
|
||
pool, llvmlite.Int8(pool))
|
||
return llvmlite.build_gep(
|
||
builder, i8_ptr_pe, ptr_val_pe, idx_val)
|
||
case _:
|
||
pass
|
||
return llvmlite.build_gep(
|
||
builder, pointee, ptr_val_pe, idx_val)
|
||
return None
|
||
# 普通指针遍历: 先 load 指针值,再 GEP
|
||
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val is None:
|
||
return None
|
||
gep_r: llvmlite.Value | t.CPtr = llvmlite.build_gep(
|
||
builder, inner_ty, ptr_val, idx_val)
|
||
return gep_r
|
||
case _:
|
||
# pointee 是普通标量类型 (如 i64, i32, i8):
|
||
# alloca 是 Ptr(标量), 直接 GEP 获取第 idx 个元素指针
|
||
# 支持 arg_vals[i] = val 这类参数数组下标赋值
|
||
# 联合类型变量(IsPtrElement=1,如 _list.vp 的
|
||
# elem_ptr: T | t.CPtr 退化为 i8*):pointee=i8 但
|
||
# 元素是 8 字节指针,须按 i8* 步长 GEP(防只写 1 字节截断)
|
||
if trans is not None:
|
||
ve_pe_cs: HandlesVar.VarEntry | t.CPtr = \
|
||
HandlesVar.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve_pe_cs is not None and ve_pe_cs.IsPtrElement == 1:
|
||
# T|t.CPtr(退化为 i8*,如 _list.vp 的 elem_ptr)变量
|
||
# 存数据区指针,元素为 8 字节指针:必须先 load alloca
|
||
# 得指针值,再按 i8* 步长 GEP——否则 GEP base 用 alloca
|
||
# (&elem_ptr)算到栈地址,赋值写不进 list 数据区
|
||
# (append 只增 count、data 全 0xbaadf00d)。
|
||
ptr_val_cs: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||
builder, pointee, alloca)
|
||
if ptr_val_cs is not None:
|
||
match pointee:
|
||
case llvmlite.LLVMType.Int8():
|
||
i8_ptr_cs: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
|
||
pool, llvmlite.Int8(pool))
|
||
return llvmlite.build_gep(
|
||
builder, i8_ptr_cs, ptr_val_cs, idx_val)
|
||
case _:
|
||
pass
|
||
return llvmlite.build_gep(
|
||
builder, pointee, ptr_val_cs, idx_val)
|
||
return None
|
||
return llvmlite.build_gep(builder, pointee, alloca, idx_val)
|
||
|
||
# 通用路径
|
||
ptr_val: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, sub.value, None, 0, trans)
|
||
if ptr_val is None or ptr_val.Ty is None:
|
||
return None
|
||
if self.is_ptr_type(ptr_val.Ty) != 0:
|
||
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
|
||
if elem_ty2 is not None:
|
||
# 数组类型: 双索引 GEP (getelementptr [N x ty], ptr, i32 0, i32 idx)
|
||
match elem_ty2:
|
||
case llvmlite.LLVMType.Array(arr_elem_ty, arr_count):
|
||
return llvmlite.build_gep_array(
|
||
builder, elem_ty2, arr_elem_ty, ptr_val,
|
||
self._zext_index_to_i32(builder, idx_val))
|
||
case _:
|
||
pass
|
||
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __setitem__
|
||
# 才返回 None 触发 HandlesAssign 的 __setitem__ 重载;
|
||
# 否则普通结构体指针按下标做 GEP 数组访问
|
||
cls_nm_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
|
||
if not (cls_nm_gen is None):
|
||
si_lookup_gen: str = pool.alloc(128)
|
||
if si_lookup_gen is not None:
|
||
viperlib.snprintf(si_lookup_gen, 128, "%s.__setitem__", cls_nm_gen)
|
||
if find_func_in_module(mod, si_lookup_gen) is not None:
|
||
return None
|
||
# 指针类型: 单索引 GEP (getelementptr ty, ptr, idx)
|
||
return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
|
||
return None
|
||
|
||
# ============================================================
|
||
# 获取属性访问的字段指针(不加载值,用于赋值)
|
||
# 对于 Name 类型的 obj,直接使用 alloca 指针(不 load 结构体)
|
||
# ============================================================
|
||
def _resolve_attr_field_class(self, pool: memhub.MemBuddy | t.CPtr,
|
||
trans: HT.Translator | t.CPtr,
|
||
attr_node: ast.AST | t.CPtr) -> str:
|
||
"""解析属性链(a.b.c)中 c 字段的注解类名。
|
||
|
||
联合类型字段(X | t.CPtr)被简化为 Ptr(i8),属性链写入时
|
||
(如 lx.tokens_tail.next = tok)obj_ptr.Ty 是 Ptr(i8),
|
||
lookup_field(i8, ...) 必然失败。此函数沿属性链递归解析
|
||
最终字段的 AnnotClassName(如 tokens_tail → 'Token'),
|
||
供按需过滤分支 bitcast 回真实结构体类型后 GEP。
|
||
"""
|
||
if attr_node is None or trans is None:
|
||
return None
|
||
k: int = attr_node.kind()
|
||
if k != ast.ASTKind.Attribute:
|
||
return None
|
||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(attr_node)
|
||
if at is None or at.attr is None or at.value is None:
|
||
return None
|
||
# 递归解析基座的注解类名(Name → 变量注解;Attribute → 字段注解)
|
||
parent_class: str = None
|
||
if at.value.kind() == ast.ASTKind.Name:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm.id is None:
|
||
return None
|
||
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, nm.id)
|
||
if var_entry is None:
|
||
return None
|
||
parent_class = var_entry.AnnotClassName
|
||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||
parent_class = self._resolve_attr_field_class(pool, trans, at.value)
|
||
else:
|
||
return None
|
||
if parent_class is None:
|
||
return None
|
||
# 在父类中查 at.attr 字段,返回其注解类名
|
||
cur_sha1: str = None
|
||
if trans is not None:
|
||
cur_sha1 = trans.ModuleSha1
|
||
fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field_by_class(
|
||
parent_class, at.attr, cur_sha1)
|
||
if fe is None:
|
||
return None
|
||
return HandlesStruct.get_field_annot_class_name(fe)
|
||
|
||
def get_attribute_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||
pool: memhub.MemBuddy | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""获取属性访问的字段指针(用于赋值 lhs)"""
|
||
if node is None or builder is None:
|
||
return None
|
||
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
|
||
if at is None or at.value is None or at.attr is None:
|
||
return None
|
||
|
||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
|
||
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
|
||
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
|
||
obj_ptr: llvmlite.Value | t.CPtr = None
|
||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm.id is not None:
|
||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
|
||
trans, nm.id)
|
||
if raw_nl_a is not None:
|
||
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
|
||
trans, nm.id)
|
||
if nl_ty_a is not None:
|
||
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
|
||
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
|
||
# 存对象指针,需 load 出对象指针
|
||
match nl_ty_a:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
obj_ptr = llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
case llvmlite.LLVMType.Array(_, _):
|
||
obj_ptr = llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
case _:
|
||
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
|
||
llvmlite.build_bitcast(
|
||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||
if nl_val_ptr_a is not None:
|
||
obj_ptr = llvmlite.build_load(
|
||
builder, nl_ty_a, nl_val_ptr_a)
|
||
else:
|
||
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||
|
||
# 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
|
||
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用
|
||
if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm_wma.id is not None:
|
||
# CDefine 常量不可写: 报 FATAL 错误,不创建未定义全局
|
||
if self._is_cdefine_name_pattern(at.attr) != 0:
|
||
err_buf_wma: str = pool.alloc(256)
|
||
if err_buf_wma is not None:
|
||
viperlib.snprintf(err_buf_wma, 256,
|
||
"[CD] FATAL: Cannot assign to CDefine constant '%s.%s'\n",
|
||
nm_wma.id, at.attr)
|
||
VLogger.error(err_buf_wma, "CD")
|
||
return None
|
||
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
|
||
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||
wma_ref: llvmlite.Value | t.CPtr = self._resolve_module_attribute_global(
|
||
pool, mod, trans, at, i8_ptr_w)
|
||
if wma_ref is not None:
|
||
return wma_ref
|
||
|
||
# 非 Name 路径:翻译对象值(会 load)
|
||
if obj_ptr is None:
|
||
# c.Deref(ptr).attr: 属性写入 base 取指针本身而非 load 后的结构体值
|
||
# (如 c.Deref(p).a = val 语义是 (*p).a = val,直接对 p 做 GEP)
|
||
cd_base_wr: llvmlite.Value | t.CPtr = self._get_cderef_attr_base(
|
||
builder, pool, mod, at.value, trans)
|
||
if cd_base_wr is not None:
|
||
obj_ptr = cd_base_wr
|
||
elif at.value.kind() == ast.ASTKind.Subscript:
|
||
# 下标基座属性写入(如 pts[0].x = val):list[T]/OOP __getitem__ 类走
|
||
# translate_value(返回元素值/地址);普通结构体指针数组走
|
||
# get_subscript_ptr(GEP 元素地址,避免结构体值 base=非指针 报错)
|
||
if self._sub_base_uses_getitem(pool, mod, trans, at.value) != 0:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = self.get_subscript_ptr(builder, pool, mod, at.value, trans)
|
||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||
# 嵌套属性基座写入(如 r.origin.x = val):取内层字段地址
|
||
# (get_attribute_ptr 递归,地址语义),指针字段由 _deref_if_ptr_ptr 解引用
|
||
obj_ptr = self.get_attribute_ptr(builder, pool, mod, at.value, trans)
|
||
if obj_ptr is None:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
else:
|
||
obj_ptr = self.translate_value(builder, pool, mod, at.value, None, 0, trans)
|
||
|
||
if obj_ptr is None or obj_ptr.Ty is None:
|
||
return None
|
||
|
||
# 如果 obj_ptr 是 Ptr(Ptr(Struct))(X|t.CPtr 变量的 alloca),
|
||
# load 解引用获取 Ptr(Struct)
|
||
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
|
||
|
||
match obj_ptr.Ty:
|
||
case llvmlite.LLVMType.Ptr(struct_ty):
|
||
# 确保跨模块结构体的完整定义在当前模块中可用(供 getelementptr 访问字段)
|
||
# 与 translate_attribute 一致,避免 opaque 类型导致 GEP 报错
|
||
HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty)
|
||
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
|
||
struct_ty, at.attr)
|
||
# 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找
|
||
# 传递 SHA1 以区分跨模块同名类
|
||
# base 注解类名提取:Name → 变量注解类名;Attribute → 属性链递归字段注解类名
|
||
# (如 lx.tokens_tail.next = tok:lx 的注解 Lexer → tokens_tail 字段注解 Token)
|
||
if field_info is None:
|
||
base_class: str = None
|
||
if at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
if nm_fb.id is not None:
|
||
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, nm_fb.id)
|
||
if var_entry is not None:
|
||
base_class = var_entry.AnnotClassName
|
||
elif at.value.kind() == ast.ASTKind.Attribute:
|
||
base_class = self._resolve_attr_field_class(pool, trans, at.value)
|
||
if base_class is not None:
|
||
cur_sha1: str = None
|
||
if trans is not None:
|
||
cur_sha1 = trans.ModuleSha1
|
||
field_info = HandlesStruct.lookup_field_by_class(
|
||
base_class, at.attr, cur_sha1)
|
||
# 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型
|
||
# 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)),
|
||
# 需用实际结构体类型做 GEP,否则 GEP i8 失败
|
||
if field_info is not None:
|
||
annot_se: HandlesStruct.StructEntry | t.CPtr = \
|
||
HandlesStruct.find_struct(base_class)
|
||
if annot_se is not None and annot_se.Ty is not None:
|
||
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = \
|
||
llvmlite.Ptr(pool, annot_se.Ty)
|
||
casted_ptr: llvmlite.Value | t.CPtr = \
|
||
llvmlite.build_bitcast(builder, obj_ptr, annot_ptr_ty)
|
||
if casted_ptr is not None:
|
||
obj_ptr = casted_ptr
|
||
struct_ty = annot_se.Ty
|
||
# 按需过滤更新 struct_ty 后,需重新确保
|
||
# 注解类型结构体定义在当前模块可用
|
||
HandlesStruct.ensure_struct_def_in_module(
|
||
pool, mod, annot_se.Ty)
|
||
if field_info is not None:
|
||
# 联合体:bitcast obj_ptr 到 field_ty*(字段指针用于 store)
|
||
if HandlesStruct.is_union_by_type(struct_ty) == 1:
|
||
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_info.Ty)
|
||
return llvmlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
|
||
# 普通结构体:GEP 获取字段指针
|
||
return llvmlite.build_gep_struct(
|
||
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
|
||
# fast-fail: 字段未找到,立即终止编译(不再打印诊断后返回 None)
|
||
if trans is not None:
|
||
diag_annot_g: str = None
|
||
diag_var_nm_g: str = None
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
dnm_g: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
|
||
diag_var_nm_g = dnm_g.id
|
||
if dnm_g.id is not None:
|
||
dve_g: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||
trans.SymTab, dnm_g.id)
|
||
if dve_g is not None:
|
||
diag_annot_g = dve_g.AnnotClassName
|
||
diag_var_txt_g: str = "?"
|
||
if diag_var_nm_g is not None:
|
||
diag_var_txt_g = diag_var_nm_g
|
||
diag_annot_txt_g: str = "None"
|
||
if diag_annot_g is not None:
|
||
diag_annot_txt_g = diag_annot_g
|
||
sha1_attr_g: str = "?"
|
||
if trans.ModuleSha1 is not None:
|
||
sha1_attr_g = trans.ModuleSha1
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"属性写入失败: .{at.attr} (var={diag_var_txt_g} annot={diag_annot_txt_g} sha1={sha1_attr_g})",
|
||
"TypeError")
|
||
return None
|
||
case _:
|
||
# fast-fail: 对非指针类型做属性写入(如 i32.x = ...),
|
||
# Python traceback 立即终止,不静默返回 None(否则下游 "store to None" 崩溃)
|
||
attr_nm_gp: str = "?"
|
||
if at is not None and at.attr is not None:
|
||
attr_nm_gp = at.attr
|
||
sha1_gp: str = "?"
|
||
if trans is not None and trans.ModuleSha1 is not None:
|
||
sha1_gp = trans.ModuleSha1
|
||
ln_gp: int = 0
|
||
if at is not None and at.lineno is not None:
|
||
ln_gp = at.lineno
|
||
HandlesType.fatal_error(
|
||
at,
|
||
f"属性写入失败: .{attr_nm_gp} (base=非指针类型,无法做属性写入 sha1={sha1_gp}:{ln_gp})",
|
||
"TypeError")
|
||
|
||
# ============================================================
|
||
# P3 迁移方法(来自 HandlesExprOps.vp:运算符重载 + 二元运算)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# BinOp 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _binop_to_dunder(self, op: int) -> str:
|
||
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None
|
||
|
||
Python 3 语义:/ 映射 __truediv__(Python 2 的 __div__ 已废弃)。
|
||
"""
|
||
if op == ast.OpKind.Add: return "__add__"
|
||
if op == ast.OpKind.Sub: return "__sub__"
|
||
if op == ast.OpKind.Mult: return "__mul__"
|
||
if op == ast.OpKind.Div: return "__truediv__"
|
||
if op == ast.OpKind.Mod: return "__mod__"
|
||
if op == ast.OpKind.BitAnd: return "__and__"
|
||
if op == ast.OpKind.BitOr: return "__or__"
|
||
if op == ast.OpKind.BitXor: return "__xor__"
|
||
if op == ast.OpKind.LShift: return "__lshift__"
|
||
if op == ast.OpKind.RShift: return "__rshift__"
|
||
if op == ast.OpKind.FloorDiv: return "__floordiv__"
|
||
return None
|
||
|
||
# ============================================================
|
||
# Compare 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _cmpop_to_dunder(self, op: int) -> str:
|
||
"""将 Compare 运算符映射到 dunder 方法名,无映射时返回 None"""
|
||
if op == ast.OpKind.Eq: return "__eq__"
|
||
if op == ast.OpKind.Ne: return "__ne__"
|
||
if op == ast.OpKind.Lt: return "__lt__"
|
||
if op == ast.OpKind.Le: return "__le__"
|
||
if op == ast.OpKind.Gt: return "__gt__"
|
||
if op == ast.OpKind.Ge: return "__ge__"
|
||
return None
|
||
|
||
# ============================================================
|
||
# UnaryOp 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _unaryop_to_dunder(self, op: int) -> str:
|
||
"""将 UnaryOp 运算符映射到 dunder 方法名,无映射时返回 None"""
|
||
if op == ast.OpKind.USub: return "__neg__"
|
||
if op == ast.OpKind.UAdd: return "__pos__"
|
||
if op == ast.OpKind.Invert: return "__invert__"
|
||
return None
|
||
|
||
# ============================================================
|
||
# try_operator_overload - 尝试运算符重载
|
||
#
|
||
# 检查 lhs 是否为结构体指针,并在该类(含继承链)中查找对应的
|
||
# dunder 方法。找到则生成方法调用 lhs.__dunder__(rhs),返回
|
||
# 调用结果;未找到返回 None,调用方回退到原生运算。
|
||
# ============================================================
|
||
def try_operator_overload(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
lhs: llvmlite.Value | t.CPtr,
|
||
rhs: llvmlite.Value | t.CPtr,
|
||
op: int,
|
||
trans: HT.Translator | t.CPtr,
|
||
is_compare: int) -> llvmlite.Value | t.CPtr:
|
||
"""尝试运算符重载:lhs 是结构体指针时调用 dunder 方法,否则返回 None"""
|
||
if lhs is None or rhs is None:
|
||
return None
|
||
|
||
# 延迟导入避免循环依赖
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
|
||
# 映射运算符到 dunder 方法名
|
||
dunder: str = None
|
||
if is_compare != 0:
|
||
dunder = self._cmpop_to_dunder(op)
|
||
else:
|
||
dunder = self._binop_to_dunder(op)
|
||
if dunder is None:
|
||
return None
|
||
|
||
# 检查 lhs 是否为结构体指针
|
||
struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(lhs)
|
||
if struct_ty is None:
|
||
return None
|
||
|
||
# 获取类名
|
||
class_name: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
|
||
if class_name is None:
|
||
return None
|
||
|
||
# 在继承链中查找 dunder 方法
|
||
lookup_name: str = pool.alloc(128)
|
||
if lookup_name is None:
|
||
return None
|
||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||
found_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, lookup_name)
|
||
|
||
search_class: str = class_name
|
||
if found_func is None:
|
||
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
|
||
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
|
||
mi: int = 1
|
||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
|
||
if anc is not None:
|
||
parent_lookup: str = pool.alloc(128)
|
||
if parent_lookup is not None:
|
||
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
|
||
parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup)
|
||
if parent_func is not None:
|
||
found_func = parent_func
|
||
search_class = anc
|
||
break
|
||
mi += 1
|
||
|
||
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
|
||
# 优先用类型指针定位 entry 获取 SHA1(规避跨模块同名 find_struct 找错)
|
||
cls_sha1: str = None
|
||
op_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
|
||
if op_entry is not None:
|
||
cls_sha1 = op_entry.ModuleSha1
|
||
if cls_sha1 is None:
|
||
cls_sha1 = HandlesStruct.get_struct_sha1(search_class)
|
||
if cls_sha1 is None:
|
||
mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class)
|
||
mj: int = 1
|
||
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
|
||
anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr))
|
||
if anc_p is not None:
|
||
cls_sha1 = HandlesStruct.get_struct_sha1(anc_p)
|
||
mj += 1
|
||
|
||
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
|
||
if found_func is None and cls_sha1 is None:
|
||
return None
|
||
|
||
# 方法在"定义它的模块"中确实不存在(同模块类但模块内无此 dunder):
|
||
# 返回 None 回退原生运算,绝不发出幽灵 call——否则 IR 残留
|
||
# `call void @...__div__`(副作用)且调用方回退原生 sdiv 双重发射,
|
||
# llc 报 invalid operand type for instruction(CPythonTest PyFloatObj 根因)
|
||
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
|
||
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
|
||
return None
|
||
|
||
# 构建 extra_args 数组(仅含 rhs 一个参数)
|
||
extra_args: t.CSizeT | t.CPtr = pool.alloc(8)
|
||
if extra_args is None:
|
||
return None
|
||
extra_args[0] = t.CSizeT(rhs)
|
||
|
||
# 调用方法: lhs.__dunder__(rhs)
|
||
return HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, search_class, dunder,
|
||
lhs, extra_args, 1, trans)
|
||
|
||
# ============================================================
|
||
# try_unary_operator_overload - 尝试一元运算符重载
|
||
#
|
||
# 检查 operand 是否为结构体指针,并在该类(含继承链)中查找对应的
|
||
# dunder 方法(__neg__/__pos__/__invert__)。找到则生成方法调用
|
||
# operand.__dunder__(),返回调用结果;未找到返回 None,调用方回退到原生运算。
|
||
# ============================================================
|
||
def try_unary_operator_overload(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
operand: llvmlite.Value | t.CPtr,
|
||
op: int,
|
||
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||
"""尝试一元运算符重载:operand 是结构体指针时调用 dunder 方法,否则返回 None"""
|
||
if operand is None:
|
||
return None
|
||
|
||
# 延迟导入避免循环依赖
|
||
import lib.core.Handles.HandlesStruct as HandlesStruct
|
||
import lib.core.Handles.HandlesExprCall as HandlesExprCall
|
||
|
||
dunder: str = self._unaryop_to_dunder(op)
|
||
if dunder is None:
|
||
return None
|
||
|
||
# 检查 operand 是否为结构体指针
|
||
struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(operand)
|
||
if struct_ty is None:
|
||
return None
|
||
|
||
# 获取类名
|
||
class_name: str = HandlesStruct.get_class_name_by_type(pool, struct_ty)
|
||
if class_name is None:
|
||
return None
|
||
|
||
# 在继承链中查找 dunder 方法
|
||
lookup_name: str = pool.alloc(128)
|
||
if lookup_name is None:
|
||
return None
|
||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||
found_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, lookup_name)
|
||
|
||
search_class: str = class_name
|
||
if found_func is None:
|
||
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
|
||
mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name)
|
||
mi: int = 1
|
||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||
anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr))
|
||
if anc is not None:
|
||
parent_lookup: str = pool.alloc(128)
|
||
if parent_lookup is not None:
|
||
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
|
||
parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup)
|
||
if parent_func is not None:
|
||
found_func = parent_func
|
||
search_class = anc
|
||
break
|
||
mi += 1
|
||
|
||
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
|
||
cls_sha1: str = None
|
||
uop_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty)
|
||
if uop_entry is not None:
|
||
cls_sha1 = uop_entry.ModuleSha1
|
||
if cls_sha1 is None:
|
||
cls_sha1 = HandlesStruct.get_struct_sha1(search_class)
|
||
if cls_sha1 is None:
|
||
mro_p: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, search_class)
|
||
mj: int = 1
|
||
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
|
||
anc_p: str = (str | t.CPtr)(t.CVoid(mro_p[mj], t.CPtr))
|
||
if anc_p is not None:
|
||
cls_sha1 = HandlesStruct.get_struct_sha1(anc_p)
|
||
mj += 1
|
||
|
||
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
|
||
if found_func is None and cls_sha1 is None:
|
||
return None
|
||
|
||
# 同模块类但模块内无此 dunder → 返回 None 回退原生运算,不发幽灵 call
|
||
# (与 try_operator_overload 一致,防 IR 残留 call void @...__neg__)
|
||
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
|
||
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
|
||
return None
|
||
|
||
# 一元方法无额外参数(extra_args 为空)
|
||
return HandlesExprCall._call_method_on_ptr(
|
||
pool, builder, mod, search_class, dunder,
|
||
operand, None, 0, trans)
|
||
|
||
# ============================================================
|
||
# 翻译二元运算(自动类型提升)
|
||
# ============================================================
|
||
def translate_binop(self, pool: memhub.MemBuddy | t.CPtr,
|
||
builder: llvmlite.IRBuilder | t.CPtr,
|
||
mod: llvmlite.LLVMModule | t.CPtr,
|
||
node: ast.AST | t.CPtr,
|
||
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
|
||
"""翻译二元运算(自动类型提升)"""
|
||
binop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node)
|
||
if binop is None:
|
||
return None
|
||
|
||
lhs_node: ast.AST | t.CPtr = binop.left
|
||
rhs_node: ast.AST | t.CPtr = binop.right
|
||
op: int = binop.op
|
||
|
||
# 先翻译 rhs(只翻译一次,避免副作用重复)
|
||
rhs: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, rhs_node, None, 0, trans)
|
||
if rhs is None:
|
||
return None
|
||
|
||
# === 运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||
# 对于值类型变量(如 cnt: Counter),translate_value 会 load 返回 Struct 值,
|
||
# 但 dunder 方法需要 Ptr(Struct) 作为 self。直接用 alloca 指针尝试重载。
|
||
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
|
||
if nm is not None and nm.id is not None:
|
||
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||
trans.SymTab, nm.id)
|
||
if lhs_alloca is not None:
|
||
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||
pool, builder, mod, lhs_alloca, rhs, op, trans, 0)
|
||
if ovl_result is not None:
|
||
return ovl_result
|
||
|
||
# 正常翻译 lhs
|
||
lhs: llvmlite.Value | t.CPtr = self.translate_value(
|
||
builder, pool, mod, lhs_node, None, 0, trans)
|
||
if lhs is None:
|
||
return None
|
||
|
||
# === 运算符重载路径 2: lhs 是 Ptr(Struct)(如 Counter|t.CPtr 变量 load 后)===
|
||
ovl_result2: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||
pool, builder, mod, lhs, rhs, op, trans, 0)
|
||
if ovl_result2 is not None:
|
||
return ovl_result2
|
||
|
||
# 获取操作数类型信息
|
||
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
|
||
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
|
||
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
|
||
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
|
||
|
||
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
|
||
# 因为 float 的 int bits 为 0,会被误认为指针)
|
||
if lhs_fbits != 0 or rhs_fbits != 0:
|
||
# 确定目标浮点类型(使用较大的位宽)
|
||
target_fbits: int = lhs_fbits
|
||
if rhs_fbits > target_fbits:
|
||
target_fbits = rhs_fbits
|
||
target_float_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||
if target_fbits == 32:
|
||
target_float_ty = llvmlite.Float(pool)
|
||
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
|
||
lhs = self.coerce_to_type(builder, lhs, target_float_ty, node)
|
||
rhs = self.coerce_to_type(builder, rhs, target_float_ty, node)
|
||
if op == ast.OpKind.Add:
|
||
return llvmlite.build_fadd(builder, lhs, rhs)
|
||
if op == ast.OpKind.Sub:
|
||
return llvmlite.build_fsub(builder, lhs, rhs)
|
||
if op == ast.OpKind.Mult:
|
||
return llvmlite.build_fmul(builder, lhs, rhs)
|
||
if op == ast.OpKind.Div:
|
||
return llvmlite.build_fdiv(builder, lhs, rhs)
|
||
if op == ast.OpKind.Mod:
|
||
return llvmlite.build_frem(builder, lhs, rhs)
|
||
return None
|
||
|
||
# 指针算术: ptr + int / ptr - int / int + ptr → getelementptr(按元素大小缩放)
|
||
# 对齐 TPC ExprGen.emit_binary_op L50-67;int - ptr 无 GEP 形式(C 语义非法)
|
||
if (lhs_bits == 0 and rhs_bits != 0) or (lhs_bits != 0 and rhs_bits == 0):
|
||
if op == ast.OpKind.Add:
|
||
if lhs_bits == 0:
|
||
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
|
||
# int + ptr(交换)
|
||
return self.translate_ptr_arith(pool, builder, rhs, lhs, op)
|
||
if op == ast.OpKind.Sub and lhs_bits == 0:
|
||
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
|
||
|
||
if lhs_bits > rhs_bits:
|
||
if self.should_zext_widen(pool, rhs, lhs, rhs_node) != 0:
|
||
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
|
||
else:
|
||
rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node)
|
||
elif rhs_bits > lhs_bits:
|
||
if self.should_zext_widen(pool, lhs, rhs, lhs_node) != 0:
|
||
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
|
||
else:
|
||
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node)
|
||
|
||
# 位运算类型安全检查:两边都是指针(lhs_bits==0 and rhs_bits==0)时,
|
||
# 位运算(BitOr/BitAnd/BitXor/LShift/RShift)非法,LLVM 要求整数操作数。
|
||
# 常见触发场景:CDefine 常量查找失败返回指针 null,与另一指针做 `|`。
|
||
if lhs_bits == 0 and rhs_bits == 0:
|
||
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor or op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||
# fail-fast:位运算操作数类型非法,立即终止编译(暴露上游根因)
|
||
HandlesType.fatal_error(
|
||
node,
|
||
"位运算操作数类型非法: lhs/rhs 均为非整数类型(指针或结构体),无法执行位运算",
|
||
"TypeError")
|
||
|
||
if op == ast.OpKind.Add:
|
||
return llvmlite.build_add(builder, lhs, rhs)
|
||
elif op == ast.OpKind.Sub:
|
||
return llvmlite.build_sub(builder, lhs, rhs)
|
||
elif op == ast.OpKind.Mult:
|
||
return llvmlite.build_mul(builder, lhs, rhs)
|
||
elif op == ast.OpKind.Div:
|
||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||
elif op == ast.OpKind.FloorDiv:
|
||
return llvmlite.build_sdiv(builder, lhs, rhs)
|
||
elif op == ast.OpKind.Mod:
|
||
return llvmlite.build_srem(builder, lhs, rhs)
|
||
elif op == ast.OpKind.BitAnd:
|
||
return llvmlite.build_and(builder, lhs, rhs)
|
||
elif op == ast.OpKind.BitOr:
|
||
return llvmlite.build_or(builder, lhs, rhs)
|
||
elif op == ast.OpKind.BitXor:
|
||
return llvmlite.build_xor(builder, lhs, rhs)
|
||
elif op == ast.OpKind.LShift:
|
||
return llvmlite.build_shl(builder, lhs, rhs)
|
||
elif op == ast.OpKind.RShift:
|
||
# 无符号 >> → lshr(逻辑右移),有符号 >> → ashr(算术右移)
|
||
# 对齐 TPC ExprGen.emit_binary_op:i8/i16 默认无符号;其余查操作数无符号性
|
||
if lhs_bits == 8 or lhs_bits == 16:
|
||
return llvmlite.build_lshr(builder, lhs, rhs)
|
||
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
|
||
return llvmlite.build_lshr(builder, lhs, rhs)
|
||
return llvmlite.build_ashr(builder, lhs, rhs)
|
||
return None
|