如何在XMM寄存器中旋转打包的Quadwords?

时间:2018-12-06 02:15:20

标签: x86 sse2

给出一个128位xmm寄存器,其中包含两个quadwords(即两个64位整数):

     ╭──────────────────┬──────────────────╮
xmm0 │ ffeeddccbbaa9988 │ 7766554433221100 │
     ╰──────────────────┴──────────────────╯

如何对单个quadwords执行轮换?例如:

prorqw xmm0, 32   // rotate right packed quadwords

     ╭──────────────────┬──────────────────╮
xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │
     ╰──────────────────┴──────────────────╯

我知道SSE2提供:

  • PSHUFW 随机包装的单词 (16位)
  • PSHUFD 随机包装的双字 (32位)

尽管我不知道指令的作用,也没有四字(64位)版本。

奖金问题

在假设{em> other 大小的打包数据的情况下,如何执行ROR寄存器中的xmm

  • 将右包装的双字旋转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
     ╰──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────╯

奖金阅读

2 个答案:

答案 0 :(得分:2)

如果轮换计数是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的函数版本来说还不错,但是对于低/高qword,您需要单独的改组。 (一个imm8控制字节仅包含四个2位字段。)或使用AVX2,用于每个通道中的奇/偶qword。


如果轮换计数不是8的倍数,则是AVX512F vprolq zmm0, zmm1, 13vprorq。也有可变计数版本,每个元素的计数都来自另一个向量,而不是立即数。 vprolvq / vprorvq。也可以以dword粒度提供,但不提供word或byte。


否则,只有SSE2且计数不是16的倍数,您需要向左+右移+或,以在asm中实际实现将C的旋转表示为 { {1}} 。 (Best practices for circular shift (rotate) operations in C++指出了通过超出范围的移位计数来解决潜在C UB的方法,这对于内在函数或asm来说不是问题,因为asm和内在函数的行为已由Intel明确定义:SIMD移位饱和移位计数,而不是像标量移位那样掩盖它。)

SSE2的移位粒度小至16位,因此您可以直接执行。

对于字节粒度,您需要额外的掩码以将在一个字的字节之间移动的位清零。 Efficient way of rotating a byte inside an AVX register。或将(x << n) | (x >> (64-n))之类的技巧与2的幂次方向量结合使用,以允许每个元素进行可变计数。 (AVX2通常只对dword / qword进行可变计数移位)。

答案 1 :(得分:0)

尽管我问过要执行向右旋转的问题,但是ROR的一个子集是当您对两个64位值乘以32位进行ROR时。这使您的任意 rotate 变成高32位和低32位的简单交换:

enter image description here

知道您只是在执行32位(即双字)交换,您可以使用另一条指令:

  • pshufd :随机打包双字

指令的编码非常棘手,英特尔竭尽全力obfuscate the documentation。这个想法是,您可以将128位xmm视为32位双字,然后将其推到您喜欢的任何位置:

enter image description here

编码很棘手:

pshufd xmm0, xmm0, 0x02030001

由于我要输入 四个 双字,因此掩码由四个块组成:

  

02 03 00 01

这些是从左到右排列的,告诉您将32位双字改写到的位置的索引:

enter image description here

如果您要旋转封装在xmm寄存器中的64位quadwords(精确到32位),则可以使用:

pshufd xmm0, xmm0, 0x02030001 //rotate packed quadwords by 32-bits¹

RotateRight(16)

现在,如果出现以下情况:

  • 而不是打包到xmm中的64位quadwords中的ROR(32)
  • 我想ROR(16)

enter image description here

我们可以应用相同的技巧。假设将64位的quadwords分为16位的单词,然后将它们洗牌:

enter image description here

pshufw xmm0, xmm0, 0x0605040702010003 //shuffle packed words¹

除了pshufw不能在xmm寄存器上运行。所以我已经停下来了。

RotateRight(24)

现在,如果出现以下情况:

  • 而不是打包到xmm中的64位quadwords中的ROR(32)
  • 我想ROR(24)

enter image description here

我们可以应用相同的方法。假设将64位quadwords分为8位字。...

pshufb xmm0,xmm0,//随机打包字节

好吧,我明天去接。现在我很累。我希望只输入一行代码。相反,这是四个小时的痛苦经历。我只是假设人们现在已经记录了所有这些基本操作。 CPU已经存在至少3年了。

RotateRight(1)

是的,稍后。

脚语

¹我认为。我不确定我的编码是否正确。