C++ 为什么icc为一个简单的main生成奇怪的程序集?
我有一个简单的建议: 启用优化的GCC和clang都可能生成2个指令二进制,但icc给出了奇怪的输出C++ 为什么icc为一个简单的main生成奇怪的程序集?,c++,assembly,x86,code-generation,icc,C++,Assembly,X86,Code Generation,Icc,我有一个简单的建议: 启用优化的GCC和clang都可能生成2个指令二进制,但icc给出了奇怪的输出 push rbp #2.1 mov rbp, rsp #2.1 and rsp, -128 #2.1
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
我不知道为什么ICC选择将堆栈按2条缓存线对齐:
and rsp, -128 #2.1
sub rsp, 128 #2.1
那很有趣。二级缓存有一个相邻行预取器,它喜欢将成对的行(在128字节对齐的组中)拉入二级缓存。但main的堆栈框架通常不会被大量使用。可能在某些程序中分配了重要的变量。(这也解释了如何设置rbp
,以保存旧的RSP,以便它可以在安定后返回。gcc在函数中使用rbp生成堆栈帧,并在函数中对齐该堆栈。)
其余的原因是
main()
是特殊的,ICC默认启用-ffast math
。(这是英特尔“肮脏”的小秘密之一,它可以自动矢量化更多的浮点代码。)
这包括在main
顶部添加代码,以设置MXCSR(SSE状态/控制寄存器)中的DAZ/FTZ位。有关这些位的更多信息,请参阅英特尔x86手册,但它们并不复杂:
- DAZ:非规范值为零:作为SSE/AVX指令的输入,非规范值被视为零
- FTZ:刷新到零:当舍入SSE/AVX指令的结果时,低于正常值的结果刷新到零
(<强> ISO C++禁止程序调用回<代码>主(),因此编译器可以在
main
本身而不是在CRT启动文件中放置run-one-one-one-one-one-one-run-one-one-one-one-one-one-one-one-one-one-one-one-run-one-one-one-one-one-one-one-one-one-run-one-one-one-one-one-one-run-one-one-one-one-one-one-one-one-one-run-one-one-one-one-one-one-one-one-one-将FP add/mul设置为关联,而不同的时间表示它实际上不是。这与设置DAZ/FTZ完全无关)
非规范在这里被用作次规范的同义词:具有最小指数和有效位的FP值,其中隐式前导位为0而不是1。i、 e.幅值小于最小可表示的标准化浮点/双精度的值
产生次正常结果的指令可能会慢得多:为了优化延迟,某些硬件中的快速路径假设标准化结果,如果结果无法标准化,则需要微码辅助。使用
perf stat-e fp_assist.any
统计此类事件
来自Bruce Dawson的优秀FP系列文章:。此外:
因此,在某些情况下,现代Intel CPU即使在低于正常值的情况下也可以避免处罚,但是如果您将函数名更改为
main2
(或任何其他名称),代码将发生更改-这已成为常态。这意味着,main
是icc编译器的特殊名称,它会在此处生成额外的特殊代码icc可能会为main
生成模板代码,作为托管环境的一部分,不会得到优化。将main
重命名为其他名称或尝试使用-ffreestanding
main
获得特殊待遇。谢谢。如果你们中的任何一个想把你的评论放到A中,并解释为什么ICC对待不同的主题,我会接受A@ OrnSeHiSalOM:问题是关于C++程序。GOOBBIT链接显示,该源被编译为C++,而不是C.ICC将作为C程序发出相同的ASM(例如PASS <代码> -XC<代码>),但是任意地将标签更改为C是不合适的。我的回答已经提到了C++程序的规则:程序调用>代码>主代码>代码>。(ISO C有相同的规则,但您没有编辑我的答案以匹配您对问题的更改。)@OrenIsh:不过,您其余的编辑都很好。您能解释一下为什么您说主堆栈框架没有被大量使用。。。我假设从main调用的所有函数(不包括异步调用和启动新线程)都使用“main stack”,因此如果它们在128B对齐,但main不对齐,它们将不会对齐,因为stack frames stacks:)@nosenseal:是的,它们都使用主调用堆栈(除非启动新线程),但其他函数调用仅保持16字节对齐。我还没有详细研究ICC的main
在进行调用时是否实际对齐了RSP 128字节,或者它是否只是对齐了128字节,然后正常工作,保留了16字节的倍数。
and rsp, -128 #2.1
sub rsp, 128 #2.1