Forum: Compiler & IDEs Bug in WinAVR oder wieso macht der Compiler so einen Müll ?


von Benedikt (Gast)


Angehängte Dateien:

Lesenswert?

Ich wollte eigentlich nur das Programm durch Verwenden der uart Routinen
als Inline beschleunigen, aber mit Inline funktioniert garnichts mehr.
Also habe ich mir den erzeugten Code angeschaut (Optimierung aus
Codegröße) und diesen Mist gefunden (ohne inline):

- Der Compiler liest der Stackpointer ein, Manipuliert den Wert und
schreibt ihn am Ende wieder korrekt zurück, obwohl der Stackpointer nie
benötigt wird.
- Der Compiler schaltet die Interrupts für die gesamte Funktion aus.
- Der Compiler legt lokalen Variablen in den RAM obwohl die Register
reichen würden.

Kann mir jemand erklären, warum der Compiler so einen Mist macht ?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Kann mir jemand erklären, warum der Compiler
> so einen Mist macht ?

Er macht zumindest weniger ,,Mist'' als du beim Interpretieren
seines Codes.

von kosmonaut pirx (Gast)


Lesenswert?

hallo,
du hast da meiner meinung nach sehr stark vereinfacht.

der stackpointer wird manipuliert, das ist fakt. aber der reihe nach.

also zuerst wird der SP in r28 und r29 geladen, und dieses dann mit
'sbiw r28,1' um einen dekrementiert (ist eine word-operation,
betrifft also auch r29)

danach wird mit 'in _tmp_reg_,__SREG__' das statusregister gerettet
in ein temporäres register. wozu? um danach den alten zustand wieder
herstellen zu können, denn mit dem nachfolgendem 'cli' wird das
I-Flag des status-registers SREG gelöscht. die irq's ausgeschaltet.

dann wird der stack high geschrieben 'out _SP_H_,r29'.

so, jetzt wird das SREG wieder hergestellt: out _SREG_,__tmp_reg__
das bedeutet auch, das, wenn vorher dem 'cli' die irq's an waren,
sind sie auch wieder an, und umgekehrt auch! warum erst nachher das
low-byte des stack geschrieben wird, weiß ich nicht.

bye kosmo

von Stefan K. (_sk_)


Lesenswert?

Kann mir jemand sagen, warum der Compiler die IR sperrt?
Ich sehe im Quellcode nichts, was ihn sazu veranlassen könnte.
Oder fehlt vom Quellcode noch etwas wichtiges?

Danke, Stefan

von kosmonaut pirx (Gast)


Lesenswert?

um die atomarität sicherzustellen, vermute ich. d.h. zum geschriebenen
r29 muss das vorhergehende r28 passen.

nötig sind zwei cycles für das schreiben des 16bit-SP. wäre vll dumm,
nach dem schreiben des ersten bytes von nem irq unterbrochen zu werden,
der dann möglicherweise 'böse' ist und irgendwas verändert. dann würde
das andere byte geschrieben, aber passt nicht mehr zum veränderten
wert.

'out' ist atomar 1 cycle. also ist es vollkommen legal, das cli schon
vor dem zweiten 'out' wieder rückgängig zu machen.

oder, jörg?

von A.K. (Gast)


Lesenswert?

Mein Exemplar von WinAVR erzeugt völlig anderen Code, sowohl mit also
auch ohne Optimierung. Den vollständigen Stack-Frame kriege ich nur
ohne Optimierung, dann aber wird rr28 auch zu Adressierung lokaler
Daten benutzt. Welche Version, welche Compiler-Flags?

Der Stack-Pointer wird nach rr28 kopiert, wo er als Basis zur
Adressierung lokaler Daten dient. Allerdings macht er das bei mir nur
wenn auch nötig.

"Kann mir jemand sagen, warum der Compiler die IR sperrt?"

Überleg mal was passiert, wenn mitten in der Manipulation des
Stack-Pointers ein Interrupt eintrifft.

"Optimierung aus"

Dann erwarte bitte auch keinen optimalen Code.

von A.K. (Gast)


Lesenswert?

"das cli schon vor dem zweiten 'out' wieder rückgängig zu machen."

Liegt an einer Eigenheit des AVR: Bei SEI ist dokumentiert, dass nach
dem Einschalten von Interrupts immer erst der nächste Befehl ausgeführt
wird (dieses Verhalten ist für den Sleep-Mode einigermassen wichtig).
Bei OUT SREG ist das auch so, nur nicht dokumentiert.

von Benedikt (Gast)


Lesenswert?

Anscheinend kommt der Compiler nicht mit dem selbst erstellen Array
zurecht, das ich in den externen RAM gelegt habe:
http://www.mikrocontroller.net/forum/read-2-417813.html#new

