fix(viperc): CDefine 常量按位宽处理时不再把可容正值翻转成负数,修常量算式折错

现场:Gargantua _start 的 string.memset(fb, 0, GW * GH * 4)(GW=160, GH=90)应折叠为 57600,实际生成 movl \(= -34560)作为 memset 长度 → 从 0x457ca0 写 4GB → 越界写撞到映射区末尾 0x467000,用户态 #PF err=0x7(cr2 与反汇编逐字对上)。根因:define GW = 160 的位宽按值推断为 8(无符号可容),而 _cdefine_bits_value/_normalize_cdefine_bits 无条件 '(value<<56)>>56' 重解释为有符号 → 160 变 -96(=-96*90*4=-34560)。现只在两种解释都装不下时才截断;位型不变(const_int 本就按位宽截断),仅不再翻转可容正值的语义。
This commit is contained in:
TermiNexus committed 2026-10-14 22:20:52 +08:00
1 parent a143c2de0c
commit dc4468593c
2 files changed
+38 -18

No files matched your search

+19 -9
View File
@@ -189,17 +189,27 @@ novirtual class AssignGenMixin:
return 0
def _normalize_cdefine_bits(self, value: int64, bits: int) -> int64:
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 ExprGenCdef._cdefine_bits_value 同口径)。
"""把值按位宽处理:**只有在当前位宽的两种解释下都装不下时才截断**,否则原样保留
(与 ExprGenCdef._cdefine_bits_value 同口径)。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0),
不是直接把原值搬过来、也不是只对"刚好越界一点"的值做重解释。
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/VPC 上可能判成 sext,
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0)。
但**不**把"无符号可容、有符号装不下"的正值重解释成负数:旧实现无条件
`(value << 56) >> 56`,8 位下的 160 变成 -96,正值静默变负,常量算式随之算错
(现场:Gargantua `GW * GH * 4` = 57600 被折成 -34560)。
实现仍避开大掩码字面量(见下)。
"""
if bits == 8: return (value << 56) >> 56
if bits == 16: return (value << 48) >> 48
if bits == 32: return (value << 32) >> 32
if bits == 8:
if value >= -128 and value <= 127: return value
if value > 127 and (value >> 8) == 0: return value
return (value << 56) >> 56
if bits == 16:
if value >= -32768 and value <= 32767: return value
if value > 32767 and (value >> 16) == 0: return value
return (value << 48) >> 48
if bits == 32:
if value >= -2147483647 - 1 and value <= 2147483647: return value
if value > 2147483647 and (value >> 32) == 0: return value
return (value << 32) >> 32
return value
def cdefine_decl_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
+19 -9
View File
@@ -842,17 +842,27 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# ============================================================
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 AssignGenMixin._normalize_cdefine_bits 同口径)。
"""把值按位宽处理:**只有在当前位宽的两种解释下都装不下时才截断**,否则原样保留。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(WORD(0x80000000) → 0),
而不是只对"刚好越界一点"的值做重解释。
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/VPC 上可能判成 sext,
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0)。
但不该把"无符号可容、有符号装不下"的**正值**重解释成负数:旧实现无条件
`(value << 56) >> 56`,8 位下的 160 被读成 -96,正值静默变负 —— 现场
Gargantua `_start` 的 `string.memset(fb, 0, GW * GH * 4)`(GW=160, GH=90)
被折成 -34560,作为长度写穿映射区 → #PF。位型不变(const_int 本就按位宽截断),
只是不再翻转可容正值的语义。实现仍避开大掩码字面量(见下)。
"""
if bits == 8: return (value << 56) >> 56
if bits == 16: return (value << 48) >> 48
if bits == 32: return (value << 32) >> 32
if bits == 8:
if value >= -128 and value <= 127: return value
if value > 127 and (value >> 8) == 0: return value
return (value << 56) >> 56
if bits == 16:
if value >= -32768 and value <= 32767: return value
if value > 32767 and (value >> 16) == 0: return value
return (value << 48) >> 48
if bits == 32:
if value >= -2147483647 - 1 and value <= 2147483647: return value
if value > 2147483647 and (value >> 32) == 0: return value
return (value << 32) >> 32
return value
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*: