为什么 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*
)
使用 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.
逼着做的相关:int *
,但显然内在函数 API 需要您创建未对齐的 __m128i*
指针以使用 loadu
/ storeu
. (并且可能未对齐 float*
在不是有效对齐的 float
对象的字节上使用 _mm_loadu_ps
,但内在函数不会取消引用 float*
,而是强制转换为__m128_u*
)