mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik bug in optimizer von avr-gcc?


Autor: hans dieter (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
ich arbeite gerade an einem teil der firmware für einen roboter. Nun
bin ich gerade auf eine Stelle gestoßen, die mir zu denken gibt.
Folgendes Problem: Ich habe in der main.c ein paar Variablen um eine
FSM zu beschreiben.
Dazu existiert noch eine Init-Funktion, die Anfangswerte setzt:
(siehe main.c im Anhang)
//...
unsigned char stateWalkNext;
//...
int main(void)
{
  initSMs();
//...
  stateWalkNext = STWALK_ROTR;
}
//...
void initSMs(void)
{
  stateWalkNext = STWALK_FORW;
}

So nun zum Problem; Wenn ich die Optimierungen einschalte - egal welche
Stufe - dann funktioniert die Initialisierung zwar noch, jedoch wird
dann die Zuweisung in der main-Funktion wegoptimiert.
Erst wenn ich die Variable volatile mache - was eigentlich nicht nötig
sein dürfte - taucht die Zuweisung bei aktivierter optimierung wieder
auf.
Habe ich da einen Denk-Fehler oder ist das eine Bug im Optimierer von
AVR-GCC?

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daumenregel: 99,9% aller hier als Compiler-Bugs vermuteten Probleme sind
nicht dem Compiler zuzuschreiben, sondern beruhen auf Miss/Unverständnis
seitens des Anwenders. Und darauf, dass der GNU-Compiler aufgrund seiner
Herkunft weit mehr Optimierungsmöglichkeiten erkennt, als bei
Mikrocontrollerncompilern sonst üblich (und gewünscht).

Hier: Einige bis alle Funktionsaufrufe in main() werden inlined. d.h.
nicht aufgerufen sondern deren Code in main direkt erzeugt. Das erlaubt
dem Compiler weitere Optimierungen, Darunter ist die Erkenntnis, dass
bei zweifacher Zuweisung an die gleiche Variable, ohne Funktionsaufruf
dazwischen, die erste überflüssig ist. Erst "volatile" sagt teilt ihm
mit, davon Abstand zu nehmen.

Autor: hans dieter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
was ich noch vergessen habe (bevor fragen kommen)
ich nutze die 3.4.3 (20050214) release von avr-gcc

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andere Perspektive: Interrupts existieren für einen Compiler nicht. Dass
eine Funktion mitten drin durch einen Interrupt unterbrochen werden kann
ist ihm egal, er nimmt darauf keine Rücksicht. Daher muss der
Programmierer jedwede Datem, die sowohl in Hauptprogramm als auch in
Interruptroutinen verwerdet werden, mit "volatile" verzieren.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, volatile ist schon ein schwieriges Thema, ich hab mich noch immer
nicht richtig daran gewöhnen können.

Der Keil C51 war stillschweigend davon ausgegangen, daß man sich was
dabei gedacht hat, eine Variable global anzulegen, da mußte ich nie
volatile verwenden.

Auch kann man leider nicht nach volatile casten. D.h. auch der
Interrupt kann eine solche Variable nicht im Register behalten, um Code
zu sparen, obwohl ihm die ja keiner unterm Hintern wegziehen kann.

Da kann dann ein Trick besser optimierten Code erzeugen:
Die Variable wird nicht als volatile deklariert (Interrupt kann nun
optimieren) und dafür in der Mainloop immer über eine Funktion gelesen.



Peter

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"und dafür in der Mainloop immer über eine Funktion gelesen"

Dann aber bitte diese Funktion so deklarieren, dass sie keinesfalls
inlined wird, oder inlining ganz abschalten, sonst wird das auch wieder
abhängig vom Wasserstand.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Keil C51 war stillschweigend davon ausgegangen, daß man sich
> was dabei gedacht hat, eine Variable global anzulegen,

Das tut auch der GCC. Aber er weiß ja nicht, daß man bei einer
speziellen Variable daran gedacht hat, diese auch in Interrupts zu
verwenden.

> da mußte ich nie volatile verwenden.

> Auch kann man leider nicht nach volatile casten. D.h. auch der
> Interrupt kann eine solche Variable nicht im Register behalten, um
> Code zu sparen, obwohl ihm die ja keiner unterm Hintern wegziehen
> kann.

Das kann man ja einfachst selbst machen. Kopiere sie am Anfang der
Interrupt-Routine in eine lokale Variable und am Ende wieder zurück.

Übrigens: wenn der Keil das so macht, wie du oben sagst, dann würde er
so eine Optimierung ja grundsätzlich niemals machen können.

> Da kann dann ein Trick besser optimierten Code erzeugen:
> Die Variable wird nicht als volatile deklariert (Interrupt kann
> nun optimieren) und dafür in der Mainloop immer über eine Funktion
> gelesen.

Das ist aber nicht zuverlässig.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Der Keil C51 war stillschweigend davon ausgegangen, daß man sich was
dabei gedacht hat, eine Variable global anzulegen"

Anders ausgedrückt: Der Optimizier ist nicht so weit entwickelt ;-).
Und diese Transparenz ist bei Controllern ja durchaus erwünscht. Der
GCC entstammt aber gänzlich anderen Sphären, die Verwendung für
Controller ist da nur ein Nebeneffekt.

