Is Clang really this smart?

c++, c++11, clang, compiler-optimization, llvm

Solution

Yes, it looks like Clang really is this smart.

Test:

#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;
}

Result:

$ 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

Observe that Clang has converted the call to `fn(0x3f800000)` into simply an increment instruction, since the value decodes to `1.0`. This is correct.

My guess is that Clang is tracing the function calls because they only involve constants, and that Clang is capable of tracing `memcpy` through type-punning (probably by simply emulating its effect on the constant value).

Problem

If I compile the following code with Clang 3.3 using `-O3 -fno-vectorize` I get the same assembly output even if I remove the commented line. The code type puns all possible 32-bit integers to floats and counts the ones in a [0, 1] range. Is Clang's optimizer actually smart enough to realize that 0xFFFFFFFF when punned to float is not in the range [0, 1], so ignore the second call to `fn` entirely? GCC produces different code when the second call is removed. ``` #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; } ``` See here: http://goo.gl/YZPw5i

Original source

Related problems