提问人:pmor 提问时间:11/27/2021 最后编辑:pmor 更新时间:11/29/2021 访问量:116
为什么生成的相同汇编程序代码不会导致相同的输出?
Why doesn't the same generated assembler code lead to the same output?
问:
示例代码 ():t0.c
#include <stdio.h>
float f(float a, float b, float c) __attribute__((noinline));
float f(float a, float b, float c)
{
return a * c + b * c;
}
int main(void)
{
void* p = V;
printf("%a\n", f(4476.0f, 20439.0f, 4915.0f));
return 0;
}
调用和执行(通过 godbolt.org):
# icc 2021.1.2 on Linux on x86-64
$ icc t0.c -fp-model=fast -O3 -DV=f
0x1.d32322p+26
$ icc t0.c -fp-model=fast -O3 -DV=0
0x1.d32324p+26
生成的汇编程序代码是相同的:https://godbolt.org/z/osra5jfYY。
为什么生成的相同汇编程序代码不会导致相同的输出?
为什么重要?void* p = f;
答:
5赞
Nate Eldredge
11/27/2021
#1
Godbolt 向你展示了通过使用 运行编译器发出的程序集。但在这种情况下,这不是实际运行的代码,因为可以在链接时进行进一步的优化。-S
尝试选中“编译为二进制文件”框(https://godbolt.org/z/ETznv9qP4),这将实际编译和链接二进制文件,然后对其进行反汇编。我们看到,在您的版本中,代码为:-DV=f
f
addss xmm0,xmm1
mulss xmm0,xmm2
ret
和以前一样。但是,我们有:-DV=0
movss xmm0,DWORD PTR [rip+0x2d88]
ret
所以已经转换为一个函数,该函数仅返回从内存加载的常量。在链接时,编译器能够看到它只被一组特定的常量参数调用,因此它可以执行过程间常量传播,并且只返回预先计算的结果。f
f
f
有一个额外的参考显然违背了这一点。编译器或链接器可能看到其地址已被占用,并且没有注意到该地址从未执行任何操作。因此,它假设可以在程序的其他地方调用,因此它必须发出代码,以便为任意参数提供正确的结果。f
f
f
至于为什么结果不同:预计算是严格进行的,评估和作为,然后将它们相加。所以它的结果是 C 规则下的“正确”结果,也是你用 或 编译时得到的。运行时版本已优化为 ,这实际上更准确,因为它避免了额外的舍入;它产生 ,它更接近 的确切值。a*c
b*c
float
122457232
-O0
-fp-model=strict
(a+b)*c
122457224
122457225
评论
0赞
Peter Cordes
11/27/2021
恒定传播可能已经完成,也许引入了两个单独的舍入步骤?不,这不能解释它;作为双倍,这些操作都是精确的,因为数字不是太大。double
2赞
Nate Eldredge
11/27/2021
@PeterCordes:整理好了。常量传播版本的计算非常严格,在执行所有操作时会产生所有舍入错误。运行时版本优化为更快、更准确,但 C 评估规则并不严格正确。默认情况下,看起来 icc 有效地做到了。(a*c)+(b*c)
float
(a+b)*c
-ffast-math
0赞
Peter Cordes
11/27/2021
是的,它确实在某种程度上(默认是),尽管问题中使用的 Godbolt 链接(即 fast=2)使其与 GCC 一样具有侵略性。intel.com/content/www/us/en/develop/documentation/....我不清楚默认情况下允许/不允许的确切内容,但 FP 关联数学假设是,因此它可以自动矢量化并在人们对其进行基准测试时看起来不错。(和分布式)-fp-model fast=1
-fp-model=fast
-ffast-math
fast=1
2赞
Nate Eldredge
11/27/2021
@PeterCordes:啊,好吧,让我们回到严格版本。(有趣的是,在我检查文档之前,我的第一个猜测是做,ICEs:godbolt.org/z/YW96vYKj9-fp-model=strict
-fp-model fast=0
)
1赞
Nate Eldredge
11/29/2021
@pmor: 嗯......在细则中,ICC手册确实说-fp-model=precision
(=strict
暗示)是“严格的ANSI一致性”所必需的。因此,没有这个选项就不能声称自己是符合要求的实现,因此没有人可以阻止他们定义任何他们喜欢的宏。icc
评论
diff
printf
-DV=0
f
f
f