"volatile" gibt es erst seit ANSI-C, K&R-C kennt das nicht. Die
Fortschritte der Compiler-Technik in den 80ern machten das nötig. Bei
älteren Compilern hörte Optimierung oft schon an den Statement-Grenzen
auf, da war sowas überflüssig (dafür war "register" mitnichten
überflüssig).

Autor: Unbekannter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@hans:

Der Compiler hat schon recht.

Denn wenn Deine Init-Funktion nur am Anfang von main aufgerufen wird,
und danach nicht wieder, kann Deine Init-Funktion die globale Variable
ja nicht weiter ändern, also kann der Compiler in main mit dieser
Variablen machen, was er will.

Wie hier schon angemerkt: Wenn die globale Variable von einem Interupt
verändert werden kann, musst Du das mit volatile dem Compiler sagen.
Für alle anderen Fälle, in denen Du kein Interrupt verwendest, brauchst
Du das nicht und kannst Dich darauf verlassen, dass der Compiler schon
weiß, was er da macht.

Um in der Interruptroutine die volatile-Veriable nicht ständig lesen zu
müssen (bei komplizierten Berechnungen) kannst Du folgendes machen:

  volatile int globale_variable;

  void interrupt_funktion()
  {
    int kopie = globale_variable;

    // komplizierte Berechnungen mit 'kopie'
    // Noch mehr Berechnungen mit 'kopie'

    globale_variable = neuer_wert;
  }


  void initailisierung()
  {
    globale_variable = irgendetwas;
  }


  int main()
  {
    initialisierung();

    for(;;)
    {
       int andere_kopie = globale_variable;

       // mache irgendetwas mit 'andere_kopie'
       // noch mehr Berechnungen mit 'andere_kopie'

       globale_variable = ganz_neuer_wert;
    }

    return 0;
  }


Einfach dem Compiler vertrauen. Der macht das schon. Und nur für
Variablen die von interrupt-Funktionen gelesen oder geschrieben werden,
volatile verwenden. Für alle anderen kein volatile benutzen.

Autor: Christian Zietz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im vorliegenden Fall, optimiert der Compiler die Zuweisung wohl weg,
weil sie die letzte Anweisung von main ist. Da der Compiler ja nicht
wissen kann, dass die Variable von später stattfindenden Interrups noch
benötigt wird, sieht er die Zuweisung als unnötig an. Das Programm ist
für ihn ja mit dem Ende von main beendet und der neue Wert der Variable
wird somit nicht mehr benötigt.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was die Optimierung angeht, ist main() eine ganz normale Funktion. Dass
ein Programm am Ende von main() endet, interessiert ihn nicht. Schon
weil es nicht stimmt [exit() Code und sonstige Aufräumaktivitäten].

Freilich wird hier main() überhaupt nicht beendet und das ist grad der
Witz daran. Das Hauptprogramm besteht jenseits der Initialisierung nur
aus der for-Schleife, und da diese Schleife nie endet und niemanden
aufruft, ist für den Compiler niemand in Sicht, der etwas mit globalen
Variablen anfangen kann.

Ändert man das Programm so, dass aus Sicht des Compilers main() beendet
werden kann, dann steht die Zuweisung wieder drin.

Korrektur: Mit inlines hat das hier nichts zu tun.

Autor: Christian Zietz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt, ich habe doch glatt die for-Schleife übersehen. A.Ks Erklärung
ist richtig.

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.