132

The following code generates different results under debug mode and release mode (using Visual Studio 2008):

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;
}

The output of debug mode, which is as expected:

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

The output of release mode, where i:15 result is not correct:

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

By choosing "Optimization -> Not to optimize" in Visual Studio under release mode, the output result will be correct. However I would like to know why the optimization process could lead to erroneous output.


Update:

As suggested by Mohit JainBy, prints by:

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

The release mode output is correct:

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
Mohit Jain
  • 30,259
  • 8
  • 73
  • 100
Lorris Lin
  • 953
  • 2
  • 6
  • 9
  • 15
    That looks like a compiler bug (and a fairly significant one at that). – WhozCraig Jul 09 '15 at 05:49
  • 1
    @WhozCraig Just updates the output of `i * 16` in the post, and the result is correct. – Lorris Lin Jul 09 '15 at 06:17
  • 4
    @juanchopanza: From my experience with MS and bugfixes to VS they fix such bugs after they got informed about them, but don't apply those fixes to older versions of VS, so if one is for some reason forced to use an older version of VS, then one is stuck with such bugs until one can upgrade to a newer version. – Kaiserludi Jul 09 '15 at 11:27
  • 2
    FWIW this works fine with upcoming Visual Studio 2015 – ismail Jul 09 '15 at 17:58

2 Answers2

115

This is interesting, at least from a historical perspective. I can reproduce the problem with VC 2008 (15.00.30729.01) and VC 2010 (16.00.40219.01) (targeting either 32-bit x86 or 64-bit x64). The problem doesn't occur with any of the compilers I have tried starting with VC 2012 (17.00.61030).

The command I used to compile: cl /Ox vc15-bug.cpp /FAsc

Since VC 2008 (and 2010) is rather old and the fix has been in for several years now, I don't think you can expect any action from Microsoft except to use a newer compiler (though maybe someone can suggest a workaround).

The problem is that the test to determine if the value should be forced to 255 is done based on the loop count rather than the actual result of the i * 16 expression. And the compiler simply gets the count wrong for when it should start forcing the value to 255. I have no idea why that happens - it's just the effect that I see:

; 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: All versions of VC I have installed earlier than VC 2008 have the same bug, except VC6 - compiling the program crashes the VC6 compiler:

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

So this is a bug that lasted in MSVC in one form or another for more than 10 years!

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • If my memory of x86 assembly timing is right the reason for the comparison to esi rather than eax is comp eax,255 would cause a pipeline stall as eax has just been written. – Loren Pechtel Jul 10 '15 at 01:04
  • 3
    My guess (transformations): result > 255, result / 16 > 255 / 16, i > 15, i <= 14 – teki Jul 10 '15 at 03:43
  • Very interesting! Also if you change the comparison from `result > 255` to `result >= 255` it behaves correctly. In VS2010 that changes `cmp esi, 14` to `cmp esi, 16` (and the `jle` to `jl`). – opello Jul 17 '15 at 15:08
16

Assuming your reported facts are correct, this would be a compiler bug. Check the latest version of the compiler. If the bug is still present, submit a bug report.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490