VS2013:float 和 /EHa + /fp:strict 的编译器错误?
VS2013 : compiler bug with float and /EHa + /fp:strict?
我们刚刚从 VS2010 迁移到 VS2013,我发现了一个奇怪的错误,我想知道这可能是由于编译器造成的。
使用命令行编译 cl ConsoleApplication1.cpp /EHa /fp:strict /O2
以下程序给出:
0xC0000005: Access violation reading location 0xFFFFFFFF.
这仅在编译为 32 位(而非 64 位)时发生
#include <iostream>
#include <cmath>
class Vector2D
{
public:
double x;
double y;
Vector2D() : x(0), y(0) {}
Vector2D(double _x, double _y) : x(_x), y(_y) {}
double Width() { return x; }
double Height() { return y; }
};
bool IsEqual(const double & a, const double & b)
{
if (a == b)
return true;
double tolerance = pow(10., -5);
if (::fabs(a) < tolerance / 2.)
{
return ::fabs(b) < tolerance / 2.;
}
double diff = ::fabs((b - a) / a);
return (diff < tolerance);
}
bool IsEqual(Vector2D & a, Vector2D & b)
{
return IsEqual(a.Width(), b.Width()) && IsEqual(a.Height(), b.Height());
}
std::string GetMsg()
{
return std::string("");
}
int main(int argc, char* argv[])
{
Vector2D v1;
Vector2D v2;
v1 = Vector2D(1, 0);
// This innocent call will cause an access violation
// the access violation occurs *only* if fp:strict and /EHa switches are used
GetMsg(), IsEqual(v1, v2);
return 0;
}
我是在指责编译器太快了吗?
这是一个自动矢量化错误,它死于访问本地 Vector2D 变量的 UNPCKLPS 指令,它没有正确对齐。基本错误在函数的序言中:
int main(int argc, char* argv[]) {
013A16B0 push ebp
013A16B1 mov ebp,esp
013A16B3 and esp,0FFFFFFF8h
AND 指令错误,它将堆栈对齐到 8 而不是 16。不足以提供 SSE2 代码所需的对齐保证。此错误的最强候选者是 /EHa,它阻止 IsEqual() 被优化掉。可能是因为优化器无法假定抛出 SEH 异常不会产生副作用。现在强加对齐要求。
您可以通过显式声明要对齐的变量来克服它:
__declspec(align(16)) Vector2D v1;
__declspec(align(16)) Vector2D v2;
代码优化器现在智能化了:
001E16B3 and esp,0FFFFFFF0h
到目前为止,最令人惊叹的解决方法是:
__declspec(noinline)
bool IsEqual(Vector2D & a, Vector2D & b) {
// etc..
}
并且优化器现在决定删除不必要的 IsEqual() 调用,从而删除对齐要求。呵呵。优化器错误有这样的行为习惯。
此错误不会出现在 VS2015 中,很难判断它是否真正得到解决,因为它生成了一个非常不同的序言,似乎假设 main() 函数已经进入对齐堆栈。如果您想从马口中听到错误,可以在 connect.microsoft.com 提交错误。
我们刚刚从 VS2010 迁移到 VS2013,我发现了一个奇怪的错误,我想知道这可能是由于编译器造成的。
使用命令行编译 cl ConsoleApplication1.cpp /EHa /fp:strict /O2
以下程序给出:
0xC0000005: Access violation reading location 0xFFFFFFFF.
这仅在编译为 32 位(而非 64 位)时发生
#include <iostream>
#include <cmath>
class Vector2D
{
public:
double x;
double y;
Vector2D() : x(0), y(0) {}
Vector2D(double _x, double _y) : x(_x), y(_y) {}
double Width() { return x; }
double Height() { return y; }
};
bool IsEqual(const double & a, const double & b)
{
if (a == b)
return true;
double tolerance = pow(10., -5);
if (::fabs(a) < tolerance / 2.)
{
return ::fabs(b) < tolerance / 2.;
}
double diff = ::fabs((b - a) / a);
return (diff < tolerance);
}
bool IsEqual(Vector2D & a, Vector2D & b)
{
return IsEqual(a.Width(), b.Width()) && IsEqual(a.Height(), b.Height());
}
std::string GetMsg()
{
return std::string("");
}
int main(int argc, char* argv[])
{
Vector2D v1;
Vector2D v2;
v1 = Vector2D(1, 0);
// This innocent call will cause an access violation
// the access violation occurs *only* if fp:strict and /EHa switches are used
GetMsg(), IsEqual(v1, v2);
return 0;
}
我是在指责编译器太快了吗?
这是一个自动矢量化错误,它死于访问本地 Vector2D 变量的 UNPCKLPS 指令,它没有正确对齐。基本错误在函数的序言中:
int main(int argc, char* argv[]) {
013A16B0 push ebp
013A16B1 mov ebp,esp
013A16B3 and esp,0FFFFFFF8h
AND 指令错误,它将堆栈对齐到 8 而不是 16。不足以提供 SSE2 代码所需的对齐保证。此错误的最强候选者是 /EHa,它阻止 IsEqual() 被优化掉。可能是因为优化器无法假定抛出 SEH 异常不会产生副作用。现在强加对齐要求。
您可以通过显式声明要对齐的变量来克服它:
__declspec(align(16)) Vector2D v1;
__declspec(align(16)) Vector2D v2;
代码优化器现在智能化了:
001E16B3 and esp,0FFFFFFF0h
到目前为止,最令人惊叹的解决方法是:
__declspec(noinline)
bool IsEqual(Vector2D & a, Vector2D & b) {
// etc..
}
并且优化器现在决定删除不必要的 IsEqual() 调用,从而删除对齐要求。呵呵。优化器错误有这样的行为习惯。
此错误不会出现在 VS2015 中,很难判断它是否真正得到解决,因为它生成了一个非常不同的序言,似乎假设 main() 函数已经进入对齐堆栈。如果您想从马口中听到错误,可以在 connect.microsoft.com 提交错误。