registres : Lors de l'exécution d'une routine, 32 registres 32 bits à usage général sont à la disposition du programmeur.
Leurs petits noms sont %r0 - %r31 dans la dénomination la plus anonyme, mais en pratique on les sépare en 4 zones :
-
Les registres généraux %g0 - %g7.
Le registre %g0 est spécial car il contient toujours la valeur 0. Il est très pratique pour certaines contraintes imposées par l'architecture RISC...
-
Les registres d'entrée %i0 - %i7 où les routines récupèrent leurs paramètres,
-
Les registres de sortie %o0 - %o7 pour fournir les paramètres
à une routine qu'on veut appeler.
-
Les registres locaux %l0 - %l7 spécialement réservés
à la routine,
Cette débauche de registres n'est pas encore finie...
En effet, la dénomination des zones de registres est en rapport avec le système de "fenêtre de registres". Le processeur, en interne, possède beaucoup plus de registres (leur nombre exact dépend de la version du processeur ; typiquement, 128 registres), dont seule un partie (une fenêtre) est visible à un moment donné pour la routine.
Cette fenêtre peut être décalée lors des appels de routines pour fournir un jeu de registres tout neuf, et doit alors être replacée lors des retours de fonctions à sa position d'origine.
De plus, ces deux fenêtres possèdent une zone de recouvrement qui facilite le passage de paramètres : les registres "Out" de l'appelant sont en fait les registres "In" de l'appelé.
Ce recouvrement est aussi utilisé dans l'autre sens pour renvoyer les résultats de fonctions. Les registres globaux, eux, sont communs à tout le monde.
Les registres locaux sont accessibles uniquement à la procédure courante.
La table ci-dessous illustre le mécanisme; les registres "absolus"
sont ici appelés a0-a127:
|
Fonction f1() |
f2() appelée par f1() |
f3() appelée par f2() |
a64-a71 |
|
|
|
a56-a63 |
|
|
o0-o7
|
a48-a55 |
|
|
l0-l7
|
a40-a47 |
|
o0-o7
|
i0-i7
|
a32-a39 |
|
l0-l7
|
|
a24-a31 |
o0-o7
|
i0-i7
|
|
a16-a23 |
l0-l7
|
|
|
a8-a15 |
i0-i7
|
|
|
a0-a7 |
g0-g7
|
g0-g7
|
g0-g7
|
(NB : Ce schéma est uniquement illustratif - ne pas le prendre pour
absolument exact; en particulier, je ne sais pas si les fenêtres
sont allouées dans le sens des An croissants)
Ce mécanisme permet d'utiliser massivement les registres avec
un minimum de contraintes ; en effet, l'allocation dynamique de registres
supprime les contraintes habituelles de sauvegarde et de restauration des
registres sur la pile, donc les références mémoire
(lentes).
Evidemment, vous allez me dire : et si on a beaucoup de niveaux d'appels
de routines, il y a un moment ou on va arriver à la fin de la réserve de registres ? En effet, quand plus aucune fenêtre n'est disponible, le processeur se sent mal... et il se repose sur le système d'exploitation :
il génère un Trap, et c'est le système qui va se charger
de recycler les registres. Les registres actuels sont stockés sur
la pile, et la zone libérée est convertie en nouvelle fenêtre.
Dans le cas d'un "Underflow" de registres, le mécanisme inverse se produit : les anciennes fenêtres sont récupérées sur la pile.
Un mécanisme similaire se produit certainement aussi lors des commutations de processus dans le contexte multitache.
Ces opérations de mise à jour des registres sont le coté un peu contraignant du système (au niveau performance) ; rassurez vous, le programmeur n'a pas a s'en occuper, tout est géré au niveau matériel et par l'OS.
Il y a juste une chose à prévoir lors de l'écriture des fonctions, c'est d'allouer une zone sur la pile pour faciliter le travail de l'OS.
En ce qui concerne le rôle des registres : ils sont à usage général (aussi bien registres d'adresse que de donnée), mais certains ont été assignés à un rôle particulier :
-
%i7 contient l'adresse de l'instruction qui a appelé la routine courante.
Ce registre est utilisé pour effectuer le retour à l'appelant.
-
%i6 est le pointeur de pile de l'appelant ; l'assembleur reconnait aussi la dénomination %fp (Frame-pointer). Il est utilisé pour accéder aux variables locales et aux éventuels paramètres passés par la pile.
-
De façon symétrique, %o6 (%sp) est le pointeur de pile (Stack-pointer) de la routine actuelle, et %o7 est le registre où est placé le pc en cas d'appel de sous-routine.
-
Rappel : %g0 vaut toujours 0 !
-
Enfin il existe d'autres registres : pointeur d'instruction (%pc et %npc), registre de flags, et registres privilegiés.
Pour terminer, sachez que l'appel de sous-routine ne s'accompagne pas obligatoirement d'une mise à jour de fenêtre de registres.
Les routines-feuilles sont celles qui peuvent se contenter des registres %o0-%o5 de l'appelant pour effectuer leurs opérations, et n'ont pas besoin d'allouer de nouveaux registres.
La demande de nouvelle fenêtre étant à la charge du programme (au début de la routine), vous pouvez choisir.