Возможная ошибка компилятора в Visual C ++ 2012 (x86)?

Я в настоящее время испытываю случайные ошибки с плавающей запятой при компиляции для целей x86, используя VC ++ 11 (CTP-обновление 1). Посмотрите короткий пример «test.cpp» ниже, и скомпилируйте с помощью:

cl /GL /O2 /EHsc test.cpp /link /MACHINE:X86

Выход должен быть 10 == 10, но это производит 10 == 0 когда /GL (оптимизация всей программы) включена. Кажется, проблема в том, что 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);
}

29

Решение

Да, это определенно ошибка оптимизатора кода, и у меня не было проблем с ее воспроизведением. Ошибки оптимизатора обычно связаны с встраиванием, но здесь это не так. Эта ошибка появилась из-за серьезных изменений в генераторе кода в 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)

Ваш репро-код очень хорош, и у Microsoft не будет проблем с исправлением этой ошибки. Вы можете оставить отзыв на сайте connect.microsoft.com, просто перейдите по ссылке на этот вопрос. Или, если вы спешите, вы можете связаться со службой поддержки Microsoft, хотя я думаю, что они предоставят вам тот же обходной путь, что и последний пакет обновления.


ОБНОВЛЕНИЕ: исправлено в VS2013.

23

Другие решения

/GL по умолчанию игнорирует соглашения о вызовах. С LTCG компилятор / компоновщик знает обо всем графе вызовов, поэтому он может соответствовать вызывающему и вызываемому объектам. Использование регистра SSE не странно в этом контексте.

Я не совсем уверен, что вы имеете в виду «get_scaling_factor() «помещает результат в стек с плавающей запятой», хотя. Вы имеете в виду, что компилятору не удается встроить его? Я ожидаю, что компилятор сделает это, поскольку у графа вызовов есть только один вызывающий объект. (Мы знаем, что get_scaling_factor (targetUnits) был встроенный, так как это вызвало бы деление на ноль в противном случае)

Если компилятор действительно не встраивается get_scaling_factor(), тогда вы на самом деле обнаружили две ошибки: одну ошибку вставки и одну ошибку пользовательского соглашения о вызовах.

1