_mm_max_ss 在 clang 和 gcc 之间有不同的行为

_mm_max_ss has different behavior between clang and gcc

我正在尝试使用 clang 和 gcc 交叉编译一个项目,但是在使用 _mm_max_ss 例如

时我看到了一些奇怪的差异
__m128 a = _mm_set_ss(std::numeric_limits<float>::quiet_NaN());
__m128 b = _mm_set_ss(2.0f);
__m128 c = _mm_max_ss(a,b);
__m128 d = _mm_max_ss(b,a);

现在我预计涉及 NaN 时会出现 std::max 类型行为,但 clang 和 gcc 会给出不同的结果:

Clang: (what I expected)
c: 2.000000 0.000000 0.000000 0.000000 
d: nan 0.000000 0.000000 0.000000 

Gcc: (Seems to ignore order)
c: nan 0.000000 0.000000 0.000000 
d: nan 0.000000 0.000000 0.000000 

_mm_max_ps 在我使用它时做了预期的事情。我试过使用-ffast-math-fno-fast-math,但似乎没有效果。有什么想法可以使编译器之间的行为相似吗?

神箭linkhere

我的理解是 IEEE-754 要求:(NaN cmp x) to return false for all cmp operators {==, <, <=, >, >=},除了 {!=},其中 returns truemax() 函数的实现可以根据任何不等式运算符来定义。

那么,问题来了,_mm_max_ps是如何实现的呢?跟{<, <=, >, >=},还是有点比较?

有趣的是,当 禁用 优化 link 时,相应的 maxss 指令被两者使用gcc 和 clang。两者产量:

2.000000 0.000000 0.000000 0.000000 
nan 0.000000 0.000000 0.000000

这表明,给定:max(NaN, 2.0f) -> 2.0f,即:max(a, b) = (a op b) ? a : b,其中 op 是以下之一:{<, <=, >, >=}。使用 IEEE-754 规则,此比较的结果始终为假,因此:

(NaN op val) 总是 错误,returning (val),
(val op NaN) 总是 错误,returning (NaN)

启用优化后,编译器可以在编译时自由预计算 (c)(d)。 clang 似乎将结果评估为 maxss 指令会纠正 'as-if' 行为。 GCC 要么退回到 max() 的另一种实现——它使用 GMP 和 MPFR 库进行编译时数字——要么只是对 _mm_max_ss 粗心大意语义。

GCC 在 godbolt 上的 10.2 和主干版本仍然出错。所以我认为你发现了一个错误!我还没有回答第二部分,因为我想不出可以有效解决这个问题的万能黑客。


来自英特尔的 ISA 参考:

If the values being compared are both 0.0s (of either sign), the value in the second source operand is returned. If a value in the second source operand is an SNaN, that SNaN is returned unchanged to the destination (that is, a QNaN version of the SNaN is not returned).

If only one value is a NaN (SNaN or QNaN) for this instruction, the second source operand, either a NaN or a valid floating-point value, is written to the result. If instead of this behavior, it is required that the NaN from either source operand be returned, the action of MAXSS can be emulated using a sequence of instructions, such as, a comparison followed by AND, ANDN and OR.