1 votes

S'agit-il d'un bogue dans l'optimisation globale de MS CL (/Og) ?

Voici un programme très simplifié qui reproduit le problème que j'ai rencontré dans l'application réelle :

#include "stdlib.h"
typedef unsigned short int shft;
struct xptr  {
    int layer;
    void *  addr;

    xptr() : layer(0), addr(NULL) {}
    xptr(int _layer, void *_addr) : layer(_layer), addr(_addr) {}

    xptr(const xptr& x) { layer = x.layer; addr = x.addr; }

/* Uncomment this to remove the bug */
/*   const xptr& operator= (const xptr& rhs) 
    {
        if (this != &rhs) {
            this->addr = rhs.addr;
            this->layer = rhs.layer;
        }
        return *this;
    }*/
};

struct n_dsc {
    xptr    pdsc;      
    xptr    ldsc;      
    xptr    rdsc;      
};

void dm_string_value_traverse(xptr node)
{    
    xptr p = node;    
    while (p.addr != 0)
    {        
        p = ((n_dsc*)p.addr)->rdsc;    
    }
}

int main()
{
    n_dsc n1, n2;

    n1.rdsc = xptr(0, &n2);
    xptr n = xptr(0, &n1);

    dm_string_value_traverse(n);
}

Je n'ai absolument pas pu comprendre pourquoi CL 2008 (avec /Og /Zi /W4) génère l'assemblage suivant pour la fonction "dm_string_value_traverse" :

    while (p.addr != 0)
004ABAC5  mov         eax,dword ptr [esp+10h] 
004ABAC9  add         esp,0Ch 
004ABACC  test        eax,eax 
004ABACE  je          dm_string_value_traverse+5Fh (4ABADFh) 
    {
        p = ((n_dsc*)p.addr)->rdsc;
004ABAD0  mov         ecx,dword ptr [eax+1Ch] 
004ABAD3  mov         dword ptr [esp],ecx 
004ABAD6  mov         eax,dword ptr [eax+20h] 
004ABAD9  mov         dword ptr [esp+4],eax 
004ABADD  jmp         dm_string_value_traverse+50h (4ABAD0h) ;NOTE THIS JMP TO THE ASSIGNMENT
    }

}

Note :

  1. La condition (p.addr != 0) n'est vérifiée qu'une seule fois ! Voir 004ABADD saute inconditionnellement à 004ABAD0 (affectation).
  2. Sans /Og le compilateur génère le bon code.
  3. Si vous décommentez le constructeur de copie, le compilateur générera également un bon code.

Est-il possible de comprendre pourquoi cela se passe ainsi ? Existe-t-il un moyen de contourner ce problème ?

2voto

Oren Trutner Points 12125

Ça ressemble à un optimiseur trop zélé. Je peux confirmer le comportement que vous décrivez avec Visual Studio 2008 SP1, /Og, et /Fa pour la sortie d'assemblage. Ce ne serait pas la première fois pour VC : essayez google visual c++ "/Og" site:support.microsoft.com .

Une solution de contournement consiste à itérer avec un pointeur xptr, au lieu d'une valeur xptr. Cela a également l'effet secondaire bénéfique de réduire le nombre d'octets copiés à chaque itération de 8 (valeur xptr) à 4 (pointeur xptr).

void dm_string_value_traverse(xptr node)
{    
    xptr *p = &node;    
    while (p->addr != 0)
    {        
        p = &(((n_dsc*)(p->addr))->rdsc);    
    }
}

Le code d'assemblage résultant, avec /Og, ressemble maintenant à ceci. Je ne peux pas faire correspondre exactement l'assemblage avec le code source, car la comparaison entre le code source et l'assemblage n'est pas possible. (p->addr != 0) se passe maintenant à deux endroits. Il est clair, cependant, que la boucle inclut maintenant un test pour sa condition finale.

; prolog
      push  ebp
      mov   ebp, esp
; while (p->addr != 0)
      mov   eax, DWORD PTR _node$[ebp+4]
      test  eax, eax
      je    SHORT $LN1@dm_string_
      npad  6
; p = &(((n_dsc*)p->addr)->rdsc);
$LL2@dm_string_:
      mov   eax, DWORD PTR [eax+20]
      test  eax, eax
      jne   SHORT $LL2@dm_string_
$LN1@dm_string_:
; epilog
      pop   ebp
      ret   0

Étant donné la difficulté de résoudre ce type de bogue, il se peut que des problèmes similaires se cachent dans le code. Pour la partie du code qui traite des xptr's et des n_dsc's, vous pourriez envisager de vous passer de /Og, ou de faire des tests unitaires tout autour.

0voto

Matthew Iselin Points 5843

Vous devriez peut-être essayer de définir "p" comme volatile :

volatile xptr p = node;    
while (p.addr != 0)
{        
    p = ((n_dsc*)p.addr)->rdsc;    
}

Cela devrait empêcher l'optimiseur d'éviter la comparaison.

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