Optimierungsstufen in gcc ändern das Verhalten des c-Programms
Lesezeit: 1 Minute
Benutzer1142769
Ich sehe ein Verhalten, das ich nicht erwarte, wenn ich diesen Code mit unterschiedlichen Optimierungsstufen in gcc kompiliere.
Der Funktionstest sollte eine 64-Bit-Ganzzahl ohne Vorzeichen mit Einsen füllen, sie um shift_size-Bits nach links verschieben und die 32 niedrigen Bits als eine 32-Bit-Ganzzahl ohne Vorzeichen zurückgeben.
Wenn ich mit -O0 kompiliere, erhalte ich die erwarteten Ergebnisse.
Wenn ich mit -O2 kompiliere, mache ich das nicht, wenn ich versuche, um 32 Bit oder mehr zu verschieben.
Tatsächlich erhalte ich genau die Ergebnisse, die ich erwarten würde, wenn ich eine 32-Bit-Ganzzahl um Verschiebungen verschieben würde, die größer oder gleich der Bitbreite auf x86 sind, was eine Verschiebung ist, die nur die 5 niedrigen Bits der Verschiebungsgröße verwendet.
Aber ich verschiebe eine 64-Bit-Zahl, also sollten Verschiebungen <64 legal sein, oder?
Ich nehme an, es ist ein Fehler in meinem Verständnis und nicht im Compiler, aber ich konnte es nicht herausfinden.
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
uint32_t test(unsigned int shift_size) {
uint64_t res = 0;
res = ~res;
res = res << shift_size; //Shift size < uint64_t width so this should work
return res; //Implicit cast to uint32_t
}
int main(int argc, char *argv[])
{
int dst;
sscanf(argv[1], "%d", &dst); //Get arg from outside so optimizer doesn't eat everything
printf("%" PRIu32 "l\n", test(dst));
return 0;
}
Könnten Sie auch die relevanten Teile des von Ihrem Compiler generierten Assemblercodes posten? (-S für gcc)
– Greg Hewgill
11. Januar 2012 um 9:50 Uhr
FWIW gibt es das richtige Ergebnis (0l) für mich auf allen Optimierungsstufen ab -O0 zu -O3 mit gcc 4.2.1, also vermute ich Sie könnte habe einen gcc-bug gefunden.
– PaulR
11. Januar 2012 um 9:52 Uhr
hm, Code funktioniert bei mir in beiden Optimierungsstufen einwandfrei … (gcc Version 4.5.0 20100604, openSUSE 11.3 (x86_64))
– Bort
11. Januar 2012 um 9:54 Uhr
@Alex: wenn du das versuchst Nicht-LLVM gcc 4.2.1 mit -O2 -m32 dann sollten Sie es sehen (das ist, was ich verwende)
Drucken a uint32_t Wert mit a "%u" (oder "%lu")-Spezifizierer kann Undefiniertes Verhalten aufrufen.
Guter Aufruf, uint32_t darf kein unsigned int sein.
– Traumlax
11. Januar 2012 um 10:18 Uhr
@dreamlax: Ich denke, der Punkt ist, dass das OP versehentlich “%ul” anstelle von “%lu” verwendet hat, was letztendlich von printf als “%u” genommen wird. Die Verwendung von PRIu32 beseitigt die Möglichkeit, einen falschen Bezeichner zu verwenden.
– Blechmann
11. Januar 2012 um 10:21 Uhr
Greg Hewgill
Dies konnte ich reproduzieren. Hier ist das relevante Bit des generierten Codes mit -O2:
movl $-1, %eax
movl $-1, %edx
sall %cl, %eax
xorl %edx, %edx
testb $32, %cl
cmovne %eax, %edx
cmovne %edx, %eax ; This appears to be the instruction in error.
; It looks as though gcc thought that %edx might still
; be zero at this point, because if the shift count is
; >= 32 then %eax should be zero after this.
i686-apple-darwin11-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)
Danke, dass du deine gepostet hast gcc -S Ausgang. Ich habe es mir angesehen und obwohl es etwas anders ist, hat der kritische Teil den gleichen Fehler wie das, was ich auf meiner Maschine gesehen habe.
Es sieht so aus als ob könnte für mich ein 32-Bit-spezifischer Compiler-Fehler sein. Mit dem Code aus der Frage und gcc 4.2.1 kann ich den Fehler reproduzieren solange ich mit kompiliere gcc -m32 -O2 ....
Wenn ich jedoch ein Debug-Printf hinzufüge:
uint32_t test(unsigned int shift_size) {
uint64_t res = 0;
res = ~res;
res = res << shift_size; //Shift size < uint64_t width so this should work
printf("res = %llx\n", res);
return res; //Implicit cast to uint32_t
}
dann ist das problem weg.
Der nächste Schritt wäre, sich den generierten Code anzusehen, um zu versuchen, den Fehler zu identifizieren/zu bestätigen.
Als vorübergehende Problemumgehung könnte man vielleicht drucken /dev/null: FILE *null; null = fopen("/dev/null","w"); fprintf(null,"res = %llx\n", res); fclose(null);
– Alex Reynolds
11. Januar 2012 um 10:10 Uhr
Als vorübergehende Problemumgehung machen resvolatile funktioniert genauso gut und erfordert keine Dateioperationen.
– Benutzer784668
11. Januar 2012 um 11:34 Uhr
Es sieht aus wie ein Fehler. Mein erraten liegt daran, dass der Compiler die letzten beiden Zeilen gefaltet hat aus:
res = res << shift_size
return (uint32_t)res;
hinein:
return ((uint32_t)res) << shift_size;
Letzteres ist jetzt für 32 oder mehr gut definiert.
Ich erinnere mich nicht, was C99 gesagt hat, aber es scheint, dass in gcc uint32_t enthalten ist wenigstens 32 Bit und kann mehr enthalten. Wenn Sie es also optimieren, verwendet es 64-Bit-Variable, was auf 64-Bit-Computern schneller ist.
Nein, uint32_t , falls verfügbar, hat genau 32 Bit. (uint_fast32_t oder uint_least32_t könnten größer sein, aber nicht uint32_t)
– Nr
11. Januar 2012 um 10:30 Uhr
uint64_t ist nicht in allen Fällen schneller als uint32_t: Es ist schneller für die Indizierung in Arrays, wenn Sie einen 64-Bit-Adressraum auf Intel/Amd64-Rechnern haben, da 16-Bit- und 32-Bit-Indizes konvertiert werden müssen, während 8-Bit und 64-Bit in dieser Assembler-Sprache zulässige Offsets sind . aber Multiplikation und Division sind langsamer, wenn Sie größere Bitgrößen verwenden.
– Komonade
11. Januar 2012 um 14:12 Uhr
Nein, uint32_t , falls verfügbar, hat genau 32 Bit. (uint_fast32_t oder uint_least32_t könnten größer sein, aber nicht uint32_t)
– Nr
11. Januar 2012 um 10:30 Uhr
uint64_t ist nicht in allen Fällen schneller als uint32_t: Es ist schneller für die Indizierung in Arrays, wenn Sie einen 64-Bit-Adressraum auf Intel/Amd64-Rechnern haben, da 16-Bit- und 32-Bit-Indizes konvertiert werden müssen, während 8-Bit und 64-Bit in dieser Assembler-Sprache zulässige Offsets sind . aber Multiplikation und Division sind langsamer, wenn Sie größere Bitgrößen verwenden.
– Komonade
11. Januar 2012 um 14:12 Uhr
12296800cookie-checkOptimierungsstufen in gcc ändern das Verhalten des c-Programmsyes
Könnten Sie auch die relevanten Teile des von Ihrem Compiler generierten Assemblercodes posten? (
-S
für gcc)– Greg Hewgill
11. Januar 2012 um 9:50 Uhr
FWIW gibt es das richtige Ergebnis (
0l
) für mich auf allen Optimierungsstufen ab-O0
zu-O3
mit gcc 4.2.1, also vermute ich Sie könnte habe einen gcc-bug gefunden.– PaulR
11. Januar 2012 um 9:52 Uhr
hm, Code funktioniert bei mir in beiden Optimierungsstufen einwandfrei … (gcc Version 4.5.0 20100604, openSUSE 11.3 (x86_64))
– Bort
11. Januar 2012 um 9:54 Uhr
@Alex: wenn du das versuchst Nicht-LLVM gcc 4.2.1 mit -O2 -m32 dann sollten Sie es sehen (das ist, was ich verwende)
– PaulR
11. Januar 2012 um 10:02 Uhr
Es ist ein bestätigter Fehler: gcc.gnu.org/bugzilla/show_bug.cgi?id=51821
– Nr
11. Januar 2012 um 11:34 Uhr