"Instruções de hardware ilegais" de código muito simples

9

Enquanto investigava uma alegação duvidosa , escrevi este pequeno programa de testenoway.c

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

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

Testando isso, recebo:

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

Wat.

Se eu compilar sem otimizações, ele trava como esperado. Eu olhei para a montagem e, sem todos os sinos e assobios, a mainfunção fica assim:

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

Onde ud2está aparentemente é uma instrução específica para comportamento indefinido. A afirmação duvidosa acima mencionada, "Uma função que nunca retorna é UB", é reforçada. Eu ainda acho difícil de acreditar. Realmente!? Você não pode escrever com segurança um loop de rotação?

Então, acho que minhas perguntas são:

  1. Esta é uma leitura correta do que está acontecendo?
  2. Se sim, alguém pode me indicar algum recurso oficial que o verifique?
  3. Em que situação você deseja que esse tipo de otimização ocorra?

Informações relevantes

$ 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
fonte
3
IIRC, o estouro int assinado é UB.
wildplasser
11
^^^^ ie int n = 0===> unsigned int n = 0;ou melhor ainda ..while (1);
WhozCraig 28/11/19
11
Hmmm ... O clang do Compiler Explorer obtém uma instrução de salto de destino automático com -O gcc.godbolt.org/z/NTCQYT e uma tradução linha por linha sem ela. Parece consistente em várias versões. Mas também me lembro (embora não o procure) que o padrão C diz que uma não terminação é ub se for livre de efeitos colaterais . Aqui está Hans Boehm explicando que isso permite certas otimizações de compiladores impossíveis: open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm
Gene
11
O comportamento indefinido é o estouro inteiro assinado, não o loop infinito. Você pode corrigi-lo usandounsigned int
MM
2
@ Gene Eu não acho que isso diz. Pode-se presumir que loops isentos de efeitos colaterais com expressões de controle não-const terminem ( port70.net/~nsz/c/c11/n1570.html#6.8.5p6 ), mas o loop ocupado deve ser bom. O UB está no excesso de número inteiro, embora eu não possa reproduzir o exemplo com a instrução UD.
PSKocik

Respostas:

3

Se você obtiver o ud2 para o código que está agora em questão, o compilador não é um compilador C em conformidade. Você pode relatar um bug do compilador.

Observe que em C ++ esse código seria realmente UB. Quando os threads foram adicionados (C11 e C ++ 11, respectivamente), foi criada uma garantia de progresso para qualquer thread, incluindo o thread de execução principal de um programa que não é multithread.

No C ++, todos os threads devem eventualmente progredir, sem exceções. No entanto, em C, um loop cuja expressão controladora é uma expressão constante não é necessário para progredir. Meu entendimento é que C adicionou essa exceção porque já era prática comum na codificação incorporada usar a while(1) {}para travar o thread.

Pergunta semelhante com respostas mais detalhadas

MILÍMETROS
fonte
Sim, eu recebo o ud2código C, mas obrigado por incluir as informações sobre a garantia de progresso avançado do C ++ (um termo que parece que eu vou pesquisar), que é o que eu realmente estava perguntando, mas que foi otimizado como Eu estava preparando a pergunta.
luqui 28/11/19
Uma frase melhor do que eu acho que o Comitê de Padrões estava tentando dizer é que, se adiar tudo dentro de um loop após uma determinada operação não afetaria notavelmente o comportamento do programa, adiando a execução do loop como um todo, essa operação não seria considerada um mudança observável de comportamento.
Supercat