Использование определенного регистра zmm во встроенном ассемблере - PullRequest
0 голосов
/ 25 августа 2018

Могу ли я указать встроенную сборку в стиле gcc , чтобы поместить мою переменную __m512i в специфический zmm регистр, например zmm31?

1 Ответ

0 голосов
/ 25 августа 2018

Как и для целей, где вообще нет ограничений конкретного регистра (например, 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
...