512 lines
28 KiB
Plaintext
512 lines
28 KiB
Plaintext
import lib.core.Gen.Generator as Generator
|
||
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import viperlib
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.Projectrans.Utils as Utils
|
||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||
|
||
|
||
# ============================================================
|
||
# ExprGenBase - ExprGenMixin 拆分(阶段 D):表达式基础工具(类型判断/指针层级/coerce)
|
||
# ============================================================
|
||
novirtual class ExprGenBase:
|
||
"""表达式基础工具(类型判断/指针层级/coerce)"""
|
||
|
||
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
|
||
Gen: Generator.Generator*
|
||
DocKeys: bytes
|
||
DocValues: bytes
|
||
DocCount: int
|
||
|
||
# --- OOM 诊断:跨模块 CDefine 查找调用计数(CONST_INT 参数翻译疑似循环查找) ---
|
||
DiagCDefineCount: int
|
||
# --- OOM 诊断:translate_name_value 调用计数(顶层 CDefine 查找 14593 次来源) ---
|
||
DiagNameValueCount: int
|
||
# --- OOM 诊断:translate_value 调用计数(CONST_INT 循环调用源定位) ---
|
||
DiagTranslateValueCount: int
|
||
# ============================================================
|
||
# P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数,
|
||
# 本模块函数调用改 self.,外部模块调用保留模块前缀)
|
||
# ============================================================
|
||
|
||
# ============================================================
|
||
# 从 AST 节点提取函数名
|
||
# ============================================================
|
||
def get_func_name(self, func_node: ast.AST*) -> str:
|
||
"""从函数节点提取函数名"""
|
||
if func_node is None: return None
|
||
k: int = func_node.kind()
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(func_node)
|
||
return nm.id
|
||
elif k == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(func_node)
|
||
return at.attr
|
||
return None
|
||
# ============================================================
|
||
# 转义 LLVM IR 字符串字面量
|
||
# ============================================================
|
||
def escape_llvm_string(self, pool: memhub.MemBuddy*,
|
||
src: str) -> str:
|
||
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
|
||
if src is None: return None
|
||
slen: sizet = string.strlen(src)
|
||
alloc_size: sizet = 4 * slen + 8
|
||
buf: str = pool.alloc(alloc_size)
|
||
if buf is None: return None
|
||
pos: sizet = 0
|
||
buf[pos] = 'c'
|
||
pos += 1
|
||
buf[pos] = '"'
|
||
pos += 1
|
||
for i in range(slen):
|
||
ch: char = 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 整数类型的位宽
|
||
#
|
||
# [S4 切读·位宽块] 实现已收拢到 `TSpec.SpecTypeBits`(该块在 TypeSpec 模块中的
|
||
# **唯一实现**),此处只做委托,不再保留同码副本(TypeGenErr.get_llvm_type_bits
|
||
# 同样委托到它)。两份副本一旦分叉,会出现「同一类型在两处算出不同位宽」的
|
||
# 隐蔽不一致。私有调用点(self.get_llvm_type_bits)保持原样不受影响。
|
||
# ============================================================
|
||
def get_llvm_type_bits(self, ty: llvmlite.LLVMType*) -> int:
|
||
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
|
||
return TSpec.SpecTypeBits(ty)
|
||
# ============================================================
|
||
# 获取 LLVM 浮点类型的位宽
|
||
# ============================================================
|
||
def get_llvm_float_bits(self, ty: llvmlite.LLVMType*) -> 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*) -> 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*) -> int:
|
||
"""计算指针层级,0=非指针, 1=一级指针, 2=二级指针...
|
||
|
||
[S3 切读·指针层级块] 实现已收拢到 `TSpec.SpecTypePtrLevel`
|
||
(该块在 TypeSpec 模块中的**唯一实现**),此处只做委托,不再保留同码副本。
|
||
原实现里记录的「match codegen 缺陷」规避说明随实现一并移入 TypeSpec。
|
||
"""
|
||
return TSpec.SpecTypePtrLevel(ty)
|
||
# ============================================================
|
||
# _is_zero_int_literal - 判断 AST 节点是否为整数字面量 0
|
||
#
|
||
# 用于指针判空比较(ptr != 0 / ptr == 0):指针与字面量 0 比较时
|
||
# 0 视为 NULL,允许并转成 null 指针(比较类型检查的例外)。
|
||
# ============================================================
|
||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||
"""判断节点是否为整数字面量 0,返回 1=是 / 0=否"""
|
||
if node is None: return 0
|
||
if node.kind() != ast.ASTKind.Constant: return 0
|
||
cn: ast.Constant* = ast.Constant*(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*) -> 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*,
|
||
idx_val: llvmlite.Value*) -> llvmlite.Value*:
|
||
"""将 <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* = 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*,
|
||
ty: llvmlite.LLVMType*) -> str:
|
||
"""检查 ty 或 ty 的 pointee 是否是已注册的自定义结构体,返回类名或 None"""
|
||
if ty is None:
|
||
return None
|
||
# 直接检查 ty 是否是注册的结构体
|
||
cls_nm_direct: str = self.Gen.StructGen.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* = ty.Pointee
|
||
if pointee_cs is not None:
|
||
return self.Gen.StructGen.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*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> int:
|
||
"""判断下标基座是否为 list[T] 变量(1=是)"""
|
||
if sub_node is None or trans is None: return 0
|
||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||
nm: ast.Name* = ast.Name*(sub.value)
|
||
if nm.id is None: return 0
|
||
alloca: llvmlite.Value* = self.Gen.VarGen.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* = 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*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> str:
|
||
"""解析下标基座变量的结构体类名(无则 None)"""
|
||
if sub_node is None or trans is None: return None
|
||
sub: ast.Subscript* = ast.Subscript*(sub_node)
|
||
if sub is None or sub.value is None or sub.value.kind() != ast.ASTKind.Name: return None
|
||
nm: ast.Name* = ast.Name*(sub.value)
|
||
if nm.id is None: return None
|
||
alloca: llvmlite.Value* = self.Gen.VarGen.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* = alloca.Ty.Pointee
|
||
if pointee is None: return None
|
||
if self.is_ptr_type(pointee) != 0:
|
||
inner: llvmlite.LLVMType* = pointee.Pointee
|
||
if inner is not None:
|
||
return self.Gen.StructGen.get_class_name_by_type(pool, inner)
|
||
return self.Gen.StructGen.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*,
|
||
mod: llvmlite.LLVMModule*,
|
||
trans: HT.Translator*,
|
||
sub_node: ast.AST*) -> 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 self.Gen.CallGenBase.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*,
|
||
obj_ptr: llvmlite.Value*) -> llvmlite.Value*:
|
||
"""如果 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* = 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* = 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*,
|
||
val: llvmlite.Value*,
|
||
target_ty: llvmlite.LLVMType*,
|
||
node: ast.AST* = None,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""将 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)
|
||
# 整数 → 指针: 禁止隐式转换(FIX 2026-08-30),唯一豁免是 C 空指针常量 0
|
||
# (NULL: t.CDefine = 0 / 字面量 0)——C 语义中 0 赋指针即 null pointer
|
||
# constant,inttoptr 0 生成合法空指针,不属于"整数当指针"的类型信息缺失。
|
||
# 原为 inttoptr 兜底(跨模块方法返回 i64/i32 默认推断时静默转指针),
|
||
# 掩盖类型信息缺失——如 `BootInfo = saved_rdi`(整数 → bootinfo*)。
|
||
# 按"按需过滤/不兜底/fail-fast"原则:非常量整数必须显式强转
|
||
# (t.CPtr(x) 无类型指针右值可隐式转为左侧类型化指针,走指针→指针
|
||
# bitcast 路径)。
|
||
if val_bits != 0 and self.is_ptr_type(target_ty) != 0:
|
||
# [FIX 2026-08-31] C 空指针常量豁免:编译期常量 0 赋给指针 → 空指针。
|
||
# NULL 是 t.CDefine = 0(如 self.handle = NULL / CreateThread(NULL,...)),
|
||
# 属标准 C 空指针用法,须放行而非报 CastError。先拓宽到 i64
|
||
# (inttoptr 要求同宽,对齐 MatchGenMixin 模式),再 inttoptr 成空指针。
|
||
if val.IsConst == 1 and val.IntVal == 0:
|
||
return llvmlite.build_inttoptr(builder, llvmlite.const_int64(builder.Pool, 0), target_ty)
|
||
self.Gen.TypeGenErr.fatal_error(node, "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型", "CastError")
|
||
return val
|
||
# 指针 → 整数: 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:
|
||
# 统一 zext/sext(问题2修正):i1/i8 默认 zext;AST 节点解析为
|
||
# 无符号类型(t.CUIntXXT 强转/无符号类型名)走 zext;否则 sext。
|
||
# 此前一律 sext(i1 除外)→ uint32 等无符号值赋值/传参被符号
|
||
# 扩展(高位置 1,如 0xFFFFFFFF 变 0xFFFFFFFFFFFFFFFF),与比较路径
|
||
# should_zext_widen(i8 默认 zext)规则相反 → 隐蔽数据损坏。
|
||
# trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经
|
||
# trans.SymTab 查变量注解(uint16/sec:uint32 等)才能判定无符号,
|
||
# 否则返回未知 → 退回 sext(无符号扩展缺陷根因)。
|
||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0:
|
||
return llvmlite.build_zext(builder, val, target_ty)
|
||
return llvmlite.build_sext(builder, val, target_ty)
|
||
# 窄化截断诊断(_Type.md 问题 8):宽→窄 trunc 静默丢位。
|
||
# 编译期已知常量且正溢出目标位宽无符号范围 → fail-fast(确定性数据
|
||
# 损坏,如 buf[idx] = 0x1FF 写入 i8 槽位被静默截断)。负常量按模
|
||
# 2^bits 语义放行(-1 → 0xFF 合法);非常量运行时窄化保留 trunc
|
||
# (值域可能安全,无法静态判定)。
|
||
if val.IsConst == 1 and val.IntVal > 0:
|
||
target_max_u: uint64 = (uint64(1) << target_bits) - 1
|
||
if uint64(val.IntVal) > target_max_u:
|
||
max_print: int64 = int64(target_max_u)
|
||
self.Gen.TypeGenErr.fatal_error(node, f"窄化截断溢出:常量 {val.IntVal} 超出目标类型 {target_bits} 位范围 [0, {max_print}],赋值将被静默截断", "OverflowError")
|
||
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):
|
||
self.Gen.TypeGenErr.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)
|
||
# 聚合值 → 指针:落栈 materialize 临时对象后传其地址。FuncGenDef 已确认「裸类/数组形参一律 lowering 为 Ptr(struct/array)」——TPV 内部函数/方法不存在"按值传结构体"的形参(唯一例外:C ABI 外部声明的裸结构体形参按值,见 VpiReader._AggregateParamTy「三处必须同步」)。故调用点的 rvalue 聚合(函数调用返回值、下标元素 load 等)必须落栈取址,否则实参类型与指针形参失配。alloca 走入口块(支配性安全),生命周期覆盖整个调用,pointee 与聚合类型一致时直接用 alloca 地址
|
||
if self.is_ptr_type(val.Ty) == 0 and self.is_ptr_type(target_ty) != 0:
|
||
vk_agg: int = val.Ty.kind()
|
||
if vk_agg == llvmlite.LLVMType.Struct or vk_agg == llvmlite.LLVMType.Array:
|
||
tmp_agg: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
|
||
if tmp_agg is not None:
|
||
llvmlite.build_store(builder, val, tmp_agg)
|
||
match target_ty:
|
||
case llvmlite.LLVMType.Ptr(pointee_agg):
|
||
if self.Gen.AssignGen._stg_ty_equal(builder.Pool, pointee_agg, val.Ty) != 0:
|
||
return tmp_agg
|
||
case _:
|
||
pass
|
||
bc_agg: llvmlite.Value* = llvmlite.build_bitcast(builder, tmp_agg, target_ty)
|
||
if bc_agg is not None:
|
||
return bc_agg
|
||
return tmp_agg
|
||
# 指针 → 非指针值: 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
|
||
|
||
def _zext_or_sext_val(self, pool: memhub.MemBuddy*,
|
||
val: llvmlite.Value*,
|
||
node: ast.AST* = None,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""统一整数拓宽方向:1=zext(无符号语义),0=sext
|
||
|
||
委托到 ExprGenOps.should_zext_widen(拓宽方向的**单一真源**),避免两套
|
||
规则各自演化导致同一「窄→宽」在 coerce 路径与比较/二元路径结论相反。
|
||
coerce 路径无「宽侧值」上下文,wide_val 传 None 使「宽侧常量超窄侧有符号
|
||
范围」那条启发式自然失效;其余三条(i1/i8、源无符号、CDefine 高位常量)
|
||
与旧实现一致或更强。
|
||
|
||
trans 供 _node_type_unsigned 经 SymTab 解析变量注解的符号性(Name/
|
||
Subscript 形态必需);省略则仅能识别显式 t.CUIntXXT 强转。
|
||
"""
|
||
if val is None or val.Ty is None: return 0
|
||
return self.Gen.ExprGenOps.should_zext_widen(pool, val, None, node, trans)
|
||
|
||
# translate_ptr_arith:指针算术 ptr±int,用 getelementptr(按 pointee 元素大小缩放索引,对齐 TPC ExprGen.emit_binary_op / HandlesAugAssign.py);数组 pointee([N x ty]*)先 decay 为元素指针(GEP 0,0),避免以整个数组为步长
|
||
def translate_ptr_arith(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
ptr_val: llvmlite.Value*,
|
||
int_val: llvmlite.Value*,
|
||
op: int) -> llvmlite.Value*:
|
||
"""指针算术: 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* = 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* = llvmlite.Int64(pool)
|
||
idx: llvmlite.Value* = 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* = 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* = 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* = 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 InitExprGen(self) -> int:
|
||
"""初始化 ExprGen 族字段(构造时由 Generator 调用)"""
|
||
if self is None: return 1
|
||
self.DocKeys = None
|
||
self.DocValues = None
|
||
self.DocCount = 0
|
||
self.DiagCDefineCount = 0
|
||
self.DiagNameValueCount = 0
|
||
self.DiagTranslateValueCount = 0
|
||
return 0
|