AVX512 掩码寄存器 (k1...k7) 的 GNU C 内联汇编输入约束?

GNU C inline asm input constraint for AVX512 mask registers (k1...k7)?

AVX512 为其算术命令引入了 opmask 功能。一个简单的例子:godbolt.org.

#include <immintrin.h>
__m512i add(__m512i a, __m512i b) {
    __m512i sum;
    asm(
        "mov ebx, 0xAAAAAAAA;                                   \n\t"
        "kmovw k1, ebx;                                         \n\t"
        "vpaddd %[SUM] %{k1%}%{z%}, %[A], %[B];  # conditional add   "
        :   [SUM]   "=v"(sum)
        :   [A]     "v" (a),
            [B]     "v" (b)
        : "ebx", "k1"  // clobbers
       );
    return sum;
}

-march=skylake-avx512 -masm=intel -O3

 mov ebx,0xaaaaaaaa
 kmovw k1,ebx
 vpaddd zmm0{k1}{z},zmm0,zmm1

问题是必须指定 k1。

对于整数是否有类似 "r" 的输入约束,只是它选择 k 寄存器而不是通用寄存器?

虽然它没有记录,但查看 here 我们看到:

(define_register_constraint "Yk" "TARGET_AVX512F ? MASK_REGS : NO_REGS" "@internal Any mask register that can be used as predicate, i.e. k1-k7.")

正在编辑你的神栓:

asm(
"vpaddd %[SUM] %{%[k]}, %[A], %[B]" 
: [SUM] "=v"(sum) 
: [A] "v" (a), [B] "v" (b), [k] "Yk" (0xaaaaaaaa) );

似乎产生了正确的输出。

就是说,我通常会尝试 discourage 人们不要使用内联汇编(和未记录的功能)。你可以使用 _mm512_mask_add_epi32 吗?

__mmask16 实际上是 unsigned short 的类型定义(以及其他普通整数类型的其他掩码类型),所以我们只需要一个约束来将它传递到 k 寄存器中。


我们必须深入挖掘 gcc 源代码 config/i386/constraints.md 才能找到它:

任何掩码寄存器的约束是"k"或者对 k1..k7 使用 "Yk"(它可以用作谓词,不像 k0)。 你会使用 "=k"例如,操作数作为比较掩码的目标。

显然,您可以将 "=Yk"(tmp)__mmask16 tmp 一起使用,让编译器为您分配寄存器,而不仅仅是在您决定使用的任何 "k" 寄存器上声明 clobber。


更喜欢像 _mm512_maskz_add_epi32

这样的内在函数

首先,https://gcc.gnu.org/wiki/DontUseInlineAsm if you can avoid it. Understanding asm is great, but use that to read compiler output and/or figure out what would be optimal, then write intrinsics that can compile the way you want. Performance tuning info like https://agner.org/optimize/ and https://uops.info/ list things by asm mnemonic, and they're shorter / easier to remember than intrinsics, but you can search by mnemonic to find intrinsics on https://software.intel.com/sites/landingpage/IntrinsicsGuide/

Intrinsics 还将让编译器将加载折叠到其他指令的内存源操作数中;使用 AVX512 甚至可以广播负载!您的内联汇编强制编译器使用单独的加载指令。 即使是 "vm" 输入也不会让编译器选择广播加载作为内存源,因为它不知道指令的广播元素宽度你正在使用它。

使用 _mm512_mask_add_epi32_mm512_maskz_add_epi32 特别是如果您已经在使用 <immintrin.h>.[=66 中的 __m512i 类型=]


此外,你的 asm 有一个错误:你使用的是 {k1} 合并屏蔽而不是 {k1}{z} 零屏蔽 ,但你使用了未初始化的 __m512i sum; 将仅输出 "=v" 约束作为合并目标!作为一个独立的函数,恰好合并到a中,因为调用约定有ZMM0 = first input = return value register。但是当内联到其他函数时,你绝对不能假设 sum 会选择与 a 相同的寄存器。最好的办法是对 "+v"(a) 使用 read/write 操作数,并使用 is 作为目标和第一个源。

合并屏蔽仅对 "+v" read/write 操作数有意义。(或者在您已经编写的包含多个指令的 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}.

早在 4.9 就可以使用 gcc 进行编译,但实际上并没有这样做,因为它不理解 -march=skylake-avx512,甚至没有针对 Skylake 或 KNL 的调整设置。使用更新的 GCC,了解您的 CPU 以获得最佳结果。

Godbolt compiler explorer:

# 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。如果您在内联 asm 之外使用新的 ISA 扩展(如 AVX512),较新的编译器会生成不那么笨重的代码。
  • gcc5 支持 -mavx512f -mavx512bw 但不知道 Skylake。
  • gcc4.9 不支持 -mavx512bw.

不幸的是,

"Yk" 尚未记录在 https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html.

感谢 Ross 在

上的回答,我知道在哪里可以查看 GCC 源代码