为GCC再现clang的__builtin_assume

u3r8eeie  于 2023-08-06  发布在  其他
关注(0)|答案(3)|浏览(184)

最近,我发现了用于clang的void __builtin_assume(bool),它可以向编译器提供有关程序状态的附加信息。这可能会带来巨大的差异,比如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函数中工作。

klsxnrf1

klsxnrf11#

我使用了__builtin_unreachable(),它表明控制流到达这里是未定义的行为。您可以将其 Package 在if中,实质上写一个Assert。这个条件可以是任何不变量false,所以在你的例子中,你可以使用相反的条件。
范例:

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

字符串
编辑:作为对注解的响应,您可以将其转换为如下所示的Assert宏:

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

cnh2zyt3

cnh2zyt32#

我觉得在这里通过未定义的行为是完全不必要的。非常简单的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;
}

字符串
不需要召唤鼻魔时,没有必要。

5ssjco0h

5ssjco0h3#

从C++23开始,这可以使用[[assume]] attribute。这就像clang的__builtin_assume一样:举例来说:

// define an ASSUME(...) function-style macro so we only need to detect compilers
// in one place
#if __has_cpp_attribute(assume) >= 202207L
  #define ASSUME(...) [[assume(__VA_ARGS__)]]
#elif defined(__clang__)
  #define ASSUME(...) __builtin_assume(__VA_ARGS__)
#endif
// TODO: consider using fallback implementations from other answers too

unsigned sum_small(unsigned data[], size_t count) {
    ASSUME(count <= 4);

    unsigned sum = 0;
    for (size_t i = 0; i < count; ++i) {
        sum += data[i];
    }
    return sum;
}

字符串
所有编译器实现这一点可能需要一些时间。在撰写本文时,只有GCC 13支持此功能。

相关问题