29 votes

Possible bogue du compilateur dans Visual C ++ 2012 (x86)?

Je suis actuellement l'expérience aléatoire à virgule flottante erreurs lors de la compilation pour les architectures x86 cibles à l'aide de VC++ 11 (CTP mise à Jour 1). Voir l'exemple court "test.cpp" ci-dessous, et de le compiler avec:

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

La sortie doit être en 10 == 10, mais il produit 10 == 0 lorsque /GL (pour l'ensemble du programme d'optimisation) est activé. Le problème semble être que l' get_scaling_factor() pousse le résultat à virgule flottante, la pile, mais la fonction d'appel est attendue dans le cadre de l'ESS s'inscrire XMM0.

Question: suis-je raté quelque chose d'évident, ou est-ce vraiment un bug? Le programme de test, bien sûr, ne fait pas de sens, car il est dépouillé de cas de test.

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

Mise à jour

Problème confirmé par Microsoft. Elle affecte directe au code comme ceci:

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

23voto

Hans Passant Points 475940

Oui, c'est vraiment un optimiseur de code bug et je n'ai eu aucune difficulté à le reproduire. L'optimiseur de bugs sont habituellement associés avec l'in-lining, mais ce n'est pas le cas ici. Ce bug était introduit par la lourde code-gen changements dans VS2012 de la nouvelle auto-vectorisation de fonctionnalité.

En un mot, la get_scaling_factor() renvoie le résultat sur la pile FPU. Le générateur de code correctement émet les instructions pour récupérer à partir de la pile et de le stocker dans un registre XMM. Mais l'optimiseur inapproprié supprime que le code entièrement, comme s'il suppose que le résultat de la fonction est déjà stocké dans XMM0.

Une solution de contournement est dur à trouver, spécialiste de la fonction de modèle pour le double n'a pas d'effet. La désactivation de l'optimisation avec #pragma optimiser œuvres:

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

Votre repro code est très bonne et Microsoft n'aura pas de difficulté de la résolution de ce bug à partir de ce. Vous pouvez déposer un rapport d'information à connect.microsoft.com, juste un lien à cette question. Ou si vous êtes pressé, vous pouvez alors contacter le Support de Microsoft, bien que j'imagine qu'ils vont vous donner la solution est la même pour le service pack.


Mise à JOUR: correction de VS2013.

1voto

MSalters Points 74024

/GL ignore par défaut de conventions d'appel, de par leur conception. Avec LTCG, le compilateur/linker sait sur l'ensemble du graphe d'appel afin qu'il corresponde à l'appelant et l'appelé. À l'aide d'un ESS registre n'est pas bizarre dans ce contexte.

Je ne suis pas entièrement sûr de ce que vous entendez par "get_scaling_factor() pousse le résultat à virgule flottante, la pile", cependant. Voulez-vous dire que le compilateur ne parvient pas à l'inclure? J'attends le compilateur de le faire, puisque le graphe d'appel n'a qu'un seul interlocuteur. (Nous savons que " get_scaling_factor(targetUnits) a été incorporé, car cela aurait provoqué une division par zéro sinon)

Si le compilateur, en effet, ne parvient pas à inline get_scaling_factor(), alors vous avez trouvé deux bugs: Un inline l'échec, et d'un autre convention d'appel d'échec.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X