„Nielegalna instrukcja sprzętu” z bardzo prostego kodu

9

Badając wątpliwe twierdzenie , napisałem ten mały program testowynoway.c

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

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

Testując to, otrzymuję:

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

Wat.

Jeśli skompiluję bez optymalizacji, zawiesza się zgodnie z oczekiwaniami. Spojrzałem na zestaw i bez wszystkich dzwonków i gwizdów mainfunkcja wygląda następująco:

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

Gdzie ud2najwyraźniej jest instrukcja specjalnie dla nieokreślonego zachowania. Wspomniane wątpliwe twierdzenie: „Funkcja, która nigdy nie powraca to UB”, jest wzmocniona. Nadal trudno mi w to uwierzyć. Naprawdę!? Nie możesz bezpiecznie napisać pętli spinowej?

Myślę, że moje pytania to:

  1. Czy to poprawny odczyt tego, co się dzieje?
  2. Jeśli tak, to czy ktoś może skierować mnie do jakiegoś oficjalnego zasobu, który to zweryfikuje?
  3. W jakiej sytuacji chcesz tego rodzaju optymalizacja?

Istotne informacje

$ 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
źródło
3
IIRC, podpisane int przepełnienie to UB.
wildplasser
1
^^^^ tj. int n = 0===> unsigned int n = 0;lub jeszcze lepiej ..while (1);
WhozCraig
1
Hmmm ... Kompilator Eksploratora Kompilatorów otrzymuje instrukcję skoku do celu z -O gcc.godbolt.org/z/NTCQYT i tłumaczenie bez wiersza. Wydaje się spójny w wielu wersjach. Ale pamiętam również (choć nie będę tego szukał), że standard C mówi, że nieterminacja jest ub, jeśli nie ma skutków ubocznych . Oto Hans Boehm wyjaśniający, że pozwala to na pewne niemożliwe w inny sposób optymalizacje kompilatora: open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm
Gene
1
Niezdefiniowane zachowanie oznacza przepełnienie liczby całkowitej, a nie nieskończoną pętlę. Możesz to naprawić, używającunsigned int
MM
2
@Gene Nie sądzę, że tak mówi. Przyjmuje się, że pętle wolne od skutków ubocznych z niepowiązanymi wyrażeniami kontrolującymi kończą się ( port70.net/~nsz/c/c11/n1570.html#6.8.5p6 ), ale zapętlenie zajęte powinno być w porządku. UB przepełnia się liczbami całkowitymi, chociaż nie mogę odtworzyć przykładu z instrukcją UD.
PSkocik

Odpowiedzi:

3

Jeśli otrzymasz ud2 dla kodu, który jest teraz w pytaniu, to kompilator nie jest zgodnym kompilatorem C. Możesz zgłosić błąd kompilatora.

Zauważ, że w C ++ tym kodem byłby UB. Po dodaniu wątków (odpowiednio C11 i C ++ 11) wprowadzono gwarancję postępu dla dowolnego wątku, w tym głównego wątku wykonawczego programu, który nie jest wielowątkowy.

W C ++ wszystkie wątki muszą się ostatecznie rozwijać, bez wyjątków. Jednak w C pętla, której kontrolne wyrażenie jest stałym wyrażeniem, nie jest wymagana do postępu. Rozumiem, że C dodał ten wyjątek, ponieważ było już powszechną praktyką w kodowaniu osadzonym, aby użyć a while(1) {}do zawieszenia wątku.

Podobne pytanie z bardziej szczegółowymi odpowiedziami

MM
źródło
Tak, dostaję ud2kod C, ale dziękuję za podanie informacji o gwarancji postępu w C ++ (termin, który wydaje się, że będę w stanie zbadać), o co tak naprawdę pytałem, ale zostałem zoptymalizowany, ponieważ Przygotowywałem pytanie.
luqui
Lepszym sformułowaniem tego, co moim zdaniem Komitet Normalizacyjny próbował powiedzieć, jest to, że jeśli odroczenie wszystkiego w pętli za pewną przeszłością pewna operacja nie wpłynie zauważalnie na zachowanie programu, odroczenie wykonania pętli jako całej przeszłości, ta operacja nie będzie uważana za zauważalna zmiana zachowania.
supercat