2016-06-29 3 views
7

J'ai récemment rencontré les concepts de registre de lien et de fonctions de feuille.Pourquoi ARM dit que "Un registre de liens prend en charge les appels de fonction feuille rapides"

Je comprends des précédentes lectures de SO que le LR indique où le code était précédemment pendant l'exécution. J'ai aussi appris qu'une fonction feuille est une fonction qui arrive à la fin de la hiérarchie des appels de fonction.

Le ARM wikipedia page says:

Un registre de liaison prend en charge les appels de fonction de feuille rapide.

Pourquoi cette affirmation est-elle vraie? J'ai regardé l'ARMARM (Architecture Reference Manual), et les informations sur le registre de liaison sont minimes.

+3

Notez que les microarchitectures modernes ont tendance à avoir des piles de retour matérielles qui prédisent où retournera la fonction, permettant une exécution spéculative à travers le retour. Même les processeurs sans registre de liaison n'attendent pas que l'adresse de retour soit rechargée de la mémoire. – EOF

Répondre

2

Sur certaines architectures (telles que x86, x86_64), l'adresse de retour d'une fonction est toujours stockée sur la pile et l'appel d'une fonction implique l'accès à la mémoire principale:

  • une écriture à la pile lorsque appel;

  • une lecture de la pile au retour.

En revanche, si votre architecture/ABI peut sauter/retour sans utiliser la mémoire principale et les paramètres et les valeurs de retour du callee peuvent être transmis dans les registres ainsi, l'appel et revenir à/de fonctions de feuilles peut être fait sans impliquer la RAM.

Si la fonction de feuille est assez simple, il peut exécuter sans toucher RAM du tout:

int callee(int a, int b) 
{ 
    return a + b; 
} 

int caller(int a, int b, int c, int d) 
{ 
    return callee(a,b) + calle(c,d); 
} 

donne (chaque fonction compilée séparément par clang -target arm-eabi -S -o- -O3):

callee: 
    add r0, r1, r0 
    bx lr 
caller: 
    .save {r4, r5, r6, r10, r11, lr} 
    push {r4, r5, r6, r10, r11, lr} 
    .setfp r11, sp, #16 
    add r11, sp, #16 
    mov r4, r3 
    mov r5, r2 
    bl callee 
    mov r6, r0 
    mov r0, r5 
    mov r1, r4 
    bl callee 
    add r0, r0, r6 
    pop {r4, r5, r6, r10, r11, lr} 
    bx lr 

Remarquez comment nous pouvons éviter accéder à la mémoire dans le noyau de caller et dans calee.

+0

Pourquoi parlez-vous du type de retour d'une fonction? Le registre de liaison stocke l'adresse de retour, pas la valeur de retour ni le type de retour. – blatinox

+0

@blatinox, je voulais dire "adresse" :) – ysdx

+0

Vous pouvez utiliser '__attribute __ ((noinline))' pour obtenir cette sortie du compilateur à partir d'une seule unité de compilation, [comme ceci sur Godbolt] (https://godbolt.org/g/YoJJGj) –

2

La raison est que cela signifie que lors de l'appel de la fonction feuille, l'adresse de retour n'a pas besoin d'être poussée dans la pile (puisqu'elle est stockée dans le registre de liaison). Cela est supposé être plus rapide que de pousser l'adresse de retour vers la pile comme vous le faites dans les processeurs qui n'ont pas de registre de lien (mais ce n'est pas certain que c'est en fait plus rapide).

Cependant, dans certaines situations, la fonction feuille doit enregistrer des données dans la pile. Par exemple, une fonction feuille avec de nombreuses variables peut avoir besoin d'utiliser la pile pour les stocker ou pour enregistrer le registre de liaison pour libérer des registres temporaires.

+0

Dans quel ABI le registre de liens peut-il être utilisé comme registre temporaire? – blatinox

+2

@blatinox L'ABI normal. La fonction normale d'une fonction non-feuille est de récupérer l'adresse de retour de la pile (sans restaurer le registre de liaison), de sorte que l'appelant ne peut pas supposer que le registre de liaison conserve la valeur qu'il a placée dans le registre. Il est assez habituel que le registre de liens soit utilisé de cette façon. – skyking