指令将寄存器的低32位复制到高32位

时间:2011-02-17 05:23:27

标签: x86-64

是否有x86指令直接将x86_64寄存器的低32位复制到高32位?

Example : rbx -> 0x0123456789ABCDEF
Resultant rbx -> 0x89ABCDEF89ABCDEF

2 个答案:

答案 0 :(得分:0)

如果我正确记住我的汇编类,只有每个寄存器中最低的两个字节可单独寻址(al,ah,bl,bh等)。因此,如果您正在寻找单一指令,那么您可能会运气不好。

如果它可以是多个指令,我可能会使用左移和屏蔽的组合(原谅我的伪代码 - 它已经有一段时间了):

tmp = rbx
#Make sure you're using the version of left shift that zeroes the right bits:
tmp = tmp << 32
rbx = rbx & 0x00000000ffffffff
rbx = rbx | tmp

希望这有帮助!

答案 1 :(得分:0)

如果您使用 AVX-512 或 BMI2,则需要在前端吞吐量(总 uops)与延迟之间进行权衡。

标准方式使用纯整数regs。左移将使低 32 位为零,并写入 32 位寄存器 will zero-extend to 64 bits。您可以使用任何其他寄存器作为临时寄存器,这对 RAX 没有好处。

  mov  eax, ebx           ; EBX = RBX & 0xFFFFFFFF
  shl  rbx, 32
  or   rbx, rax

与其他答案相比,MOV 正在创建“tmp”副本以及进行截断。如果我们在 RAX 中复制和移位,并且必须单独就地截断 RBX,情况会更糟。

吞吐量成本:前端 3 uop,后端 2 uop(假设去除了 mov)。
延迟成本:2 个周期:从 P4 开始,SHL-immediate 和 OR 在所有 CPU 上都是单周期的。 MOV 要么具有零延迟(已消除),要么可以与 SHL 并行运行。


使用 BMI2 rorx 复制和交换 64 位寄存器的两半,我们可以在 2 条指令中完成它,仅在不同的寄存器中。但其中之一指令是 shrd,它是 Intel Sandybridge 系列上的单 uop 3c 延迟(立即计数),但在 AMD Zen 上速度较慢,为 6 uop。 RORX 处处高效,单 uop 1c 延迟。

; Intel SnB 4c latency, 2 uops.  AMD Zen: 3c latency, 7 uops
    rorx   rax, rbx, 32          ; top half of RAX = EBX 
    shrd   rax, rbx, 32          ; shift in another copy of EBX
                         ; RAX = EBX:EBX, RBX = untouched

在英特尔 SnB 系列上,例如Skylake,总共 4 个周期延迟,2 个 uop(前端和后端,在不同端口上运行)。

在 AMD Zen 和 Zen2 上,有趣的是,从操作数 1 -> 1(在这种情况下从 RAX 输入到输出)的延迟 (uops.info) 仅为 2 个周期。 (并且从操作数 2 -> 1 只有 1 个周期,但 RAX 来自 RORX,因此它在 RBX 之后准备就绪,无法利用我所看到的。)所以总延迟只有 3 个周期。但是吞吐成本比较高,6 uop。


另一种 2-uop 方式需要 AVX-512,因此当前的 AMD CPU 根本无法运行它,而不仅仅是像 BMI2 版本那样慢。 Skylake-X 上的总延迟为 6 个周期(参见 "experiment 49" on uops.info's test results 了解 SKX vpbroadcastd 延迟,他们在展开循环中使用它来创建循环携带的依赖链,专门用于测量 RBX->RBX延迟)。

  vpbroadcastd xmm0, ebx       ; AVX-512VL.  Single-uop on current Intel
  vmovq        rbx, xmm0       ; AVX1

这似乎比 rorx/shrd 版本的优势为零:在英特尔当前的 AVX-512 CPU 上速度较慢。

Knight's Landing 除外(其中 shrd r64,r64,imm 非常很慢;1 uops,11c 吞吐量和延迟,尽管 rorx 是 1c)。 Agner Fog 没有 KNL 的 vpbroadcastd/q xmm, r 计时,但即使是 2 uop,这也可能更快。


如果没有 AVX-512,如果数据最初在 GP 整数寄存器(而不是内存)中开始,并且您需要将其放回那里,则使用 XMM 寄存器没有任何优势,尽管有可能:

; generally slower than the integer shl/or version
movd       xmm0, ebx
punpckldq  xmm0, xmm0     ; duplicate the low 32 bits
movq       rbx, xmm0

在 Skylake 上,movd xmm, reg/movd reg,xmm 往返有 4 个周期延迟(每个 https://uops.info/ 测试),因此总共有 5 个。它花费 3 uop,但是在 Intel Haswell / Skylake 和类似的 CPU 上,其中 2 个需要端口 5:movq xmm, r64 和 shuffle。根据周围的代码,这可能是吞吐量瓶颈。

一些早期 CPU 的延迟也更糟,尤其是 Bulldozer 系列,幸运的是现在已经过时了。但即使是 on Zen2,movd/movq 往返也有 6 个周期的延迟,再加上 1 个用于 shuffle 的周期。

如果你的数据在内存中开始,你可以用
加载它 vbroadcastss xmm0, [mem] (AVX1) / vmovq rbx, xmm0。广播加载完全由现代 Intel 和 AMD CPU 中的加载端口处理,元素大小为 4 字节或更宽。

如果您想在内存中存储多个副本(例如 wmemset,您至少需要使用 16 字节的存储空间,因此您需要 {{1} } (SSE2) 或 pshufd xmm0, xmm0, 0 (AVX2) 广播到整个向量。如果您只需要 8 个字节作为清理的一部分,您当然可以使用 vpbroadcastd ymm0, xmm0


BMI2 shlx 仅以 movq [mem], xmm0 形式提供,不能立即计数。使用寄存器中的 shlx reg, reg, reg,您可以在循环中使用它来生成结果而不会破坏输入。

32

出于同样的原因,这与普通 SHL 版本具有相同的 2c 延迟。