Ich kann nicht verstehen, warum der GCC-Compiler einen Teil meines Codes ausschneidet, während er absolut den gleichen in der Nachbarschaft beibehält.
Der C-Code:
#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;
}
Der entsprechende Teil von LSS (Assembler-Datei, vom Compiler erstellt):
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
Ich könnte annehmen, dass der Compiler herausfindet, dass ein solcher Code ein Dummy ist, und ihn ausschneidet, aber warum wird derselbe am Ende des Codes beibehalten?
Gibt es Compiler-Anweisungen, um eine solche Optimierung zu verhindern?
Antworten:
Da Sie in einem Kommentar angeben, dass "jeder CPU-Tick wert ist", schlage ich vor, eine Inline-Assembly zu verwenden, um Ihre Verzögerungsschleife so zu gestalten, wie Sie es möchten. Diese Lösung ist den verschiedenen überlegen
volatile
oder-O0
weil sie klar macht , was Ihre Absicht ist.Das sollte den Trick machen. Das Flüchtige ist da, um dem Compiler zu sagen: "Ich weiß, dass dies nichts bewirkt, behalte es einfach und vertraue mir." Die drei asm "-Anweisungen" sind ziemlich selbsterklärend. Sie können jedes Register anstelle von r24 verwenden. Ich glaube, der Compiler mag niedrigere Register, daher möchten Sie möglicherweise ein hohes verwenden. Nach dem ersten
:
sollten Sie die Ausgabevariablen (Lesen und Schreiben) c:
auflisten , und es gibt keine. Nach dem zweiten sollten Sie die Eingabevariablen (nur) auflisten. Wiederum gibt es keine, und der dritte Parameter ist eine durch Kommas getrennte Liste der geänderten Register in diesem Fall r24. Ich bin mir nicht sicher, ob Sie auch das Statusregister aufnehmen sollten, da sich dasZERO
Flag natürlich ändert, ich habe es nicht aufgenommen.Bearbeiten Sie die bearbeitete Antwort wie gewünscht. Einige Notizen.
Die
"+rm"
vor(i)
bedeutet , dass Sie den Compiler einlassen entscheiden i in platzieren m Emory oder in einem r egister. Das ist in den meisten Fällen eine gute Sache, da der Compiler besser optimieren kann, wenn er kostenlos ist. In Ihrem Fall glaube ich, dass Sie nur die r-Einschränkung beibehalten möchten, um i zu zwingen, ein Register zu sein.quelle
c
Variable anstelle des10
in der ursprünglichen Antwort erwähnten Literals akzeptiert ? Ich versuche, die GCC-Handbücher über die ordnungsgemäße Verwendung der ASM- Konstruktion zu lesen, aber sie sind für mich jetzt etwas verdeckt. Ich würde mich sehr freuen!Sie könnten versuchen, die Schleife tatsächlich dazu zu bringen, etwas zu tun. So wie es aussieht, sagt der Compiler zu Recht: "Diese Schleife macht nichts - ich werde sie loswerden."
Sie können also ein Konstrukt ausprobieren, das ich häufig verwende:
Hinweis: Nicht alle Ziele für den gcc-Compiler verwenden dieselbe Inline-Assemblysyntax. Möglicherweise müssen Sie sie für Ihr Ziel anpassen.
quelle
Ja, das könnte man annehmen. Wenn Sie die Variable i als flüchtig deklarieren, weisen Sie den Compiler an, i nicht zu optimieren.
quelle
register unsigned char volatile i __asm__("r1");
vielleicht?i
Als flüchtig zu deklarieren löst alles. Dies wird durch den C-Standard 5.1.2.3 garantiert. Ein konformer Compiler darf diese Schleifen nicht optimieren, wenn siei
flüchtig sind. Glücklicherweise ist GCC ein konformer Compiler. Es gibt leider viele potenzielle C-Compiler, die nicht dem Standard entsprechen, aber das ist für diese spezielle Frage irrelevant. Es ist absolut kein Inline-Assembler erforderlich.Nach der ersten Schleife
i
ist eine Konstante. Die Initialisierung voni
und die Schleife erzeugen lediglich einen konstanten Wert. Nichts im Standard legt fest, dass diese Schleife unverändert kompiliert werden muss. Der Standard sagt auch nichts über das Timing aus. Der kompilierte Code muss sich so verhalten, als ob die Schleife vorhanden wäre und dies auch tut. Sie können nicht zuverlässig feststellen, dass diese Optimierung unter dem Standard durchgeführt wurde (Timing zählt nicht).Die zweite Schleife sollte ebenfalls gelöscht werden. Ich halte es für einen Fehler (oder eine fehlende Optimierung), der es nicht ist. Nachdem die Schleife
i
konstant Null ist. Der Code sollte durch Setzeni
auf Null ersetzt werden.Ich denke, GCC bleibt
i
nur aus dem Grund bestehen, dass Sie einen (undurchsichtigen) Portzugriff durchführen, der sich möglicherweise auswirkti
.Benutzen
GCC dazu zu bringen zu glauben, dass die Schleife etwas tut.
quelle