from __future__ import annotations from typing import Any 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: """既有整数/浮点规则下的「无损」判定:同族且目标位宽 >= 源位宽。 整数→整数 / 浮点→浮点:按位宽(宽化无损、同宽无损、窄化有损); 整数↔浮点:跨类别,一律视为非无损(有损)。 """ def _FloatBits(x: Any) -> int: if isinstance(x, ir.DoubleType): return 64 if isinstance(x, ir.FloatType): return 32 return 0 if isinstance(Src, ir.IntType) and isinstance(Dst, ir.IntType): return Dst.width >= Src.width sb: int = _FloatBits(Src) db: int = _FloatBits(Dst) if sb and db: return db >= sb 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*)一律禁止(层数不同)。 ④ 完全相同类型不算转换 ⇒ 放行。 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, '非基本类型(结构体/类/联合)') 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,此处只兜非基本类型。 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}):不能把指针类型 {val.type} 隐式转为 {target_type};" f"如需转换请显式 cast(解引用 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