Files
ViperC/App/lib/core/Gen/Expr/ExprGenBase.vp
T
TermiNexus ea2abba574 refactor(tpv): 删除 _deps_pending 置位面与 Phase A pending 多遍收敛机制
69 个工程成功编译且 _phaseA_pending.log 恒 0 字节,证实 14 个置位点全部零命中。删除 14 个 _MarkDepsPending 调用点(原「依赖未就绪」分支保留默认类型或原报错路径);删除 _MarkDepsPending 与 _AppendPhaseAPending 及 _phaseA_pending.log 读写;删除 Phase A 出口「pending 则不写盘、回滚 MethodsEmitted、continue」整段;删除 _deps_pending 与 _pf_dbg_pend_flagged 字段及全部读取分支;清理遗留探针 [PF-DBG] 与 [METHDBG]。16 files changed, +37 / -558
2026-09-12 19:24:52 +08:00

588 lines
31 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
#
# 最底层 Base(ExprGenMixin/Const/Fstr/Ops/Cdef/Name/Attr/Cls 均 1 级继承本类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenBase:
"""表达式基础工具(类型判断/指针层级/coerce)"""
# --- docstring 登记表字段(__doc__ 魔法属性支持)---
Gen: Generator.Generator | t.CPtr
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 | 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: rowptr[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 | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0(委托 SpecTypeBits)"""
return TSpec.SpecTypeBits(ty)
# ============================================================
# 获取 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=二级指针...
[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 | 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: rowptr[memhub.MemBuddy],
ty: llvmlite.LLVMType | t.CPtr) -> 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 | t.CPtr = 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: rowptr[memhub.MemBuddy],
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 = 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 | 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: rowptr[memhub.MemBuddy],
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 = 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 | 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 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: rowptr[memhub.MemBuddy],
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 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 | 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,
trans: HT.Translator | 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)
# 整数 → 指针: 禁止隐式转换(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(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.vp:263-271 已确认「裸类/数组形参一律 lowering 为
# Ptr(struct/array)」——Viper 不存在"按值传结构体"的形参。故调用点的
# rvalue 聚合(函数调用返回值、下标元素 load 等)必须落栈取址,否则
# 实参类型与指针形参失配(gfx.vp `eye.Sub(target).Norm()`、
# `Mat4_Mul(m, Mat4_Translate(...))`、`DrawTriangle(.., v[i], ..)` 等
# rvalue 实参全部按值传 → 与 define 的 %T* 形参不符,N18)。
# 此前缺此分支 → 落到末尾 return val 原样按值传出。
# alloca 走入口块(支配性安全),生命周期覆盖整个调用,无需释放;
# pointee 与聚合类型一致时直接用 alloca 地址,避免同型 bitcast 噪声。
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 | t.CPtr = 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 | t.CPtr = 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: rowptr[memhub.MemBuddy],
val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None,
trans: HT.Translator | t.CPtr = 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 / 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: rowptr[memhub.MemBuddy],
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 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