Perché il compilatore GCC omette del codice?


9

Non riesco a capire perché il compilatore GCC stia tagliando parte del mio codice mentre conserva assolutamente lo stesso nel vicinato?

Il codice C:

#define setb_SYNCO do{(PORTA|= (1<<0));} while(0);

ISR(INT0_vect){
    unsigned char i;

    i = 10;
    while(i>0)i--;   // first pause - omitted

    setb_SYNCO;
    setb_GATE;
    i=30;
    clrb_SYNCO;
    while(i>0)i--;  // second pause - preserved
    clrb_GATE;
}

La parte corrispondente di LSS (file assembler, creato dal compilatore):

ISR(INT0_vect){
  a4:   1f 92           push    r1
  a6:   0f 92           push    r0
  a8:   0f b6           in  r0, 0x3f    ; 63
  aa:   0f 92           push    r0
  ac:   11 24           eor r1, r1
  ae:   8f 93           push    r24
    unsigned char i;

    i = 10;
    while(i>0)i--;

    setb_SYNCO;
  b0:   d8 9a           sbi 0x1b, 0 ; 27
    setb_GATE;
  b2:   d9 9a           sbi 0x1b, 1 ; 27
    i=30;
    clrb_SYNCO;
  b4:   d8 98           cbi 0x1b, 0 ; 27
  b6:   8e e1           ldi r24, 0x1E   ; 30
  b8:   81 50           subi    r24, 0x01   ; 1
    while(i>0)i--;
  ba:   f1 f7           brne    .-4         ; 0xb8 <__vector_1+0x14>
    clrb_GATE;
  bc:   d9 98           cbi 0x1b, 1 ; 27
}
  be:   8f 91           pop r24
  c0:   0f 90           pop r0
  c2:   0f be           out 0x3f, r0    ; 63
  c4:   0f 90           pop r0
  c6:   1f 90           pop r1
  c8:   18 95           reti

Potrei supporre che il compilatore capisca che tale codice è fittizio e lo interrompe, ma perché conserva lo stesso alla fine del codice?

Esistono istruzioni per il compilatore per impedire tale ottimizzazione?


1
Puoi anche dire al compilatore di non ottimizzare una singola funzione, forse vale la pena provare questo metodo con il tuo ISR. Vedi questa domanda su StackOverflow.
Vladimir Cravero,

2
Ehi Roman, ho aggiunto il tag "c" alla tua domanda rimuovendo atmega. Ho dovuto rimuovere un tag poiché esiste un limite (cinque) e quando si fa una domanda relativa al codice, l'aggiunta del nome della lingua come tag è ottima perché tutto il codice (Domande e risposte) viene evidenziato.
Vladimir Cravero,

5
In generale, le lingue di livello superiore (come C) sono esplicitamente progettate per non essere legate a una relazione 1: 1 con il loro assemblaggio risultante. Se devi contare le istruzioni per ottenere i tempi giusti, devi sempre fare affidamento sull'assemblaggio (come hanno fatto alcune delle risposte). Il punto centrale di linguaggi di alto livello è che il compilatore ha una certa libertà per aiutare a rendere il codice più veloce, più forte, migliore di prima. Dettagli come le allocazioni dei registri e le previsioni dei rami sono molto meglio lasciati al compilatore ... tranne in momenti come questo in cui tu, il programmatore, conosci esattamente le istruzioni che desideri.
Cort Ammon,

5
La domanda migliore è: perché GCC non sta ottimizzando entrambi questi loop?
Ilmari Karonen,

5
Gcc prima srotola il ciclo e solo allora nota che il codice corrispondente è inutile. Con una dimensione del loop di 30, lo srotolamento sarebbe sciocco e gcc non lo fa. Ad un livello di ottimizzazione superiore entrambi sono ottimizzati via.
Marc Glisse,

Risposte:


9

Dato che in un commento affermi che "ogni tick della CPU è degno" ti suggerisco di usare un assembly inline per rendere i tuoi ritardi ciclici come vuoi. Questa soluzione è superiore alle varie volatileo -O0perché chiarisce qual è il tuo intento.

unsigned char i = 10;
__asm__ volatile ( "loop: subi    %0, 0x01\n\t"
                   "      brne    loop"
                   : "+rm" (i)
                   : /* no inputs */
                   : /* no dirty registers to decleare*/);

