【问题标题】:Strict aliasing, -ffast-math and SSE严格的别名,-ffast-math 和 SSE
【发布时间】:2017-10-23 08:41:59
【问题描述】:

考虑以下程序:

#include <iostream>
#include <cmath>
#include <cstring>
#include <xmmintrin.h>

using namespace std;

int main()
{
    // 4 float32s.
    __m128 nans;
    // Set them all to 0xffffffff which should be NaN.
    memset(&nans, 0xff, 4*4);

    // cmpord should return a mask of 0xffffffff for any non-NaNs, and 0x00000000 for NaNs.
    __m128 mask = _mm_cmpord_ps(nans, nans);
    // AND the mask with nans to zero any of the nans. The result should be 0x00000000 for every component.
    __m128 z = _mm_and_ps(mask, nans);

    cout << z[0] << " " << z[1] << " " << z[2] << " " << z[3] << endl;

    return 0;
}

如果我使用带有和不带有-ffast-math 的Apple Clang 7.0.2 进行编译,我会得到预期的输出0 0 0 0

$ clang --version
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix

$ clang test.cpp -o test
$ ./test
0 0 0 0 

$ clang test.cpp -ffast-math -o test
$ ./test 
0 0 0 0

但是在更新到 8.1.0 之后(抱歉,我不知道这对应于哪个 Clang 的实际版本 - Apple 不再发布该信息),-ffast-math 似乎打破了这一点:

$ clang --version
Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

$ clang test.cpp -o test
$ ./test
0 0 0 0 

$ clang test.cpp -ffast-math -o test
$ ./test 
nan nan nan nan

我怀疑这是因为严格的别名规则或类似的东西。谁能解释这种行为?

编辑:我忘了提到,如果你这样做 nans = { std::nanf(nullptr), ... 它工作正常。

同时查看godbolt,似乎 Clang 3.8.1 和 Clang 3.9 之间的行为发生了变化——后者删除了 cmpordps 指令。 GCC 7.1 似乎保留了它。

【问题讨论】:

    标签: c++ clang sse strict-aliasing fast-math


    【解决方案1】:

    这不是一个严格的别名问题。如果您阅读the documentation of -ffast-math,您会看到您的问题:

    启用快速数学模式。这定义了__FAST_MATH__ 预处理器宏,并让编译器对浮点数学做出积极的、可能有损失的假设。其中包括:

    • [...]
    • 浮点运算的操作数不等于NaNInf,并且
    • [...]

    -ffast-math 允许编译器假定浮点数永远不是NaN(因为它设置了-ffinite-math-only 选项)。由于 clang 尝试匹配 gcc 的选项,我们可以阅读 GCC's option documentation 的一些内容,以更好地了解 -ffinite-math-only 的作用:

    允许对假设参数和结果不是 NaN 或 +-Infs 的浮点算术进行优化。

    任何 -O 选项都不应打开此选项,因为它可能导致依赖于 IEEE 或 ISO 规则/规范的精确实现的程序的错误输出。

    因此,如果您的代码需要使用NaN,则不能使用-ffast-math-ffinite-math-only。否则,您将面临优化器破坏您的代码的风险,正如您在此处看到的那样。

    【讨论】:

      猜你喜欢
      • 2011-11-07
      • 2013-08-14
      • 2011-02-20
      • 1970-01-01
      • 2018-08-03
      • 2019-02-09
      • 2015-09-01
      • 2019-01-29
      • 2016-11-21
      相关资源
      最近更新 更多