Guten Abend
Ich bin heut Abend über einen möglichen Bug in der aktuellen GCC Version
7.2.0 gestolpert. Und zwar erzeugt mir ein Build mit der Optimierung -Os
einen "Double Word Store (STRD)" mit einer ungeraden (!%4) Adresse.
Die (wichtigen) Flags sind jene hier:
Und irgendein SIMD Befehl schmeißt dann folgendes raus:
1
-os(r3=5369032100x20007e2a)
2
strdr4,r4,[r3,#-16]
Zum Vergleich die selbe Codestelle mit -O2:
1
-o2(r3=5369025240x20007b7c)
2
strdr5,r5,[r3,#-8]
Ist da zufällig schonmal wer drüber gestolpert?
Der Unterschied zw. -O2 und -Os ist ja relativ klein, laut GCC Doku sind
leidiglich folgende Flags betroffen:
Vincent H. schrieb:> Ist da zufällig schonmal wer drüber gestolpert?
Schon viele. Das ist wahrscheinlich kein Fehler im GCC, sondern in
deinem Programm. Klassischer Kandidat wäre sowas:
1
doubleget(char*x){
2
return*((double*)x);
3
}
Glücklicherweise ist so ein Code verboten, weshalb so etwas "eigentlich"
nicht auftritt. Zeig mal deinen Code, vielleicht lässt sich ja direkt
das Problem erkennen.
Type-punning, undefined behavior und aliasing sind mir keine Fremdworte.
Der Code ist aber amüsanterweise eine CMSIS-Funktion namens
"arm_fill_q15".
Und wie du richtig vermutet hast findet sich in Zeile 75, 76 der
Übeltäter:
1
*__SIMD32(pDst)++=packedValue;
2
*__SIMD32(pDst)++=packedValue;
Soweit sogut. Die Sache is aber, dass -O2 und -O3, die ja ebenfalls
"-fstrict-aliasing" einschalten, den Fehler nicht produzieren.
Vermutlich, weil man wohl aus Geschwindigkeitsgründen hier komplett
aligned.
Das Problem ist scheinbar, dass die beiden Instruktionen hintereinander
bei -Os den besagten "Double Word Store" provizieren... Ganz verstehen
tu ich das aber trotzdem nicht. Die Instruktion darf es ja per Reference
Manual der v7 Architektur aus in der Form überhaupt nicht geben.
Aus dem Grund kann man das in meinen Augen auch nicht mit dem
"klassischen" load/store unaligned/aligned Problem vergleichen. Ein
normales ldr/str kann je nach M0/M3 und Einstellungen halt gehen... oder
halt auch nicht. Ein strd mit einer ungeraden Adresse gibts aber nie,
auf gar keinem Core. Weshalb forciert der Compiler das nicht?
Vincent H. schrieb:> Die Instruktion darf es ja per Reference> Manual der v7 Architektur aus in der Form überhaupt nicht geben.
Die Instruktion schreibt 2 Wörter an eine per Register übergebene
variable Adresse. Was soll es daran nicht geben?
Vincent H. schrieb:> Ein strd mit einer ungeraden Adresse gibts aber nie,> auf gar keinem Core. Weshalb forciert der Compiler das nicht?
Weil der Compiler nicht die Adresse eines jeden Pointers im Code kennt?
Die wird ja hier als Funktionsargument übergeben.
Vincent H. schrieb:> Aus dem Grund kann man das in meinen Augen auch nicht mit dem> "klassischen" load/store unaligned/aligned Problem vergleichen.
Es ist aber unter den klassischen alignment Definitionen im ARMv7M
Architecture Reference Manual, auf S. 65, definiert.
Prüfe doch mal den Wert des pDst Paramters, vielleicht ist der ja schon
verkehrt.
STRD ist auch keine SIMD-Instruktion, sondern ein schnödes Store-Double.
Stimmt natürlich, der häßliche C-Cast hat mich da verleitet und so
frisch bin ich auch nicht mehr.
Zeit die Daten mit ein wenig Alignment zu beglücken.
Danke!
Hallo Dr. Sommer,
Wie würde in dem Beispiel der korrekte Code aussehen.
Ich benutze dieses Art des castings, um zB gepackte Structures auf
Bytearrays zu legen und umgekehrt.
Die Bytearrays werden dann zB per Uart gesendet/empfangen.
Bis jetzt hatte ich zum Glück noch keine Probleme.
Ich benutze CortexM4.
Danke, Adib.
Adib schrieb:> Ich benutze dieses Art des castings, um zB gepackte Structures auf> Bytearrays zu legen und umgekehrt.> Die Bytearrays werden dann zB per Uart gesendet/empfangen.
Wenn die Struktur so aufgebaut ist, daß die ints auf geraden Adressen
landen, etwa indem die Variablen von groß nach klein angeordnet sind,
dann passiert da auch nichts.
Man sollte sich aber Gedanken um Endianess machen.
Markus F. schrieb:>> und umgekehrt.>> das nicht.
Hmm, aber wie macht man es jetzt "richtig" also ohne Fehler. ???
Ich mache das eigentlich immer so wie im Code unten.
Der Parser bekommt die Byte-Daten von der serielllen Schnittstelle.
Wenn er ein komplettes Protokoll erkennt, übergibt er den pointer auf
die Daten an den Decoder, der sich dann die Daten anschaut.
Nach dem Thread hier sollte genau das ja falsch sein.
Ich habe schon gesehen, dass der deserialiser die Daten in eine lokale
Variable (Struktur) per memcpy kopiert. Möchte aber unnötiges memcpy
vermeiden.
Würde es nicht auch reichen, sicherzustellen dass der parserbuff auf
einer durch 4-teilbaren Addresse liegt?
Danke und Grüße, Adib.
Ein "Downcast" nach unsigned char * ist für jeden Zeigertyp erlaubt.
Wenn Du also beispielsweise deinen Empfangsbuffer im Hauptprogramm als
struct-Array deklarierst und deiner Empfangsroutine auf unsigned char *
gecasted übergibst, ist alles gut.
Das Problem ist hier nicht nur Alignment. Pointer aliasing kann
durchaus auch bewirken, dass der Compiler in optimiertem Code
Zuweisungen über "gealiaste" Zeiger (die ja nicht sein dürfen) einfach
wegoptimiert. Wenn Du das verhindern willst, kannst/musst Du bei gcc mit
-fno-strict-aliasing arbeiten und damit auf das letzte Quäntchen
Optimierung verzichten.
Markus F. schrieb:> Wenn Du das verhindern willst, kannst/musst Du bei gcc mit> -fno-strict-aliasing arbeiten und damit auf das letzte Quäntchen> Optimierung verzichten.
Wobei man auch ausmessen sollte, ob die Performance überhaupt beeinflußt
wird - meiner Erfahrung nach nicht. Zumindest für Release-Builds würde
ich das daher sicherheitshalber immer abschalten.
Anders mag es aussehen, wenn man mathematische Berechnungen mit Vektoren
und Matrizen hat, wo im Prinzip alles letztlich auf float oder double
zeigt, aber das kriegt man mit Aliasing auch nicht in den Griff, weil
das ja erlaubt ist; dafür gibt's dann restrict.