Langage d'assemblage MIPS R3000
Langage d'assemblage MIPS R3000
Langage d’assemblage
Version 1.4
Ce document décrit le langage d’assemblage du processeur M IPS R3000, ainsi que dif-
férentes conventions relatives à l’écriture des programmes en langage d’assemblage.
Les programmes assembleur source qui respectent les règles définies dans le présent
document peuvent être assemblés par l’assembleur MIPS de l’environnement GNU pour
générer du code exécutable. Ils sont également acceptés par le simulateur du M IPSR3000
utilisé en TP qui permet de visualiser le comportement du processeur instruction par
instruction.
Rappelons que le but d’un programme X écrit en langage d’assemblage est de four-
nir à un programme particulier (appelé << assembleur >>) les directives nécessaires
pour générer le code binaire représentant les instructions et les données qui devront être
chargées en mémoire pour permettre au programme X de s’exécuter sur du matériel.
Dans l’architecture M IPS R3000, l’espace adressable est divisé en deux segments : le
segment utilisateur, et le segment noyau.
– la section text contient le code exécutable en mode utilisateur. Elle est implantée
conventionnellement à l’adresse 0x00400000. Sa taille est fixe et calculée lors de
l’assemblage. La principale tâche de l’assembleur consiste à générer le code binaire
correspondant au programme source décrit en langage d’assemblage, qui sera chargé
dans cette section ;
– la section data contient les données globales manipulées par le programme utilisateur.
Elle est implantée conventionnellement à l’adresse 0x10000000. Sa taille est fixe et
calculée lors de l’assemblage. Les valeurs contenue dans cette section peuvent être
initialisées grace a des directives contenues dans le programme source en langage
d’assemblage ;
– la section stack contient la pile d’exécution du programme. Sa taille varie au cours de
l’exécution. Elle est implantée conventionnellement à l’adresse 0x7FFFEFFF. Contrai-
rement aux sections data et text, la pile s’étend vers les adresses décroissantes.
– la section ktext contient le code exécutable en mode noyau. Elle est implantée
conventionnellement à l’adresse 0x80000000. Sa taille est fixe et calculée lors de
l’assemblage ;
– la section kdata contient les données globales manipulées par le système d’exploita-
tion en mode noyau. Elle est implantée conventionnellement à l’adresse 0xC0000000.
Sa taille est fixe et calculée lors de l’assemblage ;
– la section kstack contient la pile d’exécution du programme. Sa taille varie au cours de
l’exécution. Elle est implantée conventionnellement à l’adresse 0xFFFFEFFF. Contrai-
rement aux sections data et text, la pile s’étend vers les adresses décroissantes.
3. les entiers :
une valeur entière décimale est notée 250, une valeur entière octale est notée 0372
(préfixée par un zéro), et une valeur entière héxadécimale est notée 0xFA (préfixée
par zéro suivi de x). En hexadécimal, les lettres de A à F peuvent être écrites en
majuscule ou en minuscule.
4. les chaînes de caractères :
elles sont simplement entre guillemets, et peuvent contenir les caractères d’échap-
pement du langage C.
Exemple : "Oh la jolie chaîne avec retour à la ligne\n"
5. les labels :
ce sont des mnémoniques correspondant à des adresses. Ces adresses peuvent
être soit des adresses de variables,soit des adresses de saut. Ce sont des chaînes
de caractères qui commencent par une lettre, majuscule ou minuscule, un $, un
_, ou un .. Ensuite, un nombre quelconque de ces mêmes caractères auquels on
ajoute les chiffres sont utilisés. Pour la déclaration, le label doit être suffixé par
<< : >>. Exemple : $LC12:
Pour y référer, on supprime le << : >>.
Exemple :
message:
.asciiz "Ceci est une chaîne de caractères...\n"
.text
__start:
Attention : sont illégaux les labels qui ont le même nom qu’un mnémonique de
l’assembleur ou qu’un nom de registre.
6. les immédiats :
ce sont les opérandes contenus dans l’instruction. Ce sont des constantes. Ce sont
soit des entiers, soit des labels. Ces constantes doivent respecter une taille maxi-
mum qui est fonction de l’instruction qui l’utilise : 16 ou 26 bits.
7. les registres :
le processeur M IPS possède 32 registres accessibles au programmeur. Chaque
registre est connu par son numéro, qui varie entre 0 et 31, et est préfixé par un $.
Par exemple, le registre 31 sera noté $31 dans l’assembleur.
En dehors du registre $0, tous les registres sont identiques du point de vue de la
machine. Lorsque le registre $0 est utilisé comme registre source dans une instruc-
tion, la valeur lue est toujours 0, et utiliser ce registre comme registre destination
dans une instruction ne modifie pas sa valeur.
Afin de normaliser et de simplifier l’écriture du logiciel, des conventions d’utilisation
des registres sont définies. Ces conventions sont particulièrement nécessaires lors
de l’utilisation des fonctions.
$0 vaut zéro en lecture, non modifié par écriture
$1 réservé à l’assembleur. Ne doit pas être employé dans
les programmes utilisateur
$2 valeur de retour des fonctions
$3, $4 argument des syscall
$5, ..., $26 registres de travail à sauver
$27, ..., $28 registres réservés aux procédures noyau. Ils ne
doivent pas être employés dans les programmes utili-
sateur
$29 pointeur de pile
$30 pointeur sur les variables globales
$31 adresse de retour d’appel de fonction
8. les arguments :
si une instruction nécessite plusieurs arguments, comme par exemple l’addition
entre deux registres, ces arguments sont séparés par des virgules. Dans une ins-
truction assembleur, on aura en général comme argument en premier le registre
Dans ce qui suit, le registre noté $rr est le registre destination, c.-à-d. qui reçoit le résultat
de l’opération, les registres notés $ri et $rj sont les registres source qui contiennent
les valeurs sur lesquelles s’effectue l’opération. Notons qu’un registre source peut être
le registre destination d’une même instruction assembleur. Un opérande immédiat sera
noté imm, et sa taille sera spécifié dans la description de l’instruction. Les instructions
de saut prennent comme argument une étiquette, où label, qui est utilisée pour calculer
l’adresse de saut. Toutes les instructions modifient un registre non accessible du logiciel,
le program counter. De même, le résultat d’une multiplication ou d’une division est mis
dans deux registres spéciaux, $hi pour les poids forts, et $lo pour les poids faibles.
Ceci nous amème à introduire quelques notations :
= test d’égalité
+ addtition entière en complément à deux
− soustraction entière en complément à deux
× multiplication entière en complément à deux
.
.
division entière en complément à deux
mod reste de la division entière en complément à deux
and opérateur et bit-à-bit
or opérateur ou bit-à-bit
nor opérateur non-ou bit-à-bit
xor opérateur ou-exclusif bit-à-bit
mem[a] contenu de la mémoire à l’adresse a
← assignation
⇒ implication
k concaténation de chaînes de bits
xn réplication du bit x dans une chaîne de n bits. Notons
que x est un unique bit
xp...q sélection des bits p à q de la chaîne de bits x
Certains opérateurs n’étant pas évidents, nous donnons ici quelques exemples.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
exception
génération d’une exception si dépassement de capacité.
addiu
action
Addition registre immédiat non-signée
syntaxe
addiu $rr, $ri, imm
description
La valeur immédiate sur 16 bits subit une extension de signe, et est ajoutée au
contenu du registre $ri pour former un résultat sur 32 bits qui est placé dans
le registre $rr.
addu
action
Addition registre registre non-signée
syntaxe
addu $rr, $ri, $rj
description
Les contenus des registres $ri et $rj sont ajoutés pour former un résultat sur
32 bits qui est placé dans le registre $rr.
opération
rr ← ri + rj
and
action
Et bit-à-bit registre registre
syntaxe
and $rr, $ri, $rj
description
Un et bit-à-bit est effectué entre les contenus des registres $ri et $rj. Le
résultat est placé dans le registre $rr.
opération
rr ← ri and rj
andi
action
Et bit-à-bit registre immédiat
syntaxe
andi $rr, $ri, imm
description
La valeur immédiate sur 16 bits subit une extension de zéros. Un et bit-à-
bit est effectué entre cette valeur étendue et le contenu du registre $ri pour
former un résultat placé dans le registre $rr.
opération
rr ← (016 k imm) and ri
beq
exception
exception
lui
action
Lecture d’une constante dans les poids forts
syntaxe
lui $rr, imm
description
La constante immédiate de 16 bits est décalée de 16 bits à gauche, et est
complétée de zéro. La valeur ainsi obtenue est placée dans $rr.
opération
rr ← imm k 016
lw
action
Lecture d’un mot de la mémoire
syntaxe
lw $rr, imm($ri)
description
L’adresse de chargement est la somme de la valeur immédiate sur 16 bits,
avec extension de signe, et du contenu du registre $ri. Le contenu de cette
adresse est placé dans le registre $rr. Attention, les deux bits de poids faible
de l’adresse résultante doivent être à zéro.
opération
rr ← mem[imm + ri]
exception
sltiu
M IPS R3000 langage d’assemblage page - 24
action
Comparaison non-signée registre immédiat
syntaxe
sltiu $rr, $ri, imm
description
Le contenu du registre $ri est comparé à la valeur immédiate sur 16 bits qui à
subit une extension de signe. Les deux valeurs étant considérées comme des
quantités non-signées, si la valeur contenue dans $ri est inférieur à celle de
l’immédiat étendu, alors $rr prend la valeur un, sinon il prend la valeur zéro.
opération
0 k ri < 0 k imm16
15 k imm ⇒ rr ← 0
31
k1
0 k ri ≥ 0 k imm16
15 k imm ⇒ rr ← 0
32
sltu
action
Comparaison non-signée registre registre
syntaxe
sltu $rr, $ri, $rj
description
Le contenu du registre $ri est comparé au contenu du registre $rj, les deux
valeurs étant considérés comme des quantités non-signées. Si la valeur conte-
nue dans $ri est inférieur à celle contenue dans $rj, alors $rr prend la
valeur un, sinon il prend la valeur zéro.
opération
0 k ri < 0 k rj ⇒ rr ← 031 k 1
0 k ri ≥ 0 k rj ⇒ rr ← 032
sra
action
Décalage à droite arithmétique immédiat
syntaxe
sra $rr, $ri, imm
description
Le registre $ri est décalé à droite de la valeur immédiate codée sur 5 bits, le
bit de signe du registre étant introduit dans les bits de poids fort. Le résultat est
placé dans le registre $rr.
Une macro-instruction est une pseudo-instruction qui ne fait pas partie du jeu d’instruc-
tions machine, mais qui est acceptée par l’assembleur qui la traduit en une séquence
d’instructions machine. Les macro-instructions utilisent le registre $1 si elles ont besoin
de faire un calcul intermédiaire. Il faut donc éviter d’utiliser ce registre dans les pro-
grammes.
bge
action
Branchement si registre plus grand ou égal que registre
syntaxe
bge $ri, $rj, label
description
Les contenus des registres $ri et $rj sont comparés. Si $ri ≥ $rj, le pro-
gramme saute à l’adresse correspondant à l’étiquette, calculée par l’assem-
bleur.
opération
addr ← label
ri ≥ rj ⇒ pc ← pc + 4 + addr
code équivalent
slt $1, $ri, $rj
beq $1, $0, label
bgt
action
Branchement si registre strictement plus grand que registre
syntaxe
bgt $ri, $rj, label
description
Les contenus des registres $ri et $rj sont comparés. Si $ri > $rj, le pro-
gramme saute à l’adresse correspondant à l’étiquette, calculée par l’assem-
bleur.
opération
addr ← label
ri > rj ⇒ pc ← pc + 4 + addr
ble
action
Branchement si registre plus petit ou égal à registre
syntaxe
ble $ri, $rj, label
description
Les contenus des registres $ri et $rj sont comparés. Si $ri ≤ $rj, le pro-
gramme saute à l’adresse correspondant à l’étiquette, calculée par l’assem-
bleur.
opération
addr ← label
ri ≤ rj ⇒ pc ← pc + 4 + addr
code équivalent
slt $1, $rj, $ri
beq $1, $0, label
blt
action
Branchement si registre strictement plus petit que registre
syntaxe
blt $ri, $rj, label
description
Les contenus des registres $ri et $rj sont comparés. Si $ri < $rj, le pro-
gramme saute à l’adresse correspondant à l’étiquette, calculée par l’assem-
bleur.
opération
addr ← label
ri < rj ⇒ pc ← pc + 4 + addr
code équivalent
slt $1, $ri, $rj
bne $1, $0, label
code équivalent
div $ri, $rj
mflo $rr
divu
action
Division entière non-signé registre registre
syntaxe
divu $rr, $ri, $rj
description
Le contenu du registre $ri est divisé par le contenu du registre $rj, le contenu
des deux registres étant considéré comme des nombres non signés. Le résultat
de la division est placé dans le registre spécial $lo, et le reste dans $hi. Le
contenu de $lo est recopié dans $rr.
opération
rr ← b0 k ri0 k rjc
code équivalent
divu $ri, $rj
mflo $rr
la
action
Chargement d’une adresse dans un registre
li
action
Chargement d’un immédiat sur 32 bits dans un registre
syntaxe
li $rr, imm
description
La valeur immédiate non-signée est chargée dans le registre $rr.
opération
rr ← imm
code équivalent
lui $rr, imm >> 16
ori $rr, $rr, imm & 0xFFFF
lb
action
Chargement de l’octet, étendu de signe, contenu à une addresse indiquée par
une étiquette dans un registre.
syntaxe
lb $rr, etiq
description
L’octet présent en mémoire à l’adresse etiq est étendu de signe et chargé
dans le registre $rr.
opération
rr ← mem[etiq]
lbu
action
Chargement de l’octet, considéré comme une entité non signée, contenu à une
addresse indiquée par une étiquette dans un registre.
syntaxe
lbu $rr, etiq
description
L’octet présent en mémoire à l’adresse etiq est chargé dans le registre $rr.
Les 3 octets de poids forts sont mis à 0.
opération
rr ← mem[etiq]
code équivalent
la $1, etiq # Utilisation de la macro la
lbu $rr, 0($1)
lh
action
Chargement du demi-mot, étendu de signe, contenu à une addresse indiquée
par une étiquette dans un registre.
syntaxe
lh $rr, etiq
description
Le demi-mot présent en mémoire à l’adresse etiq est étendu de signe et
chargé dans le registre $rr.
opération
rr ← mem[etiq]
code équivalent
la $1, etiq # Utilisation de la macro la
lh $rr, 0($1)
lhu
M IPS R3000 langage d’assemblage page - 34
action
Chargement du demi-mot, considéré comme une entité non signée, contenu à
une addresse indiquée par une étiquette dans un registre.
syntaxe
lhu $rr, etiq
description
Le demi-mot présent en mémoire à l’adresse etiq est chargé dans le registre
$rr. Les 2 octets de poids forts sont mis à 0.
opération
rr ← mem[etiq]
code équivalent
la $1, etiq # Utilisation de la macro la
lhu $rr, 0($1)
lw
action
Chargement du mot contenu à une addresse indiquée par une étiquette dans
un registre
syntaxe
lw $rr, etiq
description
Le mot de 32 bits présent en mémoire à l’adresse etiq est chargée dans le
registre $rr.
opération
rr ← mem[etiq]
code équivalent
la $1, etiq # Utilisation de la macro la
lw $rr, 0($1)
move
action
transfert registre/registre
syntaxe
move $rd, $rs
mult
action
Multiplication signé registre registre
syntaxe
mult $rr, $ri, $rj
description
Le contenu du registre $ri est multiplié par le contenu du registre $rj, le
contenu des deux registres étant considéré comme des nombres en complé-
ment à deux. Les 32 bits de poids faible sont placés dans $rr.
opération
rr ← (ri × rj)31...0
code équivalent
mult $ri, $rj
mflo $rr
multu
action
Multiplication signé registre registre
syntaxe
multu $rr, $ri, $rj
description
Le contenu du registre $ri est multiplié par le contenu du registre $rj, le
contenu des deux registres étant considéré comme des nombres non-signés.
Les 32 bits de poids faible sont placés dans $rr.
opération
rr ← (0 k ri × 0 k rj)31...0
sb
action
Écriture de l’octet de droite d’un registre à une addresse indiquée par une
étiquette
syntaxe
sb $rr, etiq
description
L’octet le plus à droite du registre $rr est écrit en mémoire à l’adresse etiq.
opération
mem[etiq31...2 ]etiq1...0 ← rr7...0
code équivalent
la $1, etiq # Utilisation de la macro la
sb $rr, 0($1)
sh
action
Écriture du demi-mot (16 bits) de droite d’un registre à une addresse indiquée
par une étiquette
syntaxe
sh $rr, etiq
description
Le demi-mot le plus à droite du registre $rr est écrit en mémoire à l’adresse
etiq.
opération
mem[etiq31...1 ]etiq0 ← rr16...0
code équivalent
la $1, etiq # Utilisation de la macro la
sh $rr, 0($1)
sw
Les directives ne sont pas des instructions exécutables par la machine, mais permettent
de donner des ordres à l’assembleur. Toutes les pseudo-instruction commencent par le
caractère << . >> ce qui permet de les différencier clairement des instructions.
Six directives permettent de spécifier quelle section de la mémoire est concernée par les
instructions, macro-instructions ou directives qui les suivent. Sur ces six directives, deux
sont dynamiquement gérées à l’exécution : ce sont celles qui concernent la pile utilisateur,
stack, et la pile système, kstack. Ceci signifie que l’assembleur gère quatre comp-
teurs d’adresse indépendants correspondants aux quatre sections text, data, ktext
et kdata.
.text
action
Passage dans la section text
syntaxe
.text
description
Toutes les instructions et directives qui suivent concernent la section text
dans le segment utilisateur.
.data
action
Passage dans la section data
syntaxe
.data
description
Toutes les instructions et directives qui suivent concernent la section data
dans le segment utilisateur.
.stack
action
Passage dans la section stack
Les directives suivantes permettent de d’initialiser certaines zones dans les sections
text ou data de la mémoire.
action
Aligne le compteur d’adresse courant afin que expression bits de poids faible
soient à zéro.
syntaxe
align num
description
Cet opérateur aligne le compteur d’adresse sur une adresse telle que les n bits
de poids faible soient à zéro. Cette opération est effectuée implicitement pour
aligner correctement les instructions, demi-mots et mots.
exemple
.align 2
.byte 12
.align 2
.byte 24
.ascii
action
Déclare et initialise une chaîne de caractères
syntaxe
.ascii chaîne, [chaîne,] . . .
description
Cet opérateur place à partir de l’adresse du compteur d’adresse correspon-
dant à la section active la suite de caractères entre guillemets. S’il y a plu-
sieurs chaînes, elles sont placées à la suite. Cette chaîne peut contenir des
séquences d’échappement du langage C, et doit être terminée par un zéro
binaire si elle est utilisé avec un appel système.
exemple message:
.ascii "Bonjour, Maître!\n\0"
.asciiz
action
Déclare et initialise une chaîne de caractères, en ajoutant un zéro binaire à la
fin.
.byte
action
Positionne des octets successifs aux valeurs des expressions.
syntaxe
.byte expression, [expression,] . . .
description
La valeur de chacunes des expressions est tronquée à 8 bits, et les valeurs
ainsi obtenues sont placées à des adresses successives de la section active.
exemple
table:
.byte 1, 2, 4, 8, 16, 32, 64, 32, 16, 8, 4, 2, 1
.half
action
Positionne des demi-mots successifs aux valeurs des expressions.
syntaxe
.half expression, [expression,] . . .
description
La valeur de chacunes des expressions est tronquée à 16 bits, et les valeurs
ainsi obtenues sont placées dans des adresses successives de la section ac-
tive.
exemple
coordonnées:
.half 0 , 1024
.word
action
Positionne des mots successifs aux valeurs des expressions.
syntaxe
.word expression, [expression,] . . .
description
La valeur de chaque expression est placée dans des adresses successives de
la section active.
exemple
entiers:
.word -1, -1000, -100000, 1, 1000, 100000
.space
action
Reserve expression octets, et les mets à zéro
syntaxe
.space expression
description
Un espace de taille expression octets est réservé à partir de l’adresse courante
de la section active.
exemple
nuls:
.space 1024 ; initialise 1 kilo de mémoire à zéro
Pour exécuter certaines fonctions système, typiquement les entrées/sorties (lire ou écrire
un nombre, ou un caractère), il faut utiliser des appels système.
Par convention, le numéro de l’appel système est contenu dans le registre $2, et son
unique argument dans le registre $4.
écrire un entier :
Il faut mettre l’entier à écrire dans le registre $4 et exécuter l’appel système numéro
1. Typiquement, on aura :
li $4, 1234567 ; met 1234567 dans l’argument
ori $2, $0, 1 ; code de ’print_integer’
syscall ; affiche 1234567
lire un entier :
La valeur de retour d’une fonction — système ou autre — est positionnée dans le
registre $2. Ainsi, lire un entier consiste à exécuter l’appel système numéro 5 et
récupérer le resultat dans le registre$2.
ori $2, $0, 5 ; code de ’read_integer’
syscall ; $2 contient ce qui a été lu
quitter :
L’appel système numéro 10 effectue l’exit du programme au sens du langage C.
ori $2, $0, 10 ; indique l’appel à exit
syscall ; quitte pour de bon!
L’exécution de fonctions nécessite une pile en mémoire. Cette pile correspond à la section
stack. L’utilisation de cette pile fait l’objet de conventions qui doivent être respectées par
la fonction appelée et par la fonction appelante.
– la pile s’étend vers les adresses décroissantes ;
– le pointeur de pile pointe toujours sur la dernière case occupée dans la pile. Ceci
signifie que toutes les cases d’adresse inférieure au pointeur de pile sont libres ;
– le R3000 ne possède pas d’instructions spécifiques à la gestion de la pile. On utilise
les instructions lw et sw pour y accéder.
Les appels de fonction utilisent un pointeur particulier, appelé pointeur de pile. Ce pointeur
est stocké conventionnellement dans le registre $29. On le désigne aussi par la notation
$sp. La valeur de retour d’une fonction est conventionellement présente dans le registre
$2.
Par ailleurs, l’architecture du processeur M IPS R3000 impose l’utilisation du registre $31
pour stocker l’adresse de retour lors d’un appel de fonction (instructions de type jal ou
bgezal).
À chaque appel de fonction est associée une zone dans la pile constituant le << contexte
d’exécution >> de la fonction. Dans le cas des fonctions récursives, une même fonction
peut être appelée plusieurs fois et possèdera donc plusieurs contextes d’exécution dans
la pile. Lors de l’entrée dans une fonction, les registres $5 à $26 sont disponibles pour tout
calcul dans cette fonction. Dans le cas général, un contexte d’exécution d’une fonction est
constitué de quatre zones qui sont, dans l’ordre d’empilement :
1. la zone de sauvegarde des registres de travail de la fonction appelante. La fonc-
tion, qui a appelée la fonction courante, a dû sauvegarder les registres qu’elle utilise
et qu’elle ne veut pas voir modifiés. Dans la suite, nous nommerons ces registres
<< persistants >> par opposition aux registres << temporaires >> dont la modi-
fication n’a pas d’influence dans la suite de la fonction appelante. Ces derniers n’ont
pas à être sauvegardé. Par convention, on positionne toujours le registre persistant
d’index le plus petit à l’adresse la plus petite ;
2. la zone de sauvegarde de l’adresse de retour à la fonction appelante (adresse à
laquelle la fonction appelée doit revenir lors de sa terminaison) ;
3. la zone des arguments de la fonction appelée. Les valeurs des arguments sont
écrites dans la pile par la fonction appelante et lus dans la pile par la fonction ap-
pelée. Par convention, on positionne toujours le premier argument de la fonction
appelée à l’adresse la plus petite ;
1) Organisation de la pile
Dans le cas général, une fonction f, utilisant des registres persistants, souhaite appeler
une fonction g avec des arguments et qui possède des variables locales.
On note R0 , Rr−1 les registres persistants. On note A0 , Aa−1 les registres utilisés dans
l’appelant pour le calcul des arguments qui peuvent être persistants ou non. On note
P0 , Pa−1 les registres utilisés dans l’appelé pour la récupération des arguments qui
peuvent être persistants ou non.
Le code à écrire, dont on impose la structure, est le suivant. Dans f :
1 ...
2 addiu $sp, $sp, −(r + a + 1) × 4
3 sw $R0 , (a + 1) × 4($sp)
4 ...
5 sw $Rr−1 , (a + r) × 4($sp)
6 sw $A0 , 0($sp)
7 ...
8 sw $Aa−1 , (a − 1) × 4($sp)
9 jal g
10 lw $R0 , (a + 1) × 4($sp)
11 ...
12 lw $Rr−1 , (a + r) × 4($sp)
13 addiu $sp, $sp, +(r + a + 1) × 4
14 ...
Dans g :
20 addiu $sp, $sp, −l × 4
21 sw $31, (l + a) × 4($sp)
22 lw $P0 , l × 4($sp)
23 ...
24 lw $Pa−1 , (l + a − 1) × 4($sp)
25 ...
26 lw $31, (l + a) × 4($sp)
27 addiu $sp, $sp, l × 4
28 jr $31
Pour être tout à fait clair, nous allons passer en revue tous les cas possibles d’appel d’une
fonction g par une fonction f, suivant que f utilise ou non des registres persistants, que g a
des arguments ou non, et que g a des variables locales ou non. Il y a huit cas possibles.
Notez que les numéros de registres dans les exemples ont été choisi arbitrairement.
– f n’utilise pas de registres persistants et appelle g qui n’a pas d’arguments ni de va-
riables locales.
Dans f : Dans g :
... sw $31, 0($sp)
# pour sauver $31 ...
addiu $sp, $sp, -4 lw $31, 0($sp)
jal g jr $31
addiu $sp, $sp, +4
...
– f utilise des registres persistants (2 dans l’exemple) et appelle g qui n’a pas d’arguments
ni de variables locales.
– f utilise des registres persistants (2 dans l’exemple) et appelle g qui a des arguments
(3 dans l’exemple) et pas de variables locales. Les registres 11 et 12 sont les registres
persistants. Les registres 5, 6, et 7 sont des registres temporaires utilisés uniquement
pour la construction des arguments.
– f n’utilise pas de registres persistants et appelle g qui n’a pas d’arguments mais qui a
des variables locales (4 ici).
Dans f : Dans g :
... # pour 4 variables entières
addiu $sp, $sp, -4 addiu $sp, $sp, -16
jal g sw $31, 16($sp)
addiu $sp, $sp, +4 ...
... lw $31, 16($sp)
addiu $sp, $sp, +16
jr $31
– f utilise des registres persistants (2 dans l’exemple) et appelle g qui n’a pas d’arguments
mais qui a des variables locales (4 ici).
Dans f : Dans g :
... addiu $sp, $sp, -16
addiu $sp, $sp, -12 sw $31, 16($sp)
sw $5, 4($sp) ...
sw $6, 8($sp) lw $31, 16($sp)
jal g addiu $sp, $sp, +16
lw $5, 4($sp) jr $31
lw $6, 8($sp)
addiu $sp, $sp, +12
...
– f utilise des registres persistants (2 dans l’exemple) et appelle g qui a des arguments (3
dans l’exemple) et des variables locales (4 ici). Les registres 11 et 12 sont les registres
persistants. Les registres 5, 6, et 7 sont des registres temporaires utilisés uniquement
pour la construction des arguments.
Dans f : Dans g :
... addiu $sp, $sp, -16
# de la place pour $31 # sauver $31
# 3 arguments et 2 registres sw $31, 28($sp)
addiu $sp, $sp, -24 lw $20, 16($sp)
# pour passer 3 arguments lw $21, 20($sp)
sw $5, 0($sp) lw $23, 24($sp)
sw $6, 4($sp) ...
sw $7, 8($sp) lw $31, 28($sp)
# et 2 registres addiu $sp, $sp, +16
sw $11, 16($sp) jr $31
sw $12, 20($sp)
jal g
lw $11, 16($sp)
lw $12, 20($sp)
addiu $sp, $sp, +24
...
3) Exemple
Exemple d’une fonction calculant une distance euclidienne, en supposant qu’il existe une
fonction int isqrt(int x) ; qui retourne la racine carrée d’un nombre entier.
int main()
{
eudistance(5, 4);
}
somme = a * a + b * b;
distance = isqrt(somme);
return distance;
}
Le programme assembleur est le suivant (sans optimisations) : main n’est pas une
fonction comme les autres puisqu’elle n’est appelée par personne : On ne se préoccupe
pas de sauver d’adresse de retour.
.text
.globl main
main :
addiu $sp, $sp, -12
# Met les paramètres dans la pile
li $5, 4
sw $5, 0($29)
li $5, 5
sw $5, 4($29)
jal eudistance
addiu $sp, $sp, +12
eudistance :