Forum: Mikrocontroller und Digitale Elektronik Seltsames Problem mit Compilerübersetzung


von Marcel_74 (Gast)


Lesenswert?

Hallo!

Ich habe ein (mittlerweile gelöstes) Problem mit dem C-Compiler erlebt. 
Aber vielleicht bin ich auch einfach nur zu dämlich ;-)

Ich habe folgendes am Ende der Main-Funktion stehen:

endlos: if(whattodo == 1)  {
   updatetime();
   updatedisplay();
   whattodo = 0;

  };

goto endlos;

Bevor jetzt alle schreien: Das Problem, welches ich gleich beschreibe, 
"funktioniert" auch mit while(1) { ... }.

Dieser Code soll in der Endlosschleife ausgeführt werden, damit auf 
"whattodo" reagiert werden kann, whattodo wird in einer ISR gesetzt.

Der Compiler macht da folgendes raus:

@000002B9: endlos
49:       endlos: if(whattodo == 1)  {
+000002B9:   91800078    LDS     R24,0x0078       Load direct from data 
space
+000002BB:   91900079    LDS     R25,0x0079       Load direct from data 
space
+000002BD:   9701        SBIW    R24,0x01         Subtract immediate 
from word
+000002BE:   F449        BRNE    PC+0x0A          Branch if not equal
50:          updatetime();
+000002BF:   940E01DB    CALL    0x000001DB       Call subroutine
51:          updatedisplay();
+000002C1:   940E0272    CALL    0x00000272       Call subroutine
52:          whattodo = 0;
+000002C3:   92100079    STS     0x0079,R1        Store direct to data 
space
+000002C5:   92100078    STS     0x0078,R1        Store direct to data 
space
+000002C7:   CFF1        RJMP    PC-0x000E        Relative jump
+000002C8:   CFFF        RJMP    PC-0x0000        Relative jump

Ich bin fast vom Glauben abgefallen. Ich suche tagelang (!!) den Fehler, 
und die if-Bedingung (BRNE) verzweigt einfach hinter mein GOTO! Der 
Compiler hat schon irgendwie gemerkt, dass was faul ist und hat 
vorsichtshalber mal noch eine eigene Endlosschleife eingebaut. Abhilfe 
hat geschaffen, dass ich zwischen dem Blockende des if-Befehls und dem 
Goto noch eine Codezeile geschrieben habe (z.B. Variablenzuweisung). 
Dann macht der Compiler z.B. sowas:

@000002B9: endlos
49:       endlos: if(whattodo == 1)  {
+000002B9:   91800078    LDS     R24,0x0078       Load direct from data 
space
+000002BB:   91900079    LDS     R25,0x0079       Load direct from data 
space
+000002BD:   9701        SBIW    R24,0x01         Subtract immediate 
from word
+000002BE:   F441        BRNE    PC+0x09          Branch if not equal
50:          updatetime();
+000002BF:   940E01DB    CALL    0x000001DB       Call subroutine
51:          updatedisplay();
+000002C1:   940E0272    CALL    0x00000272       Call subroutine
52:          whattodo = 0;
+000002C3:   92100079    STS     0x0079,R1        Store direct to data 
space
+000002C5:   92100078    STS     0x0078,R1        Store direct to data 
space
56:       ampm=0;
+000002C7:   92100077    STS     0x0077,R1        Store direct to data 
space
+000002C9:   92100076    STS     0x0076,R1        Store direct to data 
space
+000002CB:   CFED        RJMP    PC-0x0012


Also ist alles in Ordnung. Ich kann mir nur nicht erklären, wie es zu so 
einem Verhalten kommt. Kann mir jemand ein Tipp geben?

Viele Grüße
Marcel

PS: Ich benutze das neuste AVRStudio und den neuesten WinAVR. Ich habe 
für einen ATMega32 mit 8MHz compiliert.

von Michael G. (linuxgeek) Benutzerseite


Lesenswert?

Marcel_74 wrote:

> endlos: if(whattodo == 1)  {
>    updatetime();
>    updatedisplay();
>    whattodo = 0;
>
>   };
>
> goto endlos;

Wenn man solchen Code schreibt sollte ein vernuenftiger Compiler das 
sowieso nicht mehr uebersetzen.

von (prx) A. K. (prx)


Lesenswert?

Um im Bild zu bleiben: Statt vom Glauben abzufallen solltest du lieber 
die K&R Bibel nochmal lesen, und zwar im Abschnitt zu "volatile".

Der Compiler stellt nämlich völlig zurecht fest, dass dieser Code 
spätestens nach einer Runde mit "whattodo=1" auf eine leere Totschleife 
rausläuft, und optimiert die nun auf maximale Performance. Es kann nicht 
damit rechnen, dass da vielleicht ein Interrupt die Variable wieder 
setzt. Nicht ohne "volatile".

von (prx) A. K. (prx)


Lesenswert?

Einen Vorwurf kann man dem Compiler allerdings machen: Dass er den 
ersten der beiden abschliessende Sprünge drin gelassen hat. Der ist 
nämlich komplett überflüssig.

von Marcel_74 (Gast)


Lesenswert?

Hallo!

@Michael G:

1. Mein Code sah auch mal anders aus. Was Du hier siehst ist die 127. 
Änderung, da ich mir das auftretende Problem nicht erklären konnte.
2. BTW: Was bitte ist an einer simplen if-Abfrage nicht mehr 
"übersetzungswürdig"? Im Übrigen führen gerade beim Programmieren viele 
Wege zum Ziel. Es mag elegantere Wege geben und weniger elegante, aber 
das ändert nichts an der Tatsache, dass das Programm dennoch 
funktioniert und auch das macht was man möchte. Andere Haltungen sind 
eher arrogant als nützlich.
3. Du hast mir kein Stück weitergeholfen.


@A.K.
Vielen Dank für den Hinweis. Ich hatte schon die Vermutung, dass der 
Compiler hier etwas optimiert, was sich meiner Kenntnis entzieht. Ich 
bin halt "nur" Hobbyprogrammierer.
Was ich allerdings nicht verstehe, ist die Tatsache, dass "richtig" 
compiliert wird, wenn noch Code zwischen dem Ende des if-Blocks und dem 
Goto steht.


Schade finde ich an diesem Mikrocontroller-Forum allerdings immer noch 
(im Gegensatz zu manch anderen Foren), dass es immer Leute gibt, die 
schlaue Sprüche machen und sich für die Helden des Programmierens halten 
und den Fragestellen echt keinen Millimeter weiterhelfen. Zum Glück gibt 
es Leute wie A.K., die kurz und bündig helfen! Danke!

Viele Grüße
Marcel

von Ralf S. (spacedog) Benutzerseite


Lesenswert?

> Was ich allerdings nicht verstehe, ist die Tatsache, dass "richtig"
> compiliert wird, wenn noch Code zwischen dem Ende des if-Blocks und dem
> Goto steht.

Na A. K. hats doch schon gesagt: Informier dich mal über "volatile".

Der Compiler weiss nicht, dass dein whattodo in einer isr geändert wird 
und nimmt deshalb an, dass der Code im if-Block nur einmal ausgeführt 
werden kann. Mit volatile, kannst du dem Compiler sagen, dass die 
Variable ihren Wert "unvorhersehbar" ändern kann. Dann wird er solche 
Optimisierungen unterlassen.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Warscheinlich weil man goto nicht ohne Not einsetzen sollte, wegen des 
Stils ;P
1
while(1) {
2
  if(whattodo == 1)  {
3
   updatetime();
4
   updatedisplay();
5
   whattodo = 0;
6
  };
7
}
tut das von dir gewünschte :)

Wenn du Anweisungen einfügst, dann kommt der Compiler ggf zu anderen 
Annahmen bzgl der Relevanz deines Codes.

Vieleicht wäre es hilfreich die Variablendefinition mit anzugeben. Und 
ein Beispiel geht/geht nicht, das man sieht WELCHE anweisung genau 
eingefügt wurden.

von (prx) A. K. (prx)


Lesenswert?

Marcel_74 wrote:

> Was ich allerdings nicht verstehe, ist die Tatsache, dass "richtig"
> compiliert wird, wenn noch Code zwischen dem Ende des if-Blocks und dem
> Goto steht.

Und wenn er dann mal das tut was du von ihm willst bist du auch wieder 
nicht glücklich. ;-)

