Search code examples
c++visual-studio-2015compiler-errorscompiler-optimization

Visual Studio 2015 Update 3 - C++ Compiler bug?


We observe a strange case where in VS2015 Update3 compiler will omit part of the code for no obvious reason.

We found out that

  • This happens in VS2015 Update3 (Help|About says 14.0.25431.01 Update 3, cl.exe version 19.00.24215.1)
  • This doesn't happen in VS2015 Update2 (Help|About says 14.0.25123.00 Update 2, cl.exe version 19.00.23918)
  • This happens only when optimization is turned on (for example, in default Release configuration)
  • Happens in both x86 and x64
  • Happens when code snippet is inserted into brand new "Win32 Console Application" (I mean, no fancy commandline options required)

We managed to minimize the culprit code to this snippet:

#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>

int _tmain(int, _TCHAR*[])
{
    volatile int someVar = 1;

    const int indexOffset = someVar ? 0 : 1;    // Loop omitted
    // const int indexOffset = !someVar;        // Loop omitted
    // const int indexOffset = 0;               // Good
    // const int indexOffset = 1;               // Good
    // const int indexOffset = someVar;         // Good
    // const int indexOffset = someVar + 1;     // Good

    for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
    {
        printf("Test passed\n");
    }

    return 0;
}

For the lines that say "Loop omitted", the entire loop body is omitted by compiler. Why? To my knowledge, there is no undefined behavior involved.


Disassembly for the first "Loop omitted":

int _tmain(int, _TCHAR*[])
{
01151010  push        ebp  
01151011  mov         ebp,esp  
01151013  push        ecx  
    volatile int someVar = 1;
01151014  mov         dword ptr [ebp-4],1  

    const int indexOffset = someVar ? 0 : 1;    // Loop omitted
0115101B  mov         eax,dword ptr [someVar]  
    // const int indexOffset = !someVar;        // Loop omitted
    // const int indexOffset = 0;               // Good
    // const int indexOffset = 1;               // Good
    // const int indexOffset = someVar;         // Good
    // const int indexOffset = someVar + 1;     // Good

    for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
    {
        printf("Test passed\n");
    }

    system("pause");
0115101E  push        offset string "pause" (011520F8h)  
01151023  call        dword ptr [__imp__system (0115205Ch)]  
01151029  add         esp,4  
    return 0;
0115102C  xor         eax,eax  
}
0115102E  mov         esp,ebp  
01151030  pop         ebp  
01151031  ret

Test project: http://dropmefiles.com/S7mwT


Try it online!


Bug report: https://developercommunity.visualstudio.com/content/problem/71906/compiler-optimization-code-generation-bug.html


Solution

  • Yes, it's a bug. Specifically, it's a bug in the new SSA optimizer introduced in VS2015 Update 3. The undocumented command line option -d2SSAOptimizer- tells the compiler backend to use the old optimizer instead, which causes the bug to not manifest.

    FYI, you can minimize your repro to:

    int main()
    {
        volatile int someVar = 1;
    
        const int indexOffset = someVar ? 0 : 1;
    
        for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
        {
            return 0;
        }
        return 1;
    }
    

    which will help the compiler developers localize the problem more quickly.


    Addition from Codeguard (I decided that Casey's answer should be THE answer): I have received reply from Microsoft (Gratian Lup, author of blog post Introducing a new, advanced Visual C++ code optimizer):

    Yes, this is indeed a bug in the SSA Optimizer itself - usually most bugs reported as being in the new optimizer are in other parts, sometimes exposed now after 20 years.

    It's in a small opt. that tries to remove a comparison looking like (a - Const1) CMP (a - Const2), if there is no overflow. The issue is that your code has (1 - indexOffset) CMP (2 - indexOffset) and subtraction is not commutative, of course - but the optimizer code disregards that and handles (1 - indexOffset) as if it's (indexOffset - 1).

    A fix for this issue will be released in the next larger update for VS2017. Until then, disabling the SSA Optimizer would be a decent workaround. Disabling optimizations for only this function may be a better approach if it doesn't slow down things too much. This can be done with #pragma optimize("", off): https://msdn.microsoft.com/en-us/library/chh3fb0k.aspx