Встроенные трансляции с внутренностями и сборкой
В разделе 2.5.3 "Трансляции" Справочника по программированию расширений набора команд архитектуры Intel мы узнаем, что AVX512 (и Knights Corner) имеет
битовое поле для кодирования широковещательной передачи данных для некоторых команд load-op, то есть инструкций, которые загружают данные из памяти и выполняют некоторую вычислительную операцию или операцию перемещения данных.
Например, используя синтаксис сборки Intel, мы можем транслировать скаляр по адресу, хранящемуся в rax
а затем умножить на 16 поплавков в zmm2
и запишите результат в zmm1
как это
vmulps zmm1, zmm2, [rax] {1to16}
Тем не менее, нет встроенных функций, которые могут это сделать. Следовательно, с внутренностями компилятор должен быть в состоянии свернуть
__m512 bb = _mm512_set1_ps(b);
__m512 ab = _mm512_mul_ps(a,bb);
одной инструкции
vmulps zmm1, zmm2, [rax] {1to16}
но я не наблюдал GCC, делающего это. Я нашел отчет об ошибке GCC по этому поводу.
Я наблюдал нечто подобное с FMA с GCC. например, GCC 4.9 не рухнет _mm256_add_ps(_mm256_mul_ps(areg0,breg0)
к одной инструкции FMA с -Ofast
, Тем не менее, GCC 5.1 теперь сворачивает его в одну FMA. По крайней мере, есть встроенные функции, чтобы сделать это с FMA, например, _mm256_fmadd_ps
, Но нет например _mm512_mulbroad_ps(vector,scalar)
внутренняя.
GCC может исправить это в какой-то момент, но до тех пор сборка является единственным решением.
Итак, мой вопрос, как это сделать с помощью встроенной сборки в GCC?
Я думаю, что я мог придумать правильный синтаксис (но я не уверен) для встроенной сборки GCC для примера выше.
"vmulps (%%rax)%{1to16}, %%zmm1, %%zmm2\n\t"
Я действительно ищу такую функцию
static inline __m512 mul_broad(__m512 a, float b) {
return a*b;
}
где если b
в памяти указывают на в rax
это производит
vmulps (%rax){1to16}, %zmm0, %zmm0
ret
и если b
в xmm1
это производит
vbroadcastss %xmm1, %zmm1
vmulps %zmm1, %zmm0, %zmm0
ret
GCC уже сделает vbroadcastss
-из регистрационного случая с внутренностями, но если b
в памяти, компилирует это в vbroadcastss
из памяти.
__m512 mul_broad(__m512 a, float b) {
__m512 bb = _mm512_set1_ps(b);
__m512 ab = _mm512_mul_ps(a,bb);
return ab;
}
Clang будет использовать операнд вещательной памяти, если b
в памяти.
1 ответ
Как отмечает Питер Кордес, GCC не позволяет указывать другой шаблон для разных альтернатив ограничений. Поэтому вместо моего решения ассемблер выбирает правильную инструкцию в соответствии с выбранными операндами.
У меня нет версии GCC, которая поддерживает регистры ZMM, поэтому в следующем примере используются регистры XMM и пара несуществующих инструкций, чтобы продемонстрировать, как вы можете достичь того, что вы ищете.
typedef __attribute__((vector_size(16))) float v4sf;
v4sf
foo(v4sf a, float b) {
v4sf ret;
asm(".ifndef isxmm\n\t"
".altmacro\n\t"
".macro ifxmm operand, rnum\n\t"
".ifc \"\\operand\",\"%%xmm\\rnum\"\n\t"
".set isxmm, 1\n\t"
".endif\n\t"
".endm\n\t"
".endif\n\t"
".set isxmm, 0\n\t"
".set regnum, 0\n\t"
".rept 8\n\t"
"ifxmm <%2>, %%regnum\n\t"
".set regnum, regnum + 1\n\t"
".endr\n\t"
".if isxmm\n\t"
"alt-1 %1, %2, %0\n\t"
".else\n\t"
"alt-2 %1, %2, %0\n\t"
".endif\n\t"
: "=x,x" (ret)
: "x,x" (a), "x,m" (b));
return ret;
}
v4sf
bar(v4sf a, v4sf b) {
return foo(a, b[0]);
}
Этот пример должен быть скомпилирован с gcc -m32 -msse -O3
и должен сгенерировать два сообщения об ошибке на ассемблере, аналогичное следующему:
t103.c: Assembler messages:
t103.c:24: Error: no such instruction: `alt-2 %xmm0,4(%esp),%xmm0'
t103.c:22: Error: no such instruction: `alt-1 %xmm0,%xmm1,%xmm0'
Основная идея здесь заключается в том, что ассемблер проверяет, является ли второй операнд (%2
) является регистром XMM или чем-то еще, предположительно местом в памяти. Поскольку ассемблер GNU мало поддерживает операции со строками, второй операнд сравнивается с каждым возможным регистром XMM по одному в .rept
петля. isxmm
макрос используется для вставки %xmm
и регистрационный номер вместе.
Для вашей конкретной проблемы вам, вероятно, потребуется переписать ее примерно так:
__m512
mul_broad(__m512 a, float b) {
__m512 ret;
__m512 dummy;
asm(".ifndef isxmm\n\t"
".altmacro\n\t"
".macro ifxmm operand, rnum\n\t"
".ifc \"\\operand\",\"%%zmm\\rnum\"\n\t"
".set isxmm, 1\n\t"
".endif\n\t"
".endm\n\t"
".endif\n\t"
".set isxmm, 0\n\t"
".set regnum, 0\n\t"
".rept 32\n\t"
"ifxmm <%[b]>, %%regnum\n\t"
".set regnum, regnum + 1\n\t"
".endr\n\t"
".if isxmm\n\t"
"vbroadcastss %x[b], %[b]\n\t"
"vmulps %[a], %[b], %[ret]\n\t"
".else\n\t"
"vmulps %[b] %{1to16%}, %[a], %[ret]\n\t"
"# dummy = %[dummy]\n\t"
".endif\n\t"
: [ret] "=x,x" (ret), [dummy] "=xm,x" (dummy)
: [a] "x,xm" (a), [b] "m,[dummy]" (b));
return ret;
}