fix(viperc): 静态常量求值对 + - * << 按结果值加宽,杜绝 define 算式静默回绕
- 现象:Gargantua 的 define V3_POOL_SIZE=8192 / V3_POOL_BYTES=V3_POOL_SIZE*24+512 在运行时变成 512(正好是表达式里的 +512),等价于 8192*24=196608 被按 16 位回绕成 0 → 池只剩 512B → 每像素 trace 立刻 OOM 刷屏 - 根因:extract_cdefine_int_value_bits 的 BinOp 位宽规则只“取较宽”,而 + - * << 会增长结果位宽 → define 物化时按 16 位截断 - 现在:操作数的声明宽度仍保持最小(不提升);基准取较宽者;若精确结果装不进基准宽度,则按结果值加宽(向上取 8/16/32/64)—— 静态计算不再回绕;基准未知(纯字面量算式)时保持“未知”,仍由物化端按值推断 - 顺带:未知 BinOp 算子恢复 fail-fast(重构后原落空分支若留着会静默返回 0)
This commit is contained in:
1 parent
813b59e25f
commit
61f23d99a1
1 file changed
+45
-18
@@ -54,7 +54,9 @@ novirtual class AssignGenMixin:
|
|||||||
位宽规则(供 define 物化用):
|
位宽规则(供 define 物化用):
|
||||||
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / t.CUInt16T)→ 该位宽;
|
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / t.CUInt16T)→ 该位宽;
|
||||||
- Name 引用其它 define → 沿用其位宽;
|
- Name 引用其它 define → 沿用其位宽;
|
||||||
- BinOp:两侧位宽**相等** → 保持该位宽(**不提升**);不等 → 取较宽;任一未知 → 未知;
|
- BinOp:操作数的**声明宽度**保持最小(不提升),基准取较宽者;`+ - * <<` 的结果若装不进
|
||||||
|
基准宽度,就**按结果值加宽**(向上取到 8/16/32/64)—— 静态计算不允许回绕;
|
||||||
|
基准未知(如纯字面量算式)→ 保持未知,仍由物化端按值推断;
|
||||||
- 字面量/一元 → 未知(物化时按值推断)。
|
- 字面量/一元 → 未知(物化时按值推断)。
|
||||||
pool 为 None 时无法解析 typedef(仅旧调用方走此路),强转按"取实参值、位宽未知"处理。
|
pool 为 None 时无法解析 typedef(仅旧调用方走此路),强转按"取实参值、位宽未知"处理。
|
||||||
"""
|
"""
|
||||||
@@ -79,31 +81,56 @@ novirtual class AssignGenMixin:
|
|||||||
rb: int = 0
|
rb: int = 0
|
||||||
left_val: int64 = self.extract_cdefine_int_value_bits(bop.left, pool, &lb)
|
left_val: int64 = self.extract_cdefine_int_value_bits(bop.left, pool, &lb)
|
||||||
right_val: int64 = self.extract_cdefine_int_value_bits(bop.right, pool, &rb)
|
right_val: int64 = self.extract_cdefine_int_value_bits(bop.right, pool, &rb)
|
||||||
if bits_out is not None:
|
# 操作数位宽(各自的**声明宽度**,保持最小、不提升):两侧不等取较宽,一侧未知则沿用另一侧
|
||||||
if lb != 0 and rb != 0: bits_out[0] = lb if lb > rb else rb
|
wbase: int = 0
|
||||||
elif lb != 0: bits_out[0] = lb
|
if lb != 0 and rb != 0: wbase = lb if lb > rb else rb
|
||||||
elif rb != 0: bits_out[0] = rb
|
elif lb != 0: wbase = lb
|
||||||
if bop.op == ast.OpKind.BitOr: return left_val | right_val
|
elif rb != 0: wbase = rb
|
||||||
if bop.op == ast.OpKind.BitAnd: return left_val & right_val
|
res: int64 = 0
|
||||||
if bop.op == ast.OpKind.BitXor: return left_val ^ right_val
|
if bop.op == ast.OpKind.BitOr: res = left_val | right_val
|
||||||
|
elif bop.op == ast.OpKind.BitAnd: res = left_val & right_val
|
||||||
|
elif bop.op == ast.OpKind.BitXor: res = left_val ^ right_val
|
||||||
# 移位:求值器内部按 64 位算,计数为负或 >= 64 时结果无意义(宿主侧是 poison/未定义),
|
# 移位:求值器内部按 64 位算,计数为负或 >= 64 时结果无意义(宿主侧是 poison/未定义),
|
||||||
# 与 require_shift_count_in_range 同一口径直接 fail-fast,不产出垃圾常量。
|
# 与 require_shift_count_in_range 同一口径直接 fail-fast,不产出垃圾常量。
|
||||||
if bop.op == ast.OpKind.LShift or bop.op == ast.OpKind.RShift:
|
elif bop.op == ast.OpKind.LShift or bop.op == ast.OpKind.RShift:
|
||||||
if right_val < 0 or right_val >= 64:
|
if right_val < 0 or right_val >= 64:
|
||||||
self.Gen.TypeGenErr.fatal_error(val_node, f"constant expression shift count out of range: the shift count evaluates to {right_val} at compile time, " "but the evaluator works on 64-bit integers (valid range 0..63); the result would be undefined. " "Fix: use a count in 0..63, or restructure the expression.", "ValueError")
|
self.Gen.TypeGenErr.fatal_error(val_node, f"constant expression shift count out of range: the shift count evaluates to {right_val} at compile time, " "but the evaluator works on 64-bit integers (valid range 0..63); the result would be undefined. " "Fix: use a count in 0..63, or restructure the expression.", "ValueError")
|
||||||
if bop.op == ast.OpKind.LShift: return left_val << right_val
|
if bop.op == ast.OpKind.LShift: res = left_val << right_val
|
||||||
return left_val >> right_val
|
else: res = left_val >> right_val
|
||||||
if bop.op == ast.OpKind.Add: return left_val + right_val
|
elif bop.op == ast.OpKind.Add: res = left_val + right_val
|
||||||
if bop.op == ast.OpKind.Sub: return left_val - right_val
|
elif bop.op == ast.OpKind.Sub: res = left_val - right_val
|
||||||
if bop.op == ast.OpKind.Mult: return left_val * right_val
|
elif bop.op == ast.OpKind.Mult: res = left_val * right_val
|
||||||
# 编译期除零:绝不把除法交给宿主(会直接把编译器打挂),fail-fast 给出可读诊断。
|
# 编译期除零:绝不把除法交给宿主(会直接把编译器打挂),fail-fast 给出可读诊断。
|
||||||
if bop.op == ast.OpKind.Div or bop.op == ast.OpKind.FloorDiv or bop.op == ast.OpKind.Mod:
|
elif bop.op == ast.OpKind.Div or bop.op == ast.OpKind.FloorDiv or bop.op == ast.OpKind.Mod:
|
||||||
if right_val == 0:
|
if right_val == 0:
|
||||||
self.Gen.TypeGenErr.fatal_error(val_node, "constant expression division by zero: the right operand of '/', '//' or '%' evaluates to 0 at compile time.", "ValueError")
|
self.Gen.TypeGenErr.fatal_error(val_node, "constant expression division by zero: the right operand of '/', '//' or '%' evaluates to 0 at compile time.", "ValueError")
|
||||||
if bop.op == ast.OpKind.Mod: return left_val % right_val
|
if bop.op == ast.OpKind.Mod: res = left_val % right_val
|
||||||
return left_val // right_val
|
else: res = left_val // right_val
|
||||||
# Unknown BinOp operator: fail fast
|
else:
|
||||||
|
# Unknown BinOp operator: fail fast(不能静默返回 0)
|
||||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||||
|
if bits_out is not None:
|
||||||
|
# 静态计算**自行拓宽**:字面量/define 的声明宽度仍保持最小(不提升),但 `+ - * <<`
|
||||||
|
# 的精确结果可能比操作数宽 —— 若照操作数宽度物化就会静默回绕
|
||||||
|
# (现场:define A=8192(最小 16 位)的 `A*24+512` 被按 16 位物化成 512)。
|
||||||
|
# 规则:以操作数的“较宽”为基准,再用**结果值**算精确可容纳位宽,只有装不下才加宽
|
||||||
|
# (不无故提升,避免把 `CDefine + 1` 这类算式整体抬成 i64 引出新的“隐式窄化”报错);
|
||||||
|
# 基准未知(wbase==0,如纯字面量算式)时保持“未知”,仍由物化端按值推断。
|
||||||
|
w: int = wbase
|
||||||
|
if w > 0 and res != 0:
|
||||||
|
t: int64 = res
|
||||||
|
if res < 0: t = res ^ int64(-1)
|
||||||
|
vw: int = 1 # 符号位
|
||||||
|
while t != 0:
|
||||||
|
vw += 1
|
||||||
|
t = t >> 1
|
||||||
|
if vw > w: w = vw
|
||||||
|
if w > 0:
|
||||||
|
if w <= 8: bits_out[0] = 8
|
||||||
|
elif w <= 16: bits_out[0] = 16
|
||||||
|
elif w <= 32: bits_out[0] = 32
|
||||||
|
else: bits_out[0] = 64
|
||||||
|
return res
|
||||||
|
|
||||||
# Case 3: Name - a registered CDefine constant or True/False literal
|
# Case 3: Name - a registered CDefine constant or True/False literal
|
||||||
if k == ast.ASTKind.Name:
|
if k == ast.ASTKind.Name:
|
||||||
|
|||||||
Reference in new issue
Block a user