www.mikrocontroller.net

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


Autor: Stefan M. (quincy)
Datum:

Bewertung
0 lesenswert
nicht 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:
void onew_skip_rom ( void )
{
   uint8_t i;

   for (i=0;i<8;i++)
   {
      if ( i & 0x02 )
      {  ...
         _delay_us(ONEW_A); }
      else
      {  ...
         _delay_us(ONEW_D); }
   }
   onew_status++;
}

Daraus wird dann mit AVR Studio 4.13 SP2 und WinAVR 20071221 bei 
Optimierung Os:
ldi  r18, 0x00     |\ hier werden die 2 bytes fuer i auf 0 gesetzt
ldi  r19, 0x00     |/
sbrs  r18, 1               -> Bit-Abfrage
rjmp  .+4
ldi  r24, 0x04     | register fuer delay 1 laden
rjmp  .+2
ldi  r24, 0x06     | register fuer delay 2 laden
dec  r24                   |\ delay loop
brne  .-4                  |/
subi  r18, 0xFF     |\ i++
sbci  r19, 0xFF     |/
cpi  r18, 0x08             |\ i == 8 ?
cpc  r19, r1               |/
brne  .-24
lds  r24, 0x0067
subi  r24, 0xFF
sts  0x0067, r24
ret


Beste Grüße
Stefan

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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 )

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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. ;-)

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan M. (quincy)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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. ;-)

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.