Der Vergleich zweier Nibbles (siehe unten) wird vom GCC-Compiler auf 16
Bit aufgeblasen. Ist dies von C-Standard so vorgesehen? Wie kann man das
auf 8 Bit reduzieren?
1
252: if ((checksum & 0x0F) == (checksum >> 4))
2
+0000010B: 9180006B LDS R24,0x006B Load direct from data space
3
+0000010D: 2F28 MOV R18,R24 Copy register
4
+0000010E: E030 LDI R19,0x00 Load immediate
5
+0000010F: 702F ANDI R18,0x0F Logical AND with immediate
6
+00000110: 7030 ANDI R19,0x00 Logical AND with immediate
7
+00000111: 9582 SWAP R24 Swap nibbles
8
+00000112: 708F ANDI R24,0x0F Logical AND with immediate
Compi schrieb:> Die beiden Konstanten 0x0F und 4 sind vom Typ int und damit 16 Bit. Der> Compiler macht alles rivhtig.
Tut er, aber nicht wirklich deshalb. Sondern weil er sowieso wie
vorgeschrieben in "int" rechnet.
Es geht aber nicht darum, ob er es richtig macht, sondern ob er gut
macht. Und er kann erkennen, dass beide Operationen ohne Veränderung des
Wertes auch in 8 Bits gerechnet werden dürfen. Und das tut er in Version
4.7.2 mit eingeschalteter Optimierung auch:
lds r24,x
mov r25,r24
swap r25
andi r25,lo8(15)
andi r24,lo8(15)
cpse r25,r24
A. K. schrieb:> Optimizer einschalten.
Nutzt nichts. Keine der Stufen 1, 2 ,3 und S verändern das Ergebnis. Im
Gegenteil der Code wird insgesamt länger.
WinAVR Studio 4
4.18
Die Version von Atmels Studio ist irrelevant. Die Version vom Compiler
ist wichtig, also vom gcc in WinAVR.
Wie schon gezeigt: avr-gcc 4.7.2 macht es besser.
Tja, dann probier mal ein aktuelles Studio. Also das nicht unbedingt
allseits geliebte Monster, in dem Atmel den Compiler mitliefert. Der ist
m.W. neuer.
A. K. schrieb:> Tja, dann probier mal ein aktuelles Studio. Also das nicht unbedingt> allseits geliebte Monster, in dem Atmel den Compiler mitliefert. Der ist> m.W. neuer.
Diesen Vorschlag habe ich befürchtet :)
bringt dir überhaupt nichts. Noch nicht mal theoretisch.
Zum einen ist das Ergebnis der & Verknüpfung formal immer noch ein int.
Zum anderen ist der Datentyp auf der rechten Seite eines >> vollkommen
irrelevent.
Wenn schon, dann musst du dafür sorgen, das beim Vergleich ein unsigned
char mit einem unsigned char verglichen wird
Karl Heinz schrieb:> (unsigned char) (checksum & 0x0F)
|
_______________________________|
Das ist auch ein int. Wenn schon
unsigned char) (checksum & (unsigned char) 0x0F)
oder? schrieb:> Das ist auch ein int. Wenn schon> unsigned char) (checksum & (unsigned char) 0x0F)
Was jedoch absolut nichts dran ändert, dass dies de jure als "int"
gerechnet werden muss. So ist C nun einmal definiert.
Nur kann ein Compiler clever genug sein, um festzustellen, ob das
überhaupt nötig ist. Und daran ändern die Casts nichts. Sehr wohl aber
die Version des Compilers.
Es ist vollkommen korrekt und notwendig daß der compiler bei dem hier:
if ((checksum & 0x0F) == (checksum >> 4))
einen 16 bit vergleich durchführt. Da gibts nichts wegzuoptimieren, Du
willst explizit wissen ob auf der rechten Seite nach dem Shift **alle**
verbleibenden 12 bits identisch sind mit dem was auf der linken Seite
rauskommt. Du willst explizit 12 Bits verglichen haben, wie soll er das
mit nur einem Byte hinbekommen?
Der Compiler setzt das genauso um wie Du es angefordert hast. Da steht
nirgendwo was daß er nur das low-byte betrachten soll.
Wenn Dich nur das low-Byte von checksum interessisrt dann caste checksum
vorher nach uint8_t und mach **damit** Deinen Vergleich.
Also zum Beispiel so:
if (((uint8_t)checksum & 0x0F) == ((uint8_t)checksum >> 4))
Bernd K. schrieb:> Da gibts nichts wegzuoptimieren,
Doch, weil aus der ursprüngliche Frage ersichtlich ist, dass "checksum"
uint8_t sein muss. Weshalb deine Casts absolut nichts bringen.
A. K. schrieb:> Bernd K. schrieb:>> Da gibts nichts wegzuoptimieren,>> Doch, weil aus der ursprüngliche Frage ersichtlich ist, dass "checksum"> uint8_t sein muss. Weshalb deine Casts absolut nichts bringen.
Ich bin mir nicht sicher daß das aus der Frage ersichtlich ist.
Wenn checksum uint8_t ist dann führt er den Vergleich nur mit 8 bit
durch wie gewünscht und sinnvoll. Zumindest gcc macht das (avr-gcc so
wie er momentan von Atmel vertrieben wird und wahrscheinlich auch ältere
oder neuere oder anderweitig andere Versionen).
Bernd K. schrieb:> Ich bin mir nicht sicher daß das aus der Frage ersichtlich ist.
Ich schon. Es ist nämlich nur ein einziger Load-Befehl im Code.
> Wenn checksum uint8_t ist dann führt er den Vergleich nur mit 8 bit> durch wie gewünscht und sinnvoll. Zumindest gcc macht das (avr-gcc so> wie er momentan von Atmel vertrieben wird und wahrscheinlich auch ältere> oder neuere oder anderweitig andere Versionen).
So weit waren wir gestern auch schon. Nur hat er eben einen steinalten
GCC 4.3.3 im Einsatz. Der scheint sich anders zu verhalten als neuere
Versionen.
ich muss mich korrigieren. Da scheint wirklich was dran zu sein:
uint16_t checksum = P_DATA_0;
if ((checksum & 0x0F) == ((uint8_t)checksum >> 4)) {
P_DATA_1 = 42;
}
vs
uint8_t checksum = P_DATA_0;
if ((checksum & 0x0F) == ((uint8_t)checksum >> 4)) {
P_DATA_1 = 42;
}
ersteres hab ich zuerst getestet, daher mein vorheriges Posting, das
erzeugt 8 bit code, letzteres jedoch nicht nicht, egal was und wo ich
caste. Das muss ein Bug im Compiler sein.
Bernd K. schrieb:> Das muss ein Bug im Compiler sein.
Fehlende Optimierung ist normalerweise nur dann wirklich ein Bug, wenn
es in einer Vorversion schon mal besser war. Ansonsten ist das eher als
Wunsch der Anwender zu sehen.
A. K. schrieb:> Bernd K. schrieb:>> Das muss ein Bug im Compiler sein.>> Fehlende Optimierung ist normalerweise nur dann wirklich ein Bug, wenn> es in einer Vorversion schon mal besser war.
Siehe mein obiges Beispiel:
uint16_t checksum = P_DATA_0;
if ((checksum & 0x0F) == ((uint8_t)checksum >> 4)) {
P_DATA_1 = 42;
}
20e: 80 91 0c 01 lds r24, 0x010C
212: 98 2f mov r25, r24
214: 9f 70 andi r25, 0x0F ; 15
216: 82 95 swap r24
218: 8f 70 andi r24, 0x0F ; 15
21a: 98 13 cpse r25, r24
21c: 03 c0 rjmp .+6 ; 0x224 <main+0x1e>
21e: 8a e2 ldi r24, 0x2A ; 42
220: 80 93 0d 01 sts 0x010D, r24
vs:
uint8_t checksum = P_DATA_0;
if ((checksum & 0x0F) == ((uint8_t)checksum >> 4)) {
P_DATA_1 = 42;
}
20e: 80 91 0c 01 lds r24, 0x010C
212: 28 2f mov r18, r24
214: 2f 70 andi r18, 0x0F ; 15
216: 30 e0 ldi r19, 0x00 ; 0
218: 82 95 swap r24
21a: 8f 70 andi r24, 0x0F ; 15
21c: 90 e0 ldi r25, 0x00 ; 0
21e: 28 17 cp r18, r24
220: 39 07 cpc r19, r25
222: 19 f4 brne .+6 ; 0x22a <main+0x24>
224: 8a e2 ldi r24, 0x2A ; 42
226: 80 93 0d 01 sts 0x010D, r24
also scheint er schon optimieren zu können wenn er den expliziten cast
sieht, also ist dieser optimierungscode irgendwo vorhanden, jedoch im
zweiten Beispiel scheint er zuerst den (nun unnötigen) cast
wegzuoptimieren und danach vergisst er wieder daß er es nur mit 8 bit zu
tun hat.
Also ist die Optimierung prinzipiell vorhanden und offensichtlich auch
so gewollt, wird aber unter gewissen Umständen nicht angewendet. Also
ganz klar ein Bug.
Weinstein schrieb:> A. K. schrieb:>> Tja, dann probier mal ein aktuelles Studio. Also das nicht unbedingt>> allseits geliebte Monster, in dem Atmel den Compiler mitliefert. Der ist>> m.W. neuer.>> Diesen Vorschlag habe ich befürchtet :)
Es ist gar nicht so schwierig, in AVR Studio 4.18 einen aktuelleren
Compiler zu aktivieren:
1. avr-gcc-4.7.2-mingw32.zip von der Seite
http://sourceforge.net/projects/mobilechessboar/files/avr-gcc%20snapshots%20%28Win32%29/
herunterladen
2. Die Zip-Datei direkt unter C:\ entpacken, so dass ein Verzeichnis
C:\avr-gcc-4.7.2-mingw32 entsteht.
3. Umgebungsvariable PATH anpassen auf C:\avr-gcc-4.7.2-mingw32\bin
(d.h. alten Pfad auf C:\Programme\WinAVR-20100110\bin
überschreiben).
4. Verzeichnis utils aus dem alten Ver5zeichnis WinAVR-20100110
(i.a. unter C:\Programme) nach C:\avr-gcc-4.7.2-mingw32
kopieren
5. Umgebungsvariable PATH anpassen/erweitern auf
C:\avr-gcc-4.7.2-mingw32\utils\bin (d.h. alten Pfad überschreiben)
6. Registry bearbeiten:
ändern auf:
C:\avr-gcc-4.7.2-mingw32\WinAVR-20100110-uninstall.exe
Dieses Uninstall-Programm existiert zwar gar nicht, aber AVR Studio hat
die seltsame Eigenart, dass es den Pfad auf das Uninstall-Programm
auswertet, um den AVR-gcc zu finden. Krank, aber funktioniert perfekt.
AVR Studio 4.18 starten, ein Projekt laden und unter
Project -> Configuration Options -> Custom Options kontrollieren, ob
"Use WinAVR" auch angehakt ist und
avr-gcc auf C:\avr-gcc-4.7.2-mingw32\bin\avr-gcc.exe
make auf C:\avr-gcc-4.7.2-mingw32\utils\bin\make.exe
_automatisch_(!) eingestellt wurde.
7. Compilieren und über das kleinere Binary freuen
P.S.
Sollte das avr-size-Programm nicht mehr funktionieren, kann man das aus
dem alten WinAVR-20100110 Unterverzeichnis rüberkopieren.
P.P.S
Solltest Du einen anderen Pfad für avr-gcc-4.7.2-mingw32 verwenden, dann
darauf achten, dass im kompletten Pfad kein Leerzeichen vorkommt. Also
"C:\Program Files" ist suboptimal.
P.P.P.S
Ich selber habe mir eigene REG-Dateien zur Umschaltung gestrickt. So
kann ich zwischen avr-gcc-4.3.3, 4.7.2, 4.8.1, 4.9.2 (pre) mit einem
Doppelklick umschalten. Die Version 4.7.2 ist sehr stabil und kann ich
nur jedem empfehlen. 4.8.1 hat kleine Macken (u.a. falsche Warnings
bzgl. Interrupt-Vektoren). 4.9.2 ist noch keine endgültige, sondern eine
Vorab-Version.
Bernd K. schrieb:> Also ist die Optimierung prinzipiell vorhanden und offensichtlich auch> so gewollt, wird aber unter gewissen Umständen nicht angewendet. Also> ganz klar ein Bug.
... ein alter Hut und mindestens sein 2009 bekannt:
http://gcc.gnu.org/PR41076
Immer offen für Lösungsvorschläge :-)