Visual Studio版本:17.7.1(MSVC 19.37.32822)
使用默认设置和编译器标志新创建的项目。
最小可重现性示例:
#include <cstdio>
__declspec(noinline) void test2(char** data)
{
// After moving the pointer:
// data_1 now points to data[1] = 1
// data_0 now points to data[0] = 2
*data += 1;
}
__declspec(noinline) void test(char* data_1)
{
char* data_0 = data_1;
test2(&data_1);
int len = (int)(data_1 - data_0);
if (*data_1 & 1)
{
if (*data_0 & 2)
printf("good\n");
}
}
int main()
{
char data[2];
data[0] = 2;
data[1] = 1;
test(data);
return 0;
}
字符串
发布中未打印“良好”行|x64配置。
发布者|x86产生预期结果。
我做了一些实验,并查看了生成的汇编,以便将原始代码减少到这个MRE。
问题的根本原因似乎是编译器假设test2()
中的data1
保持不变。因此,可以省略data_0 = data_1
行,并且可以使用data_1
代替表达式(*data_0 & 2)
中的data_0
。
每一行都是必要的,包括将(data_1 - data_0)
转换为int,这可能解释了在x86构建中缺少bug再现的原因。
更新1
问题仅发生在x64版本中。通过将/O2更改为/Od或使用#pragma optimize("", off)
来禁用优化可以“修复”问题。
按VS版本细分的重现性:
- 17.7.1 -问题
- 17.7.2 -问题
- ???
- 17.7.6 -没有问题
- 17.8预览7 -没有问题
有可能问题已经解决,或者其复制的条件已经改变。
更新2
一个MSVC开发人员在一次私人谈话中证实了这个问题,我已经提出了这个问题来跟踪进度:https://developercommunity.visualstudio.com/t/Compiler-optimization-bug-in-VS-2022/10512534
1条答案
按热度按时间bihw5rsg1#
我可以确认这里存在相同的行为。此外,任何尝试打印值以检查else(fail)条件的行为都是正确的。Optimizer过于激进。它只获取 *data_1并对其应用两个测试(假设data_1==data_0,当然这不是真的)。
任何打印调试值的修改似乎都会导致正确的行为。我在内部的else子句中添加了printf(“bad data_0”),可以预见的是,它会被打印出来。我更喜欢注解所有路径。
FWIW Intel compiler 2023运行良好,并打印“good”。
吸烟枪- MSC编译器错误过于积极的优化无法加载 *data_0并将两个测试应用于 *data_1。这里是(轻微的添加,不影响错误的行为):
字符串
我仍然对MRE中的关键线的重要性感到有点困惑,该线被优化为不存在,但对于故障显示至关重要!即:
int len =(int)(data_1-data_0);
如果没有这一行,MSC编译器在x64版本中就可以正确执行。下面是在这种情况下生成的正确代码的反汇编:
型
这个故事的寓意是,小心做那些让优化编译器感到困惑的事情!