Hallo zusammen,
in einer Prozedur benutze ich uint8_t i als Zähler für eine for-Schleife
(und für sonst nichts). Innerhalb der Schleife steht eine if-Abfrage,
die ein Bit von i auf "1" prüft. Im Assembler-Code werden nun aber 2
Register für i verwendet! Gibt es dafür eine plausible Erklärung und
lässt sich das umgehen?
Ich hab bereits herausgefunden, dass ohne die if-Abfrage nur EIN Byte
verwendet wird. Typecasts innerhalb der if-Abfrage haben bisher nicht
zum erwünschten Ergebnis geführt.
Hier mal der C-Code: 1 | void onew_skip_rom ( void )
| 2 | {
| 3 | uint8_t i;
| 4 |
| 5 | for (i=0;i<8;i++)
| 6 | {
| 7 | if ( i & 0x02 )
| 8 | { ...
| 9 | _delay_us(ONEW_A); }
| 10 | else
| 11 | { ...
| 12 | _delay_us(ONEW_D); }
| 13 | }
| 14 | onew_status++;
| 15 | }
|
Daraus wird dann mit AVR Studio 4.13 SP2 und WinAVR 20071221 bei
Optimierung Os: 1 | ldi r18, 0x00 |\ hier werden die 2 bytes fuer i auf 0 gesetzt
| 2 | ldi r19, 0x00 |/
| 3 | sbrs r18, 1 -> Bit-Abfrage
| 4 | rjmp .+4
| 5 | ldi r24, 0x04 | register fuer delay 1 laden
| 6 | rjmp .+2
| 7 | ldi r24, 0x06 | register fuer delay 2 laden
| 8 | dec r24 |\ delay loop
| 9 | brne .-4 |/
| 10 | subi r18, 0xFF |\ i++
| 11 | sbci r19, 0xFF |/
| 12 | cpi r18, 0x08 |\ i == 8 ?
| 13 | cpc r19, r1 |/
| 14 | brne .-24
| 15 | lds r24, 0x0067
| 16 | subi r24, 0xFF
| 17 | sts 0x0067, r24
| 18 | ret
|
Beste Grüße
Stefan
GCC geht ganz natürlich davon aus, dass die zugrundeliegende Maschine
mit "int" optimal umgehen kann. Was bei fast allen Zielmaschinen auch
stimmt, nur eben bei AVR nicht. Pech.
Dafür hat GCC hundert Optionen, die den Optimizer kontrollieren. In
diesem Fall hilft -fno-tree-loop-optimize, das kann und wird aber oft
andersrum ausgehen.
@ Andreas Kaiser:
i ist hier aber kein int sondern ein unsigned char.
@ Stefan M.:
Hast du auch folgendes probiert:
if ( i & (uint8_t)0x02 )
Der Compiler erkennt das als Schleife und "optimiert" sie. Und benutzt
dafür implizit "int", davon ausgehend, dass dies nicht stört. Dass dies
bei AVR nicht passt, weiss der maschinenunabhängig arbeitende Optimizer
nicht.
Stefan Ernst wrote:
> @ Andreas Kaiser:
> i ist hier aber kein int sondern ein unsigned char.
Das ist doch gerade das Problem! int ist im C-Standard vorgesehen als
Datentyp mit der "natürlichen Breite" des Systems. C ist aber für
Systeme mit 16 Bit und mehr geschrieben, weshalb int auch gleichzeitig
mit "mindestens 16 Bit" definiert ist. Ein ANSI-C-Compiler rechnet eben
alles, was nicht anderweitig gekennzeichnet ist, in int , weil er
davon ausgeht, dass int -Berechnungen für die Maschine am einfachsten
sind (und dass kleinere Datentypen wie char oder uint8_t auch nicht
einfacher bearbeitet werden können). Und AVR-GCC ist zunächst mal ein
ANSI-C-Compiler und rechnet dementsprechend auch in int , selbst wenn
da was kleineres reichen würde. Es ist Sache der maschinenspezifischen
Erweiterungen, in jedem einzelnen Fall festzustellen, dass da
unnötigerweise 8 Bit verarbeitet werden, die eingespart werden können.
@ Johannes M.:
> Ein ANSI-C-Compiler rechnet eben
> alles, was nicht anderweitig gekennzeichnet ist, in int, ...
Richtig, deshalb ist die Konstante 0x02 ja auch ein int.
Aber i ist "anderweitig gekennzeichnet".
@ Andreas Kaiser:
Ist das irgendwo dokumentiert? In den Optimizer-Options des gcc finde
ich dazu leider nichts. Mir würde es aber auch schon reichen, wenn du
dir diesbezüglich wirklich 100%ig sicher bist. ;-)
@Stefan Ernst:
Mit "anderweitig gekennzeichnet" meinte ich eher einen größeren
Datentyp... i fällt in die Schublade "kleiner oder gleich int". Und die
Schublade wird in int bearbeitet.
Danke für die Antworten.
@Stefan Ernst:
if ( i & (uint8_t)0x02 ) tut es nicht. War mit das erste, was ich
ausprobiert habe.
@Andreas Kaiser
-fno-tree-loop-optimize führt zum erwünschten Ergebnis!
Da ich allerdings nicht so recht abschätzen kann, wie sich die Option
auf den Rest des Codes auswirkt und die Prozedur recht simpel ist, werd
ich sie wohl in Assembler nochmal schreiben.
Nochmals danke
Stefan
Stefan Ernst wrote:
> In den Optimizer-Options des gcc finde ich dazu leider nichts.
Die Option ist dort dokumentiert. Ein bischen jedenfalls.
> Mir würde es aber auch schon reichen, wenn du
> dir diesbezüglich wirklich 100%ig sicher bist. ;-)
Über was?
> Die Option ist dort dokumentiert. Ein bischen jedenfalls.
Eben, "Perform loop optimizations on trees" ist nicht gerade sehr
aussagekräftig. ;-)
> Über was?
Na, dass die Option das macht, was du geschrieben hast.
Da es ja bei Stefan M. funktioniert hat, wird es wohl so sein.
@ Johannes M.:
Dass der gcc aber jedes char grundsätzlich erstmal als int behandelt,
kann ich noch nicht so recht glauben. Wann im Compilierprozess wird denn
dann auf der AVR-Plattform aus dem "Pseudo Int" ein 8-Bit-Wert?
Stefan Ernst wrote:
> Eben, "Perform loop optimizations on trees" ist nicht gerade sehr
> aussagekräftig. ;-)
In solchen Fällen hilft nur kreatives ausprobieren. Anders bin ich auch
nicht drauf gekommen. Es hilft halt, wenn man den englischen Begriff für
"Schleife" nicht erst im Wörterbuch nachschlagen muss.
> Dass der gcc aber jedes char grundsätzlich erstmal als int behandelt,
> kann ich noch nicht so recht glauben.
Er muss. So ist Artihmetik in C nun einmal definiert. Auf 8bit
Controller spezialisierte Compiler besitzen deshalb meist eine Option,
mit der dies abgeschaltet werden kann. Was dann allerdings bedeutet,
dass
unsigned char i;
if (i+1 < 100)
bei i=255 ein anderes Ergebnis hat. Dieses Beispiel illustriert denn
auch, warum GCC manche scheinbaren 8bit-Operationen nicht auf 8bit
runteroptimieren kann.
In diesem Fall dürfte aber die integral promotion nicht wirklich
verantwortlich sein. Ich nehme eher an, dass im Compiler ein
Programmstück existiert, das typische Schleifenkonstrukte erkennt. Und
sie durch eigenen funktional äquivalenten aber möglicherweise besseren
Code ersetzt. Und dafür immer dann den Datentyp "int" als Basis wählt,
wenn der alle in der Schleife vorkommenden Werte erfasst. Und dass genau
dieses Programmstück durch die o.A. Option abgeschaltet wird.
Andreas Kaiser wrote:
> Stefan Ernst wrote:
>
>> Eben, "Perform loop optimizations on trees" ist nicht gerade sehr
>> aussagekräftig. ;-)
>
> In solchen Fällen hilft nur kreatives ausprobieren. Anders bin ich auch
> nicht drauf gekommen. Es hilft halt, wenn man den englischen Begriff für
> "Schleife" nicht erst im Wörterbuch nachschlagen muss.
Hä? Dieser Zusammenhang erschließt sich mir jetzt aber nicht. Ich muss
keines der Wörter im Wörterbuch nachschlagen, trotzdem ist für mich in
dem Satz keine Info enthalten, was diese Optimierung nun tatsächlich
tut.
>> Dass der gcc aber jedes char grundsätzlich erstmal als int behandelt,
>> kann ich noch nicht so recht glauben.
>
> Er muss. So ist Artihmetik in C nun einmal definiert.
Du meinst, irgendwo steht, dass in C jede arithmetische Operation
mindestens mit Int-Breite zu erfolgen hat?
Da hätte ich nun aber doch gerne eine Quellenangabe.
Stefan Ernst wrote:
> Hä? Dieser Zusammenhang erschließt sich mir jetzt aber nicht.
Dann muss ich es wohl einfacher ausdrücken: Ich bin schlicht der Reihe
nach jene Optimizer-Optionen durch, die das Wörtchen "loop" enthalten
und die ich nicht eindeuig anders einordnen konnte. Bei der zweiten oder
dritten wurde ich fündig.
> Da hätte ich nun aber doch gerne eine Quellenangabe.
ISO/IEC 9899:TC3: 6.3.1.1
http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
"If an int can represent all values of the original type, the value is
converted to an int; otherwise, it is converted to an unsigned int.
These are called the integer promotions.48) All other types are
unchanged by the integer promotions."
Ok, ich gebe mich geschlagen. ;-)
Und wenn du wissen willst, warum das so ist: C entstand auf der PDP-11,
und die hat von Haus aus schon exakt so gearbeitet.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|