Reproducing clang's __builtin_assume for GCC

Viewed 1334

Recently, I discovered void __builtin_assume(bool) for clang, which can provide additional information about the state of the program to the compiler. This can make a huge difference, like for example:

#include <cstddef>

// compiles to about 80 instructions at -O3
unsigned sum(unsigned data[], size_t count) {
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}

// compiles to about 10 instructions at -O3
unsigned sum_small(unsigned data[], size_t count) {
    __builtin_assume(count <= 4);
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}

I am forced to use GCC at this time and I am curious whether there exists an equivalent builtin. Unfortunately I could not find __builtin_assume in the GCC documentation. Maybe there exists a builtin but it just has a different name?

If there doesn't exist an equivalent builtin, is there maybe a way to produce the same result without __builtin_assume, such as intentionally invoking undefined behavior when the condition is not true?

Ideally, I would like a macro which is always safe to call like:

#if ... // detect clang
#define MY_ASSUME(condition) __builtin_assume(condition)
#elif ... // detect GCC
#define MY_ASSUME(condition) __gcc_builtin_assume_equivalent(condition)
#else
#define MY_ASSUME(condition)
#endif

Whatever the solution is, it should also work in a constexpr function.

2 Answers

I've used __builtin_unreachable() which indicates that it is Undefined Behavior for control flow to reach here. You can wrap it in an if to essentially write an assertion. The condition can be any invariant that is false, so in your case you would put the opposite condition.

Example :

// Basically `assert(count <= 4);`
if ( !(count <= 4) ) {
    __builtin_unreachable();
}

Edit : In response to the comment, you can convert this into an assertion macro like this :

// Line break for readability
#define my_assert( condition ) \
    { if(!(condition)) __builtin_unreachable(); }

Based on the code in the question, you would use it like this :

unsigned sum_small(unsigned data[], size_t count) {
    my_assert(count <= 4); // <--- Changed here
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}

I feel like going trough undefined behavior here is completely unneeded. The very simple if check couple with abort is well-defined and gives optimizer enough food for thought:

#include <cstddef>
#include <cstdlib>

// compiles to about 10 instructions at -O3
unsigned sum_small(unsigned data[], size_t count) {
    if (count > 4)
        std::abort();
    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}

No need to summon nasal demons when none are required.

Related