Files
TransPyC/lib/core/LLVMCG/MemoryOps.py
T
TermiNexus 9f8190d58f fix(cast): 无损口径对齐 TPV + 规则③豁免构造物化(p: Point = Point() 放行,真实 Foo*→Foo 仍禁)
_ScalarConvertLossless 改为与 TPV 一致:int↔int 全向允许、float↔float 全向允许、int→float 需目标浮点位宽≥源整数位宽、float→int 不允许(此前把整数↔浮点一律判有损,两侧不一致)

新增 _ValueNodeOf / _CtorCallStructType:指针→非基本值分支先豁免构造物化(Call 被调方命中 Gen.structs 且类型同一),其余保持禁止;报文前缀与 TPV 逐字一致;诊断沿用 _DescribeValueNode

已知未覆盖:TPC 无对称的“值→指针”检查;泛型构造不豁免;别名导入的类名可能命不中(保守不豁免,只误报不漏放)
2026-09-28 15:17:48 +08:00

590 lines
31 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
import 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` 关键字;语义与 TransPyV 侧 `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(空值常量)/ 指针层级 / 基本类型 / 无损
#
# 语义唯一来源与 TransPyV 对齐(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:
"""通用指针(t.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:
"""基本标量「无损」判定(与 TransPyV `_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 True
# 浮点 ↔ 浮点:允许
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
@staticmethod
def _ClassifyPtrImplicitConvert(Src: Any, Dst: Any) -> tuple[bool, str]:
"""隐式转换判据:返回 (Allowed, Reason)。
规则(用户决定,与 TransPyV 同口径):
① 通用指针(t.CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。
② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型
(结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止。
③ 非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止(层数不同);
**唯一例外**是「构造物化」——该豁免需 AST 层判据,故在 coerce 调用点
由 _CtorCallStructType 单独放行(本判据只给出「禁」的结论)。
④ 完全相同类型不算转换 ⇒ 放行。
Reason 供报文说明「哪一级不允许 + 原因(非基本类型/有损/层数不同)」。
"""
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, '被指类型有损')
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:
"""把「外层语句节点」下沉到其「值子表达式节点」(与 TransPyV _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
def _CtorCallStructType(self, Node: Any = None) -> Any:
"""AST 层判定:Node(或其值子节点)是否为「类/构造调用」右值;是则返回其构造的
聚合类型(IdentifiedStruct/LiteralStruct),否则返回 None。
规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」:
右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据复用本仓既有的
「Call 的被调方是类/构造」口径(见 HandlesExprCall._HandleCallLlvm 的分派):
- callee 裸名/属性名命中已注册类(`Gen.structs`)→ 构造;
- 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。
"""
N: Any = MemoryOpsMixin._ValueNodeOf(Node)
if not isinstance(N, ast.Call):
return None
Func: Any = getattr(N, 'func', None)
ClassName: str | None = None
if isinstance(Func, ast.Name):
ClassName = Func.id
elif isinstance(Func, ast.Attribute):
ClassName = Func.attr
if not ClassName:
return None
ST: Any = getattr(self, 'structs', {}).get(ClassName)
if ST is None:
return None
if isinstance(ST, ir.PointerType):
ST = ST.pointee
if not isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)):
return None
return ST
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)
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)
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 _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
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
if target_type.width < val.type.width:
return self.builder.trunc(val, target_type, name="trunc_store")
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)
if not Allowed:
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转换已禁止({Reason}):不能把指针类型 {val.type} 隐式转为 {target_type};"
f"如需转换请显式 cast(如 t.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):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
# None(空指针常量)已在上方短路为目标零值,不会落到这里。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针隐式转整数已禁止:不能把指针类型 {val.type} 赋给整数类型 {target_type};"
f"如需转换请显式 cast(如 t.CUInt64T(p) / t.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(结构体/数组按值回读)属类型信息缺失,
# 须显式解引用(c.Deref(p) / *p)或显式强转。基本标量目标已在上方
# 「指针隐式转整数」分支 fail-fast,此处只兜非基本类型。
# **唯一例外=「构造物化」**:右值来自该类型的构造调用时放行,如
# `p: Point = Point()`(参照 GCC 构造语义:这是构造,不是类型转换)。
CtorST: Any = self._CtorCallStructType(Node)
CtorExempt: bool = False
if CtorST is not None:
if CtorST == target_type:
CtorExempt = True
elif (isinstance(CtorST, ir.IdentifiedStructType)
and isinstance(target_type, ir.IdentifiedStructType)
and CtorST.name == target_type.name):
CtorExempt = True
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"如需解引用请显式 c.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
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