ter*_*ert 6 c assembly gcc inline-assembly avx512
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;
}
Run Code Online (Sandbox Code Playgroud)
-march=skylake-avx512 -masm=intel -O3
Run Code Online (Sandbox Code Playgroud)
mov ebx,0xaaaaaaaa
kmovw k1,ebx
vpaddd zmm0{k1}{z},zmm0,zmm1
Run Code Online (Sandbox Code Playgroud)
问题是必须指定k1。
是否有类似于"r"整数的输入约束,除了它选择k寄存器而不是通用寄存器外?
虽然没有记录,但在这里我们看到:
(define_register_constraint“ Yk”“ TARGET_AVX512F?MASK_REGS:NO_REGS”“ @internal可用作谓词的任何掩码寄存器,即k1-k7。”)
编辑您的螺栓:
asm(
"vpaddd %[SUM] %{%[k]}, %[A], %[B]"
: [SUM] "=v"(sum)
: [A] "v" (a), [B] "v" (b), [k] "Yk" (0xaaaaaaaa) );
Run Code Online (Sandbox Code Playgroud)
似乎产生正确的输出。
也就是说,我通常会劝阻人们不要使用嵌入式asm(和未记录的功能)。你可以用_mm512_mask_add_epi32吗?
__mmask16实际上是typedef的unsigned short类型(其他普通整数类型的其他掩码类型),因此我们只需要一个约束即可将其传递到k寄存器中。
我们必须去挖掘gcc的源config/i386/constraints.md才能找到它:
任何掩码寄存器的约束为"k"。 或"Yk"用于k1..k7(可以用作谓词,与不同k0)。 例如,您"=k"会将操作数用作掩码比较的目标。
显然,您可以使用"=Yk"(tmp)与__mmask16 tmp来让编译器为您分配寄存器,而不仅仅是在"k"决定使用的任何寄存器上声明clobbers 。
首先,如果可以避免的话,请https://gcc.gnu.org/wiki/DontUseInlineAsm。 理解 asm非常棒,但是可以使用它来读取编译器输出和/或找出最佳选择,然后编写可以编译所需方式的内在函数。性能调优信息(例如https://agner.org/optimize/和https://uops.info/)通过asm助记符列出内容,并且比内在函数更短/更容易记住,但是您可以通过助记符进行搜索以找到内在函数在https://software.intel.com/sites/landingpage/IntrinsicsGuide/
内部函数还将使编译器将负载折叠到其他指令的内存源操作数中。使用AVX512,甚至可以广播负载!内联汇编程序强制编译器使用单独的加载指令。 甚至"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.
#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),
// no clobbers needed, unlike your question which I fixed with an edit
);
return sum;
}
Run Code Online (Sandbox Code Playgroud)
早于4.9可以使用gcc进行编译,但实际上不这样做,因为它不了解-march=skylake-avx512,甚至没有为Skylake或KNL进行调整的设置。
# gcc8.3 -O3 -march=skylake-avx512 or -march=knl
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
Run Code Online (Sandbox Code Playgroud)
-mavx512bw(由表示,-march=skylake-avx512但没有knl)在"Yk"上工作需要int。如果您使用进行编译-march=knl,则整数文字需要强制转换为__mmask16或__mask8,因为unsigned int = __mask32不适用于掩码。
[mask] "Yk" (0xAAAA) 即使常量确实适合16位,也需要AVX512BW,因为裸整数文字始终具有type int。(vpadddzmm每个向量有16个元素,因此我将常数缩短为16位。)使用AVX512BW,您可以传递更宽的常数,也可以忽略更小的常数。
-march=skylake-avx512。使用它来设置调整并启用所有功能。优选gcc8或至少gcc7。如果您在内联汇编之外使用新的ISA扩展(例如AVX512),则较新的编译器会生成不太笨拙的代码。-mavx512f -mavx512bw但不了解Skylake。-mavx512bw。"Yk"不幸的是,尚未在https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html中进行记录。
由于Ross 在GNU C内联汇编中的回答,我知道在GCC源代码中可以找到的地方,单个操作数的xmm / ymm / zmm修饰符是什么?