对于GCC 5.3,以下代码与-O3 -fma

float mul_add(float a, float b, float c) {
  return a*b + c;
}

生成以下程序集
vfmadd132ss     %xmm1, %xmm2, %xmm0
ret

I noticed GCC doing this with -O3 already in GCC 4.8
叮当声3.7与-O3 -mfma产生
vmulss  %xmm1, %xmm0, %xmm0
vaddss  %xmm2, %xmm0, %xmm0
retq

但是带有-Ofast -mfma的clang 3.7生成的代码与带有-O3 fast的gcc相同。
我很惊讶gcc使用-O3是因为它说
编译器不允许合并单独的加法和乘法运算,除非允许使用宽松的浮点模型。
这是因为fma只有一个舍入,而add+mul有两个舍入。因此编译器将通过融合违反严格的ieee浮点行为。
然而,从this answer开始
不管flt_eval_方法的值是多少,任何浮点表达式都可能被压缩,也就是说,所有的中间结果都具有无限的范围和精度。
所以现在我感到困惑和担心。
GCC是否有理由在使用FMA时使用-O3
熔合是否违反了严格的ieee浮点行为?
如果熔合确实违反了ieee浮点运算,那么既然this link这不是一个矛盾吗?
因为fmaGCC returns __STDC_IEC_559__所以fma应该有两个编译器开关:一个告诉编译器在计算中使用fma,另一个告诉编译器硬件有fma。
很明显,这可以通过-ffp-contract选项来控制。对于gcc,默认值是-ffp-contract=fast,而对于clang,则不是。其他选项,如-ffp-contract=on-ffp-contract=off不产生fma指令。
例如,带有-O3 -mfma -ffp-contract=fast的clang 3.7生成vfmadd132ss
我检查了#pragma STDC FP_CONTRACT设置为ONOFF的一些排列,其中-ffp-contract设置为onofffast。在所有情况下,我也使用-O3 -mfma
对于gcc,答案很简单。#pragma STDC FP_CONTRACT打开或关闭没有区别。只有-ffp-contract才重要。
它使用的gccfma
-ffp-contract=fast(默认)。
它使用的是fma
使用-ffp-contract=fast
使用-ffp-contract=on(默认)和#pragma STDC FP_CONTRACT ON(默认为OFF)。
换句话说,使用clang可以使用fma#pragma STDC FP_CONTRACT ON获得-ffp-contract=on(因为-ffp-contract=fast是默认值)。-ffast-math(因此-Ofast)设置-ffp-contract=fast
我调查了MSVC和ICC。
对于msvc,它使用带/O2 /arch:AVX2 /fp:fast的fma指令。msvc/fp:precise是默认值。
对于icc,它使用fma和-O3 -march=core-avx2(实际上-O1就足够了)。这是因为默认情况下,icc使用-fp-model fast。但icc使用fma,即使使用-fp-model precise。要使用icc禁用fma,请使用-fp-model strict-no-fma
因此,默认情况下,gcc和icc在启用fma时使用fma(对于gcc/clang使用-mfma或对于icc使用-march=core-avx2),但clang和msvc不使用fma。

最佳答案

这并不违反ieee-754,因为ieee-754在这一点上遵从于语言:
语言标准还应定义并要求实现提供允许或禁止对块单独或集体更改值的优化的属性。这些优化可能包括但不限于:

―由乘法和加法合成fusedmultiplyadd运算。
在标准c中,STDC FP_CONTRACT杂注提供了控制此值更改优化的方法。因此,gcc在默认情况下被授权执行融合,只要它允许您通过设置STDC FP_CONTRACT OFF禁用优化。不支持这意味着不遵守c标准。

关于c - 融合乘法加法和默认舍入模式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34436233/

10-11 23:13