GNU 内置函数 `__builtin_unreachable ` 如何在此代码段中工作?

Posted

技术标签:

【中文标题】GNU 内置函数 `__builtin_unreachable ` 如何在此代码段中工作?【英文标题】:How the GNU builtin function `__builtin_unreachable ` works in this code snippet? 【发布时间】:2017-12-08 08:41:41 【问题描述】:

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

而且我从GNU __builtin_unreachable读到,似乎__builtin_unreachable函数是用来通知编译器在CPU运行期间永远不会到达这一行,这样可以提前防止编译中的很多抱怨.但我不明白为什么这段代码sn-p需要这个功能,删除__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;

【问题讨论】:

它告诉编译器假装表达式永远不正确。很可能是出于优化原因。但是,这对我来说似乎是过早的优化。 “这样可以提前避免很多编译中的抱怨”不,不是这个目的。 【参考方案1】:

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

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

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

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

【讨论】:

你的意思是编译器可以假装这些表达式是true,即使在这个函数之外? @springcc:是的,只要函数的定义可见。在这种情况下,函数定义为inline,因此调用站点可以看到定义。【参考方案2】:

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

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

但它说明的远不止这些。它还告诉(或试图告诉)将来使用相同参数调用cachePtr 将返回相同的结果。

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

【讨论】:

【参考方案3】:

这很有趣,对我来说是新的。

我的最佳理解,从您链接到的文档中说:

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

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

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

依靠它来进行某种优化对我来说似乎有点“脆弱”,因为它假设了很多关于编译器的内部功能。

【讨论】:

它没那么脆弱。这就像在不检查空指针的情况下取消引用指针。优化人员一直都在利用 UB I'd be interested in comparing... 不幸的是,由于某些技术问题,这是不可能的。 “因为它对编译器的内部功能做了很多假设。” 并非如此,它告诉编译器你的程序的不变量,否则它无法推断出来。编译器对该信息的处理取决于编译器,但您无需关心。它并不脆弱,因为无论编译器是否使用它,程序的不变量都应该始终为真。 @JonathanWakely 当然,我的意思不是像“可能会崩溃”那样脆弱,我的意思是很难依赖某些正在发生的优化。我试图在答案中澄清。 我还是不同意。 “这个指针不为空”是一个非常基本的断言,任何优化编译器都应该能够使用它,因此没有任何特定于任何单个编译器的内部细节的断言。 OP 的问题中没有任何内容表明代码 依赖 将这些附加信息提供给编译器的任何特定结果,尽管依靠 GCC 理解 GCC 自己的内置函数当然是安全的。

以上是关于GNU 内置函数 `__builtin_unreachable ` 如何在此代码段中工作?的主要内容,如果未能解决你的问题,请参考以下文章

GNU C ------ __attribute__

GNU 内存对齐

原子集的内置 GCC 是啥?

GNU C中__attribute__妙用

python如何查看内置函数

内置函数dir