__mmask16
- это буквально typedef для unsigned short
(и других типов масок для других простых целочисленных типов), поэтому нам просто нужно ограничение для его передачи в регистр k
.
Нам нужно покопаться в источниках gcc _ 4_, чтобы найти его:
Ограничение для любого регистра маски - "k"
. Или используйте "Yk"
вместо k1..k7
(который может использоваться как предикат, в отличие от k0
). Например, вы можете использовать операнд "=k"
в качестве назначения для сравнения с маской.
Очевидно, вы можете использовать "=Yk"(tmp)
с __mmask16 tmp
, чтобы компилятор выполнил за вас распределение регистров, вместо того, чтобы просто объявлять клобберы для тех "k"
регистров, которые вы решите использовать.
Предпочитаю встроенные функции вроде _mm512_maskz_add_epi32
Прежде всего, https://gcc.gnu.org/wiki/DontUseInlineAsm , если можно этого избежать. Понимание asm - это замечательно, но используйте это, чтобы прочитать вывод компилятора и / или выяснить, что было бы оптимальным, а затем написать встроенные функции, которые могут компилироваться так, как вы хотите. Информация о настройке производительности, например https://agner.org/optimize/ и https://uops.info/ перечислить элементы с помощью мнемоники asm, и они короче / легче запомнить, чем встроенные функции, но вы можете выполнить поиск по мнемонике, чтобы найдите встроенные функции на https://software.intel.com/sites/landingpage/IntrinsicsGuide/ а>
Intrinsics также позволяет компилятору складывать загрузки в исходные операнды памяти для других инструкций; с AVX512 они могут даже транслировать нагрузки! Ваш встроенный asm заставляет компилятор использовать отдельную инструкцию загрузки. Даже "vm"
ввод не позволит компилятору выбрать широковещательную загрузку в качестве источника памяти, потому что он не будет знать ширину широковещательного элемента инструкций, с которыми вы его использовали.
Используйте _mm512_mask_add_epi32
или _mm512_maskz_add_epi32
, особенно если вы уже используете __m512i
типы из <immintrin.h>
.
Кроме того, ваш asm содержит ошибку: вы используете {k1}
маскировку слияния, а не {k1}{z}
нулевую маску, но вы использовали неинициализированный __m512i sum;
с ограничением только для вывода "=v"
в качестве места назначения слияния! Как автономная функция, она сливается с a
, потому что в соглашении о вызовах ZMM0 = первый вход = регистр возвращаемого значения. Но при встраивании в другие функции вы определенно не можете предположить, что sum
выберет тот же регистр, что и a
. Лучше всего использовать операнд чтения / записи для "+v"(a)
и использовать его в качестве пункта назначения и первого источника.
Маскирование слияния имеет смысл только с "+v"
операндом чтения / записи. (Или в операторе asm с несколькими инструкциями, в котором вы уже написали вывод один раз и хотите объединить в него другой результат).
Внутренние функции не позволят вам совершить эту ошибку; версия с маскировкой слияния имеет дополнительный вход для цели слияния. (Операнд назначения asm).
Пример использования Yk
// works with -march=skylake-avx512 or -march=knl
// or just -mavx512f but don't do that.
// also needed: -masm=intel
#include <immintrin.h>
__m512i add_zmask(__m512i a, __m512i b) {
__m512i sum;
asm(
"vpaddd %[SUM] %{%[mask]%}%{z%}, %[A], %[B]; # conditional add "
: [SUM] "=v"(sum)
: [A] "v" (a),
[B] "v" (b),
[mask] "Yk" ((__mmask16)0xAAAA)
// no clobbers needed, unlike your question which I fixed with an edit
);
return sum;
}
Обратите внимание, что все {
и }
экранированы с помощью %
(https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Special-format-strings), поэтому они не анализируются как диалектные альтернативы {AT&T | Intel-syntax}
.
Он компилируется с помощью gcc уже в 4.9, но на самом деле этого не происходит, потому что он не понимает -march=skylake-avx512
и даже не имеет настроек для Skylake или KNL. Для достижения наилучших результатов используйте более свежий GCC, который знает о вашем процессоре.
<сильный> Godbolt компилятор исследователь сильный>:
# gcc8.3 -O3 -march=skylake-avx512 or -march=knl (and -masm=intel)
add(long long __vector, long long __vector):
mov eax, -21846
kmovw k1, eax # compiler-generated
# inline asm starts
vpaddd zmm0 {k1}{z}, zmm0, zmm1; # conditional add
# inline asm ends
ret
-mavx512bw
(подразумевается -march=skylake-avx512
, но не knl
) требуется для "Yk"
для работы с int
. Если вы компилируете с -march=knl
, целочисленные литералы нужно приводить к __mmask16
или __mask8
, потому что unsigned int = __mask32
недоступен для масок.
[mask] "Yk" (0xAAAA)
требует AVX512BW, даже если константа умещается в 16 битах только потому, что чистые целочисленные литералы всегда имеют тип int
. (vpaddd
zmm имеет 16 элементов на вектор, поэтому я сократил вашу константу до 16 битов.) С AVX512BW вы можете передавать более широкие константы или не использовать приведение для узких.
- gcc6 и более поздние версии поддерживают
-march=skylake-avx512
. Используйте это для настройки, а также для включения всего. Желательно gcc8 или хотя бы gcc7. Новые компиляторы генерируют менее громоздкий код с новыми расширениями ISA, такими как AVX512, если вы когда-либо использовали его вне встроенного asm.
- gcc5 поддерживает
-mavx512f -mavx512bw
, но не знает о Skylake.
- gcc4.9 не поддерживает
-mavx512bw
.
"Yk"
, к сожалению, еще не задокументирован в https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html.
Я знал, где искать исходный код GCC, благодаря ответу Росс на Каковы модификаторы изменения размера для встроенного asm GNU C для xmm / ymm / zmm для одного операнда?
person
Peter Cordes
schedule
02.05.2019
Yk
? Из здесь. - person David Wohlferd   schedule 02.05.2019