为什么LLVM Passes不优化浮点指令?
为什么LLVM Passes不优化浮点指令?
这个问题已经有了答案:
请看上面。我写了两个示例函数:
source.ll:
define i32 @bleh(i32 %x) { entry: %addtmp = add i32 %x, %x %addtmp1 = add i32 %addtmp, %x %addtmp2 = add i32 %addtmp1, %x %addtmp3 = add i32 %addtmp2, %x %addtmp4 = add i32 %addtmp3, 1 %addtmp5 = add i32 %addtmp4, 2 %addtmp6 = add i32 %addtmp5, 3 %multmp = mul i32 %x, 3 %addtmp7 = add i32 %addtmp6, %multmp ret i32 %addtmp7 }
source-fp.ll:
define double @bleh(double %x) { entry: %addtmp = fadd double %x, %x %addtmp1 = fadd double %addtmp, %x %addtmp2 = fadd double %addtmp1, %x %addtmp3 = fadd double %addtmp2, %x %addtmp4 = fadd double %addtmp3, 1.000000e+00 %addtmp5 = fadd double %addtmp4, 2.000000e+00 %addtmp6 = fadd double %addtmp5, 3.000000e+00 %multmp = fmul double %x, 3.000000e+00 %addtmp7 = fadd double %addtmp6, %multmp ret double %addtmp7 }
为什么使用
opt -O3 source[-fp].ll -o opt.source[-fp].ll -S
来优化这两个函数时,i32
的那个可以被优化但是double
的不能?我期望的是fadd
能被合并成一个fmul
。但是实际上看起来什么都没变。
这是由于设置的标志不同吗?我知道对于i32
,有一些优化是可以实现的,而对于double
则不可行。但是简单的常量折叠似乎超出了我的理解范围。
我使用的是LLVM 3.1。
并不完全是说没有优化可能。我将逐行解释哪些变换是允许的,哪些不允许:
%addtmp = fadd double %x, %x
第一行可以安全地转换为fmul double %x 2.0e+0
,但在大多数架构上(fadd
通常比fmul
更快,而且不需要产生常数2.0
),这不是一个优化。请注意,除了溢出外,此操作是精确的(就像所有倍增精度一样)。
%addtmp1 = fadd double %addtmp, %x
该行可以转换为fmul double %x 3.0e+0
。为什么这是合法的转换?因为产生%addtmp
的计算是精确的,因此无论是计算x * 3
还是x + x + x
,都只会有单个舍入。因为这些都是IEEE-754的基本操作,因此正确地四舍五入,结果是相同的。溢出呢?除非另一个也溢出,否则既不可能溢出。
%addtmp2 = fadd double %addtmp1, %x
这是第一行无法合法转换为常数*x。4 * x
可以精确计算,没有任何舍入,而x + x + x + x
会产生两次舍入:x + x + x
舍入一次,然后再添加x
可能会再舍入一次。
%addtmp3 = fadd double %addtmp2, %x
在这里也一样;5 * x
会产生一次舍入;x + x + x + x + x
会产生三次舍入。
唯一可能受益的一行是用3 * x
替换x + x + x
。但是,子表达式x + x
已经在其他地方出现,因此优化器很容易选择不使用此转换(因为如果不使用此转换,则可以利用现有的部分结果)。