XORPD 是经典的 SSE2 指令,它对两个压缩双精度浮点值执行按位逻辑异或。
VXORPD 是同一条指令的向量版本。本质上,它是带有VEX prefix 的经典SSE2 XORPD 指令。这就是操作码中“V”前缀的含义。它是与 AVX(高级矢量扩展)一起引入的,并且在任何支持 AVX 的架构上都受支持。 (实际上有两个版本,一个适用于 128 位 AVX 寄存器的 VEX.128 编码版本,一个适用于 256 位 AVX2 寄存器的 VEX.256 编码版本。)
所有旧的 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
这就解释了为什么在使用 -march=native 开关编译 64 位版本时会看到 VXORPD 及其朋友。
这就留下了一个问题,为什么当您抛出-m32 开关(这意味着为32 位平台生成代码)时您没有看到它。 SSE 和 AVX 指令在针对这些平台时仍然可用,并且我相信它们会在某些情况下使用,但由于 32 位 ABI 的显着差异,它们不能被频繁使用。具体来说,32 位 ABI 要求在 x87 浮点堆栈上返回浮点值。由于这需要使用 x87 浮点指令,因此优化器倾向于坚持使用这些指令,除非它对一段代码进行大量矢量化。这是唯一一次真正有意义地将 x87 堆栈中的值混洗到 SIMD 寄存器并再次返回。否则,这将是性能消耗,几乎没有实际好处。
您也可以看到这一点。只需抛出-m32 开关即可查看输出的变化:
Foo():
fldz
ret
FLDZ 是 x87 FPU 指令,用于在浮点堆栈顶部加载常量零,准备好返回给调用者。
显然,当您使代码更复杂时,您更有可能更改优化器的启发式方法并说服它发出 SIMD 指令。如果启用基于矢量化的优化,则更有可能。