Как встроенная функция GNU `__builtin_unreachable ` работает в этом фрагменте кода?
У меня есть фрагмент кода в моем проекте, в котором __builtin_unreachable
Функция была использована, но я не знаю, зачем она здесь нужна.
И я читал из GNU __builtin_unreachable, кажется, что __builtin_unreachable
Функция используется, чтобы сообщить компилятору, что эта строка никогда не будет достигнута во время работы ЦП, так что многие жалобы при компиляции могут быть предотвращены заранее. Но я не понимаю, почему эта функция необходима в этом фрагменте кода, кажется, ничего не произойдет, удалив __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 ответа
Это довольно интересно и ново для меня.
Насколько я понимаю, из документации, на которую вы ссылаетесь, говорится:
Если поток управления достигает точки
__builtin_unreachable()
, программа не определена.
Таким образом, в основном макрос достигает неопределенного поведения, если условие ложно. Таким образом, предполагается, что компилятор может оптимизировать, исходя из предположения, что этого не происходит, то есть условия не являются ложными.
Мне было бы интересно сравнить результат построения кода с этими макросами и без них, чтобы лучше понять, что в действительности имеет значение.
Полагаться на это, чтобы добиться какой-то оптимизации, кажется для меня "хрупким", так как она предполагает многое о внутреннем функционировании компилятора.
Как указывает комментарий кода, он рассказывает истории оптимизатору.
Первое, что это говорит, это то, что компилятор может предположить, что возвращаемое значение не является нулевым указателем. Вероятно, это улучшит читабельность кода, если вместо него будет использовано другое расширение gcc, а именно __attribute__((__returns_nonnull__))
, Добавление этого в интерфейс getDataArray
также гарантирует это свойство, даже если компилятор решит, что он не может встроить его по какой-либо причине.
Но это говорит больше, чем это. Это также говорит (или пытается сказать), что будущие вызовы cachePtr
с тем же параметром вернет тот же результат.
Все эти свойства, вероятно, лучше гарантировать, удалив неиспользованные parent
параметр (чтобы избежать анализа псевдонимов), а также добавив __attribute__((__const__))
в getDataArray
,
ATHCONTAINERS_ASSUME
говорит компилятору, что его аргумент x
не может быть ложным Это освобождает компилятор от необходимости генерировать любой код, чтобы учесть вероятность того, что x
ложно Например, когда компилятор видит ATHCONTAINERS_ASSUME (ptr != 0)
это может предполагать ptr
не является нулевым, и любой код, который противоречит этому предположению, может быть оптимизирован, поскольку это будет неопределенное поведение.
Например, так как getDataArray()
является inline
на каждом сайте вызовов компилятор может знать, что возвращаемый указатель никогда не будет нулевым. Так что, если звонящий делает это:
if (void* p = cache.getDataArray(aux, parent))
memcpy(p, "OK", 2);
Компилятор может генерировать код, который напрямую пишет "ОК", не выполняя нулевую проверку.