Assembly lscpu和cpuid说我有AVX2,但vpsllvw不工作

Assembly lscpu和cpuid说我有AVX2,但vpsllvw不工作,assembly,x86-64,avx2,cpuid,Assembly,X86 64,Avx2,Cpuid,如果我运行lscpu或查看/proc/cpuinfo,他们都会说我的处理器支持AVX2 $ lscpu | grep -o avx2 avx2 然而,当我在代码中使用vpsllvw时,它给出了SIGILL bits 64 global main section .text main: movdqa xmm0, [initial] vpsllvw xmm0, [shift] ret section .data align 16

如果我运行
lscpu
或查看
/proc/cpuinfo
,他们都会说我的处理器支持AVX2

$ lscpu | grep -o avx2
avx2
然而,当我在代码中使用
vpsllvw
时,它给出了SIGILL

bits 64
global main
section .text
main:
        movdqa xmm0, [initial]
        vpsllvw xmm0, [shift]
        ret


section .data
        align 16
        initial dw 0,1,2,3,4,5,6,7
        shift dw 4,0,4,0,4,0,4,0
$nasm-g-felf64 test.asm和&g++-g-m64 test.o组装而成

我知道这不是很多信息,但这是我能想到的

处理器为Intel Core i5-7200U

解决方案 原来只有dword和qword版本是AVX2,
vpsllvw
是AVX512。

需要AVX512AVX2每个元素只有dword/qword变量计数移位。(并且只有dword用于算术右移。
vpsravq
也需要AVX512。)旋转也需要AVX-512:
vprord
/
vprorvd
等等

这个问题的初始版本是关于可追溯到MMX/SSE2的指令的AVX形式(对所有元素使用相同的计数,从寄存器或内存位置的底部开始,或作为立即数)。这就是下面这部分的内容


对于将来有其他vpsllw/vpslld/vpsllq问题的读者(或VPSLLDQ洗牌),可能您使用了一种形式的
vpsllw
(具有即时计数和内存源数据),需要AVX-512VL,而您的CPU没有

  • AVX1/2(VEX前缀)允许
    vpsllw xmm1、xmm2、imm8
    (AVX2允许ymm)
  • AVX512(EVEX前缀)允许
    vpsllw xmm1、xmm2/mem、imm8
    ,要移位的数据来自内存。当然,还有ymm/zmm形式
  • AVX1/2和AVX512允许
    vpsllw xmm1、xmm2、xmm3/mem128
    (从内存操作数的低位64位开始计数)
因此,
vpsllw xmm1[rdi],1
只能使用EVEX前缀进行编码,默认情况下,NASM不会停止或警告您

(如果你想阻止自己意外使用CPU功能,YASM可以通过
CPU skylake AMD
指令(AMD将包括x86-64设备;它不是一个很好的工程系统)来做到这一点).但是YASM根本不支持AVX-512,所以这只适用于之前的产品,而不适用于各种级别的AVX-512。我认为NASM也支持这样做,可能是使用宏包。GAS可以使用命令行选项进行CPU功能检查。)


我不知道英特尔为什么选择不允许为AVX1/2立即计数表加载和移位内存源。这个限制似乎完全是任意的,并且没有机器代码编码的原因来解释为什么它会成为一个问题。它使用ModRM中的
r/m
字段对只读源操作数()进行编码,这与EVEX表单相同,因此似乎是任意决定将内存源设置为非法而不是允许它。(其中
r
字段为额外的操作码位,VEX VVV字段为目标寄存器。)

可能是在Sandybridge设计之前,他们在规划AVX时的某种历史原因?由于传统的SSE移位无法移位内存,Nehalem CPU内部不必为这种uop提供内存源。这似乎是一个站不住脚的借口,而且可能没有给他们带来太多好处,因为Sandybridge最终显著地重新设计了内部uop格式

存在类似形式的指令,如imul reg、[mem]、imm
,尽管它使用ModRM
/r
作为目标reg,而不是作为额外的操作码位(这是VEX编码的方式)。因此,也许没有指令将
/r
用作额外的操作码位,并将
ModRM:r/m
用作可存储的只读源操作数

常规标量移位,如
shl-dword[rdi],4
使用
r/m
作为读写操作数(其中
/r
是额外的操作码位),就像许多单操作数8086指令,如
neg-dword[rdi]
,因此,解码一个内存操作数以及来自
/r
的额外操作码位是解码器必须处理的事情


引入任意意外限制似乎是拙劣的设计,用一种允许内存源操作数的稍微紧凑的机器代码格式击败了CISC的优势。幸运的是,他们用AVX-512解决了这个问题,但这导致了在您无意或预期的情况下意外使用AVX-512的可能性。

另外,
0x29c67af&(1Oh等一下,您没有使用
xmm[mem],immediate
表单,您使用的是
xmm,xmm[mem]
form,计数来自内存,使用NASM的速记法,如果第一个源代码与目标代码相同,则可以省略第一个源代码。是的,AVX1也可以。您的问题中仍然没有包含一个出错的代码。因此,让我们知道,我做了一个大蠢事:我一直在说vpsllw,这很好,vpsllvw是专业的问题。而且它似乎是用
62
编码的(不确定如何包含它,我只是用
x(地址)
从gdb抓取了它)但是
vpsllvw
显然是AVX512,不是吗?哦,我的天哪,我正在看,我甚至没有注意到只有dword和qword版本在AVX2上可用,我已经打印了一半(或者至少在我的头脑中计划了)在OP澄清它不是即时形式之前,它肯定是未来读者可能遇到的一个坑,所以我还是写了出来。