为GCC复制clang的__builtin_assume

问题描述

最近,我发现了void __builtin_assume(bool) for clang,它可以向编译器提供有关程序状态的其他信息。这可以产生很大的变化,例如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;
}

我目前被迫使用GCC,我很好奇是否存在等效的内置函数。不幸的是,我在the GCC documentation中找不到__builtin_assume。也许存在一个内置函数,但是它只是一个不同的名字?

如果不存在等效的内置函数,是否有可能在没有__builtin_assume的情况下产生相同的结果,例如在条件不成立时有意调用未定义的行为吗?

理想情况下,我想要一个始终可以安全调用的宏,例如:

#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

无论采用哪种解决方案,它都应在constexpr函数中起作用。

解决方法

我使用了__builtin_unreachable(),它表示控制流到达此处是“未定义行为”。您可以将其包装在if中以实质上编写断言。条件可以是false的任何不变式,因此在您的情况下,您可以放置​​相反的条件。

示例:

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

编辑:响应评论,您可以将其转换为断言宏,如下所示:

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

根据问题中的代码,您将像这样使用它:

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

我觉得完全不需要通过未定义的行为。非常简单的if校验和abort的定义是明确的,并为优化程序提供了足够的思考空间:

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

不需要时,不需要召唤鼻恶魔。