Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

  • Go to http://webcompiler.cloudapp.net/
  • Put sample code to the editor
  • Put /O2 to Additional compiler flags
  • Check Run executable after compilation

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

like image 932
Codeguard Avatar asked Jun 22 '17 14:06

Codeguard


People also ask

How do I know if I have Visual Studio 2015 Update 3?

Start the Visual Studio Installer, and verify that Help > About shows version 14.0. 25431 or greater. Examine the installed updates list in Control Panel, and verify that both Visual Studio 2015 update 3 (KB3022398) and the update for Microsoft Visual Studio 2015 (KB3165756) are present.

Does MSVC compile C?

The Microsoft C/C++ compiler (MSVC) uses a basic rule to determine which language to use when it compiles your code. By default, the MSVC compiler treats all files that end in . c as C source code, and all files that end in .

Can you use GCC in Visual Studio?

In addition to the Microsoft Visual C++ compiler that many of you are likely familiar with, Visual Studio 2017 also supports Clang, GCC, and other compilers when targeting certain platforms.


1 Answers

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

like image 58
Casey Avatar answered Oct 05 '22 23:10

Casey