代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

如何旋转xmm寄存器中的压缩四字?

  •  2
  • Ian Boyd  · 技术社区  · 7 年前

    给定128位 xmm 用两个四字(即两个64位整数)填充的寄存器:

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

    我怎样才能表演 旋转 关于单个四字?例如:

    prorqw xmm0, 32   // rotate right packed quadwords
    
         ╭──────────────────┬──────────────────╮
    xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │
         ╰──────────────────┴──────────────────╯
    

    我知道SSE2提供:

    • PSHUFW : 洗牌包装 (16位)
    • PSHUFD : 随机播放已打包 双字 (32位)

    虽然我不知道说明书是怎么做的,也没有 四字 (64位)版本。

    红利问题

    你会怎么做? 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 回复  |  直到 7 年前
        1
  •  2
  •   Peter Cordes    7 年前

    如果旋转计数是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 对于一个没有SSSE3的函数版本来说,16个计数中的多个并不坏,但是对于低/高qword,您需要单独的洗牌。(imm8控制字节只包含四个2位字段。)或使用avx2,针对每个通道中的奇数/偶数qword。


    如果旋转计数为 8的倍数 有AVX512F vprolq zmm0, zmm1, 13 vprorq . 也可以在变量计数版本中使用,每个元素的计数来自另一个向量,而不是立即数。 vprolvq / vprorvq . 也可用于双字粒度,但不用于字或字节。


    否则,只有SSE2和不是16的倍数的计数,您需要左+右移位+或 在asm中实现用c表示旋转的常用方法 (x << n) | (x >> (64-n)) . ( Best practices for circular shift (rotate) operations in C++ 指出如何从超出范围的移位计数中解决潜在的C ub,这与Intrinsis或ASM无关,因为ASM和Intrinsis的行为是由Intel定义的:SIMD移位使移位计数饱和,而不是像标量移位那样屏蔽它。)

    SSE2具有16位粒度的移位,因此您可以直接这样做。

    对于字节粒度,您需要额外的屏蔽来消除在字中字节之间移动的位。 Efficient way of rotating a byte inside an AVX register . 或者使用类似的技巧 pmullw 使用2个元素的幂向量,允许每个元素的可变计数。(其中,avx2通常只有dword/qword的可变计数移位)。

        2
  •  0
  •   Ian Boyd    7 年前

    尽管我问过表演 右转 ,ror的一个子集是执行两个64位值的ror时正好32位。这使你武断 旋转 转换为高32位和低32位的简单交换:

    enter image description here

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

    • PSUFD :随机播放压缩双字

    指令的编码很复杂,英特尔尽其所能 obfuscate the documentation . 您可以将128位xmm视为32位 双字 把它们推到你喜欢的地方:

    enter image description here

    编码很复杂:

    pshufd xmm0, xmm0, 0x02030001
    

    因为我在推 双字,面具由四块组成:

    02 03 00 01

    这些是从左到右排列的,告诉您应该将32位双字无序排列到哪里的索引:

    enter image description here

    如果旋转64位四字,则将其打包为 xmm 寄存器,正好32位,您可以使用:

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

    RotateRight(16)

    现在如果:

    • 而不是 ROR(32) 在压缩到xmm中的64位四字中
    • 我想 ROR(16)

    enter image description here

    我们可以运用同样的技巧。假设64位四字被分为16位字,并对它们进行无序排列:

    enter image description here

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

    除了pshufw不能在xmm寄存器上操作。所以我让自己陷入停顿。

    RotateRight(24)

    现在如果:

    • 而不是 ROR(32) 在压缩到xmm中的64位四字中
    • 我想 ROR(24)

    enter image description here

    我们可以应用同样的东西。假设64位四字被分成8位字……

    pshufb xmm0,xmm0,something//无序排列压缩字节

    好吧,我明天去拿。现在我累了。我本来希望只输入一行代码,但结果却花了四个小时的时间。我只是假设人们现在已经记录了所有这些基本操作;CPU已经存在至少3年了。

    RotateRight(1)

    是的,以后。

    脚注

    我想。我不确定编码是否正确。

    推荐文章