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()
的点,则程序未定义。
因此,如果条件为假,那么基本上宏会达到未定义的行为。因此,假设编译器能够基于不会发生的假设进行优化,即条件不为假。
我有兴趣比较使用和不使用这些宏构建代码的结果,以进一步了解它产生的实际差异。
依靠它来进行某种优化对我来说似乎有点“脆弱”,因为它假设了很多关于编译器的内部功能。
【讨论】:
它没那么脆弱。这就像在不检查空指针的情况下取消引用指针。优化人员一直都在利用 UBI'd be interested in comparing...
不幸的是,由于某些技术问题,这是不可能的。
“因为它对编译器的内部功能做了很多假设。” 并非如此,它告诉编译器你的程序的不变量,否则它无法推断出来。编译器对该信息的处理取决于编译器,但您无需关心。它并不脆弱,因为无论编译器是否使用它,程序的不变量都应该始终为真。
@JonathanWakely 当然,我的意思不是像“可能会崩溃”那样脆弱,我的意思是很难依赖某些正在发生的优化。我试图在答案中澄清。
我还是不同意。 “这个指针不为空”是一个非常基本的断言,任何优化编译器都应该能够使用它,因此没有任何特定于任何单个编译器的内部细节的断言。 OP 的问题中没有任何内容表明代码 依赖 将这些附加信息提供给编译器的任何特定结果,尽管依靠 GCC 理解 GCC 自己的内置函数当然是安全的。以上是关于GNU 内置函数 `__builtin_unreachable ` 如何在此代码段中工作?的主要内容,如果未能解决你的问题,请参考以下文章