Forum: Compiler & IDEs AVR-GCC nutzt 16 Bit für uint8_t Variable


von Stefan M. (quincy)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

@ Andreas Kaiser:
i ist hier aber kein int sondern ein unsigned char.

@ Stefan M.:
Hast du auch folgendes probiert:
if ( i & (uint8_t)0x02 )

von Andreas K. (a-k)


Lesenswert?

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.

von Johannes M. (johnny-m)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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

von Stefan M. (quincy)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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?

von Stefan E. (sternst)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.