12
votes

Bras: Pourquoi dois-je appuyer / POP Deux registres aux appels de la fonction?

Je comprends que je dois appuyer sur le registre de lien au début d'un appel de la fonction et publier cette valeur au couter du programme avant de retourner, de sorte que l'exécution puisse en porter un de l'endroit avant l'appel de la fonction.

Ce que je ne comprends pas, c'est pourquoi la plupart des gens le font en ajoutant un registre supplémentaire à la poussée / pop. Par exemple: P>

.syntax unified

    @ --------------------------------
    .global main
main:
    @ Stack the return address (lr) in addition to a dummy register (ip) to
    @ keep the stack 8-byte aligned.
    push    {ip, lr}

    @ Load the argument and perform the call. This is like 'printf("...")' in C.
    ldr     r0, =message
    bl      printf

    @ Exit from 'main'. This is like 'return 0' in C.
    mov     r0, #0      @ Return 0.
    @ Pop the dummy ip to reverse our alignment fix, and pop the original lr
    @ value directly into pc — the Program Counter — to return.
    pop     {ip, pc}

    @ --------------------------------
    @ Data for the printf calls. The GNU assembler's ".asciz" directive
    @ automatically adds a NULL character termination.
message:
    .asciz  "Hello, world.\n"


8 commentaires

Je suis lié à un poste de blog de bras où ils recommandent ce modèle à deux registres. S'il vous plaît vérifier, il y a du code là-bas.


L'utilisation de liens est découragée, car le lien peut ne pas durer aussi longtemps que la question (et / ou simplement supprimer la question parce qu'il utilise des liens plutôt que d'avoir la discussion ici).


Ahh, donc le lien répond à votre question. Vous êtes autorisé à poster cette réponse vous-même. et fermez cette question.


En plus d'un registre factice (IP) pour garder la pile 8 octets alignés


Droite, mais comment ça marche? Pour autant que je sache, la pile a un alignement de 4 octets. En fait, lorsque je n'utilise pas le registre factice, cela fonctionne bien. Donc, ma question est toujours ouverte.


Voir Mikes Réponse ci-dessous, cela concerne les bus 64 bits, si vous gardez l'alignement, même si vous déplacez 32 bits de plus en plus, il est la même vitesse ou plus rapide, il faut 2 ou trois transactions de mémoire supplémentaires si vous êtes non aligné. Un push ou une pop (2 registres) de 64 bits est une transaction de mémoire, une poussée ou une pop non alignée 64 bits est deux transactions de mémoire.A 128 bits alignés POP est une transaction de mémoire (avec une longueur de 2) A 128 bits Pop inaligné est 3 transactions mémoire, 1 32 bits, 1 64 bits et 1 32 bits. Le désir est que le compilateur alignez toujours (et espérons que la bootstrap fait aussi).


Si un bus 32 bits pas 64 bit, le registre supplémentaire ajoute une horloge supplémentaire à la transaction, ce qui n'est pas si mauvais, pas autant de pénalité que les transferts alignés non-64 bits sont sur un bus 64 bits. J'imagine qu'il y a un commutateur de ligne de commande ou peut-être si vous sélectionnez une armv4 comme la cible au lieu de la valeur par défaut, peut-être que cela ne le fait pas.


