lscpu and cpuid say I have AVX2, but vpsllvw does not work

Viewed 335

If I run lscpu or look in /proc/cpuinfo, they both say that my processor supports AVX2.

$ lscpu | grep -o avx2
avx2

However, when I used vpsllvw in my code, it gave 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

Assembled with $ nasm -g -felf64 test.asm && g++ -g -m64 test.o

I know this isn't a lot of information to work with, but it's all I can think of.

Processor is Intel Core i5-7200U

Solution

Turns out that only the dword and qword versions are in AVX2, vpsllvw is AVX512.

1 Answers

vpsllVw requires AVX512. AVX2 only has dword / qword per-element-variable-count shifts. (And only dword for arithmetic right shift. vpsravq also requires AVX512.) Rotates also require AVX-512: vprord / vprorvd and so on.

The initial version of this question was about vpsllw, the AVX form of the instruction that dates back to MMX/SSE2 (using the same count for all elements, from the bottom of the register or memory location, or as an immediate). That's what the part below is about.


For future readers with other vpsllw / vpslld / vpsllq problems (or VPSLLDQ shuffles), perhaps you used a form of vpsllw (with an immediate count and memory source data) that requires AVX-512VL, which your CPU doesn't have.

  • AVX1/2 (VEX prefix) allows vpsllw xmm1, xmm2, imm8 (with AVX2 allowing ymm)
  • AVX512 (EVEX prefix) allows vpsllw xmm1, xmm2/mem, imm8, with the data to be shifted coming from memory. Also the ymm/zmm forms, of course.
  • AVX1/2 and AVX512 allow vpsllw xmm1, xmm2, xmm3/mem128 (count from the low 64 bits of the memory operand).

So vpsllw xmm1, [rdi], 1 could only be encoded with an EVEX prefix, and by default NASM won't stop or warn you about that.

(If you want to stop yourself from accidentally using CPU features, YASM can do that with a CPU skylake AMD directive (AMD to include x86-64 stuff; it's not a very well-engineered system). But YASM doesn't support AVX-512 at all last I checked, so this is only going to work for stuff before that, not for various levels of AVX-512. I think there's also some support for doing this with NASM, perhaps with a macro package. GAS can do CPU feature checking with a command line option.)


I don't know why Intel chose not to allow a load-and-shift memory source for the AVX1/2 immediate-count forms. The restriction appears to be entirely arbitrary, and there's no machine-code encoding reason why it would be a problem. It uses the r/m field in ModRM to encode the read-only source operand (the "D" row on the operand-encoding table for that instruction's manual entry), same as the EVEX form, so it seems like an arbitrary decision to make a memory source illegal instead of allowing it. (With the r field being extra opcode bits, and the VEX VVVV field being the destination register.)

Probably some kind of historical reason while they were planning AVX before Sandybridge was designed? Since legacy-SSE shifts couldn't ever shift memory, Nehalem CPU internals wouldn't have had to support having a memory source for that kind of uop. Seems like a lame excuse, and probably didn't benefit them much since Sandybridge ended up redesigning the internal uop formats significantly anyway.

Instructions like imul reg, [mem], imm with a similar form exist, although that uses ModRM /r as the destination reg, not as extra opcode bits (which is how it's encodeable with VEX). So perhaps there are no instructions that use /r as extra opcode bits, and use ModRM:r/m as a read-only source operand that can be memory?

Regular scalar shifts like shl dword [rdi], 4 use r/m as a read-write operand (with /r being extra opcode bits), like many one-operand 8086 instructions such as neg dword [rdi], so decoding a memory operand along with extra opcode bits from /r is something the decoders already have to handle.

Seems like poor design to introduce an arbitrary unexpected limitation, defeating the point of being CISC with a somewhat compact machine-code format that allows memory-source operands. Fortunately they fixed that with AVX-512, but that leads to the possibility of accidentally using AVX-512 when you didn't intend or expect to.

Related