GNU内置函数`__builtin_unreachable`如何在这段代码中起作用?

时间:2017-12-08 08:41:42

标签: c++ c gcc compilation gnu

我的项目中有一个代码片段,其中使用了__builtin_unreachable函数,但我不知道为什么需要它。

我从GNU __builtin_unreachable读取,似乎__builtin_unreachable函数用于通知编译器在CPU运行时期间永远不会到达此行,因此编译中的很多抱怨都可以提前预防。但我不明白为什么在此代码段中需要此功能,删除__builtin_unreachable似乎不会发生任何事情。

# define ATHCONTAINERS_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while(0)


inline
void*
AuxVectorData::Cache::getDataArray (SG::auxid_t auxid,
                                    AuxVectorData& parent)
{
  // This function is important for performance.
  // Be careful when changing it.

  void* ptr = cachePtr (auxid);
  if (ATHCONTAINERS_UNLIKELY (ptr == 0)) {
    // We don't have the variable cached.
    // Call the out-of-line routine to get it cached.
    ptr = parent.getDataOol (auxid, false);

    // These inform the compiler of what the previous call did.
    // They tell the optimizer that it can now assume that this cache
    // entry is valid.
    ATHCONTAINERS_ASSUME (ptr != 0); 
    ATHCONTAINERS_ASSUME (cachePtr (auxid) != 0); 
    ATHCONTAINERS_ASSUME (cachePtr (auxid) == ptr);
  }
  return ptr;
}

3 个答案:

答案 0 :(得分:1)

ATHCONTAINERS_ASSUME告诉编译器它的参数x不能为假。这使编译器不必生成任何代码以适应x为假的可能性。例如,当编译器看到ATHCONTAINERS_ASSUME (ptr != 0)时,它可以假设ptr不为空,并且任何与该假设相矛盾的代码都可以被优化掉,因为它将是未定义的行为。

例如,由于getDataArray()inline,编译器可以在每个调用站点知道返回的指针永远不会为空。因此,如果来电者这样做:

if (void* p = cache.getDataArray(aux, parent))
    memcpy(p, "OK", 2);

编译器可以生成直接写入“OK”而不执行空检查的代码。

答案 1 :(得分:1)

这很有意思,对我来说很新。

我最了解,从您链接的文档中可以看出:

  

如果控制流程达到__builtin_unreachable()的点,则程序未定义。

因此,如果条件为false,宏基本上会达到未定义的行为。因此,假设编译器能够基于不发生的假设进行优化,即条件不是假的。

我有兴趣比较使用和不使用这些宏来构建代码的结果,以进一步了解它所产生的实际差异。

依赖于此来实现某种优化对我来说似乎有些“脆弱”,因为它假设编译器的内部功能很多。

答案 2 :(得分:1)

正如代码注释所示,它向优化器讲述故事。

这首先说明的是编译器可以假设返回值不是空指针。如果使用另一个gcc扩展名,它可能会提高代码的可读性,即 __attribute__((__returns_nonnull__))。将此添加到getDataArray的接口也可以保证该属性,即使编译器决定他不能出于任何原因内联它。

但它告诉的不止于此。它还告诉(或试图告诉)未来使用相同参数调用cachePtr将返回相同的结果。

通过删除未使用的parent参数(以避免别名分析)并将__attribute__((__const__))添加到getDataArray,可以更好地保证所有这些属性。