Même la réponse plus simple que les autres ci-dessous ont déjà souligné "parce que le bras dit". Le bras EABI stipule 8 l'alignement de la pile d'octets, de sorte que les compilateurs génèrent maintenant du code pour maintenir cet alignement (bien sorta, j'ai vu au moins un problème).


3 Réponses :


3
votes

Puisque vous voulez les stocker et les récupérer après avoir exécuté votre fonction. Sur l'entremence de la fonction, il enregistre le ip et lr registres (nommé prolog ). Après avoir fini la fonction, il attribue à la fois ( epilog ): xxx

modifier

registre r12 < / Code> est également appelé IP et est utilisé comme un registre d'appel d'appel intra-procédure, voir Aussi .

La convention est que la fonction de callee peut modifier IP, r0-r3 Donc, vous devez les restaurer dépend des dépendances sur le Convention appelante

< Strong> Edit2: Pourquoi nous pourrions vouloir que la pile soit être 8 aligné sur le bras

Si la pile n'est pas huit octets aligné l'utilisation de LDRD et SRD ( Chargez et stockez Doubleword ) pourrait provoquer une erreur d'alignement, en fonction de la cible et de la configuration utilisé.

note que nous avons le même problème sur x86 , et sur Mac OS Nous avons 16 octets d'alignement


9 commentaires

Je sais que ça fait ça. Ma question est pourquoi la plupart des gens utilisent deux registres à Push / Pop. Pourquoi ne pas pousser {lr} et pop {pc} simplement?


puisque la langue vous permet de pousser {la liste des registres} et est une instruction d'assemblage, en supposant que vous souhaitez stocker r0-r15 vous pouvez le faire en longueur de code 32 bits ou 15 * 32 bits de longueur de code, quoi est mieux ? en.wikipedia.org/wiki/kiss_principle


Vous n'avez pas compris ma question. Je l'ai ré-édité, vérifiez-le.


Le registre "R12" est également appelé "IP" et est utilisé comme un registre des rayures d'appel intra-procédure. forums.arm.com/index.php?/topic/12986 -about-r12 ; Infocenter.arm.com/help/topic/com. arrach.doc.ihi0042e / ...


La convention est que la fonction de callee peut modifier IP, R0-R3


Merci, je ne savais pas que c'était R12. Je ne comprends toujours pas pourquoi les gars-là recommandent de passer un registre factice sur Push / Pop cependant. Vérifiez à nouveau ma question modifiée s'il vous plaît.


@Daniels laissez-moi savoir si ça va maintenant.


@Daniels: La raison est que le bras EABI spécifie que la pile reste alignée sur 64 bits, d'autres fouet LDRD / SRD n'a pu être utilisée sur la pile. En outre, la plupart des implémentations que j'ai vues jusqu'à présent sont en mesure de faire des accès de mémoire larges de 64 bits en même temps que 32 bits, si les adresses sont alignées sur 64 bits. Ajouter une adresse IP (ou tout autre registre) Dans ce cas, enregistrez simplement le code de devoir faire l'alignement explicitement (via Ajouter et sous). Si le code ne ferait que poussoir / POP LR / PC, la pile pour printf ne serait plus alignée et il pourrait se bloquer lorsque vous appelez LDRDD.


@ MASTA79: Pourquoi ne pas ajouter votre commentaire comme une réponse? C'est l'explication correcte et aucun des éléments existants n'est complet.



5
votes

Quelle est la raison du "registre du mannequin" comme ils l'appellent? Pourquoi ne pas simplement pousser {lr} et pop {pc}? Ils disent qu'il est de garder la pile 8 octets alignés, mais n'est pas l'empilement 4 octets aligné?

La pile nécessite uniquement une alignement de 4 octets; Mais si le bus de données est de 64 bits de large (comme il se trouve sur de nombreux armes modernes), il est plus efficace de la conserver à un alignement de 8 octets. Ensuite, par exemple, si vous appelez une fonction besoins pour empiler deux registres, pouvant être effectué dans une seule écriture 64 bits plutôt que deux écrit 32 bits.

MISE À JOUR: Apparemment, ce n'est pas seulement pour l'efficacité; C'est une exigence de la norme d'appel de procédure officielle, comme indiqué dans les commentaires.

Si vous ciblez des armes 32 bits plus âgés, le registre Empilé supplémentaire pourrait dégrader légèrement les performances.

Quel registre est "IP" (I.e., R7 ou quoi?)

R12 . Voir, par exemple, ici Pour l'ensemble complet d'alias d'enregistrement utilisés par la norme d'appel de procédure.



6
votes

L'alignement de 8 octets est une exigence d'interopérabilité entre les objets conformes aux AAPCS.

ARM a une note consultative sur ce sujet:

ABI pour la note de conseil Arch® Architecture - SP Doit être aligné de 8 octets sur l'entrée sur les fonctions conformes aux AAPCS

L'article mentionne deux raisons d'utiliser 8 octets d'alignement

  • Défaut d'alignement ou comportement imprévisible. (Quincaillerie / architecture Raisons liées - LDRD / SRD pourrait provoquer une faute d'alignement ou montrer un comportement imprévisible sur les architectures autres que l'ARMV7)

  • échec de l'application. (Compilateur - Différences d'hypothèse d'exécution, ils donnent va_start et va_arg comme exemple)

    Bien sûr, il s'agit de toutes les interfaces publiques, si vous effectuez une exécutable statique sans lien supplémentaire, vous pouvez aligner la pile à 4 octets.


1 commentaires

Mérite d'être mentionné: les registres de Store 2 Les recenseurs d'utilisation sont si courantes qu'en armv8, qui a chuté poussez et pop , il existe une paire poussée dédiée et des instructions de paire POP STP < / code> et LDP : Stackoverflow.com/Questtions/27941220/...