«Недопустимая инструкция по аппаратному обеспечению» из очень простого кода

9

Разрабатывая сомнительную претензию , я написал эту небольшую тестовую программуnoway.c

int proveit()
{
    unsigned int n = 0;
    while (1) n++;
    return 0;
}

int main()
{
    proveit();
    return 0;
}

Тестируя это, я получаю:

$ clang -O noway.c
$ ./a.out
zsh: illegal hardware instruction  ./a.out

Wat.

Если я компилирую без оптимизации, он зависает, как и ожидалось. Я посмотрел на сборку, и без всех наворотов mainфункция выглядит так:

_main:                                  ## @main
    pushq   %rbp
    movq    %rsp, %rbp
    ud2

Где, ud2по-видимому, инструкция специально для неопределенного поведения. Вышеупомянутое сомнительное утверждение «функция, которая никогда не возвращается - UB», подкреплено. Мне все еще трудно поверить, хотя. В самом деле!? Вы не можете безопасно написать спин-петлю?

Итак, я думаю, мои вопросы:

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

Соответствующая информация

$ clang --version
Apple clang version 11.0.0 (clang-1100.0.20.17)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
luqui
источник
3
IIRC, со знаком переполнения int является UB.
Wildplasser
1
^^^^ ie int n = 0===> unsigned int n = 0;или еще лучше ..while (1);
WhozCraig
1
Хммм ... Clang Compiler Explorer получает инструкцию перехода с самообладанием с -O gcc.godbolt.org/z/NTCQYT и построчный перевод без него. Кажется последовательным во многих версиях. Но я также вспоминаю (хотя и не буду его искать), что стандарт C говорит, что нетерминация - это ub, если она не имеет побочных эффектов . Вот Ханс Бём, объясняющий, что это допускает некоторые невозможные в противном случае оптимизации компилятора: open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm
Gene,
1
Неопределенное поведение - это целочисленное переполнение со знаком, а не бесконечный цикл. Вы можете исправить это, используяunsigned int
MM
2
@ Джен, я не думаю, что это говорит об этом. Можно предположить, что циклы без побочных эффектов с неконстантными управляющими выражениями завершаются ( port70.net/~nsz/c/c11/n1570.html#6.8.5p6 ), но циклы занятости должны быть в порядке. UB находится в переполнении целых чисел, хотя я не могу воспроизвести пример с инструкцией UD.
PSkocik

Ответы:

3

Если вы получите ud2 для кода, который сейчас находится в вопросе, то компилятор не является соответствующим компилятором Си. Вы можете сообщить об ошибке компилятора.

Обратите внимание, что в C ++ этот код будет на самом деле UB. Когда были добавлены потоки (C11 и C ++ 11 соответственно), была введена прямая гарантия прогресса для любого потока, включая основной поток выполнения программы, которая не является многопоточной.

В C ++ все потоки должны в конечном итоге прогрессировать, без исключений. Однако в Си цикл, управляющее выражение которого является константным выражением, не требуется для выполнения. Насколько я понимаю, C добавил это исключение, потому что во встроенном кодировании уже было обычной практикой использовать a while(1) {}для зависания потока.

Подобный вопрос с более подробными ответами

М.М.
источник
Да, я получаю ud2код C, но спасибо за то, что вы включили информацию о гарантии продвижения C ++ (термин, который я, вероятно, смогу исследовать), о котором я действительно спрашивал, но который был оптимизирован как Я готовил вопрос.
Луки
Лучшая формулировка того, что, как мне кажется, Комитет по стандартам пытался сказать, состоит в том, что если откладывание всего цикла внутри определенной операции не окажет заметного влияния на поведение программы, то откладывание выполнения цикла в целом после этой операции не будет считаться наблюдаемое изменение поведения.
суперкат