Énoncé et corrigé en Postscript | Réponses intégrées ou à la demande |
Examen de majeure 2 Compilation (Durée 2h) 20 mars 2000 |
On attachera une importance particulière à la clarté, à la précision, mais aussi à la concision de la rédaction. Les questions 1 et 4 sont indépendantes, ainsi que certaines sous-questions d'autres parties.
|
function sum (var n : integer) : integer; begin if n > 0 then sum := n + sum (n-1) else sum := 0 end; |
sum_f=8 sum: sub $sp, $sp, sum_f sw $ra, 4($sp) sw $s0, 0($sp) move $s0, $a0 ble $s0, $zero, L2 sub $a0, $s0, 1 jal sum add $v0, $s0, $v0 L1: lw $s0, 0($sp) lw $ra, 4($sp) add $sp, $sp, sum_f j $ra L2: move $v0, $zero j L10 |
sum
est récursive, non terminale. Il faut donc revenir
à la fonction après l'appel récursif. Pour cela l'adresse de retour (la
valeur du registre $ra
) doit être sauvegardée pendant l'appel
récursif (qui utilise lui-même $ra
). Aucun registre n'est
candidat à la sauvegarde de $ra
, car il serait lui-même écrasé:
c'est le même code qui effectue l'appel récursif donc il utilise
potentiellement (ie. c'est une approximation statique) les mêmes registres
que l'appel lui-même.$ra
dans la tas, mais cela revient à
implémenter une pile dans le tas.)
ra
et s0
, il
n'y a qu'une seule sauvegarde et restauration en pile qui est inévitable.
|
$a0
, $v0
et $t0
qui sont aussi des temporaires
particuliers, pré-coloriés (on réserve le préfixe $
aux
registres machine). Pour simplifier, on traite les registres spéciaux
($sp
, $ra
et $zero
) à part: on ne les considérera
pas comme des temporaires et on les exclut de toutes les phases d'analyse
(durée de vie, interférence, allocation de registres).
1: add t, $a0, 1 2: add t, t, $a0 3: add $a0, t, t 4: jal foo 5: add $v0, t, $v0 |
foo
lit $a0
, écrase les trois registres
machines et retourne son résultat dans v0
. On suppose que
le registre $v0
est vivant à la fin du code C.
Donner le code j(C).
1: add p1, $a0, 1 sw p1, 0($sp) 2: lw q1, 0($sp) add p2, q1, $a0 sw p2, 0($sp) 3: lw q2, 0($sp) add $a0, q2, q2 4: jal foo 5: lw q3, 0($sp) add $v0, q3, $v0 |
lw q1, 0($sp)
rappelle le contenu du sommet de pile
dans le temporaire auxiliaire q1
. C'est absurde, car ce contenu est
aussi celui du temporaire p1
:
on pourrait certainement remplacer lw p1, 0($sp)
par l'instruction
move q1, p1
qui évite une lecture en pile inutile
(de plus, cette instruction sera ensuite éliminée par l'allocation de
registres). En général, certaines occurences de t n'ont pas besoin d'être
traitées en pile (qu'il s'agisse d'une lecture ou d'une écriture).
1: add $t0, $a0, 1 2: add $t0, $t0, $a0 sw $t0, 0($sp) 3: add $a0, $t0, $t0 4: jal foo 5: lw $t0, 0($sp) add $v0, $t0, $v0 |
sub $sp, $sp, 4
au début, et add $sp, $sp 4
à la fin, ici
comme à la question précédente, bien qu'elles ne font pas partie du code de
mise en pile, mais de la phase finale postérieure à l'allocation de
registres.)
|
# Temporaires vivants en sortie 1: add p1, $a0, 1 # $a0, p1 move q1, p1 # $a0, p1, q1 move q2, p1 # $a0, p1, q1 move q3, p1 # $a0, q1 2: add p2, q1, $a0 # p2 move q1, p2 # p2 move q2, p2 # p2, q2 move q3, p2 # q2, q3 3: add $a0, q2, q2 # $a0, q3 4: jal foo # $v0, q3 5: add $v0, q3, $v0 # $v0 |
(Formellement, comme c'est évidemment vrai au début du programme, il suffit de vérifier que si la propriété est vérifiée pour s, elle l'est aussi pour s+1. Soit k Î A la position du programme dans A à l'instant s. Si l'instruction k effectue un saut dans A à l'instruction l, alors compte tenu de la correspondance entre les valeurs des registres, l'instruction j+(k) effectue un saut à l'instruction l de j+(A), donc le programme est à l'instruction l Î A et j+(l) Î j+(A) à l'instant s+1.Ainsi, à une trace d'exécution de A correspond une trace d'exécution de j+(A) ayant un comportement identique vis à vis de l'extérieur. Comme l'exécution est déterministe, la trace observée pour j+(A) est la seule possible.
- Si l'instruction l lit une valeur v dans t (la valeur de t à l'instant s), la macro-instruction j+(l) lit à la place la valeur de qi, donc v, pour un certain i de I.
- Si l'instruction l écrit v dans t , alors l'instruction j+(l) écrit v dans pj puis écrit la valeur de pj donc v dans tous les qi i Î I. )
move
.
move
dont le registre de
destination n'est pas vivant en sortie.
1: add p1, $a0, 1 # $a0, p1 move q1, p1 # $a0, p1, q1 2: add p2, q1, $a0 # p2 move q2, p2 # p2, q2 move q3, p2 # q2, q3 3: add $a0, q2, q2 # $a0, q3 4: jal foo # $v0, q3 5: add $v0, q3, $v0 # $v0 |
|
add p2, q1, $a0 move q2, p2 move q3, p2 |
q2
et q3
sont les seuls
vivants en sortie du
bloc. Donner pour chaque instruction les registres vivants en sortie.
Donner le graphe d'interférence (sans justification).
# vivants # Interférences engendrées add p2, q1, $a0 # p2 # move q2, p2 # p2 q2 # move q3, p2 # q2 q3 # q2-q3 |
|
q2
et q3
interfèrent. Comme ces
temporaires ont toujours la même valeur (lorsque celle-ci est utile), on
pourrait en fait leur associer le même registre machine, ce qui est
ici interdit par leur interférence, au risque de ne pas pouvoir les colorier
du tout.
move q2, p2
et move q3, p2
ensemble en une
macro-instruction mmv q2 q3, p2
(lire ``multi-move'')
qui lit p1
et écrit q2
et q3
. Oper
dans le cours)?
q2
et q3
qui survivent à l'instruction sont
écrits par celle-ci donc ils interfèrent entre eux.
move
:
l'instruction mmv d1 ... dn, s copie le temporaire s
dans les temporaires (dk)k Î 1..n.
|
move
introduites par
des instructions mmv
, puis en simplifiant
ces instructions après l'analyse de durée de vie (en retirant certaines
destinations, donc certains move) comme indiqué à la question 3.4.
# vivants # Interférences engendrées 1: add p1, $a0, 1 # $a0, p1 # $a0-p1 mmv q1, p1 # $a0, q1 # $a0-q1 2: add p2, $a0, q1 # p2 # mmv q2 q3, p2 # q2, q3 # 3: add $a0, q2, q2 # $a0, q3 # $a0-q3 4: jal foo # $v0, q3 # $a0-q3, $v0-q3, $t0-q3 5: add $v0, q3, $v0 # $v0 |
|
Temporaire | $a0 | $v0 | $t0 | p1 | p2 | q1 | q2 | q3 |
Registre | $a0 | $v0 | $t0 | $t0 | $t0 | $t0 | $t0 | q3 |
mmv
et
simplification des instructions move
devenues inutiles par la fusion de leur source et de leur destination.
1: add $t0, $a0, 1 2: add $t0, $t0, $a0 move q3, $t0 3: add $a0, $t0, $t0 4: jal foo 5: add $v0, q3, $v0 |
|
sw
pj, 0($sp)
suivi de
mmv Qj \ S, pj et les secondes par
lw
ri, 0($sp)
pour obtenir le code j ¥(A).
Le graphe d'interférence résultant est un sous-graphe du précédent, sans les
noeuds mis en pile, donc il est coloriable, avec les mêmes couleurs.
Aussi on n'a pas besoin de refaire un nouveau coloriage.
This document was translated from LATEX by HEVEA.