Posible error del compilador en Visual C++ 2012 (x86)?


Actualmente estoy experimentando errores de coma flotante aleatorio al compilar para destinos x86 usando VC++ 11 (CTP Update 1). Ver el breve ejemplo " test.cpp " abajo, y compilar usando:

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

La salida debe ser 10 == 10, pero produce 10 == 0 cuando /GL (optimización de todo el programa) está habilitado. El problema parece ser que get_scaling_factor() empuja el resultado en la pila de coma flotante, pero la función que llama lo espera en el registro SSE XMM0 .

Pregunta : ¿me estoy perdiendo algo obvio, o es realmente un error? El programa de prueba, por supuesto, no tiene sentido, ya que es un caso de prueba reducido.

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

Actualizar

Problema confirmado por Microsoft. Incluso afecta código directo como este:

#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);
}
Author: ildjarn, 2012-10-24

2 answers

Sí, este es definitivamente un error de optimizador de código y no tuve problemas para reproducirlo. Los errores del optimizador generalmente se asocian con la inserción, pero ese no es el caso aquí. Este error fue introducido por los pesados cambios de generación de código en VS2012 que admiten la nueva función de vectorización automática.

En pocas palabras, la función get_scaling_factor() devuelve el resultado en la pila FPU. El generador de código emite correctamente la instrucción para recuperarlo de la pila y almacenarlo en un registro XMM. Pero el optimizador inapropiado elimina ese código por completo, como si asumiera que el resultado de la función ya estaba almacenado en XMM0.

Es difícil encontrar una solución alternativa, la especialización de la función de plantilla para double no tiene efecto. Deshabilitar la optimización con # pragma optimize funciona:

#pragma optimize("", off)
__declspec(noinline)
double scale(double value, int units) 
{
    return scale<9>(value, units);
}
#pragma optimize("", on)

Su código de reproducción es muy bueno y Microsoft no tendrá problemas para corregir este error. Puede presentar un informe de comentarios en connect.microsoft.com, solo enlaza a esta pregunta. O si tienen prisa, entonces puede ponerse en contacto con el soporte de Microsoft, aunque me imagino que le darán la misma solución para durar hasta el service pack.


ACTUALIZACIÓN: arreglado en VS2013.

 23
Author: Hans Passant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-07 18:25:52

/GL ignora las convenciones de llamada predeterminadas, por diseño. Con LTCG, el compilador / enlazador conoce todo el gráfico de llamadas para que pueda coincidir con el llamante y el destinatario. Usar un registro SSE no es raro en ese contexto.

No estoy completamente seguro de lo que quieres decir con "get_scaling_factor() empuja el resultado en la pila de punto flotante", sin embargo. ¿Quiere decir que el compilador falla al insertarlo? Espero que el compilador lo haga, ya que el gráfico de llamadas solo tiene una persona que llama. (Sabemos que " get_scaling_factor(targetUnits) estaba en línea, ya que eso habría causado una división por cero de lo contrario)

Si el compilador de hecho falla al inline get_scaling_factor(), entonces de hecho ha encontrado dos errores: Un error de inlining, y un error de convención de llamada personalizada.

 1
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-24 16:53:17