为什么icc为简单的main生成奇怪的程序集?

时间:2018-09-03 00:22:13

标签: c++ assembly x86 code-generation icc

我有一个简单的program

int main()
{
    return 2*7;
}

打开优化功能的GCC和clang都生成了2条指令二进制文件,但是icc给出了奇怪的输出。

     push      rbp                                           #2.1
     mov       rbp, rsp                                      #2.1
     and       rsp, -128                                     #2.1
     sub       rsp, 128                                      #2.1
     xor       esi, esi                                      #2.1
     mov       edi, 3                                        #2.1
     call      __intel_new_feature_proc_init                 #2.1
     stmxcsr   DWORD PTR [rsp]                               #2.1
     mov       eax, 14                                       #3.12
     or        DWORD PTR [rsp], 32832                        #2.1
     ldmxcsr   DWORD PTR [rsp]                               #2.1
     mov       rsp, rbp                                      #3.12
     pop       rbp                                           #3.12
     ret

1 个答案:

答案 0 :(得分:6)

我不知道为什么ICC选择按2条缓存行对齐堆栈:

and       rsp, -128                                     #2.1
sub       rsp, 128                                      #2.1

这很有趣。 L2缓存具有一个相邻行的预取器,该预取器喜欢将成对的线(在128字节对齐的组中)拉入L2。但是main的堆栈框架通常不被大量使用。某些程序中可能在其中分配了重要的变量。 (这也说明了设置rbp来保存旧的RSP,以便它在ANDing之后可以返回。gcc还在函数中使用RBP生成堆栈帧,并在其中对齐该堆栈。)


剩下的是因为main()很特殊,并且ICC默认启用-ffast-math 。 (这是Intel的“肮脏”小秘密之一,它可以自动将更多浮点代码向量化。)

这包括将代码添加到main的顶部以设置MXCSR(SSE状态/控制寄存器)中的DAZ / FTZ位。有关这些位的更多信息,请参阅Intel的x86手册,但它们实际上并不复杂:

  • DAZ:异常为零:作为SSE ​​/ AVX指令的输入,异常被视为零。

  • FTZ:刷新为零:舍入SSE / AVX指令的结果时,次标准结果刷新为零。

相关:SSE "denormals are zeros" option

ISO C ++禁止程序调用{​​{1}},因此允许编译器将运行一次的内容放到main()本身而不是CRT启动文件中。指定gcc / clang与main来链接设置MXCSR的CRT启动文件中的链接,但是当使用gcc / clang进行编译时,它仅影响允许优化的代码生成,即处理FP添加/ mul作为关联,当不同的临时人员实际上不是这样时。这与设置DAZ / FTZ完全无关。


此处,反常数用作次正数的同义词:FP值具有最小的指数和一个有效位,其中隐含的前导位是0而不是1。即,值小于FLT_MIN or DBL_MIN(最小的值)可表示的归一化浮点/双精度。

https://en.wikipedia.org/wiki/Denormal_number


产生不正常结果的指令可能会慢得多:为了优化延迟,某些硬件中的快速路径采用了标准化的结果,如果结果无法进行标准化,则采用微码辅助。使用-ffast-math对此类事件进行计数。

摘自Bruce Dawson出色的FP系列文章:That’s Not Normal–the Performance of Odd Floats 。另外:

Agner Fog做了一些测试(请参阅他的microarch pdf),并为Haswell / Broadwell报告:

  

下溢和次常态

     

浮点运算接近时出现次正规数   下溢。在某些情况下,处理非正规数非常昂贵   情况是因为次标准结果由微码处理   例外。

     

Haswell和Broadwell的罚款约为124个时钟   在所有情况下,对普通数字进行运算得到一个   次正常的结果。乘法也有类似的惩罚   介于正常数和次正常数之间,无论是否   结果是正常还是次正常。添加法线不收取任何罚款   和一个次正规数,无论结果如何。没有罚款   对于上溢,下溢,无穷大或非整数结果。

     

如果“刷新为零”,则避免了对次普通数的惩罚   模式和“反常态为零”模式都在MXCSR中设置   注册。

因此,在某些情况下,现代的Intel CPU甚至避免使用次标准的惩罚,但是