De vez em quando, o código C ++ não funciona quando compilado com algum nível de otimização. Pode ser um compilador que está fazendo otimização que quebra o código ou pode conter código contendo comportamento indefinido que permite ao compilador fazer o que sente.
Suponha que eu tenha algum código que seja quebrado quando compilado apenas com um nível de otimização mais alto. Como sei se é o código ou o compilador e o que faço se for o compilador?
optimization
compiler
dente afiado
fonte
fonte
Respostas:
Eu diria que é uma aposta segura que, na grande maioria dos casos, é o seu código, não o compilador, que está quebrado. E mesmo no caso extraordinário em que é o compilador, você provavelmente está usando algum recurso de linguagem obscura de uma maneira incomum, para a qual o compilador específico não está preparado; em outras palavras, você provavelmente poderia alterar seu código para ser mais idiomático e evitar o ponto fraco do compilador.
De qualquer forma, se você puder provar que encontrou um bug do compilador (com base na especificação da linguagem), relate-o aos desenvolvedores do compilador, para que eles possam corrigi-lo por algum tempo.
fonte
Como sempre, como em qualquer outro bug: realize um experimento controlado. Limite a área suspeita, desative as otimizações para todo o resto e comece a variar as otimizações aplicadas a esse pedaço de código. Depois de obter 100% de reprodutibilidade, comece a variar seu código, introduzindo coisas que podem quebrar certas otimizações (por exemplo, introduza um possível alias de ponteiro, insira chamadas externas com possíveis efeitos colaterais etc.). Observar o código do assembly em um depurador também pode ajudar.
fonte
Examine o código do assembly que resultou e veja se ele faz o que sua fonte está pedindo. Lembre-se de que as chances são muito altas de que seja realmente o seu código culpado de alguma maneira não óbvia.
fonte
Em mais de 30 anos de programação, o número de erros genuínos do compilador (geração de código) que encontrei ainda é apenas ~ 10. O número dos meus próprios erros (e de outras pessoas) que encontrei e corrigi no mesmo período provavelmente é > 10.000. Minha "regra de ouro" é que a probabilidade de qualquer bug devido ao compilador é <0,001.
fonte
Comecei a escrever um comentário e depois decidi que era muito longo e direto ao ponto.
Eu diria que é o seu código que está quebrado. No caso improvável de você descobrir um bug no compilador - você deve denunciá-lo aos desenvolvedores do compilador, mas é aí que a diferença termina.
A solução é identificar a construção incorreta e refatorá-la para que ela faça a mesma lógica de maneira diferente. Isso provavelmente resolveria o problema, se o bug está do seu lado ou no compilador.
fonte
fonte
int + int
estouro exatamente como se fosse compilado em uma instrução ADD de hardware. Funcionou muito bem quando compilado com uma versão mais antiga do GCC, mas não quando compilado com o compilador mais recente. Aparentemente, as pessoas legais do GCC decidiram que, como o resultado de um estouro de número inteiro é indefinido, seu otimizador poderia operar com a suposição de que isso nunca acontece. Otimizou uma ramificação importante diretamente do código.Se você deseja saber se é o seu código ou o compilador, é necessário conhecer perfeitamente a especificação do C ++.
Se a dúvida persistir, é necessário conhecer perfeitamente a montagem x86.
Se você não está disposto a aprender tanto com perfeição, é quase certamente um comportamento indefinido que o seu compilador resolve de maneira diferente, dependendo do nível de otimização.
fonte
Obter um erro de compilação no código padrão ou um erro interno de compilação é mais provável do que os otimizadores estarem errados. Mas eu ouvi falar de compiladores que otimizam loops incorretamente esquecendo alguns efeitos colaterais que um método causa.
Não tenho sugestões sobre como saber se é você ou o compilador. Você pode tentar outro compilador.
Um dia eu queria saber se era o meu código ou não, e alguém me sugeriu um valgrind. Passei os 5 ou 10 minutos para executar meu programa com ele (acho que
valgrind --leak-check=yes myprog arg1 arg2
fiz, mas joguei com outras opções) e imediatamente me mostrou UMA linha que é executada em um caso específico que era o problema. Então, meu aplicativo funcionou sem problemas desde então, sem falhas estranhas, erros ou comportamento estranho. valgrind ou outra ferramenta como essa é uma boa maneira de saber se é o seu código.Nota lateral: Uma vez me perguntei por que o desempenho do meu aplicativo foi ruim. Acontece que todos os meus problemas de desempenho estavam em uma linha também. Eu escrevi
for(int i=0; i<strlen(sz); ++i) {
. O sz foi de alguns mb. Por alguma razão, o compilador executava strlen todas as vezes, mesmo após a otimização. Uma linha pode ser um grande negócio. De performances a falhasfonte
Uma situação cada vez mais comum é que os compiladores quebram o código escrito para dialetos de C que suportam comportamentos não exigidos pelo Padrão e permitem que o código direcionado a esses dialetos seja mais eficiente do que o código estritamente conforme. Nesse caso, seria injusto descrever como código "quebrado", que seria 100% confiável em compiladores que implementaram o dialeto de destino, ou descrever como "quebrado" o compilador que processa um dialeto que não suporta a semântica necessária . Em vez disso, os problemas decorrem simplesmente do fato de que o idioma processado pelos compiladores modernos com otimizações ativadas é diferente dos dialetos que costumavam ser populares (e ainda são processados por muitos compiladores com as otimizações desativadas ou por alguns até com as otimizações ativadas).
Por exemplo, muito código é escrito para dialetos que reconhecem como legítimos vários padrões de alias de ponteiro não exigidos pela interpretação do Padrão do gcc, e faz uso desses padrões para permitir que uma tradução direta do código seja mais legível e eficiente do que seria possível sob a interpretação da Norma C por gcc. Esse código pode não ser compatível com o gcc, mas isso não implica que ele esteja quebrado. Simplesmente depende de extensões que o gcc suporta apenas com otimizações desativadas.
fonte
Isole o ponto problemático e compare o comportamento observado com o que deve acontecer de acordo com as especificações da linguagem. Definitivamente não é fácil, mas é isso que você precisa fazer para saber (e não apenas assumir ).
Eu provavelmente não seria tão meticuloso. Em vez disso, gostaria de perguntar ao fórum de suporte / lista de discussão do fabricante do compilador. Se é realmente um bug no compilador, eles podem corrigi-lo. Provavelmente seria o meu código de qualquer maneira. Por exemplo, as especificações de idioma relacionadas à visibilidade da memória no encadeamento podem ser bastante contra-intuitivas e podem se tornar aparentes apenas ao usar alguns sinalizadores de otimização específicos, em algum hardware específico (!). Algum comportamento pode ser indefinido pela especificação, portanto, ele pode funcionar com alguns compiladores / alguns sinalizadores e não com outros, etc.
fonte
Provavelmente, seu código tem um comportamento indefinido (como outros explicaram, é muito mais provável que você tenha bugs no código do que no compilador, mesmo se os compiladores C ++ forem tão complexos que eles possuem bugs; até a especificação C ++ possui bugs de design) . E o UB pode estar aqui, mesmo que o executável compilado funcione (por azar).
Portanto, você deve ler o blog de Lattner ' O que todo programador C deve saber sobre comportamento indefinido (a maioria se aplica também ao C ++ 11).
A ferramenta valgrind e as
-fsanitize=
opções recentes de instrumentação para o GCC (ou Clang / LLVM ) também devem ser úteis. E, é claro, habilite todos os avisos:g++ -Wall -Wextra
fonte