IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

Assembleur inline & FPO - VC++ 6.0


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 464
    Points : 542
    Points
    542
    Par défaut Assembleur inline & FPO - VC++ 6.0
    Bonjour,

    J'ai déjà déposé ce post sur le forum Visual C++ puisque le problème évoqué porte sur l'optimiseur du Visual; mais le contexte étant orienté assembleur, il n'a pas vraiment trouvé d'écho. J'espère que ça parlera plus aux habitués du forum asm...

    Rapidement, ma config: VC++ 6.0 SP5 et PP5, sous XP SP2.

    Donc, mon problème est relativement simple pour qui a déjà fait de l'assembleur inline dans le Visual 6 ou .NET: je travaille actuellement sur une application de video temps réel, application pour laquelle je dois optimiser en assembleur un certain nombre de fonctions critiques. Pour ce faire je souhaiterais pouvoir utiliser le maximum de registres processeur à ma disposition, c'est à dire 7 (pas de SSE, 3DNow etc. ici), ce qui inclut EBP; or il se trouve que ce registre fait l'objet d'une utilisation un peu particulière de la part des compilateurs x86, puisqu'il sert fréquemment de pointeur de frame de pile. Pour ceux qui l'ignorent, la frame de pile sert notamment à stocker temporairement les variables automatiques (locales) d'une fonction C/C++, et le pointeur de frame est donc là pour les référencer. Mais il existe par ailleurs une option de compilation qui permet de libérer le registre EBP et oblige le compilateur à utiliser directement ESP à la place pour accéder aux variables locales: c'est moins pratique sur ce plan là, mais ça présente l'intérêt de disposer d'un registre supplémentaire pour implémenter la fonction, ce qui n'est pas du luxe sous IA-32. Ce mode est souvent dénommé FPO (comme Frame Pointer Omission, ou omission du pointeur de frame en bon françois).

    Voilà pour la théorie, j'en reviens donc plus concrètement à mon problème: dans le Visual, lorsqu'on active le FPO, le prologue qui initialise EBP et l'épilogue qui le restaure disparaissent bien, sauf que dès qu'on utilise EBP à des fins personnelles dans un bloc __asm de la fonction, ils reviennent aussi sec, et les accès aux variables se font à nouveau par EBP ! J'ai fait le test en essayant toutes les combinaisons possibles de paramètres d'optimisation, mais le problème est resté entier: impossible dans un bloc asm inline d'à la fois avoir accès aux variables locales C/C++, et de pouvoir utiliser librement EBP. Pourtant, si on observe le code asm généré par le compilateur pour des fonctions purement C/C++, avec les mêmes options d'optimisation, on s'aperçoit que le registre EBP est utilisé à outrance comme on voudrait pouvoir le faire dans les blocs asm, et que tous les accès aux variables locales se font directement par ESP...

    Comme un exemple vaut 1000 explications, voici une fonction totalement bidon, mais qui a le mérite de mettre en évidence le problème.

    Code source de la fonction d'exemple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void fpo_test (int titi)
    {
    	int	toto, tutu;
     
    	__asm
    	{
    		mov		eax,	titi
    		mov		toto,	eax
    		mov		tutu,	eax
    	}
    }
    Compilée en mode debug standard (sans FPO), ça donne:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
            push    ebp
            mov     ebp,                esp
            sub     esp,                8
     
            mov     eax,                DWORD PTR [ebp+8]       ; mov   eax,    titi
            mov     DWORD PTR [ebp-8],  eax                     ; mov   toto,   eax
            mov     DWORD PTR [ebp-4],  eax                     ; mov   tutu,   eax
     
            mov     esp,                ebp
            pop     ebp
            ret     0
    On a bien un pointeur de frame créé en début de fonction et affecté à EBP.

    Même fonction, compilée cette fois avec FPO:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            sub     esp,                8
     
            mov     eax,                DWORD PTR [esp+12]      ; mov   eax,    titi
            mov     DWORD PTR [esp+4],  eax                     ; mov   toto,   eax
            mov     DWORD PTR [esp],    eax                     ; mov   tutu,   eax
     
            add     esp,                8
            ret     0
    Là encore le résultat est conforme aux attentes: EBP a bien disparu et les accès à la pile se font directement au travers de ESP.

    Maintenant, comme le but du jeu est je le rappelle de s'approprier EBP, on ajoute une ligne au code source de la fonction initiale:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void fpo_test (int titi)
    {
        int     toto, tutu;
     
        __asm
        {
                xor       ebp,    ebp       // nouvelle instruction
                mov       eax,    titi
                mov       toto,   eax
                mov       tutu,   eax
        }
    }
    (EBP n'est volontairement pas préservé sur la pile pour éviter d'alourdir le code d'exemple).

    Résultat compilé avec FPO (avec strictement la même config que juste avant), et là, surprise:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
            push    ebp
            mov     ebp,            esp
            sub     esp,            8
     
            xor     ebp,            ebp
            mov     eax,            DWORD PTR [ebp+8]       ; mov   eax,    titi
            mov     DWORD PTR       [ebp-8], eax            ; mov   toto,   eax
            mov     DWORD PTR       [ebp-4], eax            ; mov   tutu,   eax
     
            mov     esp, ebp
            pop     ebp
            ret     0
    On voit que le compilateur a remis EBP en service alors qu'on ne lui a rien demandé...

    Voilà, toute idée ou suggestion sera la bienvenue.

    Merci de votre aide, et désolé pour la longueur du post
    "La forme même des Pyramides prouve que de tous temps, les ouvriers n'ont jamais pensé qu'à en faire de moins en moins."

    G. CLEMENCEAU

  2. #2
    sdx
    sdx est déconnecté
    Membre régulier Avatar de sdx
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    106
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 106
    Points : 90
    Points
    90
    Par défaut
    Bonjour,

    je ne suis pas parvenu à la retrouver, mais il me semble que VisualC++ propose une syntaxe pour coder entièrement une fonction en assembleur. Le compilateur ne touche donc pas du tout au code que tu donnes. Tu contrôles ainsi toi-même la récupération des arguments et les retours de valeurs, et par la même l'utilisation des registres

    J'ai cherché sur le forum ... rien ! Et sur google non plus : pourtant j'ai déjà utilisé ça, j'en suis sur

    peut être que dans le manuel de VC++, on peut trouver cela ...

    en tout cas, bon courage

  3. #3
    Responsable Pascal, Lazarus et Assembleur


    Avatar de Alcatîz
    Homme Profil pro
    Ressources humaines
    Inscrit en
    Mars 2003
    Messages
    7 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ressources humaines
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2003
    Messages : 7 938
    Points : 59 417
    Points
    59 417
    Billets dans le blog
    2
    Par défaut
    Bonjour !

    Peut-être pourrais-tu caler un sélecteur de segment sur le sommet de la pile et faire référence aux variables par leur offset par la suite ?
    (j'espère ne pas dire une c***ie car je ne suis pas très sûr de moi en mode protégé ).
    Règles du forum
    Cours et tutoriels Pascal, Delphi, Lazarus et Assembleur
    Avant de poser une question, consultez les FAQ Pascal, Delphi, Lazarus et Assembleur
    Mes tutoriels et sources Pascal

    Le problème en ce bas monde est que les imbéciles sont sûrs d'eux et fiers comme des coqs de basse cour, alors que les gens intelligents sont emplis de doute. [Bertrand Russell]
    La tolérance atteindra un tel niveau que les personnes intelligentes seront interdites de toute réflexion afin de ne pas offenser les imbéciles. [Fiodor Mikhaïlovitch Dostoïevski]

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 464
    Points : 542
    Points
    542
    Par défaut
    Peut-être pourrais-tu caler un sélecteur de segment sur le sommet de la pile et faire référence aux variables par leur offset par la suite ?
    Il n'est pas nécessaire de jouer avec les sélecteurs pour pouvoir accéder à la pile avec des offsets; je sais aussi que je peux gérer moi-même manuellement les variables locales (avec des #define), mais mon but était de pouvoir accéder à des variables C/C++ depuis un bloc asm.

    En d'autres termes, je peux effectivement faire ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void fpo_test (int titi)
    {
       #define o_titi 12
       #define o_toto 4
       #define o_tutu 0
     
       __asm
       {
          sub      esp,            8
          mov      eax,            [esp + o_titi]
          mov      [esp + o_toto], eax
          mov      [esp + o_tutu], eax
       }
    }
    au lieu de:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void fpo_test (int titi)
    {
       int   toto, tutu;
     
       __asm
       {
          mov      eax,   titi
          mov      toto,   eax
          mov      tutu,   eax
       }
    }

    Mais si j'ai besoin de mixer du code C dans la fonction, genre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void fpo_test (int titi)
    {
       int   toto, tutu, tata;
     
       __asm
       {
          mov      eax,   titi
          mov      toto,   eax
          mov      tutu,   eax
       }
     
       tata = toto + tutu;
    }
    c'est immédiat et on ne peut plus élégant dans le cas où c'est le visual qui gère les variables locales; ça doit être possible aussi avec une gestion manuelle par offsets, mais bonjour le mic-mac pour la maintenance de l'appli !

    D'autres idées ?
    "La forme même des Pyramides prouve que de tous temps, les ouvriers n'ont jamais pensé qu'à en faire de moins en moins."

    G. CLEMENCEAU

  5. #5
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    je crois qu'il existe un keyword pour ca.

    dans un premier temps, spécifié la fonction en temps que __desclspec et lui spécifier ensuite "naked". De cette façon le compilo ne génère ni prologue ni épilogue (par la même je pense qu'il cesse d'utiliser EBP).

    Je n'ai pu tester là (pas de compilo sous la main) mais je pense que ca vaut le coup de tenter

    Deux petits liens :

    MSDN __declspec

    MSDN __declspec( naked )

    En espérant que ca fonctionne, bon courage

Discussions similaires

  1. Question sur l'assembleur inline
    Par sorry60 dans le forum C
    Réponses: 5
    Dernier message: 15/06/2009, 13h10
  2. Intégrer de l'assembleur inline
    Par jackoboss dans le forum Dev-C++
    Réponses: 2
    Dernier message: 08/12/2007, 13h19
  3. [GCC] Assembleur inline et instructions SSE2
    Par progfou dans le forum Linux
    Réponses: 3
    Dernier message: 27/06/2007, 20h44
  4. [boso kernel] Assembleur inline GCC - Undefined Reference
    Par Edouard Kaiser dans le forum Programmation d'OS
    Réponses: 13
    Dernier message: 29/07/2005, 09h24
  5. [DevC++]Assembleur inline
    Par Lung dans le forum Dev-C++
    Réponses: 6
    Dernier message: 21/08/2003, 16h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo