c++ - gcc:混合 __builtin_expect 和 noreturn

标签 c++ gcc

在我正在进行的一个大项目中,我们倾向于在错误检查中使用 gcc 的 __builtin_expect(通常当该错误将中止当前操作时):

if( __builtin_expect( failed, 0 ) )
    // handle error and fail

另一方面,我们有一组(小)集中式错误函数来处理某些错误类型(即:消费者耗尽了数据)。

这些函数都以抛出异常结束,因此它们永远不会返回。

这使得我们的错误检查如下:

if( __builtin_expect( got_bytes, 0 ) )
    customError( NoDataErrorCode, "No more data" ) ; // <-- always throws an exception

为了避免出现一些警告,我决定将这些函数标记为 noreturn。

通过阅读 gcc 的 __builtin_expect 和 noreturn 文档,我明白这应该没有问题,但你永远不知道。

我编写了一个小测试程序:

#include <stdexcept>

void throwIt( void ) __attribute__( ( noreturn ) ) ;

void throwIt( bool )
{
    throw std::runtime_error( "forced error" ) ;
}

void iffail( bool failed )
{
    if( __builtin_expect( failed, 0 ) )
        throwIt( failed ) ;
}

int main( int argc, char ** )
{
    iffail( !!( argc & 1 ) ) ;

    return 0 ;
}

使用 -O3 -S 编译它并检查汇编代码,我发现内置函数和属性在这种特殊情况下都是无关的。删除其中任何一个(或两个)都会产生完全相同的汇编代码。这让我想知道 __builtin_expect 是否对带有简单函数调用的简单 if 语句有任何影响。

注意: throwIt 的 bool 参数是为了强制编译器执行更多操作,而不仅仅是调用 if 内的函数。

所以我的问题是:这种安排安全吗?或者是否会出现这种情况。

最佳答案

是的,很安全。如下所述,它可能会或可能不会对实际代码产生影响。

__attribute__(noreturn) 和 __builtin_expect 的目的是帮助编译器。

如果您执行以下操作,则 noreturn 属性会避免出现“函数返回而没有 return 语句”的警告:

void panic(const char *msg) __atrribute__(noreturn);

int func(int x)
{

   if (x < 0) 
       panic("x must not be negative");
   else
       return x * 42;
}

(当然,在这种特殊情况下,else是完全多余的 - 但我确信我们可以想出一个例子,在这种情况下发生这种事情并且返回不存在)

expect“函数”帮助编译器理解“这可能发生”或“这不太可能发生”,并且编译器将根据是否可能发生选择“最佳路径”,例如:

if (failed)
   throwIt( failed );

现在,如果编译器认为可能会失败,它会生成如下代码:

if (!failed) goto not_failed;

throwIt( failed );

not_failed:
do_other_stuff();

就好像编译器认为失败不太可能是真的:

if (failed) goto do_failed;

do_other_stuff();
return;

do_failed:
throwIt( failed );

(许多处理器还具有告诉分支预测单元“启动”“这被预测为真”或“这被预测为假”的位,这也可能被编码到第一个和第二种情况(如果您使用 __builtin_expect)。

当然,在您的情况下,编译器很可能“猜测”内联抛出在没有提示的情况下不太可能发生,因此无论哪种方式都会生成相同的代码段。或者它只是“巧合”地出现了这样的代码。

此外,不同的处理器以及 gcc 的目标后端都或多或少复杂,因此根据编译目标的具体情况,某些处理器与另一个处理器之间的差异可能会有很大差异。

关于c++ - gcc:混合 __builtin_expect 和 noreturn,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26023977/

相关文章:

c++ - Boost Socket 在 close() 上崩溃

c - 在旧 C 中使用前声明变量

c++ - 为什么 gdb 无法打印数组元素?

c++ - 使用alignas 进行参数包扩展的语法是什么?

c++ - GCC 和 clang 中奇怪的构造和析构函数语法(void * 返回类型)

c++ - Python 和 pygame 是学习 SDL 的好方法吗?

c++ - "Iterator not dereferenceable"

C++ 枚举和编译器依赖

c++ - GCC 是否准备好用于 C++14 生产代码?

C++:当动态库中的内联函数发生变化时要重新编译什么?