Visual Studio 2015 更新 3 - C++ 编译器错误?

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

我们观察到一个奇怪的情况,在 VS2015 Update3 中,编译器会无缘无故地省略部分代码。

我们发现

我们设法将罪魁祸首代码最小化为该代码段:

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

对于 "Loop omitted" 的行,编译器省略了整个循环体。为什么?据我所知,没有涉及未定义的行为。


第一个反汇编"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

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


在线试用!


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

是的,这是一个错误。具体来说,这是 VS2015 更新 3 中引入的新 SSA 优化器中的一个错误。The undocumented command line option -d2SSAOptimizer- tells the compiler backend to use the old optimizer instead, which causes the bug to not manifest

仅供参考,您可以将重现最小化为:

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

这将有助于编译器开发人员更快地定位问题。


来自 Codeguard 的补充(我决定 Casey 的答案应该是答案): 已收到微软(博客postIntroducing a new, advanced Visual C++ code optimizer作者Gratian Lup)的回复:

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