AVX-512 - Как собрать данные из памяти с помощью инструкции по сборке?

Я пытаюсь собрать 64-битные целые числа из памяти, используя инструкцию ассемблера. Ниже вы можете увидеть, как я вызываю assembly код из C. Обратите внимание, что ассемблерный код использует синтаксис NASM.

nasm_gather.asm файл

bits 64

        section .text
global nasm_gather:function

extern base_addr
extern vindex

nasm_gather:
        ; prolog
        push            rbp
        push            rbx
        push            r12
        push            r13

        mov             r12         ,[rel base_addr]                ; r12 point to base_addr
        mov             r13         ,[rel vindex]                   ; r13 points to vindex
        vmovdqu32       zmm1        ,[r13]                          ; zmm1 = [2, 5, 1, 3, 0, 4, 7, 6]
        vpxorq          zmm2        ,zmm2               ,zmm2       ; zmm2 = [0, 0, 0, 0, 0, 0, 0, 0]
        vpgatherqq      zmm2        ,[r12 + zmm1*8]                 ; ----> Illegal instruction at address = ...
        ...
        ; epilog
        pop             r13
        pop             r12
        pop             rbx
        pop             rbp
        ret

main.cpp файл

#include <iostream>
#include <immintrin.h>

using namespace std;

extern "C" int nasm_gather();

const int N=32;
int64_t* base_addr /*__attribute__ ((aligned (64)))*/  = (int64_t *) malloc(sizeof(int64_t) * N);
int64_t* vindex =  (int64_t *) malloc(sizeof(int64_t) * 8);


int main() {
    /* initialize indices */
    vindex[0]=2; vindex[1]=5; vindex[2]=1; vindex[3]=3;
    vindex[4]=0; vindex[5]=4; vindex[6]=7; vindex[7]=6;
    // ...
    int64_t result = nasm_gather();
    ...

    return 0;
}

(vpgatherqq zmm, vm64z ассемблерная инструкция соответствует _mm512_i64gather_epi64 встроенная функция в C)

Как только программа достигает этой точки:

vpgatherqq      zmm2        ,[r12 + zmm1*8]

Я получаю ошибку «Недопустимая инструкция»:

Недопустимая инструкция по адресу = 4011f0: 62 d2 fd 48 91 14 cc 62 f1 7e 48 6f c2 e8 10
Если вы считаете, что ваше приложение должно попытаться выполнить эту недопустимую инструкцию (и другие, которые могут присутствовать), то используйте это ручка: -emit-illegal-insts 0 и этого сообщения об ошибке можно избежать.

В чем проблема?


