Visual Studio 2015更新3 - c++编译器bug?

时间:2021-01-28 09:44:30

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

我们注意到一个奇怪的情况,在VS2015 Update3编译器中,由于没有明显的原因,会省略部分代码。

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)
  • 这发生在VS2015 Update3中(帮助|表示14.0.25431.01更新3,cl。exe 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)
  • 这在VS2015 Update2中不会发生(帮助|说14.0.25123.00更新2,cl)。exe 19.00.23918版)
  • This happens only when optimization is turned on (for example, in default Release configuration)
  • 只有在启动优化时才会发生这种情况(例如,在默认的发布配置中)
  • Happens in both x86 and x64
  • 在x86和x64中都有发生。
  • Happens when code snippet is inserted into brand new "Win32 Console Application" (I mean, no fancy commandline options required)
  • 当代码片段插入到全新的“Win32控制台应用程序”时发生(我的意思是,不需要复杂的命令行选项)

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

测试项目:http://dropmefiles.com/S7mwT


Try it online!

试一试在线!

  • Go to http://webcompiler.cloudapp.net/
  • 去http://webcompiler.cloudapp.net/
  • Put sample code to the editor
  • 将示例代码放到编辑器中
  • Put /O2 to Additional compiler flags
  • 将/O2添加到其他编译器标志
  • Check Run executable after compilation
  • 编译后检查运行可执行文件

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

错误报告:https://developercommunity.visualstudio.com/content/problem/71906/compiler-optimization-code-generation-bug.html

1 个解决方案

#1


24  

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.

是的,这是一个错误。具体来说,这是VS2015更新3中引入的新的SSA优化器中的一个bug。未归档的命令行选项- d2ssaoptimizer -告诉编译器后端使用旧的优化器,这会导致错误不显化。

FYI, you can minimize your repro to:

请注意,你可以将你的repro最小化到:

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):

另外,Codeguard(我认为Casey的答案应该是答案):我收到了微软的回复(Gratian Lup,介绍一种新的高级Visual c++代码优化器的博客文章的作者):

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.

是的,这确实是SSA优化器本身的一个bug——通常报告在新优化器中的大多数bug都位于其他部分,有时在20年后会公开。

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 - Const1) CMP (a - Const2),如果没有溢出。问题是您的代码有(1 - indexOffset) CMP (2 - indexOffset),减法当然不是可交换的——但是优化器代码忽略了这一点,并处理(1 - indexOffset),就好像它是(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

这个问题的修正将在2017年VS2017的下一个更大的更新中发布。在此之前,禁用SSA优化器将是一个不错的解决方案。如果只对这个函数禁用优化,而不会使事情变慢,那么这可能是一种更好的方法。这可以通过#pragma optimization (", off)来实现:https://msdn.microsoft.com/en-us/library/chh3fb0k.aspx

#1


24  

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.

是的,这是一个错误。具体来说,这是VS2015更新3中引入的新的SSA优化器中的一个bug。未归档的命令行选项- d2ssaoptimizer -告诉编译器后端使用旧的优化器,这会导致错误不显化。

FYI, you can minimize your repro to:

请注意,你可以将你的repro最小化到:

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):

另外,Codeguard(我认为Casey的答案应该是答案):我收到了微软的回复(Gratian Lup,介绍一种新的高级Visual c++代码优化器的博客文章的作者):

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.

是的,这确实是SSA优化器本身的一个bug——通常报告在新优化器中的大多数bug都位于其他部分,有时在20年后会公开。

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 - Const1) CMP (a - Const2),如果没有溢出。问题是您的代码有(1 - indexOffset) CMP (2 - indexOffset),减法当然不是可交换的——但是优化器代码忽略了这一点,并处理(1 - indexOffset),就好像它是(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

这个问题的修正将在2017年VS2017的下一个更大的更新中发布。在此之前,禁用SSA优化器将是一个不错的解决方案。如果只对这个函数禁用优化,而不会使事情变慢,那么这可能是一种更好的方法。这可以通过#pragma optimization (", off)来实现:https://msdn.microsoft.com/en-us/library/chh3fb0k.aspx