【问题标题】:lscpu and cpuid say I have AVX2, but vpsllvw does not worklscpu 和 cpuid 说我有 AVX2,但是 vpsllvw 不起作用
【发布时间】:2021-05-07 03:08:37
【问题描述】:

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

【问题讨论】:

  • 另外,0x29c67af & (1<<5) 不为零,因此CPUID 报告 AVX2 可用性 (sandpile.org/x86/cpuid.htm),这与您的问题标题相反。您尝试运行什么vpsllw 指令,确切地说(minimal reproducible example)?您是否不小心以需要 AVX512 的方式使用它?例如vpsllw ymm0, [rdi], 1? AVX2 只允许源是寄存器,用于立即数形式,或者从内存中获取计数,而不是要移位的数据。但 AVX-512 确实允许内存。
  • 哦,等一下,您使用的不是xmm, [mem], immediate 表单,而是xmm, xmm, [mem] 表单,count 来自内存,使用的是 NASM如果它与目标相同,则可以省略第一个源的速记。是的,AVX1 没问题。您仍然没有在您的问题中包含错误代码的minimal reproducible example
  • 所以让大家知道我做了一个大笨蛋:我一直在说 vpsllw,它工作正常,vpsllvw 是问题所在。此外,它似乎确实是使用62 编码的(不完全确定如何包含它,我只是使用x (the address) 从gdb 中获取它)
  • 但是vpsllvw 无疑是 AVX512,不是吗? felixcloutier.com/x86/vpsllvw:vpsllvd:vpsllvq
  • 天哪,我正看着documentation,我什至没有注意到AVX2 上只有dword 和qword 版本

标签: assembly x86-64 avx2 cpuid


【解决方案1】:

vpsllVw 需要 AVX512。 AVX2 只有 dword / qword per-element-variable-count shifts。(并且只有 dword 用于算术右移。vpsravq 也需要 AVX512。)旋转也需要 AVX-512:vprord / vprorvd 等等。

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


对于未来有其他 vpsllw / vpslld / vpsllq 问题的读者(或 VPSLLDQ shuffle),也许您使用了需要 AVX- 的 vpsllw 形式(带有即时计数和内存源数据) 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 字段对只读源操作数 (the "D" row on the operand-encoding table for that instruction's manual entry) 进行编码,与 EVEX 形式相同,因此将内存源设为非法而不是允许它似乎是任意决定。 (r 字段是额外的操作码位,而 VEX VVVV 字段是目标寄存器。)

可能是他们在设计 Sandybridge 之前计划 AVX 时的某种历史原因?由于传统的 SSE 转换永远无法转换内存,因此 Nehalem CPU 内部不必支持为这种 uop 提供内存源。这似乎是一个蹩脚的借口,而且可能并没有让他们受益多少,因为 Sandybridge 最终还是显着地重新设计了内部 uop 格式。

存在类似imul reg, [mem], imm 之类的指令,虽然它使用ModRM /r 作为目标寄存器,而不是额外的操作码位(这就是它使用VEX 编码的方式)。所以也许没有指令使用/r作为额外的操作码位,并且使用ModRM:r/m作为可以是内存的只读源操作数?

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

引入任意意外限制似乎是糟糕的设计,通过允许内存源操作数的稍微紧凑的机器代码格式破坏了作为 CISC 的意义。幸运的是,他们使用 AVX-512 解决了这个问题,但这会导致您在不打算或不期望的情况下意外使用 AVX-512。

【讨论】:

  • 在 OP 澄清它不是直接形式之前,我已经输入了一半(或至少在我的脑海中计划),这肯定是一个可能的坑未来的读者,所以我还是写了。
猜你喜欢
  • 2022-06-13
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 2020-01-05
  • 2015-05-26
  • 1970-01-01
相关资源
最近更新 更多