【问题标题】:How to split an XMM 128-bit register into two 64-bit integer registers?如何将一个 XMM 128 位寄存器拆分为两个 64 位整数寄存器?
【发布时间】: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 的版本)以获得一些建议...

标签: assembly x86 sse


【解决方案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 速度较慢。

【讨论】:

    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 2019-11-16
    • 2011-10-03
    • 2012-01-24
    • 2017-10-16
    相关资源
    最近更新 更多