Zur Zeit arbeite ich mich in das Teensy 36 Board ein. Da es die erste
ARM CPU ist, die ich benutze, interessiert mich natürlich die
Lesitungsfähigkeit, weshalb ich kleine Programme schreibe und deren
Laufzeit messe. Um die Laufzeit zu verstehen will ich mir auch den vom
Compiler erzeugten Code ansehen. Das gelingt auch schon ganz gut, aber
bei dem unten gezeigten Beispiel habe ich eine Frage.
Auf Adresse 4ae wird, soweit ich durchblicke , ein unbedingter Sprung
erzeugt, der beim ersten Durchlauf der Schleife die Cosinusauswertung
überspringt. Das kann aber nicht sein, denn die Probe zeigt, dass der
Code auch für einen einzigen Schleifendurchlauf das richtige Resultat
erzeugt.
Die Frage ist also:
Was bewirkt der Befehl auf Adresse 4ae?
Martin O. schrieb:> Auf Adresse 4ae wird, soweit ich durchblicke , ein unbedingter Sprung> erzeugt, der beim ersten Durchlauf der Schleife die Cosinusauswertung> überspringt.
Das täuscht, die Cosinus-Berechnung wird nicht übersprungen. Zuerst muss
ja die Schleifenbedinung ausgewertet werden. Der unbedingte Sprung in
4ae geht zur Schleifenbedingung. Wenn die != 0 ergibt springt bne.n (in
4c4) zur Addition in 4b0 und in Folge zum Cosinus.
Martin O. schrieb:> Die Befehle auf 4bc und 4c0 würden auf jeden Fall einmal ausgeführt, was> aber nicht korrekt wäre.
Nö, wenn d=10000 wird bei 49a direkt ans Ende gesprungen. Der Compiler
hat den 1. Schleifendurchlauf wegoptimiert und durch Konstanten ersetzt.
PS: Findet ihr es nicht auch furchtbar unübersichtlich wenn der
originale C Code mit in das Disassembly gemischt wird? Vor allem bei
eingeschalteter Optimierung...
@Dr. Sommer: Deine Erklärung macht Sinn, besten Dank. Wenn man ein
leicht geändertes Programm nimmt, kann der Compiler keinen
Schleifenduchauf mehr wegoptimieren, dann wird der erzeugte Code etwas
besser verständlich.
Ich finde diesen Code-misch-masch auch schwer lesbar. Insbesondere sind
viele Konstanten für mich nicht verständlich. Zumal der Compiler
manchmal ganze Schleifen wegoptimiert.
Martin O. schrieb:> Ich finde diesen Code-misch-masch auch schwer lesbar.
Wenn du die "-S" Option bei "objdump" raus nimmst wird der Quellcode
beim Disassemblieren weggelassen.
Deine Konstanten implizieren "double". Mit Optimierung war dein Compiler
so nett manche als "float" zu kodieren. Das muss er aber nicht tun - und
wird das bei einigen auch nicht, falls keine 1:1 Abbildung des Wertes
zwischen double und float existiert. Dann führt er ein double cast aus.
Such mal nach Stellen wie diesen wenn du schonmal das Disassembly hast:
Die Variablen sind aber vom Typ float. Der Compiler wird wohl kaum ein
double-Literal laden und zur Laufzeit nach float konvertieren; das macht
er schon vorher beim Kompilieren. Höchstens das "fd1 += 0.5;" könnte mit
double-Präzision ausgeführt werden, wobei der Compiler hier erkennen
müsste dass das nichts bringt.
Dr. Sommer schrieb:> Die Variablen sind aber vom Typ float.
Eben.
> Der Compiler wird wohl kaum ein> double-Literal laden und zur Laufzeit nach float konvertieren;
Nein, er wird ein double Literal laden und die float Variable zur
Laufzeit auf double konvertieren, dann die Operation (in double)
ausführen und zur Zuweisung wieder zurück in float konvertieren. Ist ein
Operand double erfolgt nach Regelwerk eine Konvertierung des zweiten
Operands ebenfalls auf double und kein impliziter Rückcast zu float.
> das macht> er schon vorher beim Kompilieren. Höchstens das "fd1 += 0.5;" könnte mit> double-Präzision ausgeführt werden, wobei der Compiler hier erkennen> müsste dass das nichts bringt.
Null und 0.5 sind exakt abbildbar, der Optimizer checkt das meist.
Einfach was probieren was nicht 1:1 abgebildet ist, 1.731 oder sowas.
Oder einfach mal mit -O0 compilieren, dann macht der Compiler Dienst
nach Vorschrift.
Man müßte höchstens noch aufpassen, ob man den Compiler die Annahme z.B.
von Rounding Mode und ähnlichen Spezifika verbieten kann. Dann müßte die
Konvertierung einer double Konstanten zu float theoretisch per
Instruktion passieren.
Ich war z.B. überrascht, dass dies hier:
1
float x = 0.0f/0.0f; /* NaN */
vom Compiler je nach Einstellung tatsächlich als Division durch Null
ausgeführt werden kann. Schnell getestet: