2010-02-10 6 views
3

J'ai du code (assemblage en ligne).pourquoi MS C++ ajoute ce code à l'assemblage?

void NativeLoop() 
{ 
    int m; 
    __asm 
    { 
     PUSH ECX 
     PUSH EDX 
     MOV ECX, 100000000 
NEXTLOOP: 
     MOV EDX, ECX 
     AND EDX, 0X7FFFFFFF 
     MOV DWORD PTR m, EDX 
     DEC ECX 
     JNZ NEXTLOOP 
     POP EDX 
     POP ECX 
    } 
} 

MS C++ Automagicaly ajoute ces codes (marqués d'un **) à ma procédure.
Pourquoi?
comment l'éviter?

**push  ebp 
    **mov   ebp,esp 
    **push  ecx 
    push  ecx 
    push  edx 
    mov   ecx,5F5E100h 
NEXTLOOP: 
    mov   edx,ecx 
    and   edx,7FFFFFFFh 
    mov   dword ptr m,edx 
    dec   ecx 
    jnz   NEXTLOOP 
    pop   edx 
    pop   ecx 
    **mov   esp,ebp 
    **pop   ebp 
    **ret 
+0

En fait, vous copiez des valeurs dans la variable 'm', mais vous ne faites rien avec. Est-ce une erreur? –

+0

@Thomas Matthews: non ce n'est pas une erreur.Il est pour les tests de performance. – Behrooz

Répondre

20

Il s'agit du code d'entrée et de sortie de fonction standard. Il établit et détruit le cadre de la pile. Si vous ne le voulez pas, vous pouvez utiliser __declspec (nu). N'oubliez pas d'inclure le RET si vous le faites. Cependant, votre fragment repose sur un cadre de pile valide, votre variable "m" le requiert. Il est adressé à [ebp-10]. Sans le préambule, le registre ebp ne sera pas défini correctement et vous corromprez le cadre de la pile de l'appelant.

+3

non seulement RET, mais aussi stackframe/save/restore; –

+1

C'est bon, il n'utilise pas le cadre de la pile. Pas d'arguments de fonction, pas de variables locales. –

+4

Huh? Et que pensez-vous que son 'm' est sinon une variable locale?En fait, sa référence à 'dword ptr m' sera traduite en accès mémoire basé sur' ebp', c'est pourquoi le code inséré par le compilateur est absolument nécessaire. – AnT

5

Il maintient la pile d'appels. Si vous avez défini la fonction comme

int NativeLoop() { } 

Vous verriez le même assemblage.

+0

qui a fait plus de code. – Behrooz

+4

peut-être parce que la fonction de David est de type int –

+1

Vous avez probablement vu quelque chose avec eax, comme xor eax, eax si vous avez dit "return 0;" pour la valeur de retour. C'est parce que eax est le registre de valeur de retour et un xor de valeurs identiques aboutit à zéro. –

0

Si vous pouvez effectuer une recherche sur les conventions d'appel C++, vous comprendrez mieux ce que fait le compilateur.

5

Je me souviens que vous pouvez __declspec(naked) dans MSVC++, ce qui signifie que vous devez vous occuper vous-même de la pile, ce qui signifie que vous devez sauvegarder tous les registres que vous avez supprimés et les restaurer.

Il n'y a pas de règle unique pour le faire correctement, car cela dépend de la convention d'appel. Voir http://en.wikipedia.org/wiki/X86_calling_conventions.

Sidenote: Dans gcc, vous expliquez explicitement au compilateur ce que vous allez invalider, afin que gcc produise un enregistrement/une restauration/un code de pile plus optimaux, le cas échéant. Dans MSVC, asm est principalement une boîte noire pour le compilateur, pour laquelle il sera souvent/toujours le pire.

Voir http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#ss5.3, gcc syntaxe en ligne asm est plus laide, mais effectivement plus efficace.

+0

99,9% du temps, le compilateur va générer un meilleur ASM que vous dans tous les cas, et c'est probablement une bonne idée de ne pas utiliser ASM dans le code normal. C'est peut-être pourquoi MSVCC ne prend même pas la peine de regarder les sections de code ASM. –

+0

Bien sûr. Mais dans la programmation des systèmes (pas dans mon cas, btw), vous avez souvent besoin d'assembleur (en ligne), et c'est bien quand le compilateur peut même optimiser cela. Mais alors, MSVC++ est de toute façon un mauvais choix, car il ne prend même pas en charge les cibles amd64 (vous devrez donc écrire un assembly pur et le relier à votre programme) –

4
**push  ebp ;save EBP register 
    **mov   ebp,esp ;Save the stackframe 
    **push  ecx ; So that the variable `m` has an address 
;... 
    **mov   esp,ebp ;restore the stack frame to it's original address 
    **pop   ebp ;restore EBP register 
    **ret ;return from function call 
+0

+1 pour l'explication cool. – Behrooz

+0

'push ecx' est sa façon de rendre' m' adressable. La norme exige que tous les identificateurs sans référence de type (IIRC) aient une adresse. Donc, VC met 'm' (stocké dans' ecx') sur la pile, où il aura une adresse mémoire (qui est ensuite référencée dans l'assemblage). Ce n'est pas "idiot". – greyfade

+0

Je n'étais pas sûre ... mon mixage C/assemblage est un peu rouillé. – Earlz

Questions connexes