Visual Studio 2015 更新 3 - C++ 编译器错误?
Visual Studio 2015 Update 3 - C++ Compiler bug?
我们观察到一个奇怪的情况,在 VS2015 Update3 中,编译器会无缘无故地省略部分代码。
我们发现
- 这发生在 VS2015 Update3(帮助|关于说 14.0.25431.01 Update 3,cl.exe 版本 19.00.24215.1)
- 这不会发生在 VS2015 Update2 中(帮助|关于说 14.0.25123.00 Update 2,cl.exe 版本 19.00.23918)
- 这仅在打开优化时发生(例如,在默认发布配置中)
- 发生在 x86 和 x64 中
- 将代码片段插入全新 "Win32 Console Application" 时发生(我的意思是,不需要花哨的命令行选项)
我们设法将罪魁祸首代码最小化为该代码段:
#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
在线试用!
- 转到http://webcompiler.cloudapp.net/
- 将示例代码放入编辑器
- 将
/O2
放到Additional compiler flags
- 勾选
Run executable after compilation
是的,这是一个错误。具体来说,这是 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
我们观察到一个奇怪的情况,在 VS2015 Update3 中,编译器会无缘无故地省略部分代码。
我们发现
- 这发生在 VS2015 Update3(帮助|关于说 14.0.25431.01 Update 3,cl.exe 版本 19.00.24215.1)
- 这不会发生在 VS2015 Update2 中(帮助|关于说 14.0.25123.00 Update 2,cl.exe 版本 19.00.23918)
- 这仅在打开优化时发生(例如,在默认发布配置中)
- 发生在 x86 和 x64 中
- 将代码片段插入全新 "Win32 Console Application" 时发生(我的意思是,不需要花哨的命令行选项)
我们设法将罪魁祸首代码最小化为该代码段:
#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
在线试用!
- 转到http://webcompiler.cloudapp.net/
- 将示例代码放入编辑器
- 将
/O2
放到Additional compiler flags
- 勾选
Run executable after compilation
是的,这是一个错误。具体来说,这是 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