typedef unsigned char (*fram)[XSize];
const fram pixel = (fram)0x8000;

Jetzt habe ich die noinit section in den externen RAM verlagert und in
der noinit section das unsigned char [YSize][XSize] Array erstellt, und
der Code sieht normal aus. Jetzt kommt die Funktion komplett ohne
push/pop aus.

von Benedikt (Gast)


Lesenswert?

Nein, doch nicht. Ich hatte mich verschaut.
Auch mit einem normalen Array macht der Compiler diesen Mist.

von Benedikt (Gast)


Lesenswert?

Nur wenn ich die uart_getchar() Funktion als inline deklariere, wird der
 Code normal (sogar ohne push und pops). Dafür funktioniert das Programm
aber nichtmehr...

#define FT245_Data      _SFR_MEM8((unsigned)256*16)
#define RXF      (PIND&32)

_inline_ static unsigned char uart_getchar(void)
{  while (RXF);
  return FT245_Data;
}

von Benedikt (Gast)


Lesenswert?

Es war ein Timingproblem mit dem FT245, jetzt geht die Software mit der
uart_getchar Funktion auch als inline.

Aber auch hier bläht der Compiler den Code unnötig auf:
Aus

while (PIND&32);
return FT245_Data;

macht er machnmal
.L44:
sbic 48-0x20,5
rjmp .L44
lds r24,4096

aber manchmal auch
.L48
in r24,48-0x20
clr r25
movw r18,r24
andi r18,lo8(32)
andi r19,hi8(32)
sbrc r24,5
rjmp .L48
lds r24,4096

von Aufreger deluxe (Gast)


Lesenswert?

> Aber auch hier bläht der Compiler den Code unnötig auf

Es ist bekannt, dass WinAVR unnötig großen Code erzeugt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Es ist bekannt, dass WinAVR gar keinen Code erzeugt.

Es installiert nämlich nur die Tools.  WinAVR ist kein Compiler.

Es ist aber auch bekannt, dass Compiler manchmal Dinge machen,
die man als Mensch so nicht machen würde, dafür sind es Maschinen.
Zuweilen tun sie das sogar zum Positiven: dank ihrer stupiden
Vorgehensweise entdecken sie Optimierungen, für die wir als Mensch
ein Brett vorm Kopf haben.  Zuweilen coden sie natürlich auch
Dinge, von denen ein Mensch sofort offensichtlich sagt, dass man
dies oder jenes weglassen kann.

Es ist auch bekannt (nach dem Lesen dieses Threads), dass der Herr
Benedikt sichtlich Probleme hat, funktionierenden Code zu
verfassen und erst Recht welche, den generierten Code vernünftig
im ersten Anlauf zu interpretieren.  Ich bin mir bei obigem
Code-Schnipsel ziemlich sicher, dass der Compiler nicht erst
rr18/19 belegt, um dann trotzdem nur r24 auszuwerten.  Das lässt
sich aber nur im gesamten Kontext erkennen.  Außerdem dürfen sich
auch Compiler weiter entwickeln, und GCC 3.4.6 ist natürlich nicht
mehr der Stein der Weisen -- GCC 4.1.x generiert im Allgemeinen
für den AVR bereits deutlich kompakteren Code.

von Karl H. (kbuchegg)


Lesenswert?

Da möchte ich auch noch meinen Senf dazugeben:
Es ist auch bekannt, dass Compiler im Allgemeinen besseren
Assembler Code produzieren als ca. 95% aller Programmierer.
Mit anderen Worten: Um die meisten Compiler zu schlagen, muss
man schon ziemlich gut in Assembler sein. Jemand der nur ab
zu in Assembler programmiert hat gegen einen vernünftigen
Compiler so gut wie keine Chance.

von Peter D. (peda)


Lesenswert?

@Jörg

"... GCC 3.4.6 ist natürlich nicht mehr der Stein der Weisen -- GCC
4.1.x generiert im Allgemeinen für den AVR bereits deutlich kompakteren
Code."


Das neueste WINAVR installiert aber nur 3.4.6
Wie kann man denn auf 4.1.x updaten ?


Peter

von Aufreger deluxe (Gast)


Lesenswert?

> Es ist bekannt, dass WinAVR gar keinen Code erzeugt.
> Es installiert nämlich nur die Tools.  WinAVR ist kein
> Compiler.

WinAVR installiert auch keine Tools, dass mach der Installer.

von Hans (Gast)


Lesenswert?

ich liebe immer diese "bug reports"...

