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

X86 如何在xmm寄存器中旋转压缩四字?,x86,sse2,X86,Sse2,给定一个包含两个四字(即两个64位整数)的128位xmm寄存器: 如何对单个四字执行旋转?例如: prorqw xmm0, 32 // rotate right packed quadwords ╭──────────────────┬──────────────────╮ xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │ ╰──────────────────┴──────────────────╯ 我知道SSE2提供: P

给定一个包含两个四字(即两个64位整数)的128位
xmm
寄存器:

如何对单个四字执行旋转?例如:

prorqw xmm0, 32   // rotate right packed quadwords

     ╭──────────────────┬──────────────────╮
xmm0 │ bbaa9988ffeeddcc │ 3322110077665544 │
     ╰──────────────────┴──────────────────╯
我知道SSE2提供:

  • PSHUFW
    :无序排列压缩的(16位)
  • PSHUFD
    :无序压缩双字(32位)
虽然我不知道这些指令是做什么的,但也没有一个四字(64位)版本

奖金问题 假设有其他大小的压缩数据,如何执行
xmm
寄存器的
ROR

  • 将右压缩双字旋转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的倍数,则可以使用字节洗牌。使用控制掩码,可以在一条指令中处理8的任何其他倍数

可以处理count=32,交换每个qword的两半:
\u MM\u SHUFFLE(2,3,0,1)
,或者在asm
pshufd xmm0、xmm0、0b10\u 11\u 00\u 01
(NASM支持将
\u
作为可选分隔符,就像C++11用于数字文本一样。)

对于16个计数中的多个,对于没有SSSE3的函数版本来说并不坏,但是对于低/高qword,您需要单独的洗牌。(imm8控制字节仅包含四个2位字段。)或使用AVX2,用于每个通道内的奇数/偶数Q字


如果旋转计数不是8的倍数,则存在AVX512F和
vprorq
。也可在可变计数版本中使用,每元素计数来自另一个向量,而不是立即数<代码>vprolvq/
vprorvq
。还提供dword粒度,但不提供字或字节


否则,如果只有SSE2和计数不是16的倍数,则需要左+右移位+或才能在asm中实际实现将C中的旋转表示为
(x>(64-n))
。(指出了从超出范围的移位计数中绕过潜在C UB的方法,这对于Intrinsic或asm来说不是问题,因为asm和Intrinsic的行为由Intel明确定义:SIMD移位使移位计数饱和,而不是像标量移位一样将其掩蔽。)

SSE2的移位粒度只有16位,所以您可以直接进行


对于字节粒度,您需要额外的屏蔽,以将字中字节之间移位的位归零。或者使用类似于
pmullw
的技巧,使用2个元素的幂向量,允许每个元素的计数可变。(其中AVX2通常只对dword/qword进行可变计数移位)。

尽管我询问了执行右旋转的问题,但ROR的一个子集是当您执行两个64位值的ROR时正好是32位。这使您的任意旋转变成了高32位和低32位的简单交换:

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

  • pshufd:将压缩的双字混洗
指令的编码很复杂,英特尔尽其所能做到这一点。其想法是,您可以将128位xmm视为32位双字,并将其推送到您喜欢的任何地方:

编码很棘手:

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不能在xmm寄存器上操作。所以我一直在和自己说话

旋转灯(24) 现在,如果:

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

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

pshufb xmm0,xmm0,something//shuffle压缩字节

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

旋转光(1) 是的,以后再说

脚注
我想。我不确定我的编码是否正确。

如何使用
pshufd
xmm0
中的两个四字旋转32位?@IanBoyd:交换每个四字的32位半。像
\u MM\u SHUFFLE(2,3,0,1)
一样使用内部函数。或者直接在asm中,
pshufd xmm0,xmm0,0b10_11_00_01
(您可能必须删除我在位对之间使用的
分隔符,除非您的汇编支持C++11样式的分隔符语法)。您链接到的“模糊文档”是《英特尔内部技术指南》。它的目的是用C或C++编写具有本质的人。对于4x 2位字段,始终可以使用
\u MM\u SHUFFLE
宏。但是,如果您直接在asm中编写,则应参考英特尔第2卷指令集参考手册或类似的HTML摘录。这次手术很成功
pshufd xmm0, xmm0, 0x02030001 //rotate packed quadwords by 32-bits¹
pshufw xmm0, xmm0, 0x0605040702010003 //shuffle packed words¹