Подтвердить что ты не робот

Обновление Visual Studio 2015 3 - ошибка компилятора С++?

Мы наблюдаем странный случай, когда в VS2015 компилятор Update3 будет пропускать часть кода без видимой причины.

Мы выяснили, что

  • Это происходит в VS2015 Update3 (Help | About says 14.0.25431.01 Update 3, версия cl.exe 19.00.24215.1)
  • Это не происходит в VS2015 Update2 (Help | About says 14.0.25123.00 Update 2, версия cl.exe 19.00.23918)
  • Это происходит только при включении оптимизации (например, в конфигурации по умолчанию)
  • Случается как в x86, так и в x64
  • Случается, когда фрагмент кода вставлен в новое "Консольное приложение Win32" (я имею в виду, не нужны никакие параметры командной строки)

Нам удалось свести к минимуму код виновника:

#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>

int _tmain(int, _TCHAR*[])
{
    volatile int someVar = 1;

    const int indexOffset = someVar ? 0 : 1;    // Loop omitted
    // const int indexOffset = !someVar;        // Loop omitted
    // const int indexOffset = 0;               // Good
    // const int indexOffset = 1;               // Good
    // const int indexOffset = someVar;         // Good
    // const int indexOffset = someVar + 1;     // Good

    for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
    {
        printf("Test passed\n");
    }

    return 0;
}

Для строк, которые говорят, что "Loop опущен", весь объект цикла исключается компилятором. Зачем? Насколько мне известно, не существует поведения undefined.


Разборка для первого "Loop omitted":

int _tmain(int, _TCHAR*[])
{
01151010  push        ebp  
01151011  mov         ebp,esp  
01151013  push        ecx  
    volatile int someVar = 1;
01151014  mov         dword ptr [ebp-4],1  

    const int indexOffset = someVar ? 0 : 1;    // Loop omitted
0115101B  mov         eax,dword ptr [someVar]  
    // const int indexOffset = !someVar;        // Loop omitted
    // const int indexOffset = 0;               // Good
    // const int indexOffset = 1;               // Good
    // const int indexOffset = someVar;         // Good
    // const int indexOffset = someVar + 1;     // Good

    for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
    {
        printf("Test passed\n");
    }

    system("pause");
0115101E  push        offset string "pause" (011520F8h)  
01151023  call        dword ptr [__imp__system (0115205Ch)]  
01151029  add         esp,4  
    return 0;
0115102C  xor         eax,eax  
}
0115102E  mov         esp,ebp  
01151030  pop         ebp  
01151031  ret

Проект тестирования: http://dropmefiles.com/S7mwT


Попробуйте в Интернете!

  • Перейдите к http://webcompiler.cloudapp.net/
  • Поместите образец кода в редактор
  • Поместите /O2 в Additional compiler flags
  • Отметьте Run executable after compilation

Отчет об ошибке: https://developercommunity.visualstudio.com/content/problem/71906/compiler-optimization-code-generation-bug.html

4b9b3361

Ответ 1

Да, это ошибка. В частности, это ошибка в новом оптимизаторе SSA, представленном в обновлении VS2015 3. Недопустимая опция командной строки -d2SSAOptimizer- указывает компилятору использовать вместо этого старый оптимизатор, что приводит к тому, что ошибка не отображается.

FYI, вы можете свести к минимуму ваш репродукт:

int main()
{
    volatile int someVar = 1;

    const int indexOffset = someVar ? 0 : 1;

    for (int i = 1 - indexOffset; i < 2 - indexOffset; ++i)
    {
        return 0;
    }
    return 1;
}

который поможет разработчикам компилятора быстрее локализовать проблему.


Добавление из Codeguard (я решил, что ответ Casey должен быть ответом): Я получил ответ от Microsoft (Gratian Lup, автор сообщения в блоге Представляем новый, усовершенствованный оптимизатор кода Visual С++):

Да, это действительно ошибка в самом оптимизаторе SSA - обычно большинство ошибки, о которых сообщается в новом оптимизаторе, находятся в других частях, иногда подвергается воздействию через 20 лет.

Это в небольшой выбор. который пытается удалить сравнение, похожее на (a - Const1) CMP (a - Const2), если переполнение отсутствует. Проблема в том, что ваш код имеет (1 - indexOffset) CMP (2 - indexOffset) и вычитание конечно, не является коммутативным, но код оптимизатора игнорирует это и обрабатывает (1 - indexOffset), как если бы он (indexOffset - 1).

Исправление для этой проблемы будет выпущено в следующем более крупном обновлении для VS2017. До тех пор, отключение SSA Optimizer было бы достойным обходной путь. Отключение оптимизации только для этой функции может быть лучший подход, если он не слишком замедляет работу. Это может быть сделано С#pragma optimize ( ", off): https://msdn.microsoft.com/en-us/library/chh3fb0k.aspx