x是存储在ebx中的某个整数... 如何将4个最高有效位旋转1,同时保留4个最低有效位? 其中0xABCDEF12旋转到0xDABCEF12
答案 0 :(得分: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)
这对除Intel P6系列之外的所有x86 CPU where the partial-register stall sucks都有效(在编写BX后阅读EBX)。
另请注意,Core2和早期Intel P6 CPU上的bswap r32
为2 uop,因此比ror r32, imm8
慢。但是你要避免这种情况,因为无论如何P6系列上的部分寄存器失速。例如,在Skylake上,bswap很适合吞吐量,因为它在p1 / p5上运行,而旋转在p0 / p6上运行,所以如果你在这个序列的吞吐量上遇到瓶颈,而不是延迟,它可以与自身重叠。如果您主要与其他周围代码重叠(在右侧循环中不是这样),那么可以在ror ebx,16
或bswap ebx
之间进行选择,以便在必要时平衡执行端口压力。 /强>
当然,如果你在一个数组的紧密循环中只是,那么首先不要加载整个元素,只需要ror word [mem+2], 4
来旋转在记忆中双字的高句子。 (但是在加载 数组元素之前不要这样做,因为它会在读取 - 修改 - 写入转发结束时导致16位存储的存储转发停顿一个更宽的32位负载。如果值保留在内存中,那么内存目标旋转只是一个好主意,并且你现在所做的只是你所做的一切。)
或者,您可以移位,屏蔽和OR以将位置于它们所属的位置。我认为这将需要更多的指令,并且比3个周期的延迟链更长。 (或者是4循环on Sandybridge pre-Ivybridge, where AX is still renamed separately from 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
vpshrdvw
无论如何都不能使用广播内存操作数(与dword和qword版本不同),如果它可能是16位广播,而不是32位。