Im Release-Modus ist das Code-Verhalten nicht wie erwartet

131

Der folgende Code generiert im Debug- und Release-Modus (mit Visual Studio 2008) unterschiedliche Ergebnisse:

int _tmain(int argc, _TCHAR* argv[])
{

    for( int i = 0; i < 17; i++ ) 
    { 
        int result = i * 16;

        if( result > 255 )
        {
            result = 255;
        }

        printf("i:%2d, result = %3d\n", i, result) ; 
    } 

    return 0;
}

Die Ausgabe des Debug-Modus, die wie erwartet ist:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 240
i:16, result = 255

Die Ausgabe des Freigabemodus, in dem das i: 15-Ergebnis nicht korrekt ist:

i: 0, result =   0
i: 1, result =  16
(...)
i:14, result = 224
i:15, result = 255
i:16, result = 255

Wenn Sie in Visual Studio im Release-Modus "Optimierung -> Nicht optimieren" wählen, ist das Ausgabeergebnis korrekt. Ich würde jedoch gerne wissen, warum der Optimierungsprozess zu fehlerhaften Ausgaben führen kann.


Aktualisieren:

Wie von Mohit JainBy vorgeschlagen, druckt von:

printf("i:%2d, result = %3d, i*16=%d\n", i, result, i*16) ;

Die Ausgabe im Freigabemodus ist korrekt:

i: 0, result =   0, i*16=0
i: 1, result =  16, i*16=16
(...)
i:14, result = 224, i*16=224
i:15, result = 240, i*16=240
i:16, result = 255, i*16=256
Lorris Lin
quelle
15
Das sieht aus wie ein Compiler-Fehler (und noch dazu ein ziemlich bedeutender).
WhozCraig
1
@WhozCraig Aktualisiert nur die Ausgabe i * 16im Beitrag, und das Ergebnis ist korrekt.
Lorris Lin
4
@juanchopanza: Aus meiner Erfahrung mit MS und Bugfixes für VS beheben sie solche Fehler, nachdem sie darüber informiert wurden, aber wenden diese Fixes nicht auf ältere Versionen von VS an. Wenn man also aus irgendeinem Grund gezwungen ist, eine ältere Version von VS zu verwenden VS, dann bleibt man bei solchen Fehlern stecken, bis man auf eine neuere Version upgraden kann.
Kaiserludi
2
FWIW dies funktioniert gut mit dem kommenden Visual Studio 2015
ismail

Antworten:

115

Dies ist zumindest aus historischer Sicht interessant. Ich kann das Problem mit VC 2008 (15.00.30729.01) und VC 2010 (16.00.40219.01) reproduzieren (entweder auf 32-Bit x86 oder 64-Bit x64). Das Problem tritt bei keinem der Compiler auf, die ich ab VC 2012 (17.00.61030) ausprobiert habe.

Der Befehl, den ich zum Kompilieren verwendet habe: cl /Ox vc15-bug.cpp /FAsc

Da VC 2008 (und 2010) ziemlich alt ist und das Update bereits seit mehreren Jahren verfügbar ist, können Sie von Microsoft keine Aktion erwarten, außer die Verwendung eines neueren Compilers (obwohl möglicherweise jemand eine Problemumgehung vorschlagen kann).

Das Problem besteht darin, dass der Test zur Bestimmung, ob der Wert erzwungen werden 255soll, eher auf der Anzahl der Schleifen als auf dem tatsächlichen Ergebnis des i * 16Ausdrucks basiert . Und der Compiler erhält einfach die falsche Zählung, wenn er den Wert erzwingen soll 255. Ich habe keine Ahnung, warum das passiert - es ist nur der Effekt, den ich sehe:

; 6    :    for( int i = 0; i < 17; i++ ) 

  00001 33 f6        xor     esi, esi
$LL4@main:
  00003 8b c6        mov     eax, esi
  00005 c1 e0 04     shl     eax, 4

; 7    :    { 
; 8    :        int result = i * 16;
; 9    : 
; 10   :        if( result > 255 )

  // the value `esi` is compared with in the following line should be 15!
  00008 83 fe 0e     cmp     esi, 14            ; 0000000eH
  0000b 7e 05        jle     SHORT $LN1@main

; 11   :        {
; 12   :            result = 255;

  0000d b8 ff 00 00 00   mov     eax, 255       ; 000000ffH
$LN1@main:

; 13   :        }

Update : Alle Versionen von VC, die ich vor VC 2008 installiert habe, haben denselben Fehler, außer VC6 - beim Kompilieren des Programms stürzt der VC6-Compiler ab:

vc15-bug.cpp(10) : fatal error C1001: INTERNAL COMPILER ERROR

Dies ist also ein Fehler, der in MSVC in der einen oder anderen Form mehr als 10 Jahre andauerte!

Michael Burr
quelle
Wenn meine Erinnerung an das Timing der x86-Assembly richtig ist, ist der Grund für den Vergleich mit esi anstelle von eax comp eax, 255 würde einen Pipeline-Stillstand verursachen, da eax gerade geschrieben wurde.
Loren Pechtel
3
Meine Vermutung (Transformationen): Ergebnis> 255, Ergebnis / 16> 255/16, i> 15, i <= 14
teki
Sehr interessant! Auch wenn Sie den Vergleich von ändern result > 255, result >= 255verhält er sich korrekt. In VS2010 ändert sich dies cmp esi, 14zu cmp esi, 16(und jlezu jl).
Opello
16

Vorausgesetzt, Ihre gemeldeten Fakten sind korrekt, wäre dies ein Compiler-Fehler. Überprüfen Sie die neueste Version des Compilers. Wenn der Fehler weiterhin vorhanden ist, senden Sie einen Fehlerbericht.

David Heffernan
quelle