Эмуляция __встроенного_ GCC недоступна?

#c #gcc #unreachable-code

#c #gcc #недоступен-код

Вопрос:

Я получаю целую кучу предупреждений о переключениях, которые лишь частично охватывают диапазон переключаемого перечисления. Поэтому я хотел бы иметь значение «по умолчанию» для всех этих переключателей и поставить __builtin_unreachable (GCC builtin) в этом случае, чтобы компилятор знал, что этот регистр недоступен.

Однако я узнал, что GCC4.3 пока не поддерживает этот встроенный. Есть ли какой-нибудь хороший способ эмулировать эту функциональность? Я думал о разыменовании нулевого указателя вместо этого, но это может иметь другие нежелательные эффекты / предупреждения и тому подобное. У вас есть идея получше?

Комментарии:

1. switch((int)myenum) /*silence warnings*/ ^_^

Ответ №1:

Предстоящая редакция стандарта C в 2023 году (C23, ISO / IEC 9899: 2023) будет содержать новый макрос unreachable

 #include <stddef.h>
void unreachable(void);
  

с эффектом gcc __builtin_unreachable .

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

 static _Noreturn void unreachable() {
    return; /* intentional */
}

/* ... */

foo();
bar(); /* should better not return */
unreachable();
baz(); /* compiler will know this is not reachable */
  

Обратите внимание, что вы вызываете неопределенное поведение, если функция, помеченная _Noreturn , действительно возвращается. Убедитесь, что указанная функция никогда не будет вызвана.

Комментарии:

1. Он предупреждает, говоря «Функция, помеченная как noreturn возвращает».

2. @Johannes Schaub Это верно. gcc предупреждает вас, но принимает код. До тех пор, пока ваш код фактически никогда не достигал unreachable(), ваш код не должен демонстрировать неопределенное поведение.

3. Это надлежащая основа __builtin_unreachable, которая превратилась во встроенную как есть. Отключите конкретное предупреждение, поскольку оно намеренно возвращается. Может быть какой-то дополнительный трюк, кроме возврата, но это как есть эквивалентно __builtin_недоступности. Сравнить ideone.com/f8yrLX с ideone.com/VP6NOB

4. @FUZxxl Разве ты не должен вызывать abort() вместо return . Это не только не приводит к неопределенному поведению, но и является правильным, поскольку функция прерывания определяется с помощью спецификатора функции _Noreturn .

5. @2501 Идея __builtin_unreachable() в том, чтобы дать компилятору подсказку, что он не может вмешиваться сам по себе. Эта цель не достигается путем вставки вызова в abort() . __builtin_unreachable() речь идет об оптимизации, а не о безопасности.

Ответ №2:

Хм, что-то вроде (поскольку __builtin_unreachable() появился в 4.5):

 
#define GCC_VERSION (__GNUC__ * 10000 
                                 __GNUC_MINOR__ * 100 
                                 __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40500
#define my_unreachable()  __builtin_unreachable()
#else
#define my_unreachable() do { printf("Oh noes!!!111n"); abort(); } while(0)
#endif
  

Комментарии:

1. if (x) y(); else my_unreachable(); не работает с вашим определением

2. Но это все еще предполагает, что код доступен. Компилятору не разрешено оптимизировать этот printf и это прерывание, поэтому он должен выдать этот код. Я хочу конкретно сообщить компилятору, что место, в которое помещен макрос, недоступно, так что он пропускает codegen или предупреждения для всех точек потока управления после него.

3. @litb: Таким образом, по сути, вы спрашиваете: «мой компилятор не позволяет мне указывать код как недоступный, как мне сообщить моему компилятору, что код недоступен». На самом деле не вычисляет. В любом случае, по крайней мере, в моей системе abort() помечен атрибутом (( noreturn )), поэтому компилятор должен иметь возможность избавиться от любого кода ниже этого вызова. Я думаю, вы все еще получаете предупреждения.

4. @janneb нет, я ищу обходной путь для достижения аналогичных целей __builtin_unreachable . Вызов «прервать» этого не сделает, потому что у него определенное поведение, поэтому компилятору все равно придется поместить вызов «прервать» в это место : (

5. @ nos это повредит производительности. это место оказалось узким местом, поэтому мне нужно избегать любых бесполезных ветвей и шаблонности, которые выдает компилятор на том основании, что он считает, что определенный код достижим (и если логика докажет, что он не может быть достигнут, возможно, на основе логики более высокого уровня, не видимой при чистом статическом анализе кода).

Ответ №3:

Будет ли abort (оставляя дамп ядра) или throw (позволяя альтернативный сбор данных) соответствовать вашим потребностям?

Вы действительно хотите иметь инструкции switch, которые не охватывают все перечисление? Я почти всегда стараюсь перечислить все возможные случаи (для no-op) без регистра по умолчанию, чтобы gcc предупредил меня, если будут добавлены новые перечисления, поскольку может потребоваться обработать их, а не позволить им автоматически (во время компиляции) перейти к значению по умолчанию.

Ответ №4:

сохраняйте это простым:

 assert(false);
  

или, еще лучше:

 #define UNREACHABLE (!"Unreachable code executed!")

assert(UNREACHABLE);
  

Комментарии:

1. Слишком просто. Компилятор по-прежнему будет рассматривать все, что следует за утверждением, как достижимое, что может привести к нежелательным предупреждениям.

2. вы уверены? POSIX говорит, что assert() — это макрос, и компилятор должен иметь возможность статически видеть, что false никогда не сможет принять значение true, и, следовательно, превратить assert(false) в конечном счете в вызов abort() …

3. @CAFxX это верно только в том случае, NDEBUG если не определено. Предполагается, что утверждения не должны использоваться в сборках без отладки.

Ответ №5:

 template<unsigned int LINE> class Unreachable_At_Line {}; 
#define __builtin_unreachable() throw Unreachable_At_Line<__LINE__>()
  

Редактировать:

Поскольку вы хотите, чтобы компилятор пропускал недоступный код, ниже приведен самый простой способ.

 #define __builtin_unreachable() { struct X {Xamp; operator=(const Xamp;); } x; x=x; }
  

Компилятор оптимизирует x = x; инструкции, особенно когда они недоступны. Вот использование:

 int foo (int i)
{
  switch(i)
  {
  case 0:  return 0;
  case 1:  return 1;
  default: return -1;
  }
  __builtin_unreachable();  // never executed; so compiler optimizes away
}
  

Если вы ставите __builtin_unreachable() в начале foo() , то компилятор генерирует ошибку компоновщика для unimplemented operator = . Я запускал эти тесты в gcc 3.4.6 (64-разрядный).

Комментарии:

1. СТРОКА всегда будет расширяться до строки, где определено , что __builtin_unreachable() не используется

2. Это та же проблема, что и решение от @janneb. Компилятор должен выполнить выбрасывание.

3. @Johannes, ты можешь проверить отредактированный ответ? Здесь компилятор либо выдает ошибку, либо оптимизирует недоступную часть. Таким образом, нет никаких осложнений в реальном времени.