x86帮助旋转有效位

-1 x86 assembly

x是存储在ebx中的一些整数...如何将4个最高有效位旋转1,同时保留4个最低有效位?其中0xABCDEF12旋转到0xDABCEF12

Pet*_*des 5

你在谈论半字节(十六进制数字),而不是位.4个半字节是16位,x86的16位操作数大小可用于旋转,因此您只需要将要旋转的位置于寄存器的低16位.

bswap  ebx      # ABCDEF12 -> 12 EF CD AB
ror    bx, 4    # CDAB -> BCDA   (high half unmodified)
bswap  ebx      # 12 EF BC DA -> DABCEF12   (partial-register stall on Core2 / Nehalem)
Run Code Online (Sandbox Code Playgroud)

这对于除Intel P6系列之外的所有x86 CPU都是有效的,其中部分寄存器失速很糟糕(从写入BX后读取EBX).

另请注意,bswap r32在Core2和早期的英特尔P6 CPU上是2微秒,因此慢于ror r32, imm8.但是你要避免这种情况,因为无论如何P6系列的部分寄存器失速.例如,在Skylake上,bswap对吞吐量很有用,因为它在p1/p5上运行,而旋转在p0/p6上运行,所以如果你对这个序列的吞吐量而不是延迟产生瓶颈,它可能会与自身重叠.如果你主要与其他周围代码重叠(在右循环中没有这个),那么你可以在必要时选择ror ebx,16bswap ebx平衡执行端口压力.

当然,如果你在做只是在紧密循环一个数组这一点,不要摆在首位加载整个元素,只是ror word [mem+2], 4在内存中旋转双字的高字.(但是在加载数组元素之前不要这样做,因为它会导致在读取 - 修改 - 写入转发结束时从16位存储转储到更宽的32位负载的存储转发停顿.目标轮换只是一个好主意,如果值保留在内存中,那就是你现在所做的一切.)


或者,您可以移位,屏蔽和OR以将位置于它们所属的位置.我认为这将需要更多的指令,并且比3个周期的延迟链更长.(或者在Sandybridge pre-Ivybridge上进行 4个循环,其中AX仍然与RAX分开重命名,但是可以插入合并的uop而不会停滞.)但是如果你需要它在Nehalem上有效,那么这样做吧.


AVX512F具有可变计数旋转(VPRORVD但不适用于16位元素大小(甚至不适用于AVX512BW或AVX512VBMI),否则您可以使用计数向量将每个双字的顶部字旋转4,但将底部字旋转0.

AVX512VBMI2(预计在Ice Lake)有一个SIMD版本的SHLD,您可以将其用作旋转:VPSHRDVW适用于单词元素:

section .rodata
    rotate_constant:  dw 0, 4

section .text
vpbroadcastd   xmm1, [rotate_constant]   ; 32-bit broadcast of [4, 0]

# rotate the high 16-bit of every dword element in xmm0 (or ymm0 or zmm0)
vpshrdvw       xmm0,xmm0, xmm1
Run Code Online (Sandbox Code Playgroud)

vpshrdvw 无论如何都不能使用广播内存操作数(与dword和qword版本不同),如果它可能是16位广播,而不是32位广播.