Kurzschließen auf booleschen Operanden ohne Nebenwirkungen

7

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.

%Vor%

Die verwendete CFLAGS war -g -O2 . Hier ist die entsprechende Assembly-GCC-Ausgabe (gedumpt mit objdump )

%Vor%

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?

    
Unsigned 08.09.2011, 22:47
quelle

5 Antworten

5

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

angeben %Vor%

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.

    
Seth 21.09.2011, 13:25
quelle
10

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.

    
R.. 08.09.2011 23:07
quelle
6

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

ausgeben     
David Brown 08.09.2011 23:00
quelle
4

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.

    
Jon Bright 08.09.2011 23:06
quelle
3

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 .

    
Keith Thompson 25.09.2011 03:11
quelle