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 速度较慢。