Im ersten Fall steht zwischen dem =0 und der Abfrage davon kein Code der 
irgendwas verändern könnte. Im zweiten Fall befürchtet er möglicherweise 
eine Identität der beiden Variablen (aliasing) - was zwar dank des 
gleichen Wertes egal wäre, aber trotzdem den Optimierungstest aushebelt.

von Marcel_74 (Gast)


Lesenswert?

Hallo!

Ja, ich habe mich mittlerweile über volatile informatiert. Gute Sache 
sowas ;-) Mir wäre es ja fast lieber, wenn der Compiler nicht so stark 
mitdenkt. Im Grunde habe ich mir ja etwas (nicht so ganz abwegiges) 
gedacht.

Und um die leidliche Programmierstildiskussion nicht allzu ausufern zu 
lassen: In meinem ersten Beitrag habe ich doch schon beschrieben, dass 
die goto-Variante schon eine Verzweiflungstat war. Tatsächlich wurde 
vorher mit while(1) gearbeitet. Im Übrigen war die ganze Sache 
ursprünglich noch komplizierter: Ich teste einzelne Bits in einem Byte, 
ob sie gesetzt sind oder nicht. Das hat den Hintergrund, dass meine ISR 
verschiedene "Kommandos" an das Hauptprogramm absetzen kann, um es zu 
verschiedenen Aktionen zu bewegen. Und DAS ist imho guter Stil ;-)

