我们观察到一个奇怪的情况,在 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;
}
对于“省略循环”的行,编译器省略了整个循环体。为什么?据我所知,不存在未定义的行为。
第一个“循环省略”的反汇编:
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
在线尝试!
/O2
置于 Additional compiler flags
Run executable after compilation
是的,这是一个错误。具体来说,这是 VS2015 Update 3 中引入的新 SSA 优化器中的一个错误。未记录的命令行选项
-d2SSAOptimizer-
告诉编译器后端使用旧的优化器,这会导致该错误不明显。
仅供参考,您可以将重现最小化为:
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的答案应该是答案): 我收到了 Microsoft 的回复(Gratian Lup,博客文章介绍新的高级 Visual C++ 代码优化器的作者):
是的,这确实是 SSA 优化器本身的一个错误 - 通常是大多数 报告的新优化器中的错误存在于其他部分, 有时20年后现在会暴露出来。它在一个小选项中。试图删除看起来像(a - Const1) CMP (a - Const2),如果没有溢出。问题是您的代码具有 (1 - indexOffset) CMP (2 - indexOffset) 和减法 当然,它是不可交换的 - 但优化器代码忽略了这一点 并像处理 (indexOffset - 1) 一样处理 (1 - indexOffset)。
此问题的修复将在下一个较大的更新中发布 VS2017。在那之前,禁用 SSA 优化器将是一个不错的选择 解决方法。仅禁用此功能的优化可能是 如果它不会太慢的话,这是更好的方法。这可以是 使用 #pragma optimize("", off) 完成: