【问题标题】:Is Clang really this smart?Clang真的这么聪明吗?
【发布时间】:2014-07-18 12:43:10
【问题描述】:

如果我使用 -O3 -fno-vectorize 使用 Clang 3.3 编译以下代码,即使我删除了注释行,我也会得到相同的汇编输出。代码类型将所有可能的 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;
}

请看这里:http://goo.gl/YZPw5i

【问题讨论】:

  • count 在这两种情况下都正确吗?
  • Clang 习惯于大规模优化仅常量函数(有效地对它们进行非常复杂的常量折叠)。 Figure 1.
  • @9dan 如今在现代 C 库和编译器中,memcpy 几乎总是一个编译器内部函数。
  • @9dan:这不是“了解 memcpy 的内部结构”(这可能需要编译器了解手动优化的库实现),而更多的是“了解 memcpy 的预期功能”。 C/C++ 允许您基本上执行您喜欢的任何优化,只要结果相对于规范没有变化。由于memcpy是由C/C++指定的,原则上只要结果相同,就可以任意优化。

标签: c++ c++11 clang llvm compiler-optimization


【解决方案1】:

是的,看起来 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;
}

结果:

$ 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

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

我的猜测是 Clang 正在跟踪函数调用,因为它们只涉及常量,并且 Clang 能够通过类型双关语(可能通过简单地模拟其对常量值的影响)来跟踪 memcpy

【讨论】:

  • 在那种情况下,我几乎惊讶于 Clang 没有将整个东西编译成 movl $1065353217, %eax
  • @Chris_F:我怀疑这会花费很长时间。 Clang 可能对其愿意执行的跟踪量有一些启发式限制(否则编译时间很容易超过屋顶而没有明显的好处)。
  • 啊,这很有意义,现在我尝试考虑它。在快速处理器上运行需要几秒钟,所以如果它在任何地方都做这种事情,那么什么都不会完成编译。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-21
  • 2021-09-22
相关资源
最近更新 更多