[midend-IVE]参考libdivide库,实现了魔数的正确求解,如果后续出错直接用API或者不要除法强度削弱了

This commit is contained in:
rain2133
2025-08-14 05:12:54 +08:00
parent 06a368db39
commit 7547d34598
5 changed files with 421 additions and 61 deletions

View File

@ -228,6 +228,173 @@ Branch 和 Return 指令: 这些是终结符指令,不产生一个可用于其
在提供的代码中SSAPValue 的 constantVal 是 int 类型。这使得浮点数常量传播变得复杂。对于浮点数相关的指令kFAdd, kFMul, kFCmp, kFNeg, kFNot, kItoF, kFtoI 等),如果不能将浮点值准确地存储在 int 中,或者不能可靠地执行浮点运算,那么通常会保守地将结果设置为 Bottom。一个更完善的 SCCP 实现会使用 std::variant<int, float> 或独立的浮点常量存储来处理浮点数。
## LoopSR循环归纳变量强度削弱 关于魔数计算的说明
魔数除法的核心思想是:将除法转换为乘法和移位
数学原理x / d ≈ (x * m) >> (32 + s)
m 是魔数 (magic number)
s 是额外的移位量 (shift)
>> 是算术右移
2^(32+s) / d ≤ m < 2^(32+s) / d + 2^s / d
cd /home/downright/Compiler_Opt/mysysy && python3 -c "
# 真正的迭代原因:精度要求
def explain_precision_requirement():
d = 10
print('魔数算法需要找到精确的边界值')
print('目标2^p > d * (2^31 - r)其中r是余数')
print()
# 模拟我们算法的迭代过程
p = 31
two_p = 2**p
r = two_p % d # 余数
m = two_p // d # 商
print(f'初始状态 (p={p}):')
print(f' 2^{p} = {two_p:,}')
print(f' 2^{p} / {d} = {m:,} 余 {r}')
print(f' 需要检查: 2^{p} > {d} * (2^31 - {r}) = {d * (2**31 - r):,}')
print(f' 条件满足: {two_p > d * (2**31 - r)}')
print()
# 继续迭代直到条件满足
iteration = 0
while r != 0 and m < 2**31:
iteration += 1
p += 1
two_p *= 2
m *= 2
r *= 2
if r >= d:
m += 1
r -= d
print(f'迭代 {iteration} (p={p}):')
print(f' m = {m:,}, r = {r}')
print(f' m < 2^31? {m < 2**31} (2^31 = {2**31:,})')
if m >= 2**31:
print(f' ✗ 魔数 {m:,} 超出32位范围!')
break
elif r == 0:
print(f' ✓ 余数为0找到精确解')
break
if iteration > 10: # 防止无限循环
print(' ... (限制迭代次数)')
break
explain_precision_requirement()
"
问题在于精度要求。理论魔数需要非常精确,不能简单地用 2^32 / d。让我展示真正的迭代原因
cd /home/downright/Compiler_Opt/mysysy && python3 -c "
# 真正的迭代原因:精度要求
def explain_precision_requirement():
d = 10
print('魔数算法需要找到精确的边界值:')
print('目标2^p > d * (2^31 - r)其中r是余数')
print()
# 模拟我们算法的迭代过程
p = 31
two_p = 2**p
r = two_p % d # 余数
m = two_p // d # 商
print(f'初始状态 (p={p}):')
print(f' 2^{p} = {two_p:,}')
print(f' 2^{p} / {d} = {m:,} 余 {r}')
print(f' 需要检查: 2^{p} > {d} * (2^31 - {r}) = {d * (2**31 - r):,}')
print(f' 条件满足: {two_p > d * (2**31 - r)}')
print()
# 继续迭代直到条件满足
iteration = 0
while r != 0 and m < 2**31:
iteration += 1
p += 1
two_p *= 2
m *= 2
r *= 2
if r >= d:
m += 1
r -= d
print(f'迭代 {iteration} (p={p}):')
print(f' m = {m:,}, r = {r}')
print(f' m < 2^31? {m < 2**31} (2^31 = {2**31:,})')
if m >= 2**31:
print(f' ✗ 魔数 {m:,} 超出32位范围!')
break
elif r == 0:
print(f' ✓ 余数为0找到精确解')
break
if iteration > 10: # 防止无限循环
print(' ... (限制迭代次数)')
break
explain_precision_requirement()
"
cd /home/downright/Compiler_Opt/mysysy && python3 -c "
# 检查初始状态是否会溢出
def check_initial_overflow():
int32_max = 2**31 - 1
print('检查不同除数的初始魔数是否会溢出:')
print(f'32位有符号整数最大值: {int32_max:,}')
print()
# 检查各种除数
divisors = [2, 3, 5, 7, 9, 10, 11, 13, 17, 25, 100, 1000]
for d in divisors:
# 初始魔数 = 2^31 / d
initial_magic = (2**31) // d
overflow = initial_magic > int32_max
status = \"溢出\" if overflow else \"安全\"
print(f'd={d:4d}: 初始魔数 = 2^31/{d} = {initial_magic:10,} [{status}]')
print()
print('结论: 初始状态下魔数不会溢出,溢出发生在迭代过程中')
check_initial_overflow()
"
总结
迭代的必要性:
不是为了避免初始溢出(初始状态安全)
是为了找到最精确的魔数,减少舍入误差
每次迭代提高一倍精度,但魔数也翻倍
溢出发生时机:
初始状态2^31 / d 总是在32位范围内
迭代过程2^32 / d, 2^33 / d, ... 逐渐超出32位范围
回退值的正确性:
回退值是基于数学理论和实践验证的标准值
来自LLVM、GCC等成熟编译器的实现
通过测试验证,对各种输入都能产生正确结果
算法设计哲学:
先尝试最优解:通过迭代寻找最精确的魔数
检测边界条件当超出32位范围时及时发现
智能回退:使用已验证的标准值保证正确性
保持通用性:对于没有预设值的除数仍然可以工作
# 后续优化可能涉及的改动