person him    schedule 01.05.2021    source источник
comment
mov r12, [rel base_addr] загрузит r12 со значением из base_addr[0], тогда как я думаю, вы просто хотите, чтобы оно содержало адрес base_addr. Поэтому я думаю, что вместо этого вы хотите lea r12, [rel base_addr]. Аналогично для следующей строки. Хотя я все равно ожидаю ошибки сегментации вместо недопустимой инструкции.   -  person Nate Eldredge    schedule 01.05.2021
comment
@NateEldredge Я получаю то же сообщение об ошибке Illegal instruction at address = 4011dc: 62 d2 fd 48 91 14 cc 62 f1 7e 48 6f c2 e8 07   -  person him    schedule 01.05.2021
comment
Маска обязательна. NASM использует тот же синтаксис, что и Intel: felixcloutier.com/x86/vpgatherqd:vpgatherqq. Или скомпилируйте C со встроенным и посмотрите на asm. (gcc -O2 -march=skylake-avx512 -masm=intel). Кроме того, на uops.info будет пример синтаксиса сборки именно того, что было протестировано.   -  person Peter Cordes    schedule 01.05.2021
comment
А если серьезно, если вы просто хотите использовать vpgatherqq из C, используйте _mm512_mask_i64gather_epi64 из immintrin.h. software.intel.com/sites/landingpage/IntrinsicsGuide/. И не используйте глобальные переменные для передачи аргументов! Я надеюсь, вы на самом деле не планируете использовать это для производительности.   -  person Peter Cordes    schedule 01.05.2021
comment
О, подождите, незаконная инструкция? Вы запускаете это внутри эмулятора, такого как SDE? Это может быть связано с отсутствием регистра маски. Я удивлен, что NASM собрал это без регистра маски, но это может быть причиной того, что он создает недопустимый машинный код. (т. е. кодировка, означающая, что k0 означает разоблачение, и это незаконно для сборов; им нужно где-то записывать частичный прогресс, если он прерван, например, #PF.)   -  person Peter Cordes    schedule 01.05.2021
comment
@PeterCordes Вы правы. Я запускаю это внутри эмулятора. попробовал следующее: kxnorw k1 ,k0 ,k0 // k1 = 255 vpgatherqq zmm2{k1} ,[r12 + zmm1*8] Но я получаю такое же сообщение об ошибке.   -  person him    schedule 01.05.2021
comment
Вы сказали своему эмулятору включить AVX-512? Какой эмулятор? Также обратите внимание, что ваш комментарий неверен; kxnorW выдаст 65535. Или, как написало бы большинство людей, -1 или 0xFFFF.   -  person Peter Cordes    schedule 01.05.2021
comment
Флаг компилятора выглядит так: -fno-pie -no-pie -mavx512f   -  person him    schedule 01.05.2021
comment
GCC не является эмулятором, он не запускает ваш код, поэтому его флаги не влияют на то, позволит ли ваш эмулятор выполнять машинный код AVX-512. Вам даже не нужен -mavx512f для этого кода или immintrin.h, потому что вы не используете __m512i. Шаг GCC C-asm не видит ничего SIMD, только вызов функции. (Так что это ужасно для эффективности.)   -  person Peter Cordes    schedule 01.05.2021
comment
Теперь я использую XNOR 8-битных масок kxnorb k, k, k , но проблема остается прежней   -  person him    schedule 01.05.2021
comment
Ответ @PeterCordes для шага GCC C-asm не видит никаких материалов SIMD, ...: Если да, то почему я могу успешно выполнить другие инструкции SIMD (например, vmovdqu32, kxnorb)?   -  person him    schedule 01.05.2021
comment
@him Вы не ответили на вопрос Петра о том, какой эмулятор вы используете (или какие параметры вы ему передаете). В вашем случае это будет частью минимально воспроизводимого примера (а также кода, который фактически компилируется, т.е. избавиться от ...)   -  person chtz    schedule 02.05.2021
comment
@chtz Я использую Intel SDE и передайте ему следующий параметр: sde64 -icl -- ./main   -  person him    schedule 02.05.2021


Ответы (1)


Сборам нужна маска (чтобы они могли записывать прогресс, если они были прерваны или произошел сбой одного элемента). NASM обычно не позволяет вам собирать недопустимые инструкции без предупреждения; это ошибка NASM, которая не помогает вам обнаружить эту ошибку.

Кроме того, весь ваш подход к использованию глобальных переменных вместо аргументов функций плох как для удобства обслуживания, так и для производительности. Используйте встроенные функции как обычный человек, если вы уже готовы сообщить GCC, что он может выдавать инструкции AVX-512 (-march=skylake-avx512) и #include <immintrin.h> в вашем исходном коде. например _mm512_mask_i64gather_epi64. Вызов любой функции вместо встраивания инструкции сбора будет стоить значительную часть стоимости сбора, и даже больше, если это неуклюжая неэффективная функция, написанная таким образом. Сборы довольно сомнительны, если ваши индексы еще не находятся в векторе SIMD, и это, конечно, не помогает использовать указатель, хранящийся в глобальной переменной для индекса, вместо передачи аргумента указателя для функции сбора для загрузки вектора индексов откуда-то.


Следующий код у меня работает нормально в SDE 8.33.0, NASM 2.15.05. Вы утверждаете, что добавление {k1} не решает проблему для вас. Либо ваша версия SDE не работает, либо вы делаете что-то не так. Или вы забыли пересобрать свой исполняемый файл из обновленного источника.

default rel
global _start
_start:
    lea     rax, [rel buf]      ; dummy base = static array.  In a function, use RDI (first int/pointer arg)
    vpxor   xmm1, xmm1,xmm1     ; ZMM1 = dummy index = all zeros, efficiently done with a VEX-coded AVX instruction

    kxnorb      k1, k0,k0           ; mask = -1
    vpxor       xmm0, xmm0,xmm0     ; optional: dependency-breaking before merge-masking.  GCC will do this for the intrinsic.
    vpgatherqq  zmm0{k1}, [rax + zmm1*8]
        
    mov eax, 231
    syscall             ; exit_group(RDI)

section .bss
buf: resd 1024

Я могу воспроизвести это сообщение об ошибке SDE, если удалю {k1}, что сделает его разоблаченным, как ваш исходный вопрос. Ошибки NASM 2.15.05, если вы попытаетесь использовать {k1}{z} - Сборщики поддерживают только маскирование слияния (опять же, чтобы оно могло возобновиться после частичного выполнения, прерванного #PF или, возможно, прерыванием). Но с правильным исходным кодом он отлично строится и работает в статическом исполняемом файле. Хост-процессор — это клиент i7-6700k Skylake (который не поддерживает AVX-512, поэтому SDE должен заставить его работать).

$ nasm -felf64 avx512-gather.asm
$ ld -o avx512-gather avx512-gather.o
$ /opt/sde-external-8.33.0-2019-02-07-lin/sde64 -- ./avx512-gather
$ echo $?
0

(sde64 -icl тоже работает, конечно.)

Связывание того же машинного кода с функцией, которую можно вызвать из C++, будет работать так же, но опять же, это было бы бессмысленно, когда вы можете использовать встроенные функции (и дизассемблировать с помощью objdump -drwC -Mintel a.out, чтобы увидеть, как GCC использовал инструкцию).

person Peter Cordes    schedule 02.05.2021
comment
Благодарю вас! Я проверю это. - person him; 02.05.2021
comment
Почему вы обнуляете zmm0? Зависят ли отдельные загрузки к zmm0 от остальной части регистра или что-то в этом роде? - person Noah; 02.05.2021
comment
@Noah: сборы доступны только с маскировкой merge, так что да, false dep. Хотя, раз уж вы это упомянули, значение не имеет значения, просто разбивается, так что я должен исправить комментарий. Когда я писал этот ответ, у меня был мозговой пердеж, и я подумал, что значение имеет значение для правильности, как будто это ИЛИ в него или что-то в этом роде. Но, конечно, это не так. GCC также использует xor-zeroing, но это потому, что он любит ломать dep. - person Peter Cordes; 02.05.2021