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 ast import llvmlite.ir as ir 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__'], } 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 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") if Op in ('>>', 'RShift') and isinstance(Node.left, ast.Name): vname: str = Node.left.id if vname in Gen.var_signedness and Gen.var_signedness[vname]: is_unsigned = True 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, name: str) -> Any: if isinstance(val.type, (ir.FloatType, ir.DoubleType)): return Gen.builder.fcmp_ordered('!=', val, ir.Constant(val.type, 0.0), name=name) if not isinstance(val.type, ir.IntType) or val.type.width != 1: return Gen.builder.icmp_signed('!=', val, Gen._ZeroConst(val.type), name=name) return 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, f"andcond{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, "andcond_last") 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, f"orcond{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, "orcond_last") 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': if isinstance(OperandVal.type, ir.IntType) and OperandVal.type.width == 1: return Gen.builder.not_(OperandVal, name="not_result") if isinstance(OperandVal.type, ir.PointerType): null_val: ir.Constant = ir.Constant(OperandVal.type, None) return Gen.builder.icmp_signed('==', OperandVal, null_val, name="not_result") zero: ir.Constant = ir.Constant(OperandVal.type, 0) return Gen.builder.icmp_signed('==', OperandVal, zero, 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 == '-': if isinstance(OperandVal.type, ir.IntType): return Gen.builder.neg(OperandVal, name="neg_result") elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)): return Gen.builder.fneg(OperandVal, name="fneg_result") 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(整数)的情况:联合类型 | t.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: 值类型(IntType)与 None(空指针常量)比较 # 值类型(如 i8/i32)永远不可能为 None,因此: # - 值类型 is None → 恒为 false (i1 0) # - 值类型 is not None → 恒为 true (i1 1) # 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃 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) if (LeftIsValueType and RightIsNullPtr) or (RightIsValueType and LeftIsNullPtr): IsNotResult: int = 1 if ComparatorSymbol == 'is not' else 0 return ir.Constant(ir.IntType(1), IsNotResult) 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: # 不将整数转为指针(避免 strcmp 解引用无效地址),改为将指针转为整数走正常 == 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: # 不将整数转为指针(避免 strcmp 解引用无效地址),改为将指针转为整数走正常 == 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 # str/bytes == str/bytes: 值比较(调用 strcmp),None 比较用地址 if ComparatorSymbol in ('==', '!='): if (isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8): left_is_null: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None right_is_null: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None if not (left_is_null or right_is_null): strcmp_func = Gen.get_or_declare_c_func('strcmp', ir.FunctionType(ir.IntType(32), [ir.IntType(8).as_pointer(), ir.IntType(8).as_pointer()])) cmp_result = Gen.builder.call(strcmp_func, [LeftVal, RightVal], name="str_eq_cmp") zero = ir.Constant(ir.IntType(32), 0) eq_result = Gen.builder.icmp_signed('==', cmp_result, zero, name="str_eq") if ComparatorSymbol == '!=': eq_result = Gen.builder.not_(eq_result, name="str_neq") return eq_result 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