man beachte folgendes regelwerk:
1. der compiler hat immer recht (ausgenommen er kommt direkt aus dem
cvs.. dann hat er nur fast immer recht) vorallem wenn er aus der GCC
stammt
2. ein compiler der gut optimiert erzeigt immer code den man nicht
versteht
3. rtfm
4. rtfw
5. bugreports erst wenn die neuste version auch den bug hat (ich nehme
mal an dass es was neueres gibt wenn jörg schonmal meint 3.4.6
nichtmehr der stein der weisen sei...)
6. poste alle infos sonst bekommt man keine klaren bzw nützlichen
antworten

von JojoS (Gast)


Lesenswert?

'Das neueste WINAVR installiert aber nur 3.4.6
Wie kann man denn auf 4.1.x updaten ?'

das ist auf der Seite von P. Fleury beschrieben. Habe ich zufällig bei
unserer Bootloader Diskussion entdeckt, er hat seine letzte Version
bereits mit dem 4.1 übersetzt.

von Benedikt (Gast)


Lesenswert?

@Hans

Was brauchst du noch an Infos ?

Ich habe den Code der kompletten Funktion einschließlich aller
Funktionen die darin aufgerufen werden gepostet.

Und falls der Compiler recht hat, dann kann mir bestimmt jemand
erklären warum der Compiler diesen (aus seiner Sicht) sinnvollen Weg
eingeht.


Gibt es eigentlich demnächst wieder mal eine neue WinAVR Version ?

von A.K. (Gast)


Lesenswert?

Vollständigen Quellcode. Vollständig!! Also alles was fürs übersetzen
benötigt wird, inklusive Makefile usw. Idealerweise nicht in Form von
20MB Plattenabzug, sondern soweit reduziert, dass nur noch der
problematische Teil in Form von ein paar Zeilen übrig bleibt.

Welcher Compiler? Also beispielsweise welches WinAVR.

von Benedikt (Gast)


Angehängte Dateien:

Lesenswert?

Wenn es so einfach wäre, hätte ich das schon längst gemacht. Allerdings
darf ich den kompletten Code nicht veröffentlichen. Ich habe jetzt mal
alles weggelassen, außer den beiden Funktionen, das Problem existiert
weiterhin.

GCC Version: avr-gcc (GCC) 3.4.5
WinAVR Version vom 25.01.2006

von Peter D. (peda)


Lesenswert?

Hat vielleicht einer die gcc.exe V4.1.1 schon fertig ?


Peter

von A.K. (Gast)


Lesenswert?

Interessant.

Definiere mal uart_getchar() mit "unsigned" statt "unsigned char"
als Ergebnis. Macht in dieser Funktion Null Unterschied, weil's der
Compiler ohenehin so hält. Aber dafür bleibt der LCD-Routine der Frame
erspart.

von Benedikt (Gast)


Lesenswert?

Mit unsigned int oder nur unsigned wird der Code um 46Bytes kleiner und
der ganze Mist ist weg.
Aber warum ist das so ? Kann mir das jemand erklären ?

von Karl heinz B. (kbucheg)


Lesenswert?

Auch interessant:
Original Code mit -O3 compiliert: keine lokalen Variablen und
der Compiler inlined automatisch.

von Karl heinz B. (kbucheg)


Lesenswert?

> und der ganze Mist ist weg.

Ich nehme an, mit Mist meinst du die 1 lokale Variable die
nach der Optimierung übrig geblieben ist und deretwegen
im Funktionsprolog der Stackpointer korrigiert wird.

> Aber warum ist das so ? Kann mir das jemand erklären ?
Ist nicht ganz durchsichtig warum. Ich denke es könnte
daran liegen, dass automatisches inlinen bei einer Optimierungs-
stufe Os ausgeschaltet ist. Normalerweise macht inlinen den
Code größer und Os hat sich ja auf die Fahnen geheftet, möglichst
kleinen Code zu produzieren. In deinem Fall ist es aber umgekehrt,
da das inlinen Optimierungsmöglichkeiten eröffnet, die letztlich
zum Wegfall der 1nen übriggebliebenen lokalen Variablen eröffnet
und damit den ganze Stackpointer Kram unnötig macht. Man darf
halt nicht vergessen, dass Optimierungsstufen auf Heuristiken
beruhen. Es ist ja nicht so, dass der Compiler bei Os einmal
mit Auto-Inlining compiliert und einmal ohne und schaut was kleiner
ist.

Das erklärt aber nicht den Unterschied zwischen unsigned int und
unsigned char. Der ist mir auch noch ein Rätsel.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Angehängte Dateien:

Lesenswert?

> Das erklärt aber nicht den Unterschied zwischen unsigned int und
> unsigned char.

