如何将一个 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
,或者RAX
和RDX
。
movlpd
或 movhpd
仅适用于 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 位整数寄存器?的主要内容,如果未能解决你的问题,请参考以下文章