VS2013: ошибка компилятора с float и /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;
}

Я обвиняю компилятор в быстром?


person BenjaminB    schedule 04.11.2015    source источник
comment
Код выглядит хорошо, на мой взгляд. Я бы пошел с ошибкой компилятора. Продолжайте удалять биты, чтобы минимизировать код, и сообщайте об ошибке. Вероятно, вы можете удалить строку из foo и height и немного упростить IsEqual.   -  person Mooing Duck    schedule 04.11.2015
comment
"" привязка к std::string& является неопределенным поведением, удалите этот бит и подтвердите, что ошибка все еще возникает   -  person Mooing Duck    schedule 04.11.2015
comment
@MooingDuck ошибка все еще возникает   -  person BenjaminB    schedule 04.11.2015
comment
@MooingDuck Если я перестану называть высоту и ширину и просто назову .x и .y, ошибка не возникнет. То же самое, если я упрощу IsEqual   -  person BenjaminB    schedule 04.11.2015
comment
Нет, не переставайте называть высоту и ширину, уберите только один. Вероятно, вам нужен только один для воспроизведения. Это воспроизводится? ideone.com/qhum2q Попробуйте выяснить Минимальный пример проверки.   -  person Mooing Duck    schedule 04.11.2015
comment
@MooingDuck нет, это не так. Ошибка появилась в огромной базе кода, это минимальный проверяемый пример, который я получаю. Я могу попытаться удалить некоторые мелочи, но, например, если я уберу GetMsg(),, вылет исчезнет   -  person BenjaminB    schedule 04.11.2015
comment
Ошибка появляется в VS2013, но не в VS2015.   -  person doug    schedule 05.11.2015


Ответы (1)


Это ошибка автоматической векторизации, она умирает на инструкции UNPCKLPS, которая обращается к локальной переменной Vector2D, она не выровнена должным образом. Основная ошибка находится в прологе функции:

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, если хотите услышать ее из первых уст.

person Hans Passant    schedule 05.11.2015
comment
Большое спасибо Гансу за очень подробный анализ! FIY мы подали отчет об ошибке в Microsoft здесь: connect.microsoft.com/VisualStudio/feedback/ детали/1984067 - person Pascal T.; 05.11.2015