diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index 1062c27..7d5fa6c 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -121,6 +121,21 @@ novirtual class ExprGenBase: def get_ptr_level(self, ty: mlirlite.LLVMType*) -> int: """Compute pointer level (0=non-ptr, 1=ptr, 2=ptr-to-ptr); delegates to TSpec.SpecTypePtrLevel.""" return TSpec.SpecTypePtrLevel(ty) + # _const_fits_int_type - 编译期整型常量的值能否**无损**落进目标整型位宽。 + # 有符号或无符号任一解释放得下即算「放得下」(位型不变);用于二元运算按常量适配另一侧位宽。 + def _const_fits_int_type(self, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*) -> int: + """1 = the integer constant's value fits the target integer width losslessly; else 0.""" + if val is None or target_ty is None or val.IsConst != 1: return 0 + bits: int = self.get_llvm_type_bits(target_ty) + if bits <= 0 or bits > 63: return 0 + v: int64 = val.IntVal + lo: int64 = -(int64(1) << (bits - 1)) + hi: int64 = (int64(1) << (bits - 1)) - 1 + if v >= lo and v <= hi: return 1 + if v >= 0: + if bits == 63: return 1 + if v <= (int64(1) << bits) - 1: return 1 + return 0 # _is_zero_int_literal - is the AST node the integer literal 0? # Used for ptr != 0 / ptr == 0, where 0 means NULL and is allowed. def _is_zero_int_literal(self, node: ast.AST*) -> int: diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 5985c58..a875a3e 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -1230,10 +1230,14 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): rhs_bits = 32 if lhs_bits > rhs_bits: - if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty) + # 宽侧是「值能无损落进窄侧」的编译期常量时,把**常量**降到窄侧类型(C 语义:常量优先适配另一侧), + # 否则 `crc(u32) ^ 0xFFFFFFFF(i64)` 会把 crc 抬成 64 位,结果再赋回 32 位时报隐式窄化。 + if self._const_fits_int_type(lhs, rhs.Ty) != 0: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans) + elif self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty) else: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans) elif rhs_bits > lhs_bits: - if self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty) + if self._const_fits_int_type(rhs, lhs.Ty) != 0: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans) + elif self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty) else: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans) # Bitwise type safety: when both sides are pointers (lhs_bits==0 and rhs_bits==0), bitwise ops are illegal (LLVM