2014-09-24 2 views
2

На большом проекте я работаю, мы склонны использовать __builtin_expect ССАГПЗ в нашей проверки ошибок (обычно, когда эта ошибка будет прервать текущую операцию):НКУ: смешивание __builtin_expect и noreturn

if(__builtin_expect(failed, 0)) 
    // handle error and fail 

с другой стороны, мы имеем (небольшой) набор централизованных функций ошибок, которые имеют дело с определенными типами ошибок (то есть: у потребителя закончились данные).

Все эти функции заканчиваются выбросом исключения, поэтому они никогда не возвращаются.

Это оставляет проверку нашей ошибки, как:

if(__builtin_expect(got_bytes, 0)) 
    customError(NoDataErrorCode, "No more data") ; // <-- always throws an exception 

Чтобы избежать несколько предупреждений, которые я получаю здесь и там, я решил отметить те функции, как noreturn.

Прочитав документацию gcc для __builtin_expect и noreturn, я понимаю, что с этим не должно быть никаких проблем, но вы никогда не знаете.

Я написал небольшую тестовую программу:

#include <stdexcept> 

void throwIt(void) __attribute__((noreturn)) ; 

void throwIt(bool) 
{ 
    throw std::runtime_error("forced error") ; 
} 

void iffail(bool failed) 
{ 
    if(__builtin_expect(failed, 0)) 
     throwIt(failed) ; 
} 

int main(int argc, char **) 
{ 
    iffail(!!(argc & 1)) ; 

    return 0 ; 
} 

компиляции с -O3 -S и проверкой коды сборки я узнал, что как и встроенный атрибут не имеет значение в данном конкретном случае. Удаление любых (или обоих) из них дает точно такой же код сборки. Это заставляет меня задаться вопросом, влияет ли __builtin_expect на простой оператор if с простым вызовом функции в нем.

ПРИМЕЧАНИЕ: аргумент bool throwIt заключается в том, чтобы заставить компилятор сделать что-то большее, чем просто вызвать функцию внутри if.

Так что мой вопрос: безопасно ли это соглашение или будет ли случай, когда это будет укусить.

+0

Отказ от ответственности: Я не являюсь gcc-хакером. Я верю, что 'noreturn' сообщает компилятору, что функция не может вернуться. Это семантическое утверждение, которое может привести к неправильному коду, если неправильно использовать --- возврат из функции «noreturn» - это UB или что-то еще. (Я не уверен, что подсчет исключений считается возвратом для целей «noreturn», что кажется центральным для вашего вопроса. 'Longjmp' является' noreturn', хотя ...) '__builtin_expect' просто сообщает компилятору о вероятности ветвления; это не влияет на смысл программы. – tmyklebu

+0

@tmyklebu В соответствии с собственной документацией gcc вы можете использовать функцию noreturn. –

+0

Тогда ты золотой. – tmyklebu

ответ

2

Да, это безопасно. Как поясняется ниже, это может или не может иметь значение для фактического кода.

ЦЕЛЬ __attribute__(noreturn) и __builtin_expect - помочь компилятору.

noreturn atrribute избежать предупреждений для «функции возвращает без оператора возврата», если вы делаете что-то вроде:

void panic(const char *msg) __atrribute__(noreturn); 

int func(int x) 
{ 

    if (x < 0) 
     panic("x must not be negative"); 
    else 
     return x * 42; 
} 

(Конечно, в данном конкретном случае, else совершенно лишний - но я уверен, мы могли бы придумать пример, где такое происходит, и возврата не существует)

Функция «ожидание» помогает компилятору понять, что «это может произойти» или «это вряд ли произойдет», , и компилятор будет выбирать «лучший путь» на основе, если это возможно или нет, например:

if (failed) 
    throwIt(failed); 

теперь, если компилятор думает, что не удалось, скорее всего, он будет генерировать такой код:

if (!failed) goto not_failed; 

throwIt(failed); 

not_failed: 
do_other_stuff(); 

, где, как если компилятор считает, что это маловероятно, что не удалось верно:

if (failed) goto do_failed; 

do_other_stuff(); 
return; 

do_failed: 
throwIt(failed); 

(Многие процессоры также имеют биты, чтобы сообщить блоку предсказания ветвления «prime» с «это предсказано как истинное» или «это предсказано как ложное», которое также может быть закодировано в код в первом и secon если вы используете __builtin_expect).

Конечно, в вашем случае вполне возможно, что компилятор «догадывается» о том, что встроенный бросок является маловероятным сценарием без подсказок и, таким образом, генерирует один и тот же фрагмент кода в любом случае. Или, что это просто «по совпадению», все равно появляется такой код.

Кроме того, различные процессоры, а также целевые серверы gcc более или менее сложны, поэтому в зависимости от того, для какой цели компилируется, разница может сильно различаться между некоторыми процессорами и другими.

+0

Спасибо за такое ясное объяснение. Это было мое понимание до публикации этого вопроса, но я чувствовал, что лучше спросить. –

+0

Кстати, что вызвало все это было предупреждение «нет возврата»: функция должна перебирать все доступные устройства и возвращать первую, которая соответствует определенным критериям; если он достигнет конца функции, не найдя подходящего, он попадает в кнопку паники. –

0

Я не вижу в этом ничего плохого. Основная цель __builtin_expect заключается в том, чтобы служить подсказкой для компилятора, который помогает ему генерировать лучший код предсказания ветвления. Эффект может быть очень чувствительным к целевому типу ЦП, поскольку разные ЦП имеют совершенно разные эвристики прогноза ветвления. Компилятор может полностью игнорировать оптимизацию на основе предсказания на основе ветви везде вне определенных заранее определенных и легко узнаваемых шаблонов выполнения, таких как условный переход внутри жесткого цикла.

Целью noreturn является подавление некоторых предупреждений о компиляции и, когда возникает возможность, устранить мертвый код.

Эти функции будут влиять на сгенерированный код, если обстоятельства подходят для него. Просто так случается, что ваша программа не создает никаких возможностей для этих функций, чтобы иметь какое-либо значение.

+0

' noreturn' делает больше, чем просто «подавляет некоторые предупреждения во время компиляции и устраняет мертвый код». Он изменяет семантику вашей программы. – tmyklebu

+0

@tmyklebu: Во-первых, нет, он не делает «путь больше». Во-вторых, точная семантика 'noreturn' совершенно не имеет отношения к контексту вопроса. И, в-третьих, нет, это не изменяет семантику программы, если она используется по назначению. – AnT

+0

Ключ есть «... если используется по назначению». Если вы допустили достаточно плохую ошибку, компилятор с радостью оптимизирует код в реальном времени и, возможно, сделает что-то гораздо хуже. – tmyklebu

Смежные вопросы