Vermutung: unsigned char wird nach integer promotion rules zuerst
zu `int' erweitert und erst von dort ggf. nach unsigned int.

Im Anhang übrigens der Code, der der GCC 4.1.0 daraus erzeugt
(mit den Werten aus dem Makefile, nur -g der Übersichtlichkeit
halber entfernt).

Sieht besser aus als 3.4.x, würde ich mal sagen.

von Roland Schmidt (Gast)


Lesenswert?


von Benedikt (Gast)


Lesenswert?

Reicht es aus, avrgcc411.zip in den WinAVR Ordner zu entpacken ?
Ich habe es mal gemacht, und es scheint zu funktionieren.
Es hat sich echt einiges geändert, die meisten Programme werden damit
etwas kleiner.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Es hat sich echt einiges geändert, die meisten Programme
> werden damit etwas kleiner.

Sach ich doch.  Im Durchschnitt ist die Optimierung beim AVR mit
dieser Version besser als mit den 3.x-ern.  Sicher findest du
allemal pathologische Fälle, wo der 3.x-er GCC besseren Code
erzeugt hat, aber in der realen Welt ist 4.1.x wirklich ein
Fortschritt (nachdem zuvor die 3er Versionen bezüglich des
AVR-Targets, das ja nicht gerade GCC-Mainstream ist, graduell
leicht schlechter geworden waren).

von Peter D. (peda)


Lesenswert?

Ich hab die 4.1.1 ausprobiert.

Bei vielen Projekten kriegt man tatsächlich eine ganz leichte
Reduzierung (1..2%).

Nur mein Jumbo-LED Beispiel wird dafür gleich 7% größer.


Allerdings erhalte ich nun immer folgende 2 Warnungen:

C:\DOKUME~1\danni\LOKALE~1\Temp/cc6naaaa.s:69: Warning: expression
dangerous with linker stubs
C:\DOKUME~1\danni\LOKALE~1\Temp/cc6naaaa.s:70: Warning: expression
dangerous with linker stubs


Da scheint also irgendwas noch nicht ganz astrein zu sein, weshalb der
WINAVR immer noch die alte 3.4.6. Version benutzt.
So schlimm sind ja die 1..2% mehr Code nun auch nicht.


Peter

von Benedikt (Gast)


Lesenswert?

Einige der Warnungen sind ganz sinnvoll. So erzeugt 4.1.1 z.B. Warnungen
wenn man pointer mit unterschiedlichen Vorzeichen übergibt.

Es gibt nichts schlimmeres als ein Compiler bei dem standardmäßig die
Warnungen fast alle abgeschaltet sind. Eine Klammer hinter einem
Funktionsaufruf vergessen, und man wundert sich warum nichts geht.
WinAVR gibt bei sowas (zum Glück) eine Warnung aus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Allerdings erhalte ich nun immer folgende 2 Warnungen:

> Warning: expression dangerous with linker stubs

Das wäre mal interessant, dass du dir die Stelle in den
Assemblerdateien dazu ansiehst.  Dazu musst du über alle
Einzelschritte gehen, also statt mit -c von .c über eine temporäre
Assemblerdatei nach .o zuerst mit -S von .c nach .s (generierter
Assemblercode des Compilers) gehen und dann auf diese Datei nochmal
den avr-as ansetzen.

Das hat übrigens überhaupt nichts mit GCC 4.1.x zu tun, sondern das
kommt von den linker stubs, die für den Support der ATmega256x
eingebaut worden sind.  Der Patch ist derzeit noch experimentell,
AtmanAVR hat ihn aber bereits eingebaut.

Diskussionen darüber am besten auf avr-gcc-list (at nongnu.org)
führen, dort liest Björn Haase, der Autor des Patches, mit.

> Da scheint also irgendwas noch nicht ganz astrein zu sein, weshalb
> der WINAVR immer noch die alte 3.4.6. Version benutzt.

Quatsch mit Soße.

Beim letzten WinAVR war der GCC für Eric Weddington nicht zu schaffen,
beim nächsten wird 4.1.1 dabei sein -- und zwar mit dem
experimentellen ATmega256x-Patch.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

@Jörg,

also die Warnungen kommen von diesen beiden Zeilen (48,49) in main.s:

        subi r30,lo8(-(gs(.L12)))
        sbci r31,hi8(-(gs(.L12)))


Warscheinlich erzeugt jede switch-Anweisung mit Sprungtabelle diese
Warnungen.

Da ich ja eh nie vorhabe, >=Mega128 einzusetzen, dürften die Warnungen
also belanglos sein.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

gs ist ein Operator, den Björn dafür neu eingeführt hat...
Ich kann mir vorstellen, was die Warnung bedeutet: bei dieser
Operation könnte es passieren, dass man eine 128-KB-Grenze
überschreitet.  In diesem Falle wäre man natürlich aufgeschmissen...

Wie geschrieben, wenn du das diskutieren willst, ist die
avr-gcc-list das Forum der Wahl.

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.