Come faccio a sapere se il compilatore ha rotto il mio codice e cosa devo fare se era il compilatore?

12

Di tanto in tanto il codice C ++ non funziona quando viene compilato con un certo livello di ottimizzazione. Può essere il compilatore che esegue l'ottimizzazione che interrompe il codice o potrebbe essere un codice contenente un comportamento indefinito che consente al compilatore di fare qualsiasi cosa si provi.

Supponiamo di avere qualche pezzo di codice che si rompe quando viene compilato solo con un livello di ottimizzazioni più elevato. Come faccio a sapere se è il codice o il compilatore e cosa faccio se è il compilatore?

    
posta sharptooth 26.09.2011 - 09:50
fonte

11 risposte

17

Direi che è una scommessa sicura in 99.9 ...% nella stragrande maggioranza dei casi che è il tuo codice, non il compilatore, che è rotto. E anche nel caso straordinario in cui si tratta del compilatore, probabilmente stai utilizzando alcune funzionalità del linguaggio oscuro in un modo insolito, per il quale il compilatore specifico non è preparato; in altre parole, è molto probabile che tu possa cambiare il tuo codice per essere più idiomatico ed evitare il punto debole del compilatore. Ad ogni modo, se riesci dimostrare che hai trovato un bug del compilatore (basato sulle specifiche del linguaggio), segnalalo agli sviluppatori del compilatore, in modo che possano risolverlo un po 'di tempo.

So che questa non è una risposta diretta alla tua domanda, altri potrebbero darti un aiuto migliore su questo.

    
risposta data 26.09.2011 - 09:57
fonte
13

Proprio come al solito, come con qualsiasi altro bug: esegui un esperimento controllato. Restringi l'area sospetta, disattiva le ottimizzazioni per tutto il resto e inizia a variare le ottimizzazioni applicate a quel pezzo di codice. Una volta ottenuta una riproducibilità del 100%, inizia a variare il codice, introducendo elementi che potrebbero interrompere alcune ottimizzazioni (ad esempio, introdurre possibili alias del puntatore, inserire chiamate esterne con potenziali effetti collaterali, ecc.). Anche l'esame del codice assembly in un debugger può essere d'aiuto.

    
risposta data 26.09.2011 - 10:01
fonte
9

Esaminare il codice assembly generato e vedere se fa ciò che l'origine sta richiedendo. Ricorda che le probabilità sono molto alte che in realtà il tuo codice è in errore in modo non ovvio.

    
risposta data 26.09.2011 - 19:48
fonte
6

In oltre 30 anni di programmazione, il numero di veri bug del compilatore (generazione di codice) che ho trovato è ancora solo ~ 10. Il numero di bug propri (e di altre persone) che ho trovato e risolto nello stesso periodo è probabilmente > 10.000. La mia "regola generale" è che la probabilità che un determinato bug sia dovuto al compilatore è < 0.001.

    
risposta data 26.09.2011 - 11:26
fonte
5

Ho iniziato a scrivere un commento e poi ho deciso che è troppo lungo e troppo significativo.

Direi che è il tuo codice che è rotto. Nell'improbabile caso in cui hai scoperto un bug nel compilatore, dovresti segnalarlo agli sviluppatori del compilatore, ma è lì che finisce la differenza.

La soluzione è identificare il costrutto incriminato e rifattarlo in modo che faccia la stessa logica in modo diverso. Molto probabilmente risolverebbe il problema, sia che il bug fosse dalla tua parte o nel compilatore.

    
risposta data 26.09.2011 - 10:02
fonte
5
  1. Rileggi il tuo codice accuratamente. Assicurati di non fare cose con effetti collaterali in ASSERT o altre dichiarazioni specifiche di debug (o più generale, di configurazione). Ricorda inoltre che in una memoria di debug la memoria viene inizializzata in modo diverso - i valori del puntatore rivelatori puoi controllare qui: Debugging - Memory Allocation Representations . Quando si esegue da Visual Studio, si utilizza quasi sempre l'heap di debug (anche in modalità di rilascio) a meno che non si specifichi esplicitamente con una variabile di ambiente che questo non è ciò che si desidera.
  2. Controlla la tua build. È normale che si verifichino problemi con build complesse in altri posti rispetto al compilatore vero e proprio: le dipendenze sono spesso il colpevole. So che "hai provato a ricostruire completamente" è quasi come infuriare una risposta come "hai provato a reinstallare Windows", ma spesso aiuta. Prova: a) Riavvio. b) Cancellare MANUALMENTE e ricostruire tutti i file intermedi e di output.
  3. Esamina il tuo codice per verificare eventuali potenziali posizioni in cui potresti invocare comportamenti non definiti. Se hai lavorato in C ++ per un po ', saprai che ci sono alcuni punti in cui pensi "Non sono completamente sicuro di potermi assumere che ..." - google o chiedi qui su quel particolare tipo di codice per vedere se è un comportamento indefinito o meno.
  4. Se ciò non sembra essere il caso, generare l'output preelaborato per il file che causa i problemi. Un'espansione macro inaspettata può causare ogni tipo di divertimento (mi viene in mente il momento in cui un collega ha deciso che una macro con il nome di H sarebbe stata una buona idea ...). Esamina l'output preelaborato per le modifiche inattese tra le configurazioni del progetto.
  5. Ultima risorsa - ora sei davvero nel campo del compilatore - guarda l'output del gruppo. Questo potrebbe richiedere un po 'di scavo e combattimenti solo per capire cosa sta effettivamente facendo l'assemblaggio, ma in realtà è piuttosto informativo. Puoi usare le abilità che raccogli qui per valutare anche le micro-ottimizzazioni, quindi non tutto è perduto.
