【发布时间】:2016-03-29 22:06:36
【问题描述】:
使用 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.
带有-O3 -mfma 的 Clang 3.7 产生
vmulss %xmm1, %xmm0, %xmm0
vaddss %xmm2, %xmm0, %xmm0
retq
但是带有-Ofast -mfma 的Clang 3.7 生成的代码与带有-O3 fast 的GCC 相同。
我很惊讶 GCC 使用 -O3,因为它从 this answer 说
除非您允许宽松的浮点模型,否则不允许编译器融合分离的加法和乘法。
这是因为 FMA 只有一个舍入,而 ADD + MUL 有两个。所以编译器会通过融合违反严格的 IEEE 浮点行为。
但是,this link 说
无论 FLT_EVAL_METHOD 的值如何,任何浮点表达式都可以被压缩,也就是说,计算好像所有中间结果都具有无限范围和精度。
所以现在我很困惑和担心。
- GCC 是否合理地将 FMA 与
-O3一起使用? - 融合是否违反了严格的 IEEE 浮点行为?
- 如果融合确实违反了 IEEE 浮点行为,并且由于 GCC returns
__STDC_IEC_559__这不是矛盾吗?
由于 FMA can be emulated in software 似乎应该有两个用于 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 设置为ON 和OFF 的一些排列,其中-ffp-contract 设置为on、off 和fast。在所有情况下,我也使用了-O3 -mfma。
使用 GCC,答案很简单。 #pragma STDC FP_CONTRACT ON 或 OFF 没有区别。只有-ffp-contract 很重要。
GCC 它使用fma 和
-
-ffp-contract=fast(默认)。
对于 Clang,它使用 fma
-
-ffp-contract=fast。 - 带有
-ffp-contract=on(默认)和#pragma STDC FP_CONTRACT ON(默认为OFF)。
换句话说,使用 Clang,您可以使用 #pragma STDC FP_CONTRACT ON(因为 -ffp-contract=on 是默认值)或 -ffp-contract=fast 获得 fma。 -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(-mfma 用于 GCC/Clang 或 -march=core-avx2 用于 ICC)但 Clang 和 MSVC 不使用。
【问题讨论】:
-
可能是编译器错误。考虑举报。
-
我很确定 gcc 所做的一切正常。在阅读了有关收缩 FP 表达式的 FLT_EVAL_METHOD 文档后,我很惊讶
clang不 这样做。我不会将此作为答案发布,因为它不是基于任何真正的标准文档,只是我对 我 认为事情应该如何工作/应该如何设计的理解,考虑到问题中的材料。 -
@FUZxxl,你认为浮点标签会比ieee-754更合适吗? (如果是这样,请随意更改)。我觉得我也应该使用浮点标签。
-
"融合是否违反严格的 IEEE 浮点行为?" --> 国际海事组织,是的。请改用
double fma(double x, double y, double z);,因为这是一个函数调用,在优化的编译器中将调用预期的汇编代码。这并不违反“IEEE 浮点行为”。