[英]Possible compiler bug in Visual C++ 2012 (x86)?
我正在使用VC ++ 11(CTP Update 1)编译x86目标时遇到随机浮点错误。 请参阅下面的简短示例“test.cpp”,并使用以下命令进行编译:
cl /GL /O2 /EHsc test.cpp /link /MACHINE:X86
输出应该是10 == 10
,但是当启用/GL
(整个程序优化)时它会产生10 == 0
。 问题似乎是get_scaling_factor()
将结果推送到浮点堆栈,但调用函数期望它在SSE寄存器XMM0中 。
问题 :我错过了一些明显的东西,还是这真的是一个错误? 当然,测试程序没有意义,因为它是一个简化的测试用例。
TEST.CPP:
#include <iostream>
template <typename T>
inline T get_scaling_factor(int units)
{
switch (units)
{
case 0: return 1;
case 1: return 10;
case 2: return 100;
case 3: return 1000;
case 4: return 10000;
case 5: return 100000;
case 6: return 1000000;
case 7: return 10000000;
case 8: return 100000000;
case 9: return 1000000000;
default: return 1;
}
}
template <int targetUnits, typename T>
inline T scale(T value, int sourceUnits)
{
return value * get_scaling_factor<T>(sourceUnits)
/ get_scaling_factor<T>(targetUnits);
}
__declspec(noinline)
double scale(double value, int units)
{
return scale<9>(value, units);
}
int main()
{
std::cout << "10 = " << scale(1e9, 1) << std::endl;
}
问题由Microsoft确认 。 它甚至会影响这样的直接代码:
#include <stdio.h>
double test(int a)
{
switch (a)
{
case 0: return 1.0;
case 1: return 10.0;
case 2: return 100.0;
case 3: return 1000.0;
case 4: return 10000.0;
case 5: return 100000.0;
case 6: return 1000000.0;
case 7: return 10000000.0;
case 8: return 100000000.0;
case 9: return 1000000000.0;
default: return 1.0;
}
}
void main()
{
int nine = 9;
double x = test(nine);
x /= test(7);
int val = (int)x;
if (val == 100)
printf("pass");
else
printf("fail, val is %d", val);
}
是的,这绝对是一个代码优化器错误,我没有遇到麻烦。 优化程序错误通常与内联相关,但事实并非如此。 VS2012中的重型代码更改引入了这个错误,支持新的自动矢量化功能。
简而言之,get_scaling_factor()函数返回FPU堆栈上的结果。 代码生成器正确地发出指令以从堆栈中检索它并将其存储在XMM寄存器中。 但是优化器不合适地完全删除了该代码,就好像它假设函数结果已经存储在XMM0中一样。
很难找到一种解决方法,专门针对double的模板函数没有效果。 使用#pragma optimize禁用优化工作:
#pragma optimize("", off)
__declspec(noinline)
double scale(double value, int units)
{
return scale<9>(value, units);
}
#pragma optimize("", on)
你的repro代码非常好,微软将毫无困难地解决这个问题。 您可以在connect.microsoft.com上提交反馈报告,只需链接到此问题即可。 或者,如果您赶时间,那么您可以联系Microsoft支持,但我想他们会给您相同的解决方法,以便您使用该服务包。
更新:在VS2013中修复。
/GL
按设计忽略默认调用约定。 使用LTCG,编译器/链接器知道整个调用图,因此它可以匹配调用者和被调用者。 在这种情况下,使用SSE寄存器并不奇怪。
我不完全确定你的意思是“ get_scaling_factor()
将结果推送到浮点堆栈”。 你的意思是编译器无法内联它吗? 我希望编译器这样做,因为调用图只有一个调用者。 (我们知道`get_scaling_factor(targetUnits)是内联的,因为否则会导致除零(否则)
如果编译器确实无法内联get_scaling_factor()
,那么您实际上发现了两个错误:一个内联失败,一个自定义调用约定失败。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.