Clang真的很聪明吗?

Chr*_*s_F 8 c++ llvm clang compiler-optimization c++11

如果我使用Clang 3.3编译以下代码,-O3 -fno-vectorize即使删除了注释行,也会得到相同的汇编输出.代码类型将所有可能的32位整数置于浮点数并计算[0,1]范围内的值.Clang的优化器实际上是否足够聪明,以致当被处理为浮点数时0xFFFFFFFF不在[0,1]的范围内,所以忽略fn完全的第二次调用?删除第二个调用时,GCC会生成不同的代码.

#include <limits>
#include <cstring>
#include <cstdint>

template <class TO, class FROM>
inline TO punning_cast(const FROM &input)
{
    TO out;
    std::memcpy(&out, &input, sizeof(TO));
    return out;
}

int main()
{
    uint32_t count = 0;

    auto fn = [&count] (uint32_t x) {
        float f = punning_cast<float>(x);
        if (f >= 0.0f && f <= 1.0f)
            count++;
    };

    for(uint32_t i = 0; i < std::numeric_limits<uint32_t>::max(); ++i)
    {
        fn(i);
    }
    fn(std::numeric_limits<uint32_t>::max()); //removing this changes nothing

    return count;
}
Run Code Online (Sandbox Code Playgroud)

见这里:http://goo.gl/YZPw5i

nne*_*neo 11

是的,看起来Clang真的很聪明.

测试:

#include <limits>
#include <cstring>
#include <cstdint>

template <class TO, class FROM>
inline TO punning_cast(const FROM &input)
{
    TO out;
    std::memcpy(&out, &input, sizeof(TO));
    return out;
}

int main()
{
    uint32_t count = 0;

    auto fn = [&count] (uint32_t x) {
        float f = punning_cast<float>(x);
        if (f >= 0.0f && f <= 1.0f)
            count++;
    };

    for(uint32_t i = 0; i < std::numeric_limits<uint32_t>::max(); ++i)
    {
        fn(i);
    }
#ifdef X
    fn(0x3f800000); /* 1.0f */
#endif

    return count;
}
Run Code Online (Sandbox Code Playgroud)

结果:

$ c++ -S -DX -O3 foo.cpp -std=c++11 -o foo.s
$ c++ -S -O3 foo.cpp -std=c++11 -o foo2.s
$ diff foo.s foo2.s
100d99
<   incl    %eax
Run Code Online (Sandbox Code Playgroud)

观察到Clang已将调用转换fn(0x3f800000)为简单的增量指令,因为该值解码为1.0.这是对的.

我的猜测是Clang正在跟踪函数调用,因为它们只涉及常量,并且Clang能够memcpy通过类型惩罚进行跟踪(可能只是通过模拟它对常量值的影响).

  • @Chris_F:我怀疑这会花费太长时间.Clang可能对它愿意做的跟踪量有一些启发式限制(否则编译时间可能很容易通过屋顶而没有明显的好处). (5认同)
  • 在那种情况下,我几乎感到惊讶的是,Clang并没有将整个事情编译为`movl $ 1065353217,%eax` (2认同)