Легальное использование setjmp и GCC

Используя GCC (4.0 для меня), это законно:

if(__builtin_expect(setjmp(buf) != 0, 1))
  {
    // handle error
  }
else
  {
    // do action
  }

Я нашел обсуждение, в котором говорилось, что это вызвало проблему для GCC еще в 2003 году, но я полагаю, что они уже исправили ее. Стандарт C говорит, что использование setjmp запрещено, если только это не одно из четырех условий, релевантным из которых является следующее:

  • один операнд оператора отношения или оператора равенства с другим операндом целочисленного константного выражения, при этом результирующее выражение является полным управляющим выражением оператора выбора или итерации;

Но если это расширение GCC, могу ли я гарантировать, что оно будет работать под GCC, так как это уже нестандартный функционал? Я протестировал его, и, похоже, он работал, хотя я не знаю, сколько тестов мне пришлось бы сделать, чтобы действительно сломать его. (Я прячу вызов __builtin_expect за макросом, который определяется как недействующий для не-GCC, поэтому он будет совершенно законным для других компиляторов.)


person Chris Lutz    schedule 07.01.2011    source источник
comment
Вам действительно нужно __builtin_expect там? Я бы ожидал, что GCC будет рассматривать setjmp как особый случай и оптимизировать для общего пути. Этот шаблон очень редко встречается в поиске кода Google: google.com/   -  person Giuseppe Ottaviano    schedule 07.01.2011
comment
@ Джузеппе - Наверное, нет, но всегда полезно учиться.   -  person Chris Lutz    schedule 07.01.2011
comment
Вся эта __builtin_expect — нелепая преждевременная оптимизация, которая искажает код с сомнительной выгодой. Если у gcc есть встроенный setjmp, он мог бы даже оптимизировать все условное выражение, заставив setjmp хранить адрес недостижимого в противном случае кода для ненулевого условия непосредственно в буфере перехода, поэтому я думаю, что мы действительно имеем дело с преждевременной оптимизацией, считающейся вредной здесь. .   -  person R.. GitHub STOP HELPING ICE    schedule 07.01.2011
comment
@R.: Пример кода в вопросе не был полным примером программы и ничего не говорит о том, насколько зрелым является проект или как используется этот фрагмент кода (или как часто он выполняется). Это может быть самый популярный код в приложении, интенсивно использующем процессор.   -  person nategoose    schedule 07.01.2011


Ответы (2)


Но если это расширение GCC, могу ли я гарантировать, что оно будет работать под GCC, так как это уже нестандартная функциональность? Я протестировал его, и, похоже, он работал, хотя я не знаю, сколько тестов мне пришлось бы сделать, чтобы действительно сломать его. (Я прячу вызов __builtin_expect за макросом, который определяется как недействующий для не-GCC, поэтому он будет совершенно законным для других компиляторов.)

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

person Joshua    schedule 07.01.2011
comment
Я не беспокоюсь о других компиляторах. Дает ли GCC какие-либо гарантии, что __builtin_expect сработает в этой ситуации, или это просто то, на что я должен надеяться? - person Chris Lutz; 07.01.2011
comment
__builtin_expect — это то же самое, что и #pragma. Читайте документы. - person Joshua; 07.01.2011
comment
Лично у меня есть все основания полагать, что gcc поступит правильно. - person Joshua; 07.01.2011

Я думаю, что стандарт говорил о том, чтобы сделать что-то вроде этого:

int x = printf("howdy");
if (setjmp(buf) != x ) {
    function_that_might_call_longjmp_with_x(buf, x);
} else {
    do_something_about_them_errors();
}

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

В вашем коде вы могли бы написать это как:

int conditional;
conditional = setjump(buf) != 0 ;
if(__builtin_expect( conditional, 1)) {
    // handle error
} else {
    // do action
}

И я думаю, что мы можем убедиться, что строка кода, присваивающая значение переменной conditional, соответствует этому требованию.

person nategoose    schedule 07.01.2011
comment
На самом деле даже ваша секунда не определена. Для этого вы должны объявить условную volatile (чтобы запретить оптимизацию регистра). - person Joshua; 07.01.2011
comment
@Джошуа: О чем ты говоришь? Значение, возвращаемое setjmp, является обычным целым числом и не требует особого внимания. Остальная часть программы требует особого внимания, потому что состояние может измениться после вызова setjmp, но до вызова longjmp. Это похоже на написание int x = 5, y = 6; while (y--) { printf("x == %u\n", x); x=0; } и ожидание, что оно всегда будет говорить, что x равно 5, потому что это то, что было назначено последним по номеру строки, но вы игнорируете будущие значения (за исключением циклов w/setjmp/longjmp проблема может быть усилена). - person nategoose; 07.01.2011
comment
Спецификация на самом деле говорит, что любая энергонезависимая переменная, назначенная после вызова setjmp(), не определена после вызова longjmp(). - person Joshua; 07.01.2011
comment
@Joshua: Но нас интересует присваивание энергонезависимой переменной, сделанное после вызова longjmp. Нас не интересует версия присваивания между первоначальным вызовом setjmp и вызовом longjmp, которая приводит ко второму возвращению setjmp. Я считаю, что вы неправильно интерпретируете стандарт, и как с теоретической точки зрения, так и с точки зрения реализации компилятора возвращаемое значение всегда будет во (временной) переменной, даже если я не назвал ее, поэтому ваша интерпретация сделает setjmp и longjmp полностью бесполезный. - person nategoose; 07.01.2011
comment
@nategoose: есть правило, которое вы можете прочитать по адресу pubs.opengroup.org/onlinepubs. /009696799/functions/setjmp.html, который ограничивает использование setjmp определенными контекстами, ни один из которых не допускает сохранения его значения. Наличие ограничений разумно, хотя я думаю, что эти ограничения заходят абсурдно далеко. Я не могу придумать ни одной причины, по которой у каких-либо реализаций возникли бы трудности с разрешением присваивания объявленному volatile-automatic как допустимому сценарию, но стандарт, похоже, не разрешает это. - person supercat; 06.09.2013