Files
TransPyC/lib/core/Handles/HandlesExprOps.py
T
TermiNexus f19d25782f refactor(tpc): 退役 t/c 模块并将定宽类型升级为内建
删除 lib/includes/t.py、c.py,类型模型迁入 lib/core/TypeSystem.py;移除 IsTModule/_T_MODULE_NAMES 等名称判据,语法糖与内部标记统一为裸名;builtins 的 sizet/intN/uintN/fast/least/uchar/short/long 等改为编译器内建短名。
2026-10-18 13:00:58 +08:00

596 lines
39 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 TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import llvmlite.ir as ir
from lib.core.SymbolUtils import RequireIntLiteralStorable
class ExprOpsHandle(BaseHandle):
def _HandleBinOpLlvm(self, Node: ast.BinOp, VarType: ir.Type | str | None = None) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
Gen._set_node_info(Node, "BinOp")
LeftVal: Any = self.HandleExprLlvm(Node.left, VarType)
if not LeftVal:
return None
RightVal: Any = self.HandleExprLlvm(Node.right, VarType)
if not RightVal:
return None
Op: str | None = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if not Op:
return None
OP_OVERLOAD_MAP: dict[str, list[str]] = {
'+': ['__add__'], '-': ['__sub__'], '*': ['__mul__'],
'/': ['__truediv__', '__div__'], '//': ['__floordiv__'], '%': ['__mod__'],
'**': ['__pow__'],
'&': ['__and__'], '|': ['__or__'], '^': ['__xor__'],
'<<': ['__lshift__'], '>>': ['__rshift__'],
}
# 反向运算符:左操作数无对应 dunder 时,调度右操作数的 __rX__
# (Python 语义:a + b → 先 a.__add__(b),缺失再 b.__radd__(a))
REVERSE_OVERLOAD_MAP: dict[str, list[str]] = {
'+': ['__radd__'], '-': ['__rsub__'], '*': ['__rmul__'],
'/': ['__rtruediv__'], '//': ['__rfloordiv__'], '%': ['__rmod__'],
'**': ['__rpow__'],
'&': ['__rand__'], '|': ['__ror__'], '^': ['__rxor__'],
'<<': ['__rlshift__'], '>>': ['__rrshift__'],
}
if Op in OP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_names: list[str] = OP_OVERLOAD_MAP[Op]
for op_name in op_names:
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
# 反向运算符重载:左操作数无对应 dunder 时调度右操作数的 __rX__(左值)
if Op in REVERSE_OVERLOAD_MAP:
RightClassName: str | None = None
if isinstance(RightVal.type, ir.PointerType):
pointee_r: ir.Type = RightVal.type.pointee
if isinstance(pointee_r, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee_r == ST:
RightClassName = CN
break
elif isinstance(pointee_r, ir.IntType) and pointee_r.width == 8:
RightClassName = self.Trans.ExprUtils._get_var_class(Node.right, Gen)
if RightClassName and RightClassName in Gen.structs:
TargetStructPtrR: ir.PointerType = ir.PointerType(Gen.structs[RightClassName])
RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}")
if RightClassName is None and isinstance(Node.right, ast.Name):
RightClassName = Gen.var_struct_class.get(Node.right.id)
if RightClassName and RightClassName in Gen.structs:
if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8:
TargetStructPtrR = ir.PointerType(Gen.structs[RightClassName])
RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}")
if RightClassName:
for op_name_r in REVERSE_OVERLOAD_MAP[Op]:
result_r: Any = self.Trans.ExprUtils._try_operator_overLoad(RightClassName, op_name_r, RightVal, Gen, LeftVal)
if result_r is not None:
return result_r
if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1:
CharVal: Any = Gen._load(LeftVal, name="Load_char_const")
CharAsInt: Any = Gen.builder.zext(CharVal, RightVal.type, name="char_to_int")
result = Gen.builder.add(CharAsInt, RightVal, name="char_add")
TruncResult: Any = Gen.builder.trunc(result, ir.IntType(8), name="add_to_char")
return TruncResult
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or Gen._check_node_unsigned(Node.right)
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.right) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(LeftVal.type.pointee, ir.IntType):
LeftVal = Gen._load(LeftVal, name="deref_ptr_binop")
if LeftVal.type.width < RightVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_deref_left")
elif LeftVal.type.width > RightVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_deref")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(RightVal.type.pointee, ir.IntType):
RightVal = Gen._load(RightVal, name="deref_ptr_binop_r")
if RightVal.type.width < LeftVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_deref_right")
elif RightVal.type.width > LeftVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_deref")
# `>>` 的符号性按**左操作数**判定:Name 之外的形态(字段/下标/表达式)也要覆盖。
# 注意不要退回 emit_binary_op 里那个“残留变量名”判据(已移除,见 ExprGen.emit_binary_op):
# b: int64 = -196608; b >> 1 曾是误判成 lshr 的现场(IntWidthTest Test 5/6)。
if Op in ('>>', 'RShift'):
if Gen._check_node_unsigned(Node.left):
is_unsigned = True
# 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op`
# (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数,
# 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文
# 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
return result
def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
def _to_bool(val: Any, node: ast.expr) -> Any:
if isinstance(val.type, ir.IntType) and val.type.width == 1:
return val
return self._CoerceCondToBool(Gen, node, val)
values: list[ast.expr] = Node.values
if len(values) < 2:
return self.HandleExprLlvm(values[0]) if values else None
if isinstance(Node.op, ast.And):
phi_incomings: list[tuple[ir.Constant, ir.Block]] = []
end_bb: ir.Block = Gen.func.append_basic_block(name="and.end")
for idx in range(len(values) - 1):
val: Any = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block: ir.Block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, values[idx])
cur_bb: ir.Block = Gen.builder.block
next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}")
Gen.builder.cbranch(val, next_bb, end_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val: Any = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 0)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, values[-1])
last_bb: ir.Block = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi: Any = Gen.builder.phi(ir.IntType(1), name="and.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
elif isinstance(Node.op, ast.Or):
phi_incomings = []
end_bb = Gen.func.append_basic_block(name="or.end")
for idx in range(len(values) - 1):
val = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, values[idx])
cur_bb = Gen.builder.block
next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}")
Gen.builder.cbranch(val, end_bb, next_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 1), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 1)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, values[-1])
last_bb = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi = Gen.builder.phi(ir.IntType(1), name="or.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
return None
def _HandleUnaryOpLlvm(self, Node: ast.UnaryOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
OperandVal: Any = self.HandleExprLlvm(Node.operand)
if not OperandVal:
return None
OpSymbol: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.op)
if not OpSymbol:
return None
UNARY_OVERLOAD_MAP: dict[str, str] = {
'-': '__neg__',
'+': '__pos__',
'~': '__invert__',
}
if OpSymbol in UNARY_OVERLOAD_MAP:
ClassName: str | None = None
if isinstance(OperandVal.type, ir.PointerType):
pointee: ir.Type = OperandVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
ClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
ClassName = self.Trans.ExprUtils._get_var_class(Node.operand, Gen)
if ClassName and ClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName is None and isinstance(Node.operand, ast.Name):
ClassName = Gen.var_struct_class.get(Node.operand.id)
if ClassName and ClassName in Gen.structs:
if isinstance(OperandVal.type, ir.PointerType) and isinstance(OperandVal.type.pointee, ir.IntType) and OperandVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName:
op_name: str = UNARY_OVERLOAD_MAP[OpSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, OperandVal, Gen)
if result is not None:
return result
if OpSymbol == 'not':
# not x = 逻辑取反(优先调度 __bool__,与 VPC 对齐)
bool_val: Any = self._CoerceCondToBool(Gen, Node.operand, OperandVal)
return Gen.builder.not_(bool_val, name="not_result")
elif OpSymbol == '~':
if isinstance(OperandVal.type, ir.IntType):
mask: int = (1 << OperandVal.type.width) - 1
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, mask), name="bnot_result")
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, -1), name="bnot_result")
elif OpSymbol == '+':
return OperandVal
elif OpSymbol == '-':
# 一元减 + 整数字面量的**联合越界判定**(与 VPC `_parse_factor` 同口径):
# `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。
if isinstance(Node.operand, ast.Constant) \
and isinstance(Node.operand.value, int) \
and not isinstance(Node.operand.value, bool):
RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value)
if isinstance(OperandVal.type, ir.IntType):
# 常量直接折叠成常量(保持有符号位型):此前无条件发运行期 neg,常量 `-1`
# 变成一条 `neg i32` 指令,随后加宽时被零扩展 ⇒ 4294967295
# (现场:`int64(-1)` ⇒ `x ^ int64(-1)` 变号、循环不收敛;
# IntWidthTest Test 5 第三条)。
if isinstance(OperandVal, ir.Constant) and isinstance(getattr(OperandVal, 'constant', None), int):
_uw: int = OperandVal.type.width
_uv: int = int(OperandVal.constant) & ((1 << _uw) - 1)
_sv: int = _uv - (1 << _uw) if _uv >= (1 << (_uw - 1)) else _uv
return ir.Constant(OperandVal.type, -_sv)
return Gen.builder.neg(OperandVal, name="neg_result")
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fneg(OperandVal, name="fneg_result")
elif OpSymbol == '*':
# 单目解引用:*p = load(p),要求操作数是指针
if not isinstance(OperandVal.type, ir.PointerType):
raise TypeError(
f"[fast-fail] 单目 '*' 解引用的操作数必须是指针类型,"
f"实际为 {OperandVal.type}"
)
return Gen._load(OperandVal, name="deref")
elif OpSymbol == '&':
# 单目取地址:&x 返回变量/字段/下标的地址(T*)
if isinstance(Node.operand, ast.Name):
VarName: str = Node.operand.id
if VarName in Gen.functions:
return Gen.functions[VarName]
if VarName in Gen.variables and Gen.variables[VarName] is not None:
var_ptr: ir.Value = Gen.variables[VarName]
if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, ir.PointerType):
# T**:若 T 是 struct 类(变量语义即对象),返回对象指针
if isinstance(var_ptr.type.pointee.pointee, ir.IdentifiedStructType):
return self.HandleExprLlvm(Node.operand)
return var_ptr
elif isinstance(Node.operand, ast.Attribute):
AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Node.operand)
if AttrPtr and isinstance(AttrPtr.type, ir.PointerType):
return AttrPtr
elif isinstance(Node.operand, ast.Subscript):
SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Node.operand)
if SubPtr and isinstance(SubPtr.type, ir.PointerType):
return SubPtr
elif isinstance(Node.operand, ast.Call):
# &<call>() → rvalue 取址。复用上面已求值的 OperandVal,绝不二次求值
# (构造调用会再跑一遍 __before_init__/__init__)。
# · 构造调用(_HandleClassNewLlvm)返回的就是临时对象的栈存储指针
# Ptr(Struct) —— 该指针即"栈构造结构体的地址"本身,原样返回;
# 不能再套 alloca+store(会得到 Ptr(Ptr(Struct)),赋值端还得
# load 一次回落,语义也从"对象地址"变"槽地址")。
# · 其它返回值(标量、按值返回的结构体值):入口临时槽承载——
# allocaEntry 同类型槽 → store → 返回槽地址(与 ViperC 侧
# ExprGenOps._addrof_of_call 同口径)。
OperandType: ir.Type = OperandVal.type
if isinstance(OperandType, ir.PointerType) \
and isinstance(OperandType.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
return OperandVal
TmpSlot: ir.Value = Gen._allocaEntry(OperandType, name="addrof_tmp")
Gen.builder.store(OperandVal, TmpSlot)
return TmpSlot
raise TypeError(
f"[fast-fail] 单目 '&' 取地址的操作数必须是左值"
f"(Name/Attribute/Subscript)或构造调用/函数调用结果,实际为 {type(Node.operand).__name__}"
)
return None
def _HandleCompareLlvm(self, Node: ast.Compare) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
LeftVal: Any = self.HandleExprLlvm(Node.left)
if not LeftVal:
return None
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or (Gen._check_node_unsigned(Node.comparators[0]) if Node.comparators else False)
if len(Node.ops) == 1:
Op: ast.cmpop = Node.ops[0]
# in/not in: 调用 __contains__
if isinstance(Op, (ast.In, ast.NotIn)):
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if RightVal:
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.comparators[0], Gen)
if ClassName and Gen._has_function(f'{ClassName}.__contains__'):
# 处理 RightVal 为 i8(整数)的情况:联合类型 | CPtr 降级后可能被 load 为 i8
if isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
RightVal = Gen.builder.inttoptr(RightVal, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8:
RightVal = Gen.builder.bitcast(RightVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
contains_func: ir.Function = Gen._get_function(f'{ClassName}.__contains__')
result: ir.Value = Gen.builder.call(contains_func, [RightVal, LeftVal], name=f"call_{ClassName}.__contains__")
# 归一化为 i1
if isinstance(result.type, ir.IntType) and result.type.width > 1:
zero: ir.Constant = ir.Constant(result.type, 0)
result = Gen.builder.icmp_signed('!=', result, zero, name="contains_bool")
if isinstance(Op, ast.NotIn):
result = Gen.builder.not_(result, name="not_in_result")
return result
return ir.Constant(ir.IntType(1), 0)
ComparatorSymbol: str | None = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
return None
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if not RightVal:
return None
# 比较运算符重载检测:当左侧是结构体指针时,尝试调用 __eq__/__ne__/__lt__/__le__/__gt__/__ge__
CMP_OVERLOAD_MAP: dict[str, str] = {
'==': '__eq__', '!=': '__ne__', '<': '__lt__',
'<=': '__le__', '>': '__gt__', '>=': '__ge__',
}
if ComparatorSymbol in CMP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_name: str = CMP_OVERLOAD_MAP[ComparatorSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
if isinstance(LeftVal, ir.Constant) and isinstance(RightVal, ir.Constant):
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
lv: int = LeftVal.constant
rv: int = RightVal.constant
lw: int = LeftVal.type.width
rw: int = RightVal.type.width
if lw < rw:
lv = lv if lv >= 0 else lv + (1 << lw)
elif rw < lw:
rv = rv if rv >= 0 else rv + (1 << rw)
if is_unsigned:
mask_l: int = (1 << max(lw, rw)) - 1
lv = lv & mask_l
rv = rv & mask_l
cmp_map: dict[str, bool] = {
'==': lv == rv, '!=': lv != rv,
'<': lv < rv, '>': lv > rv,
'<=': lv <= rv, '>=': lv >= rv,
}
result: bool = cmp_map.get(ComparatorSymbol, False)
return ir.Constant(ir.IntType(1), 1 if result else 0)
# is / is not / == / != : 整数与 None(空指针常量)比较
# 整数上的 None 隐式表示 0 ⇒ `x is None` / `x == None` 等价 `x == 0`,
# `x is not None` / `x != None` 等价 `x != 0`(与 VPC 的 ExprGenOps 统一)
# 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃;
# 也必须在下方"指针 vs 整型"特例分支之前,否则 i8 会命中 `Gen._load(null)`
if ComparatorSymbol in ('is', 'is not', '==', '!='):
LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType)
RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType)
LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType)
RightIsValueType: bool = isinstance(RightVal.type, ir.IntType)
IsNoneNegated: bool = ComparatorSymbol in ('is not', '!=')
if LeftIsValueType and RightIsNullPtr:
IsNoneCmpL: ir.Value = Gen.builder.icmp_signed('==', LeftVal, ir.Constant(LeftVal.type, 0), name="is_none_int")
if IsNoneNegated:
IsNoneCmpL = Gen.builder.not_(IsNoneCmpL, name="is_not_result")
return IsNoneCmpL
if RightIsValueType and LeftIsNullPtr:
IsNoneCmpR: ir.Value = Gen.builder.icmp_signed('==', RightVal, ir.Constant(RightVal.type, 0), name="is_none_int")
if IsNoneNegated:
IsNoneCmpR = Gen.builder.not_(IsNoneCmpR, name="is_not_result")
return IsNoneCmpR
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
# 如果宽类型常量值超出窄类型有符号范围,则必须用zext
# i8 类型默认使用 zext(系统编程中 i8 几乎总是无符号字节)
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_cmp")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left_cmp")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.comparators[0]) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_cmp")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right_cmp")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
LeftVal = Gen._load(LeftVal, name="Load_char_ptr_cmp")
else:
if isinstance(RightVal, ir.Constant) and RightVal.constant == 0:
RightVal = ir.Constant(LeftVal.type, None)
else:
# 指针 vs 非零整数: 将指针转为整数走正常 ==(地址值比较)
LeftVal = Gen.builder.ptrtoint(LeftVal, RightVal.type, name="ptr2int_cmp")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8 and isinstance(LeftVal.type, ir.IntType) and LeftVal.type.width == 8:
RightVal = Gen._load(RightVal, name="Load_char_ptr_cmp")
else:
if isinstance(LeftVal, ir.Constant) and LeftVal.constant == 0:
LeftVal = ir.Constant(RightVal.type, None)
else:
# 非零整数 vs 指针: 将指针转为整数走正常 ==(地址值比较)
RightVal = Gen.builder.ptrtoint(RightVal, LeftVal.type, name="ptr2int_cmp")
# is / is not: 地址比较(指针)或值比较(int)
if ComparatorSymbol in ('is', 'is not'):
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
addr_cmp = Gen.builder.icmp_unsigned('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
addr_cmp = Gen.builder.not_(addr_cmp, name="is_not_result")
return addr_cmp
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
val_cmp = Gen.builder.icmp_signed('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
val_cmp = Gen.builder.not_(val_cmp, name="is_not_result")
return val_cmp
# == / !=: 指针间做地址比较(C-Like 语义,不隐式调用 strcmp)
# 如需字符串内容比较,应显式调用 string.strcmp()
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name="int_to_float_cmp")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name="int_to_float_cmp")
result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif is_unsigned:
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
else:
result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
return result
EndBB: ir.Block = Gen.func.append_basic_block(name="cmp.end")
phi_incomings: list[tuple[ir.Value, ir.Block]] = []
for i, (Op, Comparator) in enumerate(zip(Node.ops, Node.comparators)):
ComparatorSymbol = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
continue
RightVal = self.HandleExprLlvm(Comparator)
if not RightVal:
continue
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name=f"zext_left_{i}")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name=f"sext_left_{i}")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Comparator) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name=f"zext_right_{i}")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name=f"sext_right_{i}")
if is_unsigned:
cmp_result: Any = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name=f"int_to_float_cmp_{i}")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name=f"int_to_float_cmp_{i}")
cmp_result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
else:
cmp_result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
if i < len(Node.ops) - 1:
NextBB: ir.Block = Gen.func.append_basic_block(name=f"cmp.next.{i}")
cur_bb: ir.Block = Gen.builder.block
Gen.builder.cbranch(cmp_result, NextBB, EndBB)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(NextBB)
LeftVal = RightVal
else:
cur_bb = Gen.builder.block
Gen.builder.branch(EndBB)
phi_incomings.append((cmp_result, cur_bb))
Gen.builder.position_at_start(EndBB)
final_result: Any = Gen.builder.phi(ir.IntType(1), name="final_cmp")
for val, bb in phi_incomings:
final_result.add_incoming(val, bb)
return final_result