Forum: Compiler & IDEs Problem mit GCC-Optimierung


von Andreas (Gast)


Lesenswert?

Hallo zusammen,

ich benutze in einem STM32-F4 die Hardware-CRC-Berechnung, dafür habe 
ich eine Funktion, die folgendermaßen aussieht:
1
Tu32 SingleCrc(Tu32 Data)
2
{
3
  CRC->CR = CRC_CR_RESET;
4
  CRC->DR = Data;
5
  return (CRC->DR);
6
}
Wenn ich nun ohne Optimierung compiliere, ist alles in Ordnung und der 
Assembercode sieht folgendermaßen aus:
1
SingleCrc(unsigned long):
2
08019a94:   push {r7}
3
08019a96:   sub sp, #12
4
08019a98:   add r7, sp, #0
5
08019a9a:   str r0, [r7, #4]
6
08019a9c:   str r1, [r7, #0]
7
142         CRC->CR = CRC_CR_RESET;
8
08019a9e:   mov.w r3, #12288        ; 0x3000
9
08019aa2:   movt r3, #16386 ; 0x4002
10
08019aa6:   mov.w r2, #1
11
08019aaa:   str r2, [r3, #8]
12
143         CRC->DR = Data;
13
08019aac:   mov.w r3, #12288        ; 0x3000
14
08019ab0:   movt r3, #16386 ; 0x4002
15
08019ab4:   ldr r2, [r7, #0]
16
08019ab6:   str r2, [r3, #0]
17
144         return (CRC->DR);
18
08019ab8:   mov.w r3, #12288        ; 0x3000
19
08019abc:   movt r3, #16386 ; 0x4002
20
08019ac0:   ldr r3, [r3, #0]

Sobald ich aber die Optimierung (-O1 oder -O irgendwas) einschalte 
funktioniert die Berechnung nicht mehr und der Assemblercode wurde zu
1
SingleCrc(unsigned long):
2
08012370:   mov.w r3, #12288        ; 0x3000
3
08012374:   movt r3, #16386 ; 0x4002
4
08012378:   mov.w r2, #1
5
0801237c:   str r2, [r3, #8]
6
143         CRC->DR = Data;
7
0801237e:   str r1, [r3, #0]
8
144         return (CRC->DR);
9
08012380:   ldr r0, [r3, #0]

optimiert.

Die Variablen aus der ST-Bibliothe sind als __IO also volatile 
deklariert, daher wundert es mich, dass der Compiler hier überhaupt 
etwas optimiert.
Ich bin kein Compilerexperte und noch weniger ein Assembler-Experte, hat 
jemand eine Idee, was schiefläuft, und wie ich das verhindere?

Ich meine, ich könnte die Optimierung ja für diese Funktion ausschalten, 
kein Problem, aber ich habe Angst, irgendwo andres im Code taucht das 
gleiche Problem auf und ich bemerke es nicht.

Viele Grüße
Andreas

von René B. (Gast)


Lesenswert?

Andreas schrieb:
> Die Variablen aus der ST-Bibliothe sind als __IO also volatile
> deklariert, daher wundert es mich, dass der Compiler hier überhaupt
> etwas optimiert.

Optimieren geht meistens immer, egal ob volatile oder nicht. Für deine 
CRC Register muss er ja auch die Adressen laden um damit arbeiten zu 
können.
Bei O0 macht er das für jedes Register einzeln. Wenn du optimieren 
lässt, reicht ihm die Basisadresse und dann wird nur noch mit Offsets 
hantiert.

08019a9e:   mov.w r3, #12288        ; 0x3000
08019aa2:   movt r3, #16386 ; 0x4002
08019aa6:   mov.w r2, #1
08019aaa:   str r2, [r3, #8]
143         CRC->DR = Data;
08019aac:   mov.w r3, #12288        ; 0x3000
08019ab0:   movt r3, #16386 ; 0x4002

->

08012370:   mov.w r3, #12288        ; 0x3000
08012374:   movt r3, #16386 ; 0x4002
08012378:   mov.w r2, #1
0801237c:   str r2, [r3, #8]
143         CRC->DR = Data;
0801237e:   str r1, [r3, #0]

Schau mal in das LST-File ob du noch mehr Fragmente deiner Funktion 
findest. Was mir z.B. fehlt sind die Übergabeparameter deiner Funktion. 
Alternativ kann er sie beim Aufruf auch direkt in Register mappen und 
nicht den Umweg über den Stack gehen.
Auf jeden Fall nicht einfach zu durchschauen.
Hast du mal nen Debugger bemüht?

von (prx) A. K. (prx)


Lesenswert?

Der Code sieht ziemlich korrekt aus. Das er kürzer ist, sollte bei 
Optimierung nicht überraschen. Das ist die Aufgabe davon.

Volatile Zugriffe werden dort auch weiterhin so ausgeführt wie es sein 
soll. Aber natürlich nur die. Es ist keineswegs so, dass eine volatile 
Variable in einem Programm oder eine Funktion, sämtliche Optimierungen 
im ganzen Programm oder der Funktion ausschaltet. Sondern nur 
Zugriffsoptimierungen auf die betreffende Variable.

von Andreas (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe mal die CPP-Datei und die lst-Datei angehangen. Ich blicke da 
selber leider überhaupt nicht durch.

Mit dem Debugger gibts ein ganz interessantes Problem: Wenn ich die 
Funktion einzeln durchsteppe, ist der berechnete CRC-Wert so lange 
richtig, wie ich Einzelschritte mache. Sobald die Funktion im normalen 
Modus durchlaufen wird, schlägt sie fehl.
Auch dafür habe ich leider bisher keine Erklärung.

Das alles tritt nur auf, wenn die Optimierung eingeschaltet ist.
Ich würde ja auch sagen, mein Problem liegt an anderer Stelle im Code, 
aber es reicht schon, für die SingleCRC-Funktion die Optimierung 
abzuschalten und alles ist in Ordnung.

Bei der Block-CRC-Funktion gibt es die Probleme übringens auch genau 
dann, wenn man sie mit fester Blocklänge 1 ausführt. Sobald die Funktion 
mehrmals durchlaufen wird, ist alles in Ordnung.

Bin für jeden Tipp dankbar, bin nämlich mit meinem Latein am Ende.

Habe schon versucht, alle Optimierungsflags einzeln zu aktivieren und zu 
deaktivieren, aber irgendwie klappt das auch nicht, wie ich es mir 
vorstelle.

Gruß
Andreas

von Dr. Sommer (Gast)


Lesenswert?

Das klingt stark nach zu schnellem Code... D.h. es funktioniert wenn die 
Registerzugriffe langsam nacheinander erfolgen (wie im Debugger oder 
wenn ohne Optimierungen durchgeführt). Schau vllt mal im Datenblatt ob 
es für diese Register bestimmte Anforderungen gibt und füge ggf ein paar 
'asm volatile("nop");' ein...

von Jay (Gast)


Lesenswert?

Hallo,

laut Datenblatt braucht die Berechnung 4 AHB Clocks cycles.

Interessant ist, dass die Funktion in der Standard Library genauso 
aussieht wie beim Threadersteller (abgesehen vom Reset am Anfang):
1
uint32_t CRC_CalcCRC(uint32_t Data)
2
{
3
  CRC->DR = Data;
4
  
5
  return (CRC->DR);
6
}
Ohne es zu probieren behaupte ich einfach mal ganz kühn, dass es dort 
beim Optimieren auch zu Problemem kommen wird.

CU,
Jay

von (prx) A. K. (prx)


Lesenswert?

Jay schrieb:
> laut Datenblatt braucht die Berechnung 4 AHB Clocks cycles.

Klarer Compilerfehler. Wieso berücksichtigt der das nicht? :-)

: Bearbeitet durch User
von Andreas (Gast)


Lesenswert?

Hallo zusammen,

vielen Dank für den klasse Hinweis.
Ich war irgendwie felsenfest davon überzeugt, die CRC-Unit bräuchte nur 
einen Taktzyklus. Da hätte ich vermutlich noch ewig gebraucht, bis ich 
darauf gekommen wäre.
Nochmal vielen Dank!

@ A.K. Ich wollte das Problem nicht dem GCC unterstellen, für mich war 
halt nur ein Zusammenhang zur Optimierung erkennbar.

von (prx) A. K. (prx)


Lesenswert?

Andreas schrieb:
> @ A.K. Ich wollte das Problem nicht dem GCC unterstellen, für mich war
> halt nur ein Zusammenhang zur Optimierung erkennbar.

Hatte ich auch nicht so aufgefasst. Unlängst war hier allerdings jemand 
zugange, der sich über ähnliche "Mängel" am Compiler beklagte.

von Uwe (Gast)


Lesenswert?

Woher soll denn der Compiler wissen wie hoch du deine AHB Clock 
eingestellt hast ?! Und selbst wenn dann müßte er ja auch noch wissen 
wie lange die verschiedenen CRC Einheiten brauchen zum berechnen. 
Vieleich wird bei einer anderen Architektur gar ein Bit gesetzt wenn das 
Ergebnis bereit ist oder ein Interupt signalisiert. Das ist doch nicht 
die Aufgabe eines Compilers. Sondern des Datenblattes.

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.