如何将一个 XMM 128 位寄存器拆分为两个 64 位整数寄存器?

Posted

技术标签:

【中文标题】如何将一个 XMM 128 位寄存器拆分为两个 64 位整数寄存器?【英文标题】:How to split an XMM 128-bit register into two 64-bit integer registers? 【发布时间】:2016-12-19 12:25:53 【问题描述】:

如何将一个 128 位的xmm 寄存器拆分为两个 64 位的四字?

我在xmm1 中有一个非常大的数字,我想将较高的四字分配给r9,将较小的四字分配给r10,或者RAXRDX

movlpdmovhpd 仅适用于 reg to mem,反之亦然。

【问题讨论】:

用 gcc 编译 long long f(long long __attribute__((vector_size(16))) x)return x[1];(以及带有 0 的版本)以获得一些建议... 【参考方案1】:

SSE2(x86-64 的基线)具有直接在 XMM 和整数寄存器之间移动数据的指令(无需在内存中反弹)。向量的低元素很容易:MOVD or MOVQ。要提取较高的元素,您只需将所需的元素随机排列到向量的较低元素即可。

SSE4.1 还为 16 位以外的大小添加了插入/提取(例如 PEXTRQ)。除了代码大小,它是not actually faster than a separate shuffle and movq on any existing CPUs,但这意味着你不需要任何额外的 tmp 寄存器。

#SSE4.1
movq    rax, xmm0       # low qword
pextrq  rdx,  xmm0, 1   # high qword
# 128b result in rdx:rax, ready for use with div r64 for example.
# (But watch out for #DE on overflow)
# also ready for returning as a __int128_t in the SystemV x86-64 ABI

#SSE2
movq       r10, xmm0
punpckhqdq xmm0, xmm0    # broadcast the high half of xmm0 to both halves
movq       r9,  xmm0

PUNPCKHQDQ 是最有效的方法。即使在旧 CPU 上,对于小于 64 位的元素大小(如 65nm Core2 (Merom/Conroe)),它的速度也很快。有关详细信息,请参阅my horizontal sum answer。 PUNPCKHQDQ 没有立即数操作数,并且只有 SSE2,所以它只有 4 个字节的代码大小。

要保留 xmm0 的原始值,请将 pshufd 与不同的目标一起使用。或者就地交换高半和低半,或者其他什么。


movlpd 或 movhpd ...

使用它们毫无意义。请改用 movlps / movhps,因为它们更短,并且没有 CPU 关心 float 与 double。

您可以使用movhlps xmm1, xmm0 将 xmm0 的高半部分提取到另一个寄存器中,但是将 FP shuffle 与整数向量运算混合会导致某些 CPU(特别是 Intel Nehalem)出现旁路延迟。还要注意对 xmm1 的依赖会导致延迟瓶颈。

一般来说,肯定更喜欢pshufd。但是,如果您正在针对特定 CPU(例如 Core2)进行调优,您可以使用 movhlps,其中 movhlps 速度快并在整数域中运行,而 pshufd 速度较慢。

【讨论】:

以上是关于如何将一个 XMM 128 位寄存器拆分为两个 64 位整数寄存器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 xmm 寄存器中旋转压缩四字?

如何将单精度浮点数的 XMM 寄存器转换为整数?

将单个浮点数移动到 xmm 寄存器

将 __m256 拆分为两个 __m128 寄存器

如何在 XMM 寄存器之间交换值?

将有效地址加载到 x86_64 中的 XMM 寄存器的一条指令?