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