fix(tpc): 负数常量与负值整型加宽改为保符号,sext 与常量折叠,不再零扩展

根因:多处手写 zext 做整型加宽,把有符号为负的常量当无符号处理。涉及 HandlesAnnAssign 局部标量初始化两处、HandlesExprCall 的 typedef 与 builtins 强转两处。

一元负号对常量无条件发运行期 neg,常量 -1 变成 neg i32 指令后再被零扩展成 4294967295。

_eval_global_const 用无符号读回值取负,-196608 被物化成 4294770688 即 0x00000000FFFD0000。

现场:b 为 int64 等于 -196608 后,b 右移 1 呈逻辑右移假象,负数右移循环不收敛,见 IntWidthTest Test 5 与 Test 6。

改法:统一收敛到 _coerce_value,加宽用 sext,窄化按其规则 fail-fast;一元负号对常量直接折叠为有符号常量;_eval_global_const 先按位宽还原带符号真值。

另修右移符号性:去掉 emit_binary_op 里按 self._last_var_name 的残留变量名重判,调用方改按 _check_node_unsigned 判定。

验收:Test/IntWidthTest 10 项全 PASS,此前 TPC 侧 5 项通过 2 项失败。
This commit is contained in:
TermiNexus committed 2026-10-14 20:49:35 +08:00
1 parent 55a6856c4e
commit aaf9330b3b
3 files changed
+43 -22

No files matched your search

+16 -11
View File
@@ -896,13 +896,14 @@ class AnnAssignHandle(BaseHandle):
if isinstance(VarPtr.type, ir.PointerType):
TargetType: ir.Type = VarPtr.type.pointee
if InitValue.type != TargetType:
if isinstance(InitValue.type, ir.IntType) and isinstance(TargetType, ir.IntType):
if TargetType.width > InitValue.type.width:
InitValue = Gen.builder.zext(InitValue, TargetType, name=f"zext_{VarName}")
elif TargetType.width < InitValue.type.width:
InitValue = Gen.builder.trunc(InitValue, TargetType, name=f"trunc_{VarName}")
elif isinstance(InitValue.type, ir.PointerType) and isinstance(TargetType, ir.PointerType):
InitValue = Gen.builder.bitcast(InitValue, TargetType, name=f"cast_{VarName}")
# 统一走 `_coerce_value`(隐式转换的唯一收敛点):
# 此前这里手写 `zext/trunc`,整型加宽一律 zext ⇒ 负常量被当无符号
# 处理(现场:`b: int64 = -196608` 落成 0x00000000FFFD0000 大正数,
# IntWidthTest Test 5/6 的 FAIL)。`_coerce_value` 对 i32→i64 用
# `sext` 保持符号,隐式窄化亦按规则 fail-fast。
_coerced: ir.Value | None = Gen._coerce_value(InitValue, TargetType, Node)
if _coerced is not None:
InitValue = _coerced
Gen._store(InitValue, VarPtr)
except Exception as _e:
if _config_mode == "strict":
@@ -1070,10 +1071,14 @@ class AnnAssignHandle(BaseHandle):
InitValue = (ir.Constant(VarType, None) if isinstance(VarType, ir.PointerType)
else Gen._ZeroConst(VarType))
elif isinstance(InitValue.type, ir.IntType) and isinstance(VarType, ir.IntType):
if VarType.width > InitValue.type.width:
InitValue = Gen.builder.zext(InitValue, VarType, name=f"zext_{VarName}")
elif VarType.width < InitValue.type.width:
InitValue = Gen.builder.trunc(InitValue, VarType, name=f"trunc_{VarName}")
# 统一走 `_coerce_value`(隐式转换的唯一收敛点):此处原先手写
# `zext`,整型加宽一律零扩展 ⇒ 负常量被当无符号处理。
# 现场:`b: int64 = -196608` 落成 4294770688(0x00000000FFFD0000)
# ⇒ 随后 `b >> 1` 呈现“逻辑右移”假象(IntWidthTest Test 5/6)。
# `_coerce_value` 对 i32→i64 用 `sext` 保符号;隐式窄化按其规则 fail-fast。
_coerced_i: ir.Value | None = Gen._coerce_value(InitValue, VarType, Node.value)
if _coerced_i is not None:
InitValue = _coerced_i
elif isinstance(InitValue.type, ir.PointerType) and isinstance(VarType, ir.PointerType):
Allowed: bool
Reason: str
+18 -11
View File
@@ -1886,7 +1886,10 @@ class ExprCallHandle(BaseHandle):
if expected_type.width < val.type.width:
return Gen.builder.trunc(val, expected_type)
else:
return Gen.builder.zext(val, expected_type)
# 整型加宽必须**保符号**(i32/i64 按有符号语义):此前一律 zext,会把负值
# 当无符号处理。现场:`int64(-1)` 里 `-1` 的 i32 位型被 zext 成 4294967295
# ⇒ `x ^ int64(-1)` 变号 ⇒ IntWidthTest Test 5 第三条(循环不收敛)FAIL。
return Gen.builder.sext(val, expected_type)
if isinstance(val.type, ir.PointerType) and isinstance(expected_type, ir.IntType):
if expected_type.width == 64:
return Gen.builder.ptrtoint(val, expected_type)
@@ -2470,10 +2473,13 @@ class ExprCallHandle(BaseHandle):
if ArgVal.type == TargetType:
return ArgVal
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType):
if ArgVal.type.width < TargetType.width:
return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast")
elif ArgVal.type.width > TargetType.width:
return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast")
# 统一走 `_coerce_value`(隐式/显式转换的唯一收敛点):此前手写 `zext`,
# 整型加宽一律零扩展 ⇒ 负值被当无符号(现场:`int64(-1)` 里 -1 的 i32 位型
# 变成 4294967295 ⇒ `x ^ int64(-1)` 变号 ⇒ IntWidthTest Test 5 第三条
# 循环不收敛)。`_coerce_value` 加宽用 `sext` 保符号,窄化按其规则 fail-fast。
_coerced_td: ir.Value | None = Gen._coerce_value(ArgVal, TargetType, Node)
if _coerced_td is not None:
return _coerced_td
return ArgVal
if isinstance(TargetType, ir.PointerType):
if isinstance(ArgVal.type, ir.PointerType):
@@ -2535,12 +2541,13 @@ class ExprCallHandle(BaseHandle):
if ArgVal.type == TargetType:
return ArgVal
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType):
# builtins typedef 数值转换(int64(bool) / uint64(i32) 等):按宽度
# zext/trunc,与 _HandleTypedefCastLlvm 的语义一致。
if ArgVal.type.width < TargetType.width:
return Gen.builder.zext(ArgVal, TargetType, name="zext_typedef_cast")
elif ArgVal.type.width > TargetType.width:
return Gen.builder.trunc(ArgVal, TargetType, name="trunc_typedef_cast")
# 统一走 `_coerce_value`(隐式/显式转换的唯一收敛点):此前手写 `zext`,
# 整型加宽一律零扩展 ⇒ 负值被当无符号。
# 现场:`int64(-1)` → i64 4294967295 ⇒ `x ^ int64(-1)` 变号、循环不收敛
# (IntWidthTest Test 5 第三条);`_coerce_value` 加宽用 `sext` 保符号。
_coerced_b: ir.Value | None = Gen._coerce_value(ArgVal, TargetType, Node)
if _coerced_b is not None:
return _coerced_b
return ArgVal
if isinstance(TargetType, ir.PointerType):
if isinstance(ArgVal.type, ir.PointerType):
+9
View File
@@ -287,6 +287,15 @@ class ExprOpsHandle(BaseHandle):
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")