XMM 寄存器的按位取反

Posted

技术标签:

【中文标题】XMM 寄存器的按位取反【英文标题】:Bitwise negation of XMM register 【发布时间】:2016-01-19 16:34:45 【问题描述】:

如何获得 XMM 寄存器中值的按位取反?据我所知,没有这样的指示。唯一带有否定的指令是pandn,但要使用它来简单地否定一个 XMM 寄存器中的值,我必须让另一个 XMM 寄存器填充 1

还有其他方法可以否定 XMM 寄存器中的位吗?或者有没有一种巧妙的方法可以在不访问内存的情况下用1 填充 XMM 寄存器?

【问题讨论】:

【参考方案1】:

要加载一个全为 1 的寄存器,请使用

pcmpeqd xmm0, xmm0

之后你可以简单地从xmm0中减去xmmX得到~xmmX,或者使用pandn

您还可以轻松地将其他常量加载到 xmm 寄存器中

pcmpeqd xmm0, xmm0
psrld   xmm0, 30   ; 3 (32-bit)

pcmpeqd xmm0, xmm0 ; -1

pcmpeqw xmm0, xmm0 ; 1.5f
pslld   xmm0, 24
psrld   xmm0, 2

pcmpeqw xmm0, xmm0 ; -2.0f
pslld   xmm0, 30

阅读 Agner Fog 的优化指南,13.4 Generating constants - Making constants for integer vectors in XMM registers

【讨论】:

...或pxor 与全1寄存器。 psubdpandn 相对于 pxor 的优势为零。 psubd 的吞吐量更差,而且两者都不是可交换的。 Constant floats with SIMD, What are the best instruction sequences to generate vector constants on the fly?【参考方案2】:

pxor 与全1 寄存器一起使用。

pandn 也可以使用,但优势为零。没有任何情况下,pandn 和一个全为常量可以让你做任何你不能用 pxor 做的事情。

psubd 也可用(2 的补码标识),但比pandn 更差,因为它在某些 CPU 上的吞吐量较低(执行端口较少)。


pcmpedq  xmm1, xmm1      ; create the all-ones.  No false dependency.

pxor     xmm0, xmm1      ; flip all the bits in XMM0. Doesn't destroy XMM1
;pandn    xmm0, xmm1      ; equivalent but no advantage.  (~xmm0) & xmm1

PXOR 很好,因为它是可交换的。借助 AVX,您可以使用一个微融合 uop 加载和不加载:

vpxor    xmm0, xmm1, [rdi]

VPANDN 无法做到这一点,因为可以是内存或寄存器的操作数是非反转操作数。 (但是,如果没有 AVX,只需 movdqadqu 加载,然后 pxor 加载结果。reg-copy 和 micro-fused load+pxor 是 3 个未融合域 uops,而 2 个)


或者没有 AVX,如果你想销毁全一常量而不是你正在反转的数据,pxor 再次获胜:

movdqa  xmm2, xmm1      ; copy the all-ones constant.  Off the critical path for latency
pxor    xmm2, xmm0

您可以将movdqamovdqa xmm2, xmm0 / pandn xmm2,xmm1 分开。 (只有 IvyBridge+ 和 Bulldozer-family/Ryzen 对矢量寄存器具有零延迟 movdqa。)或者,如果您每次都在目标寄存器中使用 pcmpeqd 重新实现全1(可能是因为寄存器压力或因为您不要循环执行),这将是另一种情况,您需要 pxor 而不是 pandn


使用pcmpeqb/w/d 生成一个全1 常量是一种特殊情况,不会对旧值产生错误依赖(在 Silvermont 上除外),但仍然需要一个执行单元(与 Sandybridge 上的异或归零不同-家庭)。不过,它很便宜,而且它是编译器用于_mm_set1_epi32(-1) 的。

每次需要时重新创建常量而不是从另一个寄存器复制在 IvyBridge 及更高版本以及 Bulldozer-family 和 Ryzen 上稍微更糟。 XMM 副本的mov-elimination 避免占用向量执行单元/端口,以防向量-ALU 执行端口成为您的瓶颈。

但它在 Intel P6 系列(Core2/Nehalem)上稍好一些:当在一个问题组中读取太多“冷”寄存器时,寄存器读取停顿可能是一个问题。 (参见 Agner Fog 的 microarch pdf https://agner.org/optimize/)。 P6 系列已过时,但仍在一些旧机器中使用。如果您有在带有 AVX 的 CPU 上运行的 AVX 版本,您可能希望在代码的非 AVX 版本中对其进行调整。 (但 Haswell/Skylake “pentium”/“celeron”仍然是一个东西,而且他们没有 AVX,所以没有 AVX 并不意味着旧 CPU。)

【讨论】:

参见 Is NOT missing from SSE, AVX? 了解内在函数,以及 AVX-512F vpernlogd 在没有向量常数的情况下完成工作。

以上是关于XMM 寄存器的按位取反的主要内容,如果未能解决你的问题,请参考以下文章

c语言的按位运算符怎么操作!?

用异或代替按位求反

c51较c比较,单片机最小系统

位操作

按位取反~100=-101

2017.11.20 定时器与数码管