为什么 Clang 抱怨 SSE 固有未对齐负载的对齐

Why does Clang complain about alignment on SSE intrinsic unaligned loads

使用 GCC 编译 FLAC 项目时,我(几乎)没有收到任何编译器警告。但是,在使用 clang 进行编译时,我收到 很多 此类警告

 lpc_intrin_sse2.c:85:49: warning: cast from 'const FLAC__int32 *' (aka 'const int *') to 'const __m128i *' increases required alignment from 4 to 16 [-Wcast-align]
                                                mull = _mm_madd_epi16(q9, _mm_loadu_si128((const __m128i*)(data+i-10))); summ = _mm_add_epi32(summ, mull);
                                                                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~

我不太明白为什么。这里使用的指令是 特别是 接受未对齐加载的指令(因此是 loadu),gcc 似乎并不介意。我知道对齐加载是 better/faster,但这里的代码实际上不允许这样做,因为每条指令进一步访问数据 4 个字节。对齐需要将数据以不同的对齐方式复制 4 次,这可能会导致缓存问题。

我判断确实没有问题吗?如果确实没有问题,那么消除此警告的最佳方法是什么?在这里可以用 (const __m128i*)(const void*) 替换 (const __m128i*) 吗?

alignof(__m128i) == 16。该转换发生在之前__m128i*作为参数传递给_mm_loadu_si128,它再次转换它,实际上并没有取消引用__m128i*

正如@chtz 指出的那样,您可以通过强制转换为 __m128i_u const * 来解决 clang 问题。 GCC/clang 使用 __attribute__((may_alias,aligned(1),vector_size(16))) 定义这些类型,这与不覆盖 alignment-requirement 的标准 __m128i 类型不同。但我不认为 MSVC 定义了一个 __m128i_u,所以那不会是可移植的。


你是对的,没有实际问题,只是英特尔对其内在函数 API 糟糕设计的产物,甚至 unaligned-load 内在函数也需要一个单独取消引用是不安全的指针。 (对于 AVX-512,新内在函数采用 void*,也避免了愚蠢的转换,但他们没有追溯更改旧内在函数采用 void*。)

如果 clang 的警告检查器遵循该指针值的使用链,它会发现它没有被取消引用。但它并没有这样做,而是当场警告您创建了一个可能无法安全取消引用的指针。这通常不是你想做的事情,但正如我所说,你是被英特尔的笨拙 API.

逼着做的

相关: 讨论了编译器必须定义为支持内部函数的一部分的行为 API,包括创建未对齐的指针。它是 ISO C UB,即使不取消引用也可以简单地创建未对齐的 int *,但显然内在函数 API 需要您创建未对齐的 __m128i* 指针以使用 loadu / storeu. (并且可能未对齐 float* 在不是有效对齐的 float 对象的字节上使用 _mm_loadu_ps,但内在函数不会取消引用 float*,而是强制转换为__m128_u*)