risposta data 26.09.2011 - 10:30
fonte
2

Se vuoi sapere se è il tuo codice o il compilatore, devi conoscere perfettamente le specifiche del C ++.

Se il dubbio persiste, devi conoscere perfettamente l'assembly x86.

Se non sei in vena di imparare entrambi alla perfezione, allora è quasi certamente un comportamento indefinito che il tuo compilatore risolve in modo diverso a seconda del livello di ottimizzazione.

    
risposta data 26.09.2011 - 10:06
fonte
1

Ottenere un errore di compilazione sul codice standard o un errore di compilazione interno è più probabile che gli ottimizzatori siano errati. Ma ho sentito parlare di compilatori che ottimizzano i loop dimenticando erroneamente alcuni effetti collaterali di un metodo.

Non ho suggerimenti su come sapere se sei tu o il compilatore. Puoi provare un altro compilatore.

Un giorno mi stavo chiedendo se fosse il mio codice o meno e qualcuno mi ha suggerito di darmi un tocco. Ho speso i 5 o 10 minuti per eseguire il mio programma con esso (penso che valgrind --leak-check=yes myprog arg1 arg2 l'abbia fatto ma ho giocato con altre opzioni) e immediatamente mi ha mostrato UNA linea che viene eseguita in un caso specifico che era il problema. Poi la mia app ha funzionato senza problemi da allora, senza strani crash, errori o comportamenti strani. valgrind o un altro strumento come questo è un buon modo per sapere se è il tuo codice.

Nota a margine: una volta mi sono chiesto perché le prestazioni della mia app hanno risucchiato. Si è scoperto che tutti i miei problemi di prestazioni erano in una sola riga. Ho scritto for(int i=0; i<strlen(sz); ++i) { . Il sz era alcuni mb. Per qualche motivo il compilatore ha eseguito strlen ogni volta anche dopo l'ottimizzazione. Una linea può essere un grosso problema. Dalle prestazioni agli arresti anomali

    
risposta data 14.01.2012 - 08:45
fonte
1

Una situazione sempre più comune è che i compilatori interrompono il codice scritto per i dialetti di C che supportano i comportamenti non imposti dallo standard e il codice che consente a tali dialetti di essere più efficiente di un codice strettamente conforme potrebbe essere. In tal caso, sarebbe ingiusto descrivere un codice "spezzato" che sarebbe affidabile al 100% sui compilatori che implementavano il dialetto obiettivo o per descrivere come "interrotto" il compilatore che elabora un dialetto che non supporta la semantica richiesta . Invece, i problemi derivano semplicemente dal fatto che il linguaggio elaborato dai moderni compilatori con ottimizzazioni abilitate è divergente dai dialetti che erano popolari (e sono ancora elaborati da molti compilatori con ottimizzazioni disattivate, o da alcuni anche con ottimizzazioni abilitate). / p>

Ad esempio, viene scritto un sacco di codice per i dialetti che riconoscono come legittimo un numero di pattern di aliasing del puntatore non richiesto dall'interpretazione dello standard di gcc, e fa uso di tali pattern per consentire una semplice traduzione del codice per essere più leggibile e efficiente di quanto sarebbe possibile sotto l'interpretazione di gcc del C Standard. Tale codice potrebbe non essere compatibile con gcc, ma non è così implica che è rotto. Si basa semplicemente su estensioni che solo gcc supporta con ottimizzazioni disabilitate.

    
risposta data 27.06.2016 - 00:50
fonte
0

Isolare il punto problematico e confrontare il comportamento osservato con quello che dovrebbe accadere secondo le specifiche della lingua. Sicuramente non è facile, ma è quello che devi fare per sapere (e non solo assumere ).

Probabilmente non sarò così meticoloso. Piuttosto, chiederei al forum di supporto del produttore del compilatore / alla mailing list. Se è davvero un bug nel compilatore, potrebbero ripararlo. Probabilmente sarebbe comunque il mio codice. Ad esempio, le specifiche della lingua relative alla visibilità della memoria nel threading possono essere alquanto controintuitive e potrebbero risultare evidenti solo quando si utilizzano alcuni flag di ottimizzazione specifici su alcuni hardware specifici (!). Alcuni comportamenti potrebbero essere indefiniti dalle specifiche, quindi potrebbero funzionare con alcuni compilatori / alcuni flag e non funzionare con altri, ecc.

    
risposta data 26.09.2011 - 10:05
fonte
0

Molto probabilmente il tuo codice ha qualche comportamento indefinito (come altri hanno spiegato, hai molte più probabilità di avere bug nel tuo codice che nel compilatore, anche se i compilatori C ++ sono così complessi da avere bug, anche le specifiche C ++ hanno bug di progettazione). E UB può essere qui anche se l'eseguibile compilato dovesse funzionare (per sfortuna).

Quindi dovresti leggere il blog di Lattner Cosa dovrebbe sapere ogni programmatore C sul comportamento non definito (la maggior parte si applica anche a C ++ 11)

Lo strumento valgrind e il recente -fsanitize= opzioni di strumentazione a GCC (o Clang / LLVM ), dovrebbe anche essere utile. E, naturalmente, attiva tutti gli avvisi: g++ -Wall -Wextra

    
risposta data 27.06.2016 - 13:17
fonte

Leggi altre domande sui tag