【发布时间】:2021-06-10 20:13:58
【问题描述】:
我正在将一个项目转换为使用来自 clang 的 gcc 进行编译,但我遇到了一个使用 sse 函数的函数的问题:
void dodgy_function(
const short* lows,
const short* highs,
short* mins,
short* maxs,
int its
)
{
__m128i v00[2] = { _mm_setzero_si128(), _mm_setzero_si128() };
__m128i v10[2] = { _mm_setzero_si128(), _mm_setzero_si128() };
for (int i = 0; i < its; ++i) {
reinterpret_cast<short*>(v00)[i] = lows[i];
reinterpret_cast<short*>(v10)[i] = highs[i];
}
reinterpret_cast<short*>(v00)[its] = reinterpret_cast<short*>(v00)[its - 1];
reinterpret_cast<short*>(v10)[its] = reinterpret_cast<short*>(v10)[its - 1];
__m128i v01[2] = {_mm_setzero_si128(), _mm_setzero_si128()};
__m128i v11[2] = {_mm_setzero_si128(), _mm_setzero_si128()};
__m128i min[2];
__m128i max[2];
min[0] = _mm_min_epi16(_mm_max_epi16(v11[0], v01[0]), _mm_min_epi16(v10[0], v00[0]));
max[0] = _mm_max_epi16(_mm_max_epi16(v11[0], v01[0]), _mm_max_epi16(v10[0], v00[0]));
min[1] = _mm_min_epi16(_mm_min_epi16(v11[1], v01[1]), _mm_min_epi16(v10[1], v00[1]));
max[1] = _mm_max_epi16(_mm_max_epi16(v11[1], v01[1]), _mm_max_epi16(v10[1], v00[1]));
reinterpret_cast<__m128i*>(mins)[0] = _mm_min_epi16(reinterpret_cast<__m128i*>(mins)[0], min[0]);
reinterpret_cast<__m128i*>(maxs)[0] = _mm_max_epi16(reinterpret_cast<__m128i*>(maxs)[0], max[0]);
reinterpret_cast<__m128i*>(mins)[1] = _mm_min_epi16(reinterpret_cast<__m128i*>(mins)[1], min[1]);
reinterpret_cast<__m128i*>(maxs)[1] = _mm_max_epi16(reinterpret_cast<__m128i*>(maxs)[1], max[1]);
}
现在有了 clang,它给了我预期的输出,但在 gcc 中它打印全零:godbolt link
我发现当我使用 -O1 编译时 gcc 给了我正确的结果,但使用 -O2 和 -O3 时出错,这表明优化器出错了。我做的有什么特别错误的事情会导致这种行为吗?
作为一种变通方法,我可以将事情包装在一个联合中,然后 gcc 会给我正确的结果,但这感觉有点恶心:godbolt link 2
有什么想法吗?
【问题讨论】:
-
是的,看起来就像消除写入中的别名一样:godbolt.org/z/bfanne。如果你不能像我一样强制对齐,我认为联合是最干净的方式
-
这么多 reinterpret_cast...
-
"优化器出错了"...实际发生的情况是由于严格的别名违规,您正在调用未定义的行为,因此对于结果没有任何意义程序。
-
这就是我试图理解的部分内容,如果这是一个 gcc 错误或其他东西,如 UB,或两者兼而有之。
-
我们也应该期待一个严格的别名违规警告吗?
标签: c++ gcc sse intrinsics strict-aliasing