c++visual-studiofloating-pointcompiler-bugmsvc12

VS2013 : compiler bug with float and /EHa + /fp:strict?


We just moved from VS2010 to VS2013 and I found a strange bug and I wonder it might be due to the compiler.

Compiled with the command line cl ConsoleApplication1.cpp /EHa /fp:strict /O2 the following program gives : 0xC0000005: Access violation reading location 0xFFFFFFFF.

This occurs only when compiling to 32 bits (not 64 bits)

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

Am I accusing the compiler to quickly ?


Solution

  • This is an auto-vectorization bug, it dies on an UNPCKLPS instruction that accesses the local Vector2D variable, it is not aligned properly. Basic bug is in the prologue of the function:

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

    The AND instruction is wrong, it aligns the stack to 8 instead of 16. Not good enough to provide the alignment guarantee that SSE2 code requires. The strongest candidate for this bug is /EHa, that prevents IsEqual() from getting optimized away. Perhaps because the optimizer cannot assume that it might not have a side-effect by throwing an SEH exception. Now imposing the alignment requirement.

    You can whack it over the head by explicitly declaring the variables to be aligned:

    __declspec(align(16)) Vector2D v1;
    __declspec(align(16)) Vector2D v2;
    

    And the code optimizer now wisens up to:

    001E16B3  and         esp,0FFFFFFF0h 
    

    By far the most stunning workaround is:

    __declspec(noinline)
    bool IsEqual(Vector2D & a, Vector2D & b) {
        // etc..
    }
    

    And the optimizer now decides to remove the unnecessary IsEqual() call, thus removing the alignment requirement. Hehe. Optimizer bugs have a strong habit of behaving whacko like this.

    This bug does not occur in VS2015, whether it was truly addressed is hard to tell because it generates a very different prologue that appears to assume that the main() function is already entered with an aligned stack. You can file the bug at connect.microsoft.com if you want to hear it from the horse's mouth.