Forum: Compiler & IDEs C: "unsigned char"-Vergleich immer mit 16 Bit?


von Weinstein (Gast)


Lesenswert?

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
9
+00000113:   E090        LDI       R25,0x00       Load immediate
10
+00000114:   1728        CP        R18,R24        Compare
11
+00000115:   0739        CPC       R19,R25        Compare with carry
12
+00000116:   F421        BRNE      PC+0x05        Branch if not equal

von Compi (Gast)


Lesenswert?

Die beiden Konstanten 0x0F und 4 sind vom Typ int und damit 16 Bit. Der 
Compiler macht alles rivhtig.

von (prx) A. K. (prx)


Lesenswert?

Optimizer einschalten.

von (prx) A. K. (prx)


Lesenswert?

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

von Weinstein (Gast)


Lesenswert?

D. h. auch mit einem 'cast' (siehe unten) komme ich nicht weiter?

Was kann ich stattdessen machen?

1
252:if ((checksum & (unsigned char)0x0F) == (checksum >> (unsigned char)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
9
+00000113:   E090        LDI       R25,0x00       Load immediate
10
+00000114:   1728        CP        R18,R24        Compare
11
+00000115:   0739        CPC       R19,R25        Compare with carry
12
+00000116:   F421        BRNE      PC+0x05        Branch if not equal

von Weinstein (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Weinstein (Gast)


Lesenswert?

Tja, da habe ich die Version 4.3.3

von (prx) A. K. (prx)


Lesenswert?

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.

von Weinstein (Gast)


Lesenswert?

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 :)

von Moritz A. (moritz_a)


Lesenswert?

Wieso gleich das "Monster", 
http://www.atmel.com/tools/ATMELAVRTOOLCHAINFORWINDOWS.aspx sollte es 
doch auch tun.

von Karl H. (kbuchegg)


Lesenswert?

Der Vollständigkeit halber

Das hier
1
  if ((checksum & (unsigned char)0x0F) == (checksum >> (unsigned char)4))
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
1
  if( (unsigned char) (checksum & 0x0F) == (unsigned char) (checksum >> 4) )

ob der Compiler jetzt die Absicht dahinter erkennt oder nicht, hab ich 
nicht ausprobiert. Kannst du ja machen.

von Weinstein (Gast)


Lesenswert?

1
if( (unsigned char) (checksum & 0x0F) == (unsigned char) (checksum >> 4) )

Führt nicht zum Erfolg.


Liegen die Zwischenergebnisse in Variablen, dann ist der Zielcode 
kürzer.

1
253:            i = checksum & 0x0F;
2
+0000010B:   9180006B    LDS       R24,0x006B     Load direct from data space
3
254:            checksum = checksum >> 4;
4
+0000010D:   2F98        MOV       R25,R24        Copy register
5
+0000010E:   9592        SWAP      R25            Swap nibbles
6
+0000010F:   709F        ANDI      R25,0x0F       Logical AND with immediate
7
+00000110:   9390006B    STS       0x006B,R25     Store direct to data space
8
257:            if (i == checksum)
9
+00000112:   708F        ANDI      R24,0x0F       Logical AND with immediate
10
+00000113:   1789        CP        R24,R25        Compare
11
+00000114:   F421        BRNE      PC+0x05        Branch if not equal

von oder? (Gast)


Lesenswert?

Karl Heinz schrieb:
> (unsigned char) (checksum & 0x0F)
                               |
_______________________________|

Das ist auch ein int. Wenn schon
unsigned char) (checksum & (unsigned char) 0x0F)

von (prx) A. K. (prx)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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))

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

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).

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

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.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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:
1
   HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinAVR\UninstallString

   ä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.

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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 :-)

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.