是否有x86指令直接将x86_64寄存器的低32位复制到高32位?
Example : rbx -> 0x0123456789ABCDEF Resultant rbx -> 0x89ABCDEF89ABCDEF
答案 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 延迟。