Für das Kopfgeld: Wie kann dieses Verhalten von Fall zu Fall deaktiviert werden, ohne die Optimierungsstufe zu deaktivieren oder zu verringern?
Der folgende Bedingungsausdruck wurde in MinGW GCC 3.4.5 kompiliert, wobei a
vom Typ signed long
und m
vom Typ unsigned long
ist.
Die verwendete CFLAGS
war -g -O2
. Hier ist die entsprechende Assembly-GCC-Ausgabe (gedumpt mit objdump
)
120
- 131
kann leicht als erste Auswertung von !a
verfolgt werden, gefolgt von der Auswertung von m > 0x002
. Die erste Sprungbedingung tritt erst nach 133
auf. Zu diesem Zeitpunkt wurden zwei Ausdrücke ausgewertet, unabhängig vom Ergebnis des ersten Ausdrucks: !a
. Wenn a
gleich Null ist, kann (und sollte) der Ausdruck sofort abgeschlossen werden, was hier nicht gemacht wird.
Wie verhält es sich mit dem C-Standard, der Boolesche Operatoren zum Kurzschließen benötigt, sobald das Ergebnis bestimmt werden kann?
Wie andere bereits erwähnt haben, ist diese Assembly-Ausgabe eine Compiler-Optimierung, die die Programmausführung nicht beeinflusst (soweit der Compiler dies feststellen kann). Wenn Sie diese Optimierung selektiv deaktivieren möchten, müssen Sie dem Compiler mitteilen, dass Ihre Variablen nicht über die Sequenzpunkte im Code optimiert werden sollen .
Sequenzpunkte sind Kontrollausdrücke (die Auswertungen in if
, switch
, while
, do
und alle drei Abschnitte von for
). , logische ORs und ANDs, Bedingungen ( ?:
), Kommas und die return
-Anweisung.
Um eine Compileroptimierung zwischen diesen Punkten zu verhindern, müssen Sie Ihre Variable volatile
deklarieren. In Ihrem Beispiel können Sie
Der Grund dafür ist, dass volatile
verwendet wird, um den Compiler anzuweisen, das Verhalten einer äquivalenten Maschine in Bezug auf die Variable nicht vorhersagen zu können. Daher muss es genau die Sequenzpunkte in Ihrem Code befolgen.
Der C-Standard spezifiziert nur das Verhalten einer "abstrakten Maschine"; Es gibt nicht die Generierung der Assembly an. Solange das beobachtbare Verhalten eines Programms mit dem der abstrakten Maschine übereinstimmt, kann die Implementierung jeden beliebigen physikalischen Mechanismus verwenden, der für die Implementierung der Sprachkonstrukte erforderlich ist. Der relevante Abschnitt im Standard (C99) ist 5.1.2.3 Programmausführung.
Es ist wahrscheinlich eine Compiler-Optimierung, da der Vergleich von Integraltypen keine Nebenwirkungen hat. Sie könnten versuchen, ohne Optimierungen zu kompilieren oder eine Funktion mit Nebeneffekten anstelle des Vergleichsoperators zu verwenden, um zu sehen, ob dies immer noch funktioniert.
Versuchen Sie zum Beispiel
%Vor% und es sollte ac
Die Optimierung des Compilers - es bringt das Ergebnis in EBX, verschiebt es nach AL, Teil von EAX, führt die zweite Überprüfung in EDX durch und verzweigt dann basierend auf dem Vergleich von EAX und EDX. Das spart eine Verzweigung und lässt den Code schneller laufen, ohne dass es irgendwelche Nebenwirkungen gibt.
Wenn Sie mit -O0
statt mit -O2
kompilieren, stelle ich mir vor, dass dies zu einer naiven Assembly führt, die Ihren Erwartungen besser entspricht.
Der Code verhält sich in beiden Richtungen korrekt (d. h. gemäß den Anforderungen des Sprachstandards).
Es scheint, dass Sie versuchen, einen Weg zu finden, um spezifischen Assemblercode zu generieren. Von zwei möglichen Assembler-Codefolgen, die sich beide gleich verhalten, finden Sie eine zufrieden stellende und die andere unbefriedigend.
Der einzige wirklich zuverlässige Weg, um die zufriedenstellende Assemblercodefolge zu garantieren, ist, den Assemblercode explizit zu schreiben. Gcc unterstützt Inline-Assembly.
C-Code spezifiziert das Verhalten. Der Zusammenstellungscode gibt den Maschinencode an.
Aber das wirft die Frage auf: Warum ist es dir wichtig? (Ich sage nicht, dass es nicht sollte, ich verstehe einfach nicht, warum es sollte.)
BEARBEITEN: Wie genau sind a
und m
definiert? Wenn, wie Sie vorschlagen, sie mit Speicherkarten-verbundenen Geräten verwandt sind, sollten sie als volatile
deklariert werden - und das könnte genau die Lösung für Ihr Problem sein. Wenn es sich nur um gewöhnliche Variablen handelt, kann der Compiler mit ihnen machen, was er will (solange es das sichtbare Verhalten des Programms nicht beeinflusst) , weil Sie nicht danach gefragt haben .
Tags und Links c gcc compiler-construction mingw compiler-optimization