2010-09-13 35 views
5

Actuellement, j'écris des procédures en langage assembleur. Comme le dit une convention, quand je veux retourner une valeur à l'appelant, disons un entier, je devrais le retourner dans le registre EAX. Maintenant je me demande si je veux retourner un float, un double, un enum, ou même un struct complexe. Comment retourner ce type de valeurs?Comment retourner une valeur de retour complexe?

Je peux penser à retourner une adresse dans l'EAX qui pointe vers la valeur réelle dans la mémoire. Mais est-ce la façon standard?

Un grand merci ~~~

+2

Si vous souhaitez connaître la "méthode standard", vous devez spécifier votre plate-forme. Quel matériel, quel système d'exploitation et quel compilateur utilisez-vous? –

+0

@StephenCanon Je n'ai pas réalisé que ceux-ci sont liés lorsque j'ai posté la question. Merci. – smwikipedia

Répondre

10

Tout dépend de vous, si l'appelant est votre code. Si l'appelant n'est pas sous votre contrôle, vous devez soit suivre leur convention existante ou développer votre propre convention ensemble. Par exemple, sur une plate-forme x86, lorsque l'arithmétique en virgule flottante est traitée par des instructions FPU, le résultat d'une fonction est renvoyé comme valeur supérieure de la pile de registres FPU. (Si vous le savez, les registres FPU x86 sont organisés en une sorte de "pile circulaire"). A ce moment, il n'est ni float ni double, c'est une valeur stockée avec une précision FPU interne (qui peut être supérieure à float ou double) et il est de la responsabilité de l'appelant de récupérer cette valeur du haut de la pile FPU et de la convertir en tapez le désire. En fait, c'est ainsi que fonctionne une instruction FPU typique: elle prend ses arguments du haut de la pile FPU et repousse le résultat sur la pile FPU. En implémentant votre fonction de la même manière, vous émulez essentiellement une instruction FPU "complexe" avec votre fonction - une manière plutôt naturelle de le faire. Lorsque l'arithmétique à virgule flottante est traitée par les instructions SSE, vous pouvez choisir un registre SSE dans le même but (utilisez xmm0 comme vous utilisez EAX pour les entiers).

Pour les structures complexes (c'est-à-dire plus grandes qu'un registre ou une paire de registres), l'appelant transmet normalement un pointeur à un tampon réservé à la fonction. Et la fonction mettrait le résultat dans le tampon. En d'autres termes, sous le capot, les fonctions ne "retournent" jamais vraiment de gros objets, mais les construisent plutôt dans un tampon mémoire fourni par l'appelant. Bien sûr, vous pouvez utiliser cette méthode de "mémoire tampon" pour renvoyer des valeurs de tout type, mais avec des valeurs plus petites, c'est-à-dire des valeurs de type scalaire, il est beaucoup plus efficace d'utiliser des registres qu'un emplacement de mémoire. Cela s'applique également aux petites structures.

Les énumérations sont généralement simplement un wrapper conceptuel sur un type entier. Donc, il n'y a pas de différence entre retourner un enum ou un entier.

0

En général, vous devez utiliser la pile

+1

En utilisant le C ABI, vous utilisez la pile. En C++, il n'est pas défini, ce qui permet aux fabricants de compilateurs de choisir des mécanismes plus efficaces (s'ils le souhaitent). –

0

Si vous prévoyez d'interface avec C ou une autre langue de niveau supérieur, vous devez généralement accepter l'adresse d'un tampon de mémoire un argument à votre fonction et renvoyez votre valeur complexe en remplissant ce tampon. Si c'est uniquement l'assemblage, vous pouvez définir votre propre convention en utilisant n'importe quel ensemble de registres, bien que vous ne fassiez généralement cela que si vous avez une raison spécifique (par exemple, la performance).

6

Un double devrait être retourné en tant que premier article de la pile.

Voici un exemple de code C++ (x86):

double sqrt(double n) 
{ 
    _asm fld n 
    _asm fsqrt 
} 

Si vous préférez gérer la pile manuellement (sauvegarde des cycles CPU):

double inline __declspec (naked) __fastcall sqrt(double n) 
{ 
    _asm fld qword ptr [esp+4] 
    _asm fsqrt 
    _asm ret 8 
} 

Pour les types complexes, vous devez passer un pointeur ou renvoyer un pointeur.

+0

Si vous dites que vous avez également besoin de spécifier le compilateur (version wih) et OS (avec la version) mais vous devez probablement spécifier le document ABI pour votre compilateur. C'est parce que tous les compilateurs C++ ne le font pas. Tout compilateur C sera parce que le C ABI est bien défini. –

+0

Droite. c'est pour Win32, VC++ 2008. Mais fonctionnera probablement pour toutes les versions de VC. –

1

C99 a un type de données intégré complexe (_Complex). Donc si vous avez un compilateur compatible C99, vous pouvez simplement compiler une fonction qui retourne un complexe et le compiler en assembleur (habituellement avec une option -S). Là, vous pouvez voir la convention qui est prise.

+0

essayez également avec '-save-temps'. –

2

Lorsque vous avez des questions sur les conventions d'appel ou le langage d'assemblage, écrivez une fonction simple dans un langage de haut niveau (dans un fichier séparé). Ensuite, demandez à votre compilateur de générer une liste en langage assembleur ou demandez à votre débogueur d'afficher un "assemblage entrelacé".

Non seulement la liste vous indiquera comment le compilateur implémente le code, mais aussi vous montrera les conventions d'appel. Beaucoup plus facile que de poster à S.O. et habituellement plus vite. ;-)

1

Cela dépend de l'ABI. Par exemple, Linux sur x86 utilise le Sys V ABI, spécifié dans le Intel386 Architecture Processor Supplment, Fourth Edition.

La section Séquence d'appel de fonction contient des informations sur la manière dont les valeurs doivent être renvoyées. Brièvement, dans cette API:

  • Les fonctions retournant des scalaires ou aucune valeur utilisent %eax;
  • Les fonctions renvoyant des valeurs à virgule flottante utilisent %st(0);
  • Pour les fonctions renvoyant des types struct ou union, l'appelant fournit un espace pour la valeur renvoyée et transmet son adresse en tant que premier argument masqué. L'appelé renvoie cette adresse au %eax.