为什么LLVM Passes不优化浮点指令?

14 浏览
0 Comments

为什么LLVM Passes不优化浮点指令?

这个问题已经有了答案:

为什么GCC不会把a*a*a*a*a*a优化为(a*a*a)*(a*a*a)?

请看上面。我写了两个示例函数:

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。

admin 更改状态以发布 2023年5月21日
0
0 Comments

并不完全是说没有优化可能。我将逐行解释哪些变换是允许的,哪些不允许:

  %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已经在其他地方出现,因此优化器很容易选择不使用此转换(因为如果不使用此转换,则可以利用现有的部分结果)。

0