2

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;
}

私はコンパイラをすぐに非難していますか?

4

1 に答える 1

4

これは自動ベクトル化のバグです。ローカルの Vector2D 変数にアクセスする UNPCKLPS 命令で停止し、適切に配置されていません。基本的なバグは関数のプロローグにあります:

int main(int argc, char* argv[]) {
013A16B0  push        ebp  
013A16B1  mov         ebp,esp  
013A16B3  and         esp,0FFFFFFF8h  

AND 命令は間違っています。スタックを 16 ではなく 8 にアラインします。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 でバグを報告できます。

于 2015-11-05T14:24:14.987 に答える