Viele Grüße und herzlichen Dank für die hilfreichen Kommentare
Marcel

von (prx) A. K. (prx)


Lesenswert?

Marcel_74 wrote:

> Ja, ich habe mich mittlerweile über volatile informatiert. Gute Sache
> sowas ;-) Mir wäre es ja fast lieber, wenn der Compiler nicht so stark
> mitdenkt.

Anfangs gab es das in C-Compilern auch nicht. War nicht nötig. Bis weit 
in der 80er Jahre haben viele C-Compiler weitgehend das gemacht, was in 
der Zeile drin stand. Erst als die Compiler anfingen, auch über 
Statement-Grenzen hinweg zu optimieren, statt Registeroptimierung dem 
Programmierer zu überlassen, wurde das wichtig (hast du dich mal 
gefragt, warum es in C ein Keyword "register" gibt?).

Manch andere Compiler, vor allen solche die auf Microcontroller 
spezialisiert sind, sind bei derartiger Optimierung und Umordnung von 
Code nicht so agressiv. Sei es weil eine Akkumulator-Architektur wie 
68xx,PIC,8051 dafür wenig Raum lässt oder fehlendes tiefes Pipelining es 
einfach unnötig macht, sei es weil es manchen Programmierern übel 
aufstösst ;-). Der GNU-Compiler hingegen muss sich mit Compilern für 
High-Performance-Computing messen und optimiert dementsprechend 
agressiv. Und einiges davon merkt man auch in der AVR-Version, und es 
gereicht dort auch nicht immer zum Vorteil.

von Marcel_74 (Gast)


Lesenswert?

Hallo!

Vielen Dank für die Hintergrundinfos. Aber selbst wenn ich vorher über 
"volatile" gestolpert wäre, hätte ich zumindest in diesem Fall nicht 
wirklich daran gedacht es zu benutzen. Aber jetzt weiß ich worauf ich 
achten muss.

Gibt es eine Übersicht über die Art der Optimierungen, die der Compiler 
vornimmt? Woher soll ich wissen was kritisch ist und was nicht? In 
meinem speziellen Fall gab es noch nicht einmal eine Warnung, obwohl 
sonst wirklich alles angemakelt wird.

Viele Grüße
Marcel

von (prx) A. K. (prx)


Lesenswert?

Marcel_74 wrote:

> Gibt es eine Übersicht über die Art der Optimierungen, die der Compiler
> vornimmt? Woher soll ich wissen was kritisch ist und was nicht?

Falscher Ansatz. Richtig programmieren heisst die Devise. Und um dich 
vor sowas warnen zu können, müsste er mehr wissen als er wissen kann. 
Das ist ja grad der Witz dran: erst "volatile" sagt ihm das.

Regel: Der Compiler weiss nichts über Interrupt-Routinen, d.h. auch wenn 
es im Quelltext anders aussieht, weiss er nicht dass timer_interrupt() 
eine Interrupt-Routine ist.

von Marcel_74 (Gast)


Lesenswert?

Hallo!

