如何在 xmm 寄存器中旋转打包四字?
How to rotate packed quadwords in xmm register?
给定一个包含两个四字(即两个 64 位整数)的 128 位 xmm
寄存器:
╭──────────────────┬──────────────────╮
xmm0 │ ffeeddccbbaa9988 │ 7766554433221100 │
╰──────────────────┴──────────────────╯
如何对单个四字执行旋转?例如:
prorqw xmm0, 32 // rotate right packed quadwords
╭──────────────────┬──────────────────╮
xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │
╰──────────────────┴──────────────────╯
我知道 SSE2 提供:
PSHUFW
:随机打包单词(16位)
PSHUFD
:随机打包双字(32位)
虽然我不知道指令的作用,也没有 quadword(64 位)版本。
奖金问题
您将如何执行 xmm
寄存器的 ROR
- 假设打包数据为 other 大小?
向右旋转打包的 doublewords 16 位:
╭──────────┬──────────┬──────────┬──────────╮
xmm0 │ ffeeddcc │ bbaa9988 │ 77665544 │ 33221100 │
╰──────────┴──────────┴──────────┴──────────╯
⇓
╭──────────┬──────────┬──────────┬──────────╮
xmm0 │ ddccffee │ 9988bbaa │ 55447766 │ 11003322 │
╰──────────┴──────────┴──────────┴──────────╯
按 8 位向右旋转打包的 字:
╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮
xmm0 │ ffee │ ddcc │ bbaa │ 9988 │ 7766 │ 5544 │ 3322 │ 1100 │
╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯
⇓
╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮
xmm0 │ eeff │ ccdd │ aabb │ 8899 │ 6677 │ 4455 │ 2233 │ 0011 │
╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯
加分题
如果是 256 位 ymm
寄存器,您将如何执行上述操作?
╭──────────────────────────────────┬──────────────────────────────────╮
ymm0 │ 2f2e2d2c2b2a29282726252423222120 │ ffeeddccbbaa99887766554433221100 │ packed doublequadwords
╰──────────────────────────────────┴──────────────────────────────────╯
╭──────────────────┬──────────────────┬──────────────────┬──────────────────╮
ymm0 │ 2f2e2d2c2b2a2928 │ 2726252423222120 │ ffeeddccbbaa9988 │ 7766554433221100 │ packed quadwords
╰──────────────────┴──────────────────┴──────────────────┴──────────────────╯
╭──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────╮
ymm0 │ 2f2e2d2c │ 2b2a2928 │ 27262524 │ 23222120 │ ffeeddcc │ bbaa9988 │ 77665544 │ 33221100 │ packed doublewords
╰──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────╯
╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮
ymm0 │ 2f2e │ 2d2c │ 2b2a │ 2928 │ 2726 │ 2524 │ 2322 │ 2120 │ ffee │ ddcc │ bbaa │ 9988 │ 7766 │ 5544 │ 3322 │ 1100 │ packed words
╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯
红利阅读
如果旋转计数是 8 的倍数,则可以使用字节随机播放。 SSSE3 pshufb
使用控制掩码可以在一条指令中处理任何其他 8 的倍数。
SSE2 pshufd
可以处理 count=32,交换每个 qword 的两半:_MM_SHUFFLE(2,3, 0,1)
,或者在 asm pshufd xmm0, xmm0, 0b10_11_00_01
(NASM 支持 _
作为可选的分隔符,如 C++11 中的数字文字。)
SSE2 pshuflw
+ pshufhw
对于 16 的倍数计数对于没有 SSSE3 的函数版本来说还不错,但是您需要为 low/high qword 单独洗牌。 (一个 imm8 控制字节只包含四个 2 位字段。)或者对于 AVX2,每个通道中的 odd/even 个 qwords。
如果旋转计数不是8的倍数,则有AVX512Fvprolq zmm0, zmm1, 13
和vprorq
。也有可变计数版本,每个元素计数来自另一个向量而不是立即数。 vprolvq
/ vprorvq
。也可用于双字粒度,但不能用于字或字节。
否则,只有 SSE2 和一个不是 16 的倍数的计数,您需要左移+右移 + OR 才能在 asm 中实际实现在 C 中表达旋转的常用方式作为 (x << n) | (x >> (64-n))
。 (Best practices for circular shift (rotate) operations in C++ 指出了解决超出范围的移位计数的潜在 C UB 的方法,这不是内在函数或 asm 的问题,因为 asm 和内在函数的行为由英特尔明确定义:SIMD shifts saturate移位计数,而不是像标量移位那样屏蔽它。)
SSE2 的移位粒度小至 16 位,因此您可以直接这样做。
对于字节粒度,您需要额外的屏蔽来将字中字节之间移动的位清零。 。或者使用 pmullw
之类的技巧和 2 的幂元素向量,允许每个元素的可变计数。 (其中 AVX2 通常只有 dword/qword 的可变计数移位)。
虽然我询问了执行向右旋转,但 ROR 的一个子集是当您对两个 64 位值执行恰好 32 位的 ROR 时。这使得你的任意 rotate 变成高低 32 位的简单交换:
知道您只是在执行 32 位(即 双字)交换,您可以使用另一条指令:
- pshufd:随机打包双字
指令的编码很棘手,英特尔尽最大努力obfuscate the documentation。这个想法是,您可以将 128 位 xmm 视为 32 位 doublewords,并将它们推送到您喜欢的任何位置:
编码很棘手:
pshufd xmm0, xmm0, 0x02030001
因为我正在推动 四个 双字,掩码由四个块组成:
02
03
00
01
这些是从左到右排列的,告诉您应该将 32 位双字改组到何处的索引:
如果您要将打包到 xmm
寄存器中的 64 位四字按正好 32 位旋转,您可以使用:
pshufd xmm0, xmm0, 0x02030001 //rotate packed quadwords by 32-bits¹
向右旋转(16)
现在如果:
- 而不是打包到 xmm
中的 64 位四字的 ROR(32)
- 我想
ROR(16)
我们可以应用相同的技巧。假设将64位四字分成16位字,打乱:
pshufw xmm0, xmm0, 0x0605040702010003 //shuffle packed words¹
除了 pshufw 不能对 xmm 寄存器进行操作。所以我说服自己停了下来。
向右旋转(24)
现在如果:
- 而不是打包到 xmm
中的 64 位四字的 ROR(32)
- 我想
ROR(24)
我们可以应用相同的东西。假设将 64 位四字分成 8 位字....
pshufb xmm0, xmm0, something //打乱打包字节
好吧,我明天去拿这个。现在我累了。我希望只输入一行代码;取而代之的是四个小时的痛苦。我只是假设人们现在已经记录了所有这些基本操作; CPU 已经存在至少 3 年了。
向右旋转(1)
是的,稍后。
脚注
¹我想。我不确定我的编码是否正确。
给定一个包含两个四字(即两个 64 位整数)的 128 位 xmm
寄存器:
╭──────────────────┬──────────────────╮
xmm0 │ ffeeddccbbaa9988 │ 7766554433221100 │
╰──────────────────┴──────────────────╯
如何对单个四字执行旋转?例如:
prorqw xmm0, 32 // rotate right packed quadwords
╭──────────────────┬──────────────────╮
xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │
╰──────────────────┴──────────────────╯
我知道 SSE2 提供:
PSHUFW
:随机打包单词(16位)PSHUFD
:随机打包双字(32位)
虽然我不知道指令的作用,也没有 quadword(64 位)版本。
奖金问题
您将如何执行 xmm
寄存器的 ROR
- 假设打包数据为 other 大小?
向右旋转打包的 doublewords 16 位:
╭──────────┬──────────┬──────────┬──────────╮ xmm0 │ ffeeddcc │ bbaa9988 │ 77665544 │ 33221100 │ ╰──────────┴──────────┴──────────┴──────────╯ ⇓ ╭──────────┬──────────┬──────────┬──────────╮ xmm0 │ ddccffee │ 9988bbaa │ 55447766 │ 11003322 │ ╰──────────┴──────────┴──────────┴──────────╯
按 8 位向右旋转打包的 字:
╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮ xmm0 │ ffee │ ddcc │ bbaa │ 9988 │ 7766 │ 5544 │ 3322 │ 1100 │ ╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯ ⇓ ╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮ xmm0 │ eeff │ ccdd │ aabb │ 8899 │ 6677 │ 4455 │ 2233 │ 0011 │ ╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯
加分题
如果是 256 位 ymm
寄存器,您将如何执行上述操作?
╭──────────────────────────────────┬──────────────────────────────────╮
ymm0 │ 2f2e2d2c2b2a29282726252423222120 │ ffeeddccbbaa99887766554433221100 │ packed doublequadwords
╰──────────────────────────────────┴──────────────────────────────────╯
╭──────────────────┬──────────────────┬──────────────────┬──────────────────╮
ymm0 │ 2f2e2d2c2b2a2928 │ 2726252423222120 │ ffeeddccbbaa9988 │ 7766554433221100 │ packed quadwords
╰──────────────────┴──────────────────┴──────────────────┴──────────────────╯
╭──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────╮
ymm0 │ 2f2e2d2c │ 2b2a2928 │ 27262524 │ 23222120 │ ffeeddcc │ bbaa9988 │ 77665544 │ 33221100 │ packed doublewords
╰──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────╯
╭──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────╮
ymm0 │ 2f2e │ 2d2c │ 2b2a │ 2928 │ 2726 │ 2524 │ 2322 │ 2120 │ ffee │ ddcc │ bbaa │ 9988 │ 7766 │ 5544 │ 3322 │ 1100 │ packed words
╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯
红利阅读
如果旋转计数是 8 的倍数,则可以使用字节随机播放。 SSSE3 pshufb
使用控制掩码可以在一条指令中处理任何其他 8 的倍数。
SSE2 pshufd
可以处理 count=32,交换每个 qword 的两半:_MM_SHUFFLE(2,3, 0,1)
,或者在 asm pshufd xmm0, xmm0, 0b10_11_00_01
(NASM 支持 _
作为可选的分隔符,如 C++11 中的数字文字。)
SSE2 pshuflw
+ pshufhw
对于 16 的倍数计数对于没有 SSSE3 的函数版本来说还不错,但是您需要为 low/high qword 单独洗牌。 (一个 imm8 控制字节只包含四个 2 位字段。)或者对于 AVX2,每个通道中的 odd/even 个 qwords。
如果旋转计数不是8的倍数,则有AVX512Fvprolq zmm0, zmm1, 13
和vprorq
。也有可变计数版本,每个元素计数来自另一个向量而不是立即数。 vprolvq
/ vprorvq
。也可用于双字粒度,但不能用于字或字节。
否则,只有 SSE2 和一个不是 16 的倍数的计数,您需要左移+右移 + OR 才能在 asm 中实际实现在 C 中表达旋转的常用方式作为 (x << n) | (x >> (64-n))
。 (Best practices for circular shift (rotate) operations in C++ 指出了解决超出范围的移位计数的潜在 C UB 的方法,这不是内在函数或 asm 的问题,因为 asm 和内在函数的行为由英特尔明确定义:SIMD shifts saturate移位计数,而不是像标量移位那样屏蔽它。)
SSE2 的移位粒度小至 16 位,因此您可以直接这样做。
对于字节粒度,您需要额外的屏蔽来将字中字节之间移动的位清零。 pmullw
之类的技巧和 2 的幂元素向量,允许每个元素的可变计数。 (其中 AVX2 通常只有 dword/qword 的可变计数移位)。
虽然我询问了执行向右旋转,但 ROR 的一个子集是当您对两个 64 位值执行恰好 32 位的 ROR 时。这使得你的任意 rotate 变成高低 32 位的简单交换:
知道您只是在执行 32 位(即 双字)交换,您可以使用另一条指令:
- pshufd:随机打包双字
指令的编码很棘手,英特尔尽最大努力obfuscate the documentation。这个想法是,您可以将 128 位 xmm 视为 32 位 doublewords,并将它们推送到您喜欢的任何位置:
编码很棘手:
pshufd xmm0, xmm0, 0x02030001
因为我正在推动 四个 双字,掩码由四个块组成:
02
03
00
01
这些是从左到右排列的,告诉您应该将 32 位双字改组到何处的索引:
如果您要将打包到 xmm
寄存器中的 64 位四字按正好 32 位旋转,您可以使用:
pshufd xmm0, xmm0, 0x02030001 //rotate packed quadwords by 32-bits¹
向右旋转(16)
现在如果:
- 而不是打包到 xmm 中的 64 位四字的
- 我想
ROR(16)
ROR(32)
我们可以应用相同的技巧。假设将64位四字分成16位字,打乱:
pshufw xmm0, xmm0, 0x0605040702010003 //shuffle packed words¹
除了 pshufw 不能对 xmm 寄存器进行操作。所以我说服自己停了下来。
向右旋转(24)
现在如果:
- 而不是打包到 xmm 中的 64 位四字的
- 我想
ROR(24)
ROR(32)
我们可以应用相同的东西。假设将 64 位四字分成 8 位字....
pshufb xmm0, xmm0, something //打乱打包字节
好吧,我明天去拿这个。现在我累了。我希望只输入一行代码;取而代之的是四个小时的痛苦。我只是假设人们现在已经记录了所有这些基本操作; CPU 已经存在至少 3 年了。
向右旋转(1)
是的,稍后。
脚注
¹我想。我不确定我的编码是否正确。