2012-08-13 29 views
4

见上。我写信给样本功能:为什么LLVM不通过优化浮点指令?

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

的同时优化功能3210得到优化,但double一个不?我预计fadd将合并为一个fmul。相反,它看起来完全一样。

这是由于标志设置不同吗?我知道i32可能对double不可行的某些优化。但是缺乏简单的不断折叠是我无法理解的。

我正在使用LLVM 3.1。

+3

高度相关,虽然我不确定它是否是重复的:[为什么不GCC优化a * a * a * a * a到(a * a * a)*(a * a * a )?](http://stackoverflow.com/q/6430448/395760) – delnan 2012-08-13 21:33:28

+3

@delan这与许多类似的浮点问题一样,确实是重复的。即使问题的细节有所不同,答案也是一样的。这个问题的任何好的答案都会指出浮点算术和提及 - 数学 - 数学的非关联性,就像这个问题的接受答案一样。 – 2012-08-13 21:41:40

+0

谢谢你们两位。链接问题的答案提出了http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html,并强调了其中的歧义部分。 – f00id 2012-08-13 21:44:27

回答

7

这是不完全正确的说,没有优化是可能的。我会去通过第几行,以显示其中转换是和不准:

%addtmp = fadd double %x, %x 

这第一行可以安全地转化为fmul double %x 2.0e+0,但实际上并不是在大多数架构优化(fadd一般为快或比fmul快,并且不需要产生常数2.0)。请注意,禁止溢出,这个操作是准确的(就像所有按2的幂进行缩放)。

%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招致三人。

唯一可能进行转换的行将替换x + x + x3 * x。但是,子表达式x + x已经存在于其他地方,所以优化器可以轻松地选择不使用此转换(因为如果不存在,它可以利用现有的部分结果)。

+0

谢谢你的详细解答。那么做一个'fmul double%x 2.0e + 0'并且不断传播结果仍然比重复'fadd'慢?或者我缺少舍入问题? – f00id 2012-08-13 22:05:42

+0

'2 * x'和'x + x'在数值上是等价的;然而,在常见架构中,'x + x'不会比'2 * x'更慢(有时更快),所以优化器通常没有理由使用'2 * x'。 – 2012-08-13 22:10:28

+0

但是计算'x + x'两次(然后加上'x')看起来比执行像'%y1 = fadd double%x,%x','%y2 = fadd double%y1,%y1' %res = fadd double%y2,%x'。我不想强调'fmul'这可能是误导性的。 – f00id 2012-08-13 22:41:42