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



如果我运行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

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

vpsllVw要求AVX512。AVX2只有dword/qword每元素变量计数移位(而且只有算术右移。vpsravq也需要AVX512。)旋转也需要AVX-512:vprord/vprorvd等。

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


供将来有其他vpsllw/vpslld/vpsllq问题的读者参考(或VPSLLDQ shuffles),也许您使用了一种形式的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字段对只读源操作数("D"操作数编码表上的行(用于该指令的手动条目),与EVEX形式相同,因此使内存源非法而不是允许它似乎是一个武断的决定。(r字段是额外的操作码位,VEX VVVV字段是目的寄存器)

可能是某种历史原因,当他们在设计Sandybridge之前计划AVX时?由于传统sse移位无法移位内存,Nehalem CPU内部不需要支持为这种移位提供内存源。这似乎是一个站不住脚的借口,而且可能对他们没有多大好处,因为Sandybridge最终重新设计了内部的顶部格式。

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的可能性,当你没有打算或期望。

最新更新