奇怪的gcc6.1 -O2编译行为

时间:2016-05-31 15:03:21

标签: gcc optimization x86 x86-64 compiler-optimization

我正在使用gcc -O2 -march=native标志编译相同的基准。但是,有趣的是,当我查看objdump时,它实际上会生成一些指令,例如vxorpd等,我认为只有在启用-ftree-vectorize时才会出现(-O2默认情况下不应该启用它?)如果我在32位指令中添加-m32标志进行编译,这些打包指令就会消失。遇到类似情况的人可以给出一些解释吗?感谢。

2 个答案:

答案 0 :(得分:3)

XORPD是经典的SSE2指令,它对两个压缩的双精度浮点值执行按位逻辑XOR。

VXORPD是同一指令的矢量版本。从本质上讲,它是带有VEX prefix的经典SSE2 XORPD指令。这就是“V”前缀在操作码中的含义。它是随AVX(高级矢量扩展)引入的,并且在任何支持AVX的架构上都受支持。 (实际上有两个版本,VEX.128编码版本适用于128位AVX寄存器,VEX.256编码版本适用于256位AVX2寄存器。)

所有旧版SSE和SSE2指令都可以添加一个VEX前缀,为它们提供三操作数形式,并允许它们与其他新AVX指令进行交互和更有效地安排。它也避免了the high cost of transitions between VEX and non-VEX modes。否则,这些新编码保持相同的行为。因此,只要目标体系结构支持这些指令,编译器通常就会生成这些指令的VEX前缀版本。显然,在您的情况下,march=native指定的架构至少支持AVX。

在GCC和Clang上,即使关闭了优化(-O0),您实际上也会发出这些指令,所以在启用优化时你肯定会得到它们。 -ftree-vectorize开关或任何其他向量化特定的优化开关都不需要打开,因为这实际上与向量化代码没有任何关系。更确切地说,代码流没有改变,只是指令的编码。

你可以用最简单的代码看到这个:

double Foo()
{
   return 0.0;
}
Foo():
        vxorpd  xmm0, xmm0, xmm0
        ret

这就解释了为什么当您使用VXORPD开关编译64位版本时,您会看到-march=native及其朋友。

这就留下了为什么当你抛出-m32开关时看到它的原因(这意味着为32位平台生成代码)。当针对这些平台时,SSE和AVX指令仍然可用,我相信它们将在某些情况下使用,但由于32位ABI的显着差异,它们不能经常使用。具体来说,32位ABI要求在x87浮点堆栈上返回浮点值。由于这需要使用x87浮点指令,因此优化器倾向于坚持使用这些指令,除非它对一段代码进行大量矢量化。这是将x87堆栈中的值重新混合到SIMD寄存器并再次返回的唯一时间。否则,这是一种性能消耗,几乎没有实际效益。

你也可以看到这一点。通过抛出-m32开关来查看输出中的变化:

Foo():
        fldz
        ret

FLDZ是用于在浮点堆栈顶部加载常量零的x87 FPU指令,可以将其返回给调用者。

显然,当您使代码更复杂时,您更有可能更改优化程序的启发式方法并说服它发出SIMD指令。如果启用基于矢量化的优化,则更有可能。

答案 1 :(得分:1)

只需添加到Cody Gray's very good answer,您就可以通过输出到汇编程序并启用-fverbose-asm来检查gcc的内部启用选项。

例如:

gcc -O2 -fverbose-asm -S -o test.S test.c

将在test.S列出所选优化级别启用的所有优化选项(此处为-O2)。