为 GCC 重现 clang 的 __builtin_assume
Reproducing clang's __builtin_assume for GCC
最近,我发现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;
}
此时我被迫使用 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
的结合是 well-defined 并为优化器提供了足够的思考空间:
#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;
}
需要none时不需要召唤鼻魔
最近,我发现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;
}
此时我被迫使用 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
的结合是 well-defined 并为优化器提供了足够的思考空间:
#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;
}
需要none时不需要召唤鼻魔