Files
TransPyC/lib/core/LLVMCG/MemoryOps.py
T
TermiNexus 0bded58999 refactor(type): 全面清理旧联合与 t/c 标记判断
审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
2026-10-18 18:01:20 +08:00

817 lines
46 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from typing import Any
# 必须用 TPC 自己的解析器模块(vast),**不是**标准库 ast:TPC 全部源码
# (.vp 与 .py)都由 vast 解析,产出的是 lib.vast.Nodes.* 节点,与标准库
# ast 的节点类互不兼容(isinstance 恒 False)。此前误用标准库 ast,使本文件
# 全部节点类型判据对 Viper 源码静默失效(_value_node_of 的子表达式下沉、
# _CtorCallStructType 的构造物化豁免、指针 cast 判据都不生效)。
import vast as ast
import llvmlite.ir as ir
class _VolatileLoadInstr(ir.LoadInstr):
"""带 LLVM `volatile` 限定符的 load 指令。
llvmlite 0.47 的 `ir.LoadInstr` / `ir.StoreInstr` 不支持 volatile(无该属性,
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
仅多一个 `volatile` 关键字;语义与 ViperC 侧 `build_load(..., vol)` 对齐。
"""
def descr(self, buf: list[str]) -> None:
[val] = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("load volatile {0}, {1} {2}{3}{4}\n".format(
self.type,
val.type,
val.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class _VolatileStoreInstr(ir.StoreInstr):
"""带 LLVM `volatile` 限定符的 store 指令(与 _VolatileLoadInstr 同款理由)。"""
def descr(self, buf: list[str]) -> None:
val, ptr = self.operands
if self.align is not None:
align = ', align %d' % (self.align)
else:
align = ''
buf.append("store volatile {0} {1}, {2} {3}{4}{5}\n".format(
val.type,
val.get_reference(),
ptr.type,
ptr.get_reference(),
align,
self._stringify_metadata(leading_comma=True),
))
class MemoryOpsMixin:
def _register_local_heap_ptr(self, val: ir.Value, ClassName: str | None = None, VarName: str | None = None) -> None:
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
self._local_heap_ptrs.append((val, 'struct', ClassName))
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
self._local_heap_ptrs.append((val, 'i8', ClassName))
if VarName:
self._var_to_heap_ptr[VarName] = val
def _unregister_local_heap_ptr(self, val: ir.Value) -> None:
self._local_heap_ptrs = [(v, t, cn) for v, t, cn in self._local_heap_ptrs if v is not val]
def _emit_local_heap_frees(self) -> None:
if not self._local_heap_ptrs or not self.builder:
return
deleted_objects: set[ir.Value] = set()
for ptr, ptr_type, ClassName in self._local_heap_ptrs:
if ptr_type == 'i8':
Loaded_ptr: ir.Value = self.builder.load(ptr, name="load_ptr")
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
is_null: ir.Value = self.builder.icmp_unsigned('==', Loaded_ptr, null_ptr, name="is_null")
not_null_block: ir.Block = self.builder.append_basic_block(name="not_null")
next_block: ir.Block = self.builder.append_basic_block(name="next")
self.builder.cbranch(is_null, next_block, not_null_block)
self.builder.position_at_end(not_null_block)
if ClassName and self._has_function(f'{ClassName}.__del__'):
cast_ptr: ir.Value
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
cast_ptr = self.builder.bitcast(ptr, ir.PointerType(self.structs[ClassName]), name=f"cast_{ClassName}")
else:
cast_ptr = ptr
if cast_ptr not in deleted_objects:
self.builder.call(self._get_function(f'{ClassName}.__del__'), [cast_ptr], name=f"call_{ClassName}.__del__")
deleted_objects.add(cast_ptr)
raw: ir.Value
if ptr_type == 'struct':
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="local_free_cast")
elif ptr_type == 'i8':
raw = ptr
else:
continue
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
free_func: ir.Function = self._get_or_declare_func('free', free_type)
self.builder.call(free_func, [raw])
if ptr_type == 'i8':
self.builder.branch(next_block)
self.builder.position_at_end(next_block)
self._local_heap_ptrs = []
def _RegisterTempPtr(self, val: ir.Value) -> None:
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in self.structs.items():
if pointee == ST:
self._temp_struct_ptrs.append(val)
return
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
self._temp_struct_ptrs.append(val)
def _UnregisterTempPtr(self, val: ir.Value) -> None:
self._temp_struct_ptrs = [t for t in self._temp_struct_ptrs if t is not val]
def _EmitTempFrees(self) -> None:
if not self._temp_struct_ptrs or not self.builder:
return
for ptr in self._temp_struct_ptrs:
raw: ir.Value
if isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
raw = self.builder.bitcast(ptr, ir.PointerType(ir.IntType(8)), name="free_cast")
elif isinstance(ptr.type, ir.PointerType) and isinstance(ptr.type.pointee, ir.IntType) and ptr.type.pointee.width == 8:
raw = ptr
else:
continue
free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))])
free_func: ir.Function = self._get_or_declare_func('free', free_type)
self.builder.call(free_func, [raw])
self._temp_struct_ptrs = []
def _ZeroConst(self, typ: ir.Type) -> ir.Constant:
if isinstance(typ, ir.PointerType):
return ir.Constant(typ, None)
if isinstance(typ, (ir.LiteralStructType, ir.IdentifiedStructType)):
return ir.Constant(typ, None)
if isinstance(typ, ir.ArrayType):
return ir.Constant(typ, None)
return ir.Constant(typ, 0)
# ============================================================
# 隐式转换公共判据:None(空值常量)/ 指针层级 / 基本类型 / 无损
#
# 语义唯一来源与 ViperC 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
# 收敛于此,供赋值 / 返回 / 实参 / 数组元素等所有 coerce 入口复用,避免
# 各处各自演化出相反结论。
# ============================================================
@staticmethod
def _IsNoneValue(val: Any, node: Any = None) -> bool:
"""None 判据:True=空值常量(None),False=否。
None 无语义类型信息,**允许隐式转换为任意类型**:指针目标→空指针,
其余目标→目标类型零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。故
「指针↔整数」「指针↔非指针值」「指针↔指针(非基本/有损/层数不同)」
这类针对**真实类型值**的隐式转换禁令都要对 None 短路放行。
两条判据(任一命中):
① AST 层:node 是 Constant 且 value is None;
② 值层(主判据):已生成好的 **空指针常量**(ir.Constant 且 constant is None
且类型为指针)。整数常量 0 的类型是整数,不会误命中。
"""
if node is not None and type(node).__name__ == 'Constant' and getattr(node, 'value', 1) is None:
return True
return (isinstance(val, ir.Constant) and val.constant is None
and isinstance(val.type, ir.PointerType))
@staticmethod
def _PtrLevel(typ: Any) -> int:
"""指针层级:PointerType 嵌套层数(0=非指针)。"""
n: int = 0
while isinstance(typ, ir.PointerType):
n += 1
typ = typ.pointee
return n
@staticmethod
def _IsGenericPtrType(typ: Any) -> bool:
"""通用指针(CPtr / void*):指针链末端为 i8(i8* / i8** / ... 均算)。"""
if not isinstance(typ, ir.PointerType):
return False
p: Any = typ.pointee
while isinstance(p, ir.PointerType):
p = p.pointee
return isinstance(p, ir.IntType) and p.width == 8
@staticmethod
def _IsBasicScalarType(typ: Any) -> bool:
"""基本类型 = 整数/浮点(含布尔 i1);结构体/类/联合/数组/指针一律非基本。"""
return isinstance(typ, (ir.IntType, ir.FloatType, ir.DoubleType))
@staticmethod
def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool:
"""基本标量「无损」判定(与 ViperC `_scalar_convert_reason` 同口径,逐条照搬):
- 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁);
- 浮点 ↔ 浮点:**所有方向都允许**;
- 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁);
- 浮点 → 整数:**不允许**(有损 → 禁)。
"""
def _FloatBits(x: Any) -> int:
if isinstance(x, ir.DoubleType):
return 64
if isinstance(x, ir.FloatType):
return 32
return 0
s_int: bool = isinstance(Src, ir.IntType)
d_int: bool = isinstance(Dst, ir.IntType)
s_flt: int = _FloatBits(Src)
d_flt: int = _FloatBits(Dst)
# 整数 ↔ 整数:低→高无损放行;高→低有损窄化,禁
if s_int and d_int:
return Dst.width >= Src.width
# 浮点 ↔ 浮点:允许
if s_flt and d_flt:
return True
# 整数 → 浮点:目标浮点位宽 ≥ 源整数位宽才无损
if s_int and d_flt:
return d_flt >= Src.width
# 浮点 → 整数:有损,禁止
if s_flt and d_int:
return False
return False
def _ClassNameOfPointee(self, Pointee: Any) -> str | None:
"""把 LLVM 被指类型映射到已注册类名(复用本仓既有 find_struct_by_pointee;
身份比较失败时按短名(去 sha1 前缀)回退匹配 Gen.structs,与 HandlesAssign
既有回退同口径)。非结构体 / 未注册 → None。不新造表。
"""
if Pointee is None:
return None
Found: Any = self.find_struct_by_pointee(Pointee)
if Found:
return Found[0]
if isinstance(Pointee, ir.IdentifiedStructType):
PShort: str = self._extract_short_name(Pointee.name)
if PShort:
for CN, ST in self.structs.items():
if isinstance(ST, ir.IdentifiedStructType) and \
self._extract_short_name(ST.name) == PShort:
return CN
return None
def _IsInheritanceUpcastPointee(self, SrcPointee: Any, DstPointee: Any) -> bool:
"""继承上转判据:SrcPointee → DstPointee 是否为「派生类 → 基类」(含多级)。
复用本仓既有继承判据:_ClassNameOfPointee(→ find_struct_by_pointee)取
类名,Gen._get_mro_chain 取 MRO 线性化(含自身,父类在后)。目标类名落在
源类名 MRO 链「自身之后」⇒ 允许上转(Derived* → Base*,C++/GCC 同口径);
下转(Base* → Derived*)与无关类型(Foo* ↔ Bar*)不命中 ⇒ False。
"""
SrcName: str | None = self._ClassNameOfPointee(SrcPointee)
if not SrcName:
return False
DstName: str | None = self._ClassNameOfPointee(DstPointee)
if not DstName:
return False
SrcShort: str = self._extract_short_name(SrcName)
DstShort: str = self._extract_short_name(DstName)
if SrcShort == DstShort:
return False
Chain: list[str] = self._get_mro_chain(SrcName)
for Anc in Chain[1:]:
if self._extract_short_name(Anc) == DstShort:
return True
return False
def _IsExplicitPtrCastNode(self, Node: Any) -> bool:
"""值节点是否为「显式指针 cast」调用(用户规则:显式转换一律放行)。
形式(TPC 解析产物的共同点 = ast.Call 且 func 是**指针类型表达式**):
- `CPtr(x)`
- `rawptr[T](x)` / `CPtr[T](x)` / 裸名 `CPtr[T](x)`
- `(T*)x` / `(T*)(x)` / `T*(x)`(含 `**` 两层;ExprParser 统一产成
Call(func = `CPtr[T]` Subscript)
- 裸 `rawptr(x)`(≡ CPtr)
非 cast 调用(构造函数 / 普通函数,如 `MemBuddy(x)`、`list[X](...)`)不命中
⇒ 仍按隐式转换规则判定。只判 func 节点形状,不做类型解析(不与 Gen 耦合)。
`T1 | T2` 类型联合已禁止,故不再有 `Call(func=BinOp(BitOr))` 形态。
"""
if not isinstance(Node, ast.Call):
return False
Func: Any = Node.func
from lib.core.SymbolUtils import IsCPtrNode, IsPtrSubscriptNode
if IsCPtrNode(Func) or IsPtrSubscriptNode(Func):
return True
if isinstance(Func, ast.Name) and Func.id == 'rawptr':
return True
return False
def _ClassifyPtrImplicitConvert(self, Src: Any, Dst: Any,
Node: Any = None) -> tuple[bool, str]:
"""隐式转换判据:返回 (Allowed, Reason)。
规则(用户决定,与 ViperC 同口径):
① 通用指针(CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。
② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型
(结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止;**例外**有二:
继承上转 Derived* → Base*(目标被指类型是源被指类型的基类,含多级)
——见 _IsInheritanceUpcastPointee;数组退让 [N]T* → T*(目标被指类型
是源被指数组的元素类型,单向)——见下。下转 Base* → Derived*、反向
T* → [N]T* 仍禁止。
③ 非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止(层数不同);
**唯一例外**是「构造物化」——该豁免需 AST 层判据,故在 coerce 调用点
由 _CtorCallStructType 单独放行(本判据只给出「禁」的结论)。
④ 完全相同类型不算转换 ⇒ 放行。
⑤ **显式 cast 一律放行**(用户规则「显式转换都可以」):值侧节点本身是写明的
指针转换调用(CPtr(x) / rawptr[T](x) / (T*)x ...,见
_IsExplicitPtrCastNode)⇒ 目标类型即程序员指定的转换目标,调用方按
bitcast 重解释,不做层数/被指类型判定。
Reason 供报文说明「哪一级不允许 + 原因(非基本类型/有损/层数不同)」。
"""
# ⑤ 显式 cast:先于一切判据放行(Node 缺省 None ⇒ 不适用,等价既有行为)
if self._IsExplicitPtrCastNode(Node):
return (True, '显式 cast(按 bitcast 重解释)')
if Src == Dst:
return (True, '同类型(不构成转换)')
s_ptr: bool = isinstance(Src, ir.PointerType)
d_ptr: bool = isinstance(Dst, ir.PointerType)
if s_ptr and d_ptr:
if MemoryOpsMixin._PtrLevel(Src) != MemoryOpsMixin._PtrLevel(Dst):
return (False, '层数不同')
if MemoryOpsMixin._IsGenericPtrType(Src) or MemoryOpsMixin._IsGenericPtrType(Dst):
return (True, '通用指针通配')
sp: Any = Src.pointee
dp: Any = Dst.pointee
if sp == dp:
return (True, '被指类型相同')
if MemoryOpsMixin._IsBasicScalarType(sp) and MemoryOpsMixin._IsBasicScalarType(dp):
if MemoryOpsMixin._ScalarConvertLossless(sp, dp):
return (True, '被指类型无损')
return (False, '被指类型有损')
# 豁免:Derived* → Base* 继承上转(目标被指类型是源被指类型的基类,
# 含多级)允许(C++/GCC 同口径);下转/无关类型不命中,仍禁止。
if self._IsInheritanceUpcastPointee(sp, dp):
return (True, '继承上转(派生类指针 → 基类指针)')
# 豁免:数组退让(单向)——源的被指类型是定长数组 [N]T、目标的被指类型
# 恰为该数组的**元素类型** T ⇒ 允许(数组指针 → 元素指针,C/C++ 同口径)。
# 多维数组按「最外层退让一层」自然处理:[N][M]T* → [M]T* 命中(元素即
# [M]T),[N][M]T* → T* 不命中(元素是 [M]T ≠ T)。反向 T* → [N]T* 因
# 源的被指类型非数组而不命中,仍按下方非基本类型禁止。元素类型同一性
# 复用本处既有的 `==` 口径(与 sp == dp / 数组元素比较一致)。
if isinstance(sp, ir.ArrayType) and sp.element == dp:
return (True, '数组退让(数组指针 → 元素指针)')
return (False, '非基本类型(结构体/类/联合)')
if not s_ptr and not d_ptr:
return (True, '非指针间(本判据不裁决)')
# 一方指针、一方非指针:基本标量走既有「指针隐式转整数」禁令;非基本
# 类型即规则③(T ↔ T* 隐式互转)。
other: Any = Dst if s_ptr else Src
if MemoryOpsMixin._IsBasicScalarType(other):
return (False, '指针与标量(须显式 cast)')
return (False, '非基本类型(结构体/类/联合)')
@staticmethod
def _ValueNodeOf(Node: Any = None) -> Any:
"""把「外层语句节点」下沉到其「值子表达式节点」(与 ViperC _value_node_of 同口径)。
Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。
规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()`
里的 `Point()`),而 coerce 入口常收到外层语句节点,故统一在此下沉。
"""
if Node is None:
return None
if isinstance(Node, ast.Assign):
return Node.value
if isinstance(Node, ast.AnnAssign):
return Node.value
if isinstance(Node, ast.AugAssign):
return Node.value
if isinstance(Node, ast.Expr):
return Node.value
if isinstance(Node, ast.Return):
return Node.value
return Node
@staticmethod
def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool:
"""AST 判据:Node 是否交出「当前帧栈上存储的地址」。
两种形态(与 ViperC fail_if_stack_addr_escape 同口径):
(a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参);
(b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`),
交出的是首元素地址,同属栈地址。
模块级全局在静态存储、函数在代码段,取址可以逃逸,故排除。裸聚合名 + 同型聚合目标
是按值拷贝,不是逃逸,故不在 (b) 判据内。
"""
Vars: Any = getattr(Gen, 'variables', None)
if isinstance(Node, ast.UnaryOp) and isinstance(Node.op, ast.AddrOf):
Inner: Any = Node.operand
if not isinstance(Inner, ast.Name) or Inner.id is None:
return False
Var: Any = Vars.get(Inner.id) if Vars is not None else None
if isinstance(Var, ir.AllocaInstr):
return True
Module: Any = getattr(Gen, 'module', None)
if Module is not None and Inner.id in getattr(Module, 'globals', {}):
return False
# 全局/函数之外的已知名(如未登记的形参)保守判为栈地址
return True
if isinstance(Node, ast.Name) and Node.id is not None and isinstance(TargetType, ir.PointerType):
Var2: Any = Vars.get(Node.id) if Vars is not None else None
if (isinstance(Var2, ir.AllocaInstr) and isinstance(Var2.type, ir.PointerType)
and isinstance(Var2.type.pointee, ir.ArrayType)):
return True
return False
@staticmethod
def _IsSameAggregate(A: Any, B: Any) -> bool:
"""两个 LLVM 聚合类型是否指同一类型(对象同一,或 Identified 结构体 IR 名相同)。"""
if A is None or B is None:
return False
if A == B:
return True
return (isinstance(A, ir.IdentifiedStructType)
and isinstance(B, ir.IdentifiedStructType)
and A.name == B.name)
@staticmethod
def _CtorCalleeNames(Func: Any) -> list[str]:
"""构造调用 callee 的候选聚合名(类构造 / REnum 变体构造)。
- `Point()` → ['Point'](callee 即类名)
- `Qual.Variant(...)` → ['Variant', 'Qual']
- `mod.Qual.Variant(...)` → ['Variant', 'Qual']
变体短名在前(同名类构造优先,与实际 dispatch 一致),限定名(REnum 类名)
在后。
"""
Names: list[str] = []
if isinstance(Func, ast.Name):
Names.append(Func.id)
elif isinstance(Func, ast.Attribute):
Names.append(Func.attr)
QualNode: Any = getattr(Func, 'value', None)
if isinstance(QualNode, ast.Name):
Names.append(QualNode.id)
elif isinstance(QualNode, ast.Attribute):
Names.append(QualNode.attr)
return Names
def _CtorCallStructType(self, Node: Any, TargetType: Any) -> Any:
"""AST 层判定:Node(或其值子节点)是否为「构造调用」右值;是则返回其构造的
聚合类型(IdentifiedStruct/LiteralStruct),否则返回 None。
规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」:
右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据(见
HandlesExprCall._HandleCallLlvm 的分派口径):
- callee 裸名/属性名命中已注册聚合(`Gen.structs`)→ 类构造(`Point()`);
- callee 是 `Qual.Variant(...)`(REnum 变体构造,如 `LLVMType.Struct(...)`)
—— 变体短名不是聚合,构造出的聚合类型挂在**限定名 Qual**(REnum 类)下。
REnum 变体构造返回 **boxed 指针**(`Gen._allocaEntry(RenumType)`),其
pointee 恰是该聚合 —— 正是「构造物化」要放行的形态(boxed 指针 → 值槽的
整对象拷贝,语义即 load + store)。
- 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。
`Gen.structs` 的键在跨模块命名隔离下可能是 `sha1.ClassName` 全名,且裸名键
可能另有一份 opaque 占位条目(两者不是同一 IR 类型)——故不按裸名直查,而是
按「键等于候选名,或以 `.<候选名>` 结尾」扫描,并要求取到的聚合与 TargetType
同一(对象同一或 IR 名相同)。这样无论构造走的是哪份条目、无论结果走
`_HandleREnumConstructLlvm` 还是 stub 侧同签名的变体工厂函数,都能正确放行。
"""
N: Any = MemoryOpsMixin._ValueNodeOf(Node)
if not isinstance(N, ast.Call):
return None
Structs: Any = getattr(self, 'structs', {})
for Name in MemoryOpsMixin._CtorCalleeNames(getattr(N, 'func', None)):
if not Name:
continue
for Key, Val in Structs.items():
if not isinstance(Key, str):
continue
if Key != Name and not Key.endswith('.' + Name):
continue
ST: Any = Val.pointee if isinstance(Val, ir.PointerType) else Val
if not isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)):
continue
if MemoryOpsMixin._IsSameAggregate(ST, TargetType):
return ST
return None
def _alloca(self, typ: ir.Type, name: str = '', align: int | None = None, size: ir.Value | None = None) -> ir.AllocaInstr:
if align is None:
align = self._get_align(typ)
instr: ir.AllocaInstr
if size is not None:
instr = self.builder.alloca(typ, size=size, name=name)
else:
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
return instr
def _allocaEntry(self, typ: ir.Type, name: str = '', align: int | None = None) -> ir.AllocaInstr:
if align is None:
align = self._get_align(typ)
saved_block: ir.Block = self.builder.block
entry_block: ir.Block = self.func.entry_basic_block
instr: ir.AllocaInstr
if entry_block.instructions:
first_instr: ir.Instruction = entry_block.instructions[0]
self.builder.position_before(first_instr)
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
self.builder.position_at_end(saved_block)
else:
self.builder.position_at_start(entry_block)
instr = self.builder.alloca(typ, name=name)
if align:
instr.align = align
self.builder.position_at_end(saved_block)
return instr
def _EmitLoad(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool = False) -> ir.Value:
"""发射一条 load;volatile=True 时走 _VolatileLoadInstr(LLVM `load volatile`)。
类型推断完全交给 LoadInstr 自身(与 llvmlite 原始路径同一套逻辑),
不额外兜底:推不出类型时与原生 `builder.load` 一样直接抛错。
"""
if not volatile:
return self.builder.load(ptr, name=name, align=align)
# volatile 分支直接构造 LoadInstr,绕过了 builder.load 的类型守卫,
# 故此处先行判定(否则 llvmlite 在 LoadInstr.__init__ 里访问
# ptr.type.is_opaque 抛无位置信息的裸 AttributeError)。
from lib.core.LLVMCG.BaseGen import RequirePointerOperand
RequirePointerOperand(ptr, 'load volatile', self, f"load 名='{name}'")
instr: ir.LoadInstr = _VolatileLoadInstr(self.builder.block, ptr, name=name)
instr.align = align
self.builder._insert(instr)
return instr
def _EmitStore(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool = False) -> ir.StoreInstr:
"""发射一条 store;volatile=True 时走 _VolatileStoreInstr(LLVM `store volatile`)。"""
if not volatile:
return self.builder.store(val, ptr, align=align)
# 同 _EmitLoad:绕过 builder.store 的守卫,先判定指针操作数
# (非指针 ptr 会静默生成非法 IR 文本,而不是报错)。
from lib.core.LLVMCG.BaseGen import RequirePointerOperand
RequirePointerOperand(ptr, 'store volatile', self,
f"store 值类型={getattr(val, 'type', None)}")
instr: ir.StoreInstr = _VolatileStoreInstr(self.builder.block, val, ptr)
instr.align = align
self.builder._insert(instr)
return instr
def _load(self, ptr: ir.Value, name: str = '', align: int | None = None,
volatile: bool | None = None) -> ir.LoadInstr:
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
# volatile=None ⇒ 按登记名自动判定(变量自身/所指物/字段,见 _IsPtrVolatile);
# 显式传入 True/False 时以调用方为准。判定必须先于下方 bitcast(bitcast
# 结果的名字不再是变量名,会丢登记名)。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType):
pointee_type: ir.Type = ptr.type.pointee
if isinstance(pointee_type, ir.IdentifiedStructType) and pointee_type.name in self.typedef_int_widths:
width: int = self.typedef_int_widths[pointee_type.name]
if width > 0:
actual_type: ir.IntType = ir.IntType(width)
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast_Load")
return self._EmitLoad(bitcast_ptr, name=name, align=align, volatile=volatile)
return self._EmitLoad(ptr, name=name, align=align, volatile=volatile)
def _Load_var(self, name: str) -> ir.Value | None:
if name in self._direct_values:
return self._direct_values[name]
if name in self.variables and self.variables[name] is not None:
VarPtr: ir.Value = self.variables[name]
if isinstance(VarPtr, ir.GlobalVariable) and isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return VarPtr
if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return VarPtr
return self._load(VarPtr, name=name)
if name in self.global_vars and name in self.module.globals:
GVar: ir.GlobalVariable = self.module.globals[name]
self.variables[name] = GVar
if isinstance(GVar, ir.Function):
return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar
return self._load(GVar, name=name)
if name in self.module.globals:
GVar = self.module.globals[name]
self.variables[name] = GVar
if isinstance(GVar, ir.Function):
return GVar
if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)):
return GVar
return self._load(GVar, name=name)
return None
def _CoerceIntToI1(self, val: ir.Value, name: str = "bool_norm") -> ir.Value:
"""整数 → **i1** 目标的真值归一化(C 6.3.1.2 `_Bool` 转换语义):`x != 0` → 1/0。
判据与三处真值化上下文**同一**:`bool(x)` 内建(HandlesExprBuiltin.py:74/76)、
条件 `if/while`(HandlesIf.py:196)、`and/or` 操作数(HandlesExprOps.py:122-127)。
此前「落存储」(本文件 `_coerce_value`、HandlesAnnAssign._StoreWithCoerce)与
「显式强转」(HandlesExprCall._HandleTypeCastLlvm)对 i1 目标都走 `trunc`
(取最低位)⇒ 运行时值 2 得 0 → false,与 `if 2:` 的真值结论相反
(静默语义分裂);值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
非整数 / 已是 i1 原样返回。
"""
if not isinstance(val.type, ir.IntType) or val.type.width == 1:
return val
return self.builder.icmp_signed('!=', val, self._ZeroConst(val.type), name=name)
def _coerce_value(self, val: ir.Value, target_type: ir.Type, Node: Any = None) -> ir.Value | None:
if val.type == target_type:
return val
# None(空值常量)允许隐式转换为任意类型:指针目标→空指针,其余目标→
# 目标类型零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。收敛到
# _IsNoneValue 单一判据,任何「指针↔整数 / 指针↔非指针值」禁令都不拦 None。
if self._IsNoneValue(val, Node):
if isinstance(target_type, ir.PointerType):
return ir.Constant(target_type, None)
return self._ZeroConst(target_type)
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
if isinstance(val.type, ir.IntType):
return val
# 聚合目标(数组/结构体)不接受标量(整数/浮点)初始化或赋值:标量→聚合无
# 转换语义,旧行为让本方法返回 None,_store 再抛 TypeError,而该 TypeError 在
# 非 strict 模式被静默吞掉 → 聚合完全未初始化(历史现场:`l: [10]char = 0`)。
# 直接 fail-fast(FailFastError 不被 TypeError 捕获,故能穿透调用点的 try)并
# 提示正确写法。typedef 伪整数已在上方短路,不受影响。
if (isinstance(target_type, (ir.ArrayType, ir.LiteralStructType, ir.IdentifiedStructType))
and isinstance(val.type, (ir.IntType, ir.FloatType, ir.DoubleType))):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"标量(整数/浮点)不能初始化或赋值给聚合类型 {target_type}:"
f"旧行为只写首元素字节或整体不初始化,聚合其余部分保持未初始化;"
f"聚合零初始化请写 None,填充内容请用列表字面量 [...] 或逐元素赋值"
f"{self._DescribeValueNode(Node)}"
)
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
if target_type.width < val.type.width:
# 目标 i1:真值归一化(与 bool(x)/if 条件同一判据),不是 trunc 取最低位
if target_type.width == 1:
return self._CoerceIntToI1(val, name="bool_norm_store")
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。
# 唯一豁免:源是**编译期常量且值落进目标位宽**(无信息损失)。
if isinstance(val, ir.Constant) and isinstance(val.constant, int):
Lo: int = -(1 << (target_type.width - 1))
Hi: int = (1 << target_type.width) - 1
if Lo <= val.constant <= Hi:
return self.builder.trunc(val, target_type, name="trunc_const_store")
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"隐式窄化禁止:{val.type.width} 位整数不能隐式赋给 {target_type.width} 位类型"
f"(会丢高位);请显式转换(如 int32(x) / CInt32T(x))或把目标类型加宽"
f"{self._DescribeValueNode(Node)}"
)
elif val.type.width == 1:
# i1 是布尔值(来自 icmp/fcmp/__contains__ 归一化),无符号语义,
# 必须用 zext 而非 sext,否则 i1 的 1 会被符号扩展为 -1
return self.builder.zext(val, target_type, name="zext_bool_store")
else:
return self.builder.sext(val, target_type, name="sext_store")
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.PointerType):
# 新增指针隐式转换规则①/②:通用指针通配双向放行;非通用 T*→U* 仅在
# 被指类型无损时放行,非基本类型/有损/层数不同一律 fail-fast。
Allowed: bool
Reason: str
Allowed, Reason = self._ClassifyPtrImplicitConvert(val.type, target_type, Node)
if not Allowed:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转换已禁止({Reason}):不能把指针类型 {val.type} 隐式转为 {target_type};"
f"如需转换请显式 cast(如 CPtr(...))"
f"{self._DescribeValueNode(Node)}"
)
return self.builder.bitcast(val, target_type, name="ptr_bitcast_store")
if isinstance(target_type, ir.PointerType) and isinstance(val.type, ir.IntType):
if val.type.width < 64:
val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store")
return self.builder.inttoptr(val, target_type, name="int_to_ptr_store")
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType):
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
# None(空指针常量)已在上方短路为目标零值,不会落到这里。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
f"如需转换请显式 cast(如 CUInt64T(p) / CInt(p))"
f"{self._DescribeValueNode(Node)}"
)
if isinstance(target_type, ir.FloatType) and isinstance(val.type, ir.DoubleType):
return self.builder.fptrunc(val, target_type, name="fptrunc_store")
if isinstance(target_type, ir.DoubleType) and isinstance(val.type, ir.FloatType):
return self.builder.fpext(val, target_type, name="fpext_store")
if isinstance(target_type, ir.IntType) and isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return self.builder.fptosi(val, target_type, name="fptosi_store")
if isinstance(target_type, (ir.FloatType, ir.DoubleType)) and isinstance(val.type, ir.IntType):
return self.builder.sitofp(val, target_type, name="sitofp_store")
if isinstance(target_type, ir.ArrayType) and isinstance(val.type, ir.ArrayType):
if target_type.count == val.type.count and target_type.element == val.type.element:
return val
if (isinstance(target_type.element, ir.IntType) and target_type.element.width == 8
and isinstance(val.type.element, ir.IntType) and val.type.element.width == 8):
if target_type.count > val.type.count:
padded: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else []
if padded and len(padded) == val.type.count:
padded.extend([ir.Constant(ir.IntType(8), 0)] * (target_type.count - val.type.count))
return ir.Constant(target_type, padded)
elif target_type.count < val.type.count:
trimmed: list[ir.Constant] = list(val.constant) if hasattr(val, 'constant') else []
if trimmed and len(trimmed) == val.type.count:
return ir.Constant(target_type, trimmed[:target_type.count])
if isinstance(val.type, ir.PointerType) and not isinstance(target_type, ir.PointerType):
# 规则③:非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止——
# 指针→非指针值的隐式 load(结构体/数组按值回读)属类型信息缺失,
# 须显式解引用(Deref(p) / *p)或显式强转。基本标量目标已在上方
# 「指针隐式转整数」分支 fail-fast,此处只兜非基本类型。
# **唯一例外=「构造物化」**:右值来自该类型的构造调用时放行,如
# `p: Point = Point()`、`*slot = LLVMType.Struct(...)`(参照 GCC 构造
# 语义:这是构造,不是类型转换)。判据把 TargetType 传下去,要求 AST 侧
# 的构造类型与目标类型**同一**(对象同一或 IR 名相同)才算命中。
CtorExempt: bool = self._CtorCallStructType(Node, target_type) is not None
if not CtorExempt:
Allowed3: bool
Reason3: str
Allowed3, Reason3 = self._ClassifyPtrImplicitConvert(val.type, target_type)
if not Allowed3:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"非基本类型的值/指针隐式转换已禁止(构造物化除外,{Reason3}):"
f"不能把指针类型 {val.type} 隐式转为值类型 {target_type};"
f"如需解引用请显式 Deref(p) / *p,或显式强转"
f"{self._DescribeValueNode(Node)}"
)
if val.type.pointee == target_type:
return self.builder.load(val, name="Load_struct_for_store")
if isinstance(val.type.pointee, ir.IdentifiedStructType) and isinstance(target_type, ir.IdentifiedStructType):
if val.type.pointee.name == target_type.name:
Loaded: ir.Value = self.builder.load(val, name="Load_struct_for_store")
if Loaded.type == target_type:
return Loaded
if CtorExempt:
# 构造物化命中,但源的被指类型与目标不是同一 LLVM 对象、IR 名也不一致
# (跨模块命名隔离:`Gen.structs` 里裸名 opaque 占位与 sha1 全名实体
# 并存,构造走的是另一份条目)⇒ 按整对象拷贝语义落地:先把指针
# bitcast 到目标被指类型再 load(指针→指针 bitcast 恒合法),确保
# 不会把指针值写进值槽。
Src: ir.Value = self.builder.bitcast(
val, ir.PointerType(target_type), name="ctor_ptr_bitcast")
return self.builder.load(Src, name="Load_ctor_for_store")
return None
def _store(self, val: ir.Value, ptr: ir.Value, align: int | None = None,
volatile: bool | None = None, Node: Any = None) -> ir.StoreInstr:
if align is None:
if isinstance(ptr.type, ir.PointerType):
align = self._get_align(ptr.type.pointee)
else:
align = 0
# volatile=None ⇒ 按登记名自动判定(见 _IsPtrVolatile)。判定先于下方任何
# ptr 重建(null_struct_ptr_cast 等),否则会丢登记名。
if volatile is None:
volatile = self._IsPtrVolatile(ptr)
if isinstance(ptr.type, ir.PointerType):
target_type: ir.Type = ptr.type.pointee
if isinstance(target_type, ir.IdentifiedStructType) and target_type.name in self.typedef_int_widths:
if isinstance(val.type, ir.IntType):
width: int = self.typedef_int_widths[target_type.name]
if width > 0:
actual_type: ir.IntType = ir.IntType(width)
bitcast_ptr: ir.Value = self.builder.bitcast(ptr, ir.PointerType(actual_type), name="typedef_bitcast")
if val.type.width != actual_type.width:
if val.type.width > actual_type.width:
val = self.builder.trunc(val, actual_type, name="trunc_typedef")
else:
val = self.builder.zext(val, actual_type, name="zext_typedef")
return self._EmitStore(val, bitcast_ptr, align=align, volatile=volatile)
if val.type != target_type:
if isinstance(val, ir.Constant) and val.constant is None:
if isinstance(target_type, ir.PointerType):
val = ir.Constant(target_type, None)
elif isinstance(target_type, ir.IdentifiedStructType):
val = ir.Constant(ir.PointerType(target_type), None)
ptr = self.builder.bitcast(ptr, ir.PointerType(ir.PointerType(target_type)), name="null_struct_ptr_cast")
elif isinstance(target_type, ir.IntType):
# null 指针常量赋给 int 字段 → 0(与 _coerce_value 一致,
# 防止 `store i32* null, i32*` 类型错乱 IR)
val = ir.Constant(target_type, 0)
else:
coerced: ir.Value | None = self._coerce_value(val, target_type, Node)
if coerced is not None:
val = coerced
else:
src_info: str = self._get_node_info()
raise TypeError(f"cannot store {val.type} to {ptr.type}: mismatching types{src_info}")
return self._EmitStore(val, ptr, align=align, volatile=volatile)
def _get_var_ptr(self, name: str) -> ir.Value | None:
if name in self._reg_values:
val: ir.Value = self._reg_values[name]
var: ir.AllocaInstr = self._alloca(val.type, name=name)
self._store(val, var)
self.variables[name] = var
del self._reg_values[name]
return var
if name in self.variables and self.variables[name] is not None:
return self.variables[name]
return None