我的项目中有一个代码片段,其中使用了__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;
}
答案 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
,可以更好地保证所有这些属性。