Как и для целей, где вообще нет ограничений конкретного регистра (например, ARM), используйте переменные локального регистра , чтобы получить широкие ограничения для выбора конкретного регистра для операторов asm
. Компилятор все еще может оптимизировать иначе, потому что единственный документированный гарантированный локальный регистр эффект для asm
входов / выходов.
Компилятор предпочтет указанный регистр, даже если asm
нет. (Таким образом, вы можете написать код, который, кажется, работает, но в целом небезопасен с такими вещами, как register int ebx asm("ebx"); return ebx;
. Документация GCC - это то, что делает поведение гарантированным / перспективным, даже если текущий gcc предпочитает использовать указанный регистр достаточно сильно, чтобы тратить его инструкции, когда ограничение не совместимо с указанным регистром, см. ниже.)
В любом случае, использование переменных register-asm local является only вещью, для которой они гарантированно будут работать :
#include <immintrin.h>
__m512i foo() {
register __m512i z31 asm("zmm31") = _mm512_set1_epi32(123);
register __m512i z30 asm("zmm30");
asm("vmovdqa64 %1, %0 # from inline asm"
: "=v"(z30)
: "v"(z31)
);
return z30;
}
На проводнике компилятора Godbolt , компилируется в него с помощью clang6.0:
# clang -O3 -march=skylake-avx512
vbroadcastss .LCPI0_0(%rip), %zmm31 # zmm31 = [1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43,1.72359711E-43]
vmovdqa64 %zmm31, %zmm30 # from inline asm
vmovaps %zmm30, %zmm0
retq
и gcc8.2:
# gcc -O3 -march=skylake-avx512
foo():
movl $123, %eax
vpbroadcastd %eax, %zmm31
vmovdqa64 %zmm31, %zmm30 # from inline asm
vmovdqa64 %zmm30, %zmm0
ret
Обратите внимание на ограничения "v"
, которые допускают любой векторный регистр EVEX (0..31), в отличие от "x"
, который допускает только первые 16. "x"
задокументировано как «любой регистр SSE» , но также относится к регистрам YXM AVX. https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html.
Использование "x"
для этого не привело к каким-либо предупреждениям, но с gcc "x"
выиграно против объявления переменной регистра, поэтому он выбрал% zmm2 и% zmm1 (как ни странно, не zmm0
, поэтому дополнительный ход был необходим). Таким образом, объявление register-asm стоило нам эффективности.
В clang по-прежнему использовались zmm31 и zmm30, очевидно, нарушающие ограничение "x"
, поэтому сборка не удалась бы, если бы вы использовали инструкцию без версии EVEX в части XMM или YMM операнда регистра, например AVX2 vpcmpeqd ymm,ymm,ymm
(сравнить с вектором, а не с маской). ( В GNU C inline asm, каковы модификаторы для xmm / ymm / zmm для одного операнда? ).
//#ifndef __clang__
__m512i broken_with_clang() {
register __m512i z31 asm("zmm31") = _mm512_set1_epi32(123);
register __m512i z30 asm("zmm30") = _mm512_setzero_si512();
// notice that gcc still inits these in zmm31 and 30, *then* copies
// so register asm costs us efficiency.
// AVX512 only has compares into k registers, not into YMM registers.
asm("vpcmpeqd %t1, %t0, %t0 # from inline asm. input was %0"
: "+x"(z30)
: "x"(z31)
);
return z30;
}
//#endif
С помощью clang мы получаем ошибку для каждого операнда; Я полагаю, что clang не поддерживает модификаторы t
для получения имени регистра YMM (потому что он не работает с clang6.0, даже если я полностью удаляю register ... asm()
).
<source>:21:9: error: invalid operand in inline asm: 'vpcmpeqd ${1:t}, ${0:t}, ${0:t} # from inline asm. input was $0'
asm("vpcmpeqd %t1, %t0, %t0 # from inline asm. input was %0"
^
...
<source>:21:9: error: unknown token in expression
<inline asm>:1:11: note: instantiated into assembly here
vpcmpeqd , , # from inline asm. input was %zmm30
Но gcc компилирует это просто:
broken_with_clang():
movl $123, %eax
vpbroadcastd %eax, %zmm31
vpxord %xmm30, %xmm30, %xmm30
vmovdqa64 %zmm30, %zmm1 # extra overhead because of register asm
vmovdqa64 %zmm31, %zmm2 # which didn't match the constraints
vpcmpeqd %ymm2, %ymm1, %ymm1 # from inline asm. input was %zmm1
vmovdqa64 %zmm1, %zmm0 # extra overhead because gcc didn't pick zmm0
ret