Ich Grunde weiß doch der Compiler, dass ich eine Variable in einer 
if-Bedingung teste, die im Verlaufe des Programms (genauer: main) nicht 
verändert wird. Ich werde ja auch gewarnt, wenn ich eine Variable 
daklariere und diese anschließend nicht verwende.

Aber Du hast dennoch recht: Wenn ich wieder mehr in der Materie stecke, 
werde ich auch wieder besser programmieren. Hinzugelernt habe ich ja 
bereits ;-)


Viele Grüße
Marcel

von holm (Gast)


Lesenswert?

Betreffs Compiler und Optimierung.
Ich habe da eine Software auf einem Atmega644p die über 2 serielle
Schnittstellen Protokolle verschiedener Geräte konvertiert.

Das ist ein ziemlich vergriesgnaddeltes Ding mit dynamischem Speicher
und doppelt verketteten Listen, als Interrupts laufen Timer und die 
Sende- und Empfangsinterrupts der beiden UARTs.

Ich habe 2 Notebooks, ein ältliches mit W2K und einem 550er Professor,
auf dem die Applikation ursprünglich geschrieben wurde und ein etwas 
neueres, mit 1,6GHz/1GB RAM. Auf letzterem habe ich ein neueres WinAVR 
installiert gehabt, ich glaube die letzte Version, auf dem alten Laptop 
eine 20071221
oder so.

Auf dem neuern Notebook machte die Software wirklich nur noch quatsch 
wenn
der Compiler mit -Os optimierte und verhielt sich völlig anders im 
Debugger (Jtag Ice MkII) als ohne. Nachdem ich mich 3 Tage lang mit 
Schattenboxen beschäftigt habe habe ich "J" mal zu dem WinAVR Versionen 
befragt.
Aussage so in etwa " ich habe da bei dem Bugs etwas die Übersicht 
verloren, duch den Xmega Import sind da wohl etliche Fehler rein 
gekommen, benutze 20071221".

Mir sind volatile Variablen durchaus bekannt und mein Code enthält IMHO 
Nichts, über was der Compiler bei Optimierungen stolpern sollte.
Mit dem älteren WinAVR sind die Probleme wie weg geblasen, Debugger
und RUN sind konsistent...

Also meine Empfehlung für die "Standard Atmegas": benutzt den älteren 
Compiler...

Ehe der Hinweis kommt "malloc benutzt man bei Embedded nicht":
Mir bleibt nichts Anderes übrig. Ich muß jede Menge Strings
analysieren und umsortieren da reicht der Platz nur mit dynamischem 
Speicher.

Hallo Atmel: Ich brauche mehr RAM, und von wegen RAM ist teuer;
ich bezahle den auch!!!

Gruß,

Holm

von (prx) A. K. (prx)


Lesenswert?

Marcel_74 wrote:

> Ich Grunde weiß doch der Compiler, dass ich eine Variable in einer
> if-Bedingung teste, die im Verlaufe des Programms (genauer: main) nicht
> verändert wird. Ich werde ja auch gewarnt, wenn ich eine Variable
> daklariere und diese anschließend nicht verwende.

Das ist richtig. Vielleicht gibt's auch eine Warnung die man extra dazu 
einschalten kann.

Allerdings ist das mit den Warnungen so eine Sache. Wenn man damit zu 
weit geht, dann hat jedes normale Programm so viele ganz normale 
Warnungen, dass man die ernsten übersieht.

So war es früher in Compilern durchaus üblich, auf Code wie
  if (1 == 2)
lautstark hinzuweisen. Die Devise war, dass der Anwender doch den 
Präprozessor verwenden soll. Das hat sich allerdings geändert, seit der 
Präprozessor etwas schlecht angesehen ist und umgebungsabhängige 
Fallunterscheidungen in übersichtlicher Weise direkt dem Compiler 
überlassen werden.

von Marcel_74 (Gast)


Lesenswert?

Hallo!

Dumme Frage jetzt: Wozu braucht(e) man eine Konstruktion wie if (1==2) ?


Viele Grüße
Marcel

von (prx) A. K. (prx)


Lesenswert?

Im Makefile steht dann beispielsweise
   -DVERSION=1
und im Code
   if (VERSION == 2)
      ...
was man traditionell aber unübersichtlich mit
   #if VERSION == 2
gemacht hat.

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.