4

为什么clangfabs(double)变成vandps而不是vandpd(像GCC一样)?


来自编译器资源管理器的示例:

#include <math.h>

double float_abs(double x) {
    return fabs(x);
}

铿锵声12.0.1-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

海合会 11.2-std=gnu++11 -Wall -O3 -march=znver3

float_abs(double):
        vandpd  xmm0, xmm0, XMMWORD PTR .LC0[rip]
        ret
.LC0:
        .long   -1
        .long   2147483647
        .long   0
        .long   0

(具有讽刺意味的是,GCC 使用vandpdbut 将常量定义为 32 位.long块(有趣的是上半部分为零),而 clang 使用vandpsbut 将常量定义为两.quad半。

4

1 回答 1

6

TL:DR:可能是因为优化器/代码生成器总是更容易做到这一点,而不是只使用传统的 SSE 指令来节省代码大小。没有性能下降,它们在架构上是等效的(即没有正确性差异。)


可能 clang 总是将架构上等效的指令“规范化”到它们的ps版本,因为这些指令对于传统 SSE 版本具有更短的机器代码编码。

现有的 x86 CPU 没有任何用于在psandpd指令之间转发的绕过延迟延迟1,因此在or指令[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 位尾数之间共享一些尾数乘法器硬件位元素。

拥有单独的转发域可能只有在您也有单独的浮点运算与双精度运算的执行单元而不共享晶体管时才有用,除非您有不同类型的不同输入和输出端口但实际内部结构相同?IDK 足以了解该级别的 CPU 设计细节。


AVX VEX 编码版本没有优势ps,但也没有劣势,因此 LLVM 的优化器/代码生成器总是这样做可能更简单,而不是关心尝试尊重源内在函数。(Clang / LLVM 通常不会尝试这样做,例如,它会自由地将 shuffle 内在函数优化为不同的 shuffle。通常这很好,但有时当它不知道作者的技巧时,它会取消优化精心设计的内在函数内在函数做到了。)

例如,LLVM 可能会考虑“FP 域 128 位按位与”,并且知道该指令是andps/ vandps。clang 甚至没有理由知道它的vandpd存在,因为没有任何情况可以帮助使用它。


脚注 1:Bulldozer 隐藏元数据和数学指令之间的转发: AMD Bulldozer 系列对诸如->
之类的无意义事物有惩罚,对于实际关心 FP 值的符号/指数/尾数分量的实际 FP数学指令(不是布尔值或洗牌)。mulpsmulpd

将两个 IEEE binary32 FP 值的串联视为 binary64 基本上是没有意义的,因此这不是需要解决的问题。它主要是让我们深入了解 CPU 内部的设计方式。

Agner Fog 的 microarch 指南的 Bulldozer-family 部分中,他解释说,在 FMA 单元上运行的两条数学指令之间转发的绕过延迟比另一条指令阻碍时低 1 个周期。假设这三个指令形成依赖链,egaddps / orps / addps的延迟比 更差。addps / addps / orps

但是对于像这样的疯狂的事情addps / addpd / orps,你会得到额外的延迟。但不是为了addps / orps / addpd。(orpsvs orpdnever 在这里有所作为。 shufps也将是等价的。)

可能的解释是,BD 保留了向量元素的额外内容,以便在该特殊转发情况下重复使用,以避免在转发 FMA->FMA 时进行一些格式化/规范化工作。如果格式错误,那么乐观的方法必须恢复并执行架构所需的事情,但同样,只有当您实际上将浮点 FMA/add/mul 的结果视为双精度时才会发生这种情况,反之亦然。

addps可以unpcklpd毫不拖延地转发到随机播放,因此这不是 3 个单独的旁路网络的证据,也不是使用(或存在)andpd/的任何理由orpd

于 2021-09-08T00:47:03.153 回答