soc*_*soc 4 x86 assembly clang avx llvm-codegen
为什么 clang 变成fabs(double)了vandps而不是vandpd(像 GCC 一样)?
编译器资源管理器的示例:
#include <math.h>
double float_abs(double x) {
return fabs(x);
}
Run Code Online (Sandbox Code Playgroud)
-std=gnu++11 -Wall -O3 -march=znver3.LCPI0_0:
.quad 0x7fffffffffffffff # double NaN
.quad 0x7fffffffffffffff # double NaN
float_abs(double): # @float_abs(double)
vandps xmm0, xmm0, xmmword ptr [rip + .LCPI0_0]
ret
Run Code Online (Sandbox Code Playgroud)
-std=gnu++11 -Wall -O3 -march=znver3float_abs(double):
vandpd xmm0, xmm0, XMMWORD PTR .LC0[rip]
ret
.LC0:
.long -1
.long 2147483647
.long 0
.long 0
Run Code Online (Sandbox Code Playgroud)
(讽刺的是,GCC 使用vandpd但将常量定义为 32 位.long块(有趣的是上半部分为零),而 clang 使用vandps但将常量定义为两.quad半。
TL:DR:可能是因为优化器/代码生成器总是更容易执行此操作,而不是仅使用旧版 SSE 指令来节省代码大小。没有性能方面的缺点,而且它们在架构上是等效的(即没有正确性差异。)
也许 clang 总是将架构上等效的指令“标准化”为其ps版本,因为这些指令对于旧版 SSE 版本具有较短的机器代码编码。
ps现有的 x86 CPU 在和pd指令1之间转发时没有任何旁路延迟延迟,因此在或指令[v]andps之间使用始终是安全的。[v]mulpd[v]fmadd...pd
作为orpd 等 SSE2 指令有什么意义?指出,像movupd和 之类的指令andpd完全是无用的空间浪费,只为解码器一致性而存在:66SSE1 操作码前面的前缀总是执行它的 pd 版本。为未来的其他扩展节省一些编码空间可能更明智,但英特尔没有这样做。
或者动机可能是未来 CPU 可能具有独立的 SIMD-double 与 SIMD-float 域,因为当 SSE2 在纸面上设计时,一般来说英特尔的 FP SIMD 还处于早期阶段。如今,我们可以说这不太可能,因为 FMA 单元需要大量晶体管,并且显然可以构建为在每个 64 位元素一个 53 位尾数与每个 2x 32- 两个 23 位尾数之间共享一些尾数乘法器硬件。位元素。
拥有单独的转发域可能只有在您还具有用于浮点与双精度数学的单独执行单元而不共享晶体管的情况下才有用,除非您对不同类型有不同的输入和输出端口但实际内部结构相同?我对 CPU 设计细节的级别已经足够了解了。
psAVX VEX 编码版本没有优势,但也没有劣势,因此 LLVM 的优化器/代码生成器可能更简单,始终这样做,而不是关心尝试尊重源内部函数。(Clang / LLVM 通常不会尝试这样做,例如,它可以自由地将 shuffle 内在函数优化为不同的 shuffle。通常这很好,但有时,当它不知道作者的技巧时,它会取消优化精心设计的内在函数。内在函数做到了。)
例如,LLVM 可能会按照“FP 域 128 位按位 AND”进行思考,并且知道其指令是andps/ vandps。clang 甚至没有理由知道它的vandpd存在,因为没有任何情况可以帮助使用它。
脚注 1:Bulldozer 隐藏元数据和数学指令之间的转发: AMD Bulldozer 系列对诸如-> 等
无意义的事情有惩罚,对于实际关心 FP 值的符号/指数/尾数部分(不是布尔值或洗牌)。mulpsmulpd
将两个 IEEE 二进制 32 FP 值的串联视为二进制 64 基本上没有任何意义,因此这不是一个需要解决的问题。它主要只是让我们深入了解 CPU 内部结构的设计方式。
在Agner Fog 的微架构指南的 Bulldozer 系列部分中,他解释说,在 FMA 单元上运行的两条数学指令之间转发的旁路延迟比另一条指令妨碍时低 1 个周期。假设这三个指令形成依赖链,egaddps / orps / addps的延迟比 更差。addps / addps / orps
但对于像这样疯狂的事情addps / addpd / orps,你会得到额外的延迟。但不适合addps / orps / addpd。(orpsvsorpd在这里没有什么区别。 shufps也是等价的。)
可能的解释是,BD 保留了向量元素的额外内容,以便在特殊转发情况下重用,以便在转发 FMA->FMA 时避免一些格式化/标准化工作。如果格式错误,乐观方法必须恢复并执行架构所需的操作,但同样,只有当您实际上将浮点 FMA/add/mul 的结果视为双精度数时才会发生这种情况,反之亦然。
addps可以像没有延迟一样转发到洗牌unpcklpd,因此它不是 3 个独立的旁路网络的证据,也不是使用(或存在)andpd/的任何理由orpd。