Questo dovrebbe fare il trucco. La cosa volatile è lì per dire al compilatore "So che questo non fa nulla, tienilo e fidati di me". Le tre "asserzioni" sono abbastanza autoesplicative, puoi usare qualsiasi registro invece di r24, credo che al compilatore piacciano i registri più bassi, quindi potresti volerne usare uno alto. Dopo il primo :dovresti elencare le variabili c di output (leggi e scrivi), e non ce ne sono, dopo il secondo :dovresti elencare le variabili c di input (solo), ancora, non ce ne sono, e il terzo parametro è un elenco separato da virgole di registri modificati , in questo caso r24. Non sono sicuro se dovresti includere anche il registro di stato dalZERO bandiera cambia ovviamente, non l'ho incluso.

modificare risposta modificata come richiesto dall'OP. Alcune note

La "+rm"prima (i)significa che si sta lasciando il compilatore decide di mettere i in m Emory o in un r egister. Questa è una buona cosa nella maggior parte dei casi poiché il compilatore può ottimizzare meglio se è gratuito. Nel tuo caso credo che tu voglia mantenere solo il vincolo r per forzare i a essere un registro.


Sembra che questa sia una cosa di cui ho davvero bisogno. Ma potresti modificare la tua risposta per accettare qualsiasi cvariabile anziché letterale che 10ho citato nella risposta originale? Sto cercando di leggere i manuali GCC sull'uso corretto della costruzione di asm, ma è un po 'oscurato per me ora. Sarei molto grato!
Roman Matveev,

1
@RomanMatveev modificato come da lei richiesto
Vladimir Cravero il

13

Potresti provare a fare in modo che il loop faccia effettivamente qualcosa. Allo stato attuale il compilatore sta giustamente dicendo "Questo ciclo non sta facendo nulla - me ne libererò".

Quindi potresti provare un costrutto che uso frequentemente:

int i;
for (i = 0; i < 10; i++) {
    asm volatile ("nop");
}

Nota: non tutti i target per il compilatore gcc usano la stessa sintassi dell'assembly inline - potrebbe essere necessario modificarlo per il target.


La tua soluzione sembra molto più elegante della mia ... forse la mia è migliore quando è richiesto il conteggio dei cicli PRECISE? Voglio dire, non è garantito che il tutto sia compilato in un certo modo, vero?
Vladimir Cravero,

8
Il semplice fatto che sta usando C significa che non puoi garantire cicli. Se hai bisogno di un conteggio dei cicli preciso, ASM è l'unica strada da percorrere. Voglio dire, ottieni un tempo diverso con il ciclo C se hai i -funroll-loop abilitati che se non lo fai, ecc.
Majenko,

Sì, è quello che ho pensato. Quando eseguo ritardi HW con valori i sufficientemente alti (100 o più), immagino che la tua soluzione produca praticamente gli stessi risultati migliorando la leggibilità.
Vladimir Cravero,

6

Sì, puoi supporre che. Se si dichiara la variabile i come volatile, si dice al compilatore di non ottimizzare su i.


1
Questo non è del tutto vero secondo me.
Vladimir Cravero,

1
@VladimirCravero, cosa vuoi dire dicendo che? Potresti chiarire di più?
Roman Matveev,

2
Quello che volevo dire è che non sarei così sicuro di ciò che fa un compilatore. Dichiarare una variabile volatile dice al compilatore che potrebbe cambiare altrove, quindi dovrebbe davvero farlo.
Vladimir Cravero,

1
@Roman Matveev register unsigned char volatile i __asm__("r1"); forse?
a3f,

2
Dichiarare icome volatile risolve tutto. Ciò è garantito dalla norma C 5.1.2.3. Un compilatore conforme non deve ottimizzare quei loop se iè volatile. Fortunatamente, GCC è un compilatore conforme. Sfortunatamente ci sono molti aspiranti compilatori C che non sono conformi allo standard, ma questo è irrilevante per questa particolare domanda. Non è assolutamente necessario un assemblatore in linea.
Lundin,

1

Dopo il primo ciclo iè una costante. L'inizializzazione dii e il ciclo non fanno altro che produrre un valore costante. Nulla nello standard specifica che questo ciclo deve essere compilato così com'è. Lo standard non dice nulla nemmeno sui tempi. Il codice compilato deve comportarsi come se il loop fosse presente e lo fa. Non si può dire con certezza che questa ottimizzazione è stata eseguita secondo lo standard (i tempi non contano).

Anche il secondo ciclo dovrebbe essere eliminato. Lo considero un bug (o un'ottimizzazione mancante) che non lo è. Dopo il ciclo iè costante zero. Il codice deve essere sostituito con l'impostazione isu zero.

Penso che GCC si mantenga isolo per il motivo per cui si esegue un accesso (opaco) alla porta potrebbe influire i.

Uso

asm volatile ("nop");

indurre GCC a credere che il loop faccia qualcosa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.