diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 366c0ad..740f48e 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -1166,6 +1166,9 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): # · 移位 → **计数(rhs)不代表结果位宽**,不参与比较;结果位宽 = 左操作数位宽(无目标时)。 # 任一侧位宽为 0(未知/未定)会天然退化为另一侧(0 不抬高 max);只有连目标与两侧位宽**全都缺失** # (类型信息完全缺失)时才回退到 32 —— 这只是可编译性兜底,不代表"提升"。 + # 注意:本 helper 的 max 不含「常量豁免」——「能无损落进另一侧位宽的常量不抬高工作位宽」这条规则 + # 由等宽化处的 _const_operand_demotes 实施(见下方),因为本语言非移位运算的实际结果位宽就是 + # 「两侧中较宽的一侧」,等宽化处才是决定点;移位本就不让计数参与,故与此无关。 def _binop_work_bits(self, op: int, tgt_bits: int, lhs_bits: int, rhs_bits: int) -> int: """Working bit width for translate_binop (no C-style integer promotion).""" w: int = tgt_bits @@ -1175,6 +1178,29 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): if w == 0: w = 32 return w + # _const_operand_demotes - 位宽上更宽的一侧是编译期常量时,它能否**降为另一侧位宽**, + # 从而不抬高运算的工作位宽(常量适配的唯一收口处,位运算与算术共用)。 + # 用户规则:常量应隐式适配另一侧位宽——`baseport(u16) + 7` 的 `7` 默认是 i32,但应适配 u16, + # 使整个加法保持在 u16(结果位宽 16),而不是把 u16 变量抬到 i32 后在赋值/传参处报隐式窄化。 + # 判定: + # 1) 常量值必须能**无损**落进另一侧位宽(复用 _const_fits_int_type);否则常量照常计入 max + # (如 `u8 变量 + 0x1234`:0x1234 落不进 u8,仍把变量拓宽到 i32,该报窄化就照旧报)。 + # 2) 位运算 & | ^:无进位、结果不会超出参与值位宽,常量总能无损降——既有行为,保持不变。 + # 3) 算术 + - * / %:另一侧也是常量时**不降**——`(MEMBUDDY_MAX_ORDERS + 1) * 8` 这类窄常量 + # 乘积若逐个降宽会在 i8 回绕(老 bug:`i8(32)+1 → 33`、`×8` 溢出成 8,见 includes/memhub.vp), + # 必须保持「不降宽」的既有行为。 + # 4) 算术:常量位宽不高于目标位宽时也不降——此时 max(目标位宽, 另一侧位宽) 已不小于常量位宽, + # 常量并没有抬高工作位宽(见 _binop_work_bits),无需也不应改变结果。 + def _const_operand_demotes(self, is_bitwise: int, const_val: mlirlite.Value*, const_bits: int, other_val: mlirlite.Value*, other_ty: mlirlite.LLVMType*, tgt_bits: int) -> int: + """1 = the wider-side constant may be demoted to the other side's type (no work-width raise).""" + if const_val is None or other_val is None: return 0 + if const_val.IsConst != 1: return 0 + if self._const_fits_int_type(const_val, other_ty) == 0: return 0 + if is_bitwise != 0: return 1 + if other_val.IsConst == 1: return 0 + if const_bits <= tgt_bits: return 0 + return 1 + # translate_binop - binary operations with automatic type promotion # ============================================================ def translate_binop(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*: @@ -1301,17 +1327,18 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): else: rhs = mlirlite.build_trunc(builder, rhs, lhs.Ty) rhs_bits = lhs_bits else: - # 「常量适配」只对**位运算(& | ^)**生效:位运算结果不会超出参与值的位宽,把能无损落进另一侧的 - # 常量降下来是安全的。**算术(+ - * / %)不做适配**——窄位宽下会回绕(如 define 的 - # `(MEMBUDDY_MAX_ORDERS + 1) * 8`:i8(32)+1 → 33、再 ×8 溢出成 8),必须按 C 语义把窄操作数拓宽。 + # 「常量适配」:一侧是能无损落进另一侧位宽的编译期常量时,把**常量**降到另一侧位宽,使其 + # 不抬高工作位宽(用户规则:常量应隐式适配另一侧,`baseport(u16) + 7` 结果仍是 u16)。 + # 也覆盖既有的位运算(& | ^)常量无损适配,二者共用 _const_operand_demotes 一处判定; + # 算术的两处例外(另一侧是常量 / 常量未超过目标位宽)见该 helper 的注释。 is_bitwise_op: int = 0 if op == ast.OpKind.BitAnd or op == ast.OpKind.BitOr or op == ast.OpKind.BitXor: is_bitwise_op = 1 if lhs_bits > rhs_bits: - if is_bitwise_op != 0 and self._const_fits_int_type(lhs, rhs.Ty) != 0: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans) + if self._const_operand_demotes(is_bitwise_op, lhs, lhs_bits, rhs, rhs.Ty, tgt_bits) != 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 is_bitwise_op != 0 and self._const_fits_int_type(rhs, lhs.Ty) != 0: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans) + if self._const_operand_demotes(is_bitwise_op, rhs, rhs_bits, lhs, lhs.Ty, tgt_bits) != 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)