融合乘法加法和默认舍入模式

使用GCC 5.3,以下代码包含-O3 -fma

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

生成以下程序集

 vfmadd132ss %xmm1, %xmm2, %xmm0 ret 

我注意到GCC已经在GCC 4.8中使用了-O3

Clang 3.7与-O3 -mfma产生

 vmulss %xmm1, %xmm0, %xmm0 vaddss %xmm2, %xmm0, %xmm0 retq 

但是Clang 3.7和-Ofast -mfma产生的代码与GCC相同, -O3 fast-O3 fast

我很惊讶GCC使用-O3因为从这个答案来看

除非允许放宽的浮点模型,否则不允许编译器融合分离的加法和乘法。

这是因为FMA只有一个舍入,而ADD + MUL有两个舍入。 因此,编译器将通过融合违反严格的IEEE浮点行为。

但是,从这个链接说

无论FLT_EVAL_METHOD的值如何,任何浮点表达式都可以收缩,即,计算好像所有中间结果都具有无限范围和精度。

所以现在我感到困惑和担忧。

  1. GCC是否有理由将-FMA与-O3一起使用?
  2. 融合是否违反了严格的IEEE浮点行为?
  3. 如果融合确实违反了IEEE浮点beahviour并且由于GCC返回__STDC_IEC_559__这不是一个矛盾吗?

由于FMA 可以在软件中模拟,因此似乎应该有两个用于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 ON或OFF没有区别。 只有-ffp-contract重要。

GCC它使用fma

  1. -ffp-contract=fast (默认)。

使用Clang它使用fma

  1. -ffp-contract=fast
  2. with -ffp-contract=on (默认值)和#pragma STDC FP_CONTRACT ON (默认为OFF )。

换句话说,对于Clang,您可以使用#pragma STDC FP_CONTRACT ON获取fma (因为-ffp-contract=on是默认值)或者使用-ffp-contract=fast-ffast-math (因此-Ofast )set -ffp-contract=fast


我查看了MSVC和ICC。

对于MSVC,它使用fma指令和/O2 /arch:AVX2 /fp:fast 。 使用MSVC /fp:precise是默认值。

对于ICC,它使用fma -O3 -march=core-avx2-O3 -march=core-avx2就足够了)。 这是因为默认ICC -fp-model fast使用-fp-model fast 。 但是ICC甚至使用-fp-model precise使用fma。 要使用ICC禁用fma,请使用-fp-model strict-no-fma

因此默认情况下,GCC和ICC在启用fma时使用fma(对于GCC / Clang使用-mfma或使用ICC使用-march=core-avx2 )但Clang和MSVC不使用。

它不违反IEEE-754,因为IEEE-754在这一点上遵循语言:

语言标准还应定义并要求实现提供允许和禁止对块进行单独或共同的值更改优化的属性。 这些优化可能包括但不限于:

– 通过乘法和加法合成fusedMultiplyAdd运算。

在标准C中, STDC FP_CONTRACT pragma提供了控制此值更改优化的方法。 因此,GCC默认授权执行融合,只要它允许您通过将STDC FP_CONTRACT OFF设置STDC FP_CONTRACT OFF来禁用优化。 不支持这意味着不遵守C标准。

当你引用允许融合乘法加法时,你忽略了重要条件“除非pragma FP_CONTRACT关闭”。 这是C中的一个新特性(我认为在C99中引入)并且由PowerPC绝对必要,它们从一开始就融合了乘法 – 实际上,x * y相当于fma(x,y,0)和x + y相当于fma(1.0,x,y)。

FP_CONTRACT控制融合乘法/加法,而不是FLT_EVAL_METHOD。 虽然如果FLT_EVAL_METHOD允许更高的精度,那么签约总是合法的; 只是假装操作以非常高的精度执行然后舍入。

如果您不想要速度,那么fma函数很有用。 它将缓慢但正确地计算合同结果,即使它在硬件中不可用。 如果它在硬件中可用,则应该内联。