assembly “_mm256_zeroall()”无法初始化寄存器变量

o3imoua4  于 2023-03-08  发布在  其他
关注(0)|答案(1)|浏览(132)

我想将所有YMM寄存器清零,如下所示。=:

#include <immintrin.h>

void fn(float *out) {
    register __m256 r0;
    _mm256_zeroall();
    _mm256_storeu_ps(out, r0);
}

但是gcc/clang给了我一个警告:

warning: 'r0' is used uninitialized [-Wuninitialized]

使用_mm256_setzero_ps()是可以的,但是代码和生成的程序集都很难看。
如果我定义了12个寄存器变量,gcc可能会生成12个vmovaps,而clang可能会生成12个vxorps指令,最坏的情况下,gcc会生成memset函数调用和许多vmovaps
我只需要一条vzeroall指令。
有什么方法可以让编译器知道_mm256_zeroall()会在不手写asm的情况下将所有寄存器清零吗?

**编辑1:**实际上我在写一个矩阵乘积的程序,在开始的时候需要清空很多寄存器,为了简化问题,我用了最简单的代码来提问。

我已经确认vzeroall与Zen 3上的许多vmovaps/vxorps相比并不慢,而且vzeroall的代码更小,更适合缓存。
移除寄存器限定符在GCC/Clang上不起作用。它生成的程序集与前一个程序集相同。
我发现可以使用specify the register name on GCC来消除警告,如下所示:

register __m256 r0 asm("ymm0");

但是clang不遵守定义,仍然生成相同的警告。

au9on6nz

au9on6nz1#

答案是,虽然指令名为vzeroall,但它只将前16个向量寄存器清零,而其他寄存器保持不变,因此,分配器可能会为存储选择一个较高的寄存器,从而导致错误的行为。
更多讨论:
首先,你实际上并不是在汇编语言中编程,你是在C++中编程(尽管是x86 intrinsic),如果你需要一个变量多次,你只是多次使用它,编译器会决定在必要时溢出它。相反,即使你定义了多个_mm256_setzero_ps(),编译器也会将它们理想化为一个变量。
第二,为什么需要多个零寄存器,我相信大多数avx指令都是非破坏性的,除了合并屏蔽指令,但是对零进行合并屏蔽操作相当于只做一个零屏蔽,就像你说的是针对多个累加器的,我看到编译器不做循环剥离,那么你可以手动剥离第一次迭代,这将去除零寄存器(example)的过多初始化。

相关问题