AVR.
Ich benutze gerne einzelne Bits als Status-Flags für alle möglichen
Zustände im Programm (Ersatz für bool). Damit es schneller geht, bringe
ich die Bits in Registern unter. Zum ersten mal beobachte ich einen
Optimierungsfehler:
1
volatileregisterstruct{
2
unsignednibble:4;
3
unsignedbit4:1;
4
unsignedbit5:1;
5
unsignedbit6:1;
6
unsignedbit7:1;
7
}statusasm("r2");
8
9
intmain(void){
10
while(status.bit4);
11
}
aus der while-Schleife macht der GCC folgendes:
1
11: int main(void) {
2
+0000001C: 2D82 MOV R24,R2 Copy register
3
+0000001D: 7180 ANDI R24,0x10 Logical AND with immediate
4
+0000001E: 2388 TST R24 Test for Zero or Minus
5
+0000001F: F7F1 BRNE PC-0x01 Branch if not equal
6
13: }
d.h. er holt R2 nur einmal in den R24, aber danach überprüft er R24
immer wieder auf Null, ohne Ihn vorher zu beschreiben.
Wenn ich die Flags nicht im Register ablege:
1
volatilestruct{
2
unsignednibble:4;
3
unsignedbit4:1;
4
unsignedbit5:1;
5
unsignedbit6:1;
6
unsignedbit7:1;
7
}status;
8
9
intmain(void){
10
while(status.bit4);
11
}
funktioniert alles wie es soll:
1
11: int main(void) {
2
+00000024: 91800060 LDS R24,0x0060 Load direct from data space
3
+00000026: FD84 SBRC R24,4 Skip if bit in register cleared
4
+00000027: CFFC RJMP PC-0x0003 Relative jump
5
13: }
Bei diesem kleinen Beispiel nicht sichtbar, aber mein volles Programm
wird um 300 Byte dicker, wenn ich die zweite Variante benutze. Und
wahrscheinlich auch langsamer.
Ist das ein Bug, der gemeldet werden soll, oder ein Feature, das ich
nicht verstehe?
Wie kann ich das Programm sonst noch veranlassen solange zu warten oder
etwas zu tun, bis eine ISR ein flag setzt?
Danke.
Nun ja, erstens ist das Attribut "register" aus der C-Steinzeit, und
zweitens ist die Semantic eines "volatilen Registers" per se irgendwie
widersprüchlich.
Weil das so ist, wirft der gcc dazu auch eine warning:
>warning: optimization may eliminate reads and/or writes to register variables
Die gcc-Doku sagt dazu folgendes:
>The volatile modifier does not inhibit all optimizations that may eliminate >reads and/or writes to register variables.
Lange Rede, kurzer Sinn: Laß es sein. Register gehören dem Compiler, und
sonst niemandem.
Oliver
Oliver schrieb:> Lange Rede, kurzer Sinn: Laß es sein. Register gehören dem Compiler, und> sonst niemandem.
Dem kann ich nur zustimmen.
Die Situation ist ja auch deutlich entspannter, als zu Zeiten des
ATtiny12.
Jetzt haben ja schon die 8-Pinner 8kB Flash/256Byte RAM, da muß man
nicht mehr so geizig sein.
Oftmals nehme ich für ein Flag sogar ein ganzes Byte, spart etwas Code
und Zeit.
Peter
Man koennte ein GCC asm statement in der while Schleife benutzen um dem
Optimizer klar zu machen, das sich der Wert von R2 vielleicht geaendert
hat. Der eigentliche assembler Block waere leer, aber man definiert R2
als output parameter. Die GCC Hilfe (oder Google) sollten mit den
Details helfen.
Kann nicht garantieren dass es funktioniert, ist halt so eine Idee.
Frage: Wie verhinderst Du eigentlich, dass der GCC das R2 register von
sich aus benutzt ? Habe wohl irgendwo mal so eine Option gesehen, um ein
globales Register zu reservieren. Erinnere mich aber nicht an Details.
Peter Dannegger schrieb:> Jetzt haben ja schon die 8-Pinner 8kB Flash/256Byte RAM, da muß man> nicht mehr so geizig sein.
So einen 8kB-Teil habe ich und ich stehe schon mit dem Rücken zur Wand:
1
Size before:
2
tec-pwm.elf :
3
section size addr
4
.text 7246 0
5
6
...
7
8
Size after:
9
tec-pwm.elf :
10
section size addr
11
.text 7526 0
(nach dem löschen von register)
Kai S.:
> Frage: Wie verhinderst Du eigentlich, dass der GCC das R2 register von> ich aus benutzt ?http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
Irgendwie habe ich es damals so verstandanden, dass der Compiler nur
R8-R32 und R0, R1 benutzt. Bis jetzt hat's funktioniert.
Danke an Alle, ich werde umdenken!
Die allererste Frage lautet doch: Was bringt dieses "Optimierung"? (bzw.
was würde sie bringen, wenn das erzeugte Programm das täte, was es
sollte?)
Ein MOV benötigt zwar einen (in Worten: Einen) Zyklus weniger, als ein
LD, aber wenn es schon darauf ankommt, dann gute Nacht. Da wäre dann
doch der Einsatz von handoptimiertem Assembler angeraten.
Oliver
W. Nickel schrieb:> .text 7526 0> (nach dem löschen von register)
Klar. Nicht, weil du "register" gelöscht hast, sondern weil die
"volatile"s nun endlich auch wirklich berücksichtigt werden. Das sind
immer Pessimierungen.
Aber: ein ganzes Byte als Flag zu benutzen, kann dir codemäßig (und
nur daran klemmt es ja im Moment bei dir) billiger kommen als einzelne
Bits zu benutzen. Letzteres ist nur dann interessant, wenn du mit dem
SRAM am Ende bist.
> Irgendwie habe ich es damals so verstandanden, dass der Compiler nur> R8-R32 und R0, R1 benutzt.
Keineswegs, der benutzt auch die anderen, wenn es gebraucht wird.
Allerdings kannst du von R2 beginnend bis wenigstens ca. R7 dir
"private" Registervariablen reservieren (und sei es nur, damit du auf
diese anschließend in inline asm-Anweisungen zugreifen kannst). Du
musst das allerdings in allen Übersetzungseinheiten gleichermaßen so
deklarieren.
W. Nickel schrieb:> So einen 8kB-Teil habe ich und ich stehe schon mit dem Rücken zur Wand:
Da ist bestimmt noch reichlich Optimierungspotential.
Wenn Du Lust hast, kannst Du ja mal den Code posten oder mir schicken.
Peter
Peter Dannegger schrieb:> Wenn Du Lust hast, kannst Du ja mal den Code posten oder mir schicken.
Danke für das Angebot, ich werde es erstmal selbst versuchen, zumal es
ein Closed-Source-Projekt ist und ich damit Geld verdiene.
OffTopic. Darf ich mal eine private Frage stellen? Bist du Rentner?
Du scheinst viel Erfahrung und trotzdem viel Zeit zu haben ;-)