AVR-GCC nutzt 16 Bit für uint8_t Variable

OP #834967
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
#834978
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.
#834996
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.
#835028
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. ;-)
OP #835037
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
#835057
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?
#835071
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.
#835113
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.
#835133
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
#835156
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. ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren