MASTER 1ère année

Travaux Pratiques de

Compilation

sujet n°4

(Génération de code assembleur MIPS)

Introduction.

Une rapide présentation de l'assembleur MIPS.

Exercice 1.

Écrire un programme qui prend en argument (sur la ligne de commande) une suite de valeurs entières ou réelles, avec au plus 5 valeurs de chaque type, et qui produit en sortie un programme en assembleur MIPS qui affiche tous les entiers sur une première ligne et tous les réels sur une deuxième ligne. Par exemple, pour la suite 5 4.56 8.6 12 0.25 8 8, le programme produira en sortie le programme assembleur ci-dessous dont l'exécution affichera :

5 12 8 8
4.56 8.6 0.25


Programme assembleur :

  .text
main:
  li $v0,1         # appel système pour afficher un entier
  li $a0,5         # chargement de l'entier à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,1         # appel système pour afficher un entier
  li $a0,12        # chargement de l'entier à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,1         # appel système pour afficher un entier
  li $a0,8         # chargement de l'entier à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,1         # appel système pour afficher un entier
  li $a0,8         # chargement de l'entier à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,4         # appel système pour afficher un retour à la ligne
  la $a0,str1      # chargement de l'adresse de la chaîne
  syscall          # affichage
  li $v0,2         # appel système pour afficher un réel
  l.s $f12,temp0   # chargement du réel à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,2         # appel système pour afficher un réel
  l.s $f12,temp1   # chargement du réel à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,2         # appel système pour afficher un réel
  l.s $f12,temp2   # chargement du réel à afficher
  syscall          # affichage
  li $v0,4         # appel système pour afficher un blanc
  la $a0,str2
  syscall          # affichage
  li $v0,4         # appel système pour afficher un retour à la ligne
  la $a0,str1      # chargement de l'adresse de la chaîne
  syscall          # affichage
# exit
  li $v0,10        # on sort du programme proprement
  syscall
  .data
temp0: .float 4.560000
temp1: .float 8.600000
temp2: .float 0.250000
str1:  .asciiz "\n"
str2:  .asciiz " "

Exercice 2.

L'objectif de cet exercice est d'écrire un petit compilateur pour le langage EXPR des expressions arithmétiques avec variables dont vous trouverez la grammaire ci-dessous. Ce petit compilateur doit produire du code MIPS. Pour simplifier le problème et se concentrer davantage sur le génération de code MIPS, le code pour les phases d'analyse lexicale, d'analyse syntaxique et de génération de code intermédiaire (code à 3 adresses), est donné. Il s'agira donc de compléter ce code et en particulier d'ajouter la partie correspondante à la phase de génération de code MIPS.

Grammaire du langage EXPR :

L -> I ; L | I

I -> id = E | print id

E -> E + E | E - E | E * E | - E | ( E ) | id | num

Le code du compilateur à compléter.

NB : Dans ce code, la structure de données utilisée pour représenter la table des symboles est peu efficace, mais ceci n'a pas d'importance dans le contexte de cet exercice.

Pour vous aider à trouver le code MIPS à produire, voici par exemple le code assembleur que l'on pourrait écrire pour réaliser le calcul de l'expression arithmétique 2*(3+2)+1 :

  .text
main:
# calcul de 3+2
  lw $t0,temp2    # chargement de 3 dans le registre t0
  lw $t1,temp3    # chargement de 2 dans le registre t1
  add $t0,$t0,$t1 # t0 = t0 + t1
  sw $t0,temp5    # écriture du résultat en mémoire à l'adresse dont le label est temp5
# transfert du résultat de temp5 à temp6 ( E -> ( E ) )
  lw $t0,temp5
  sw $t0,temp6
# calcul de 2*(3+2)
  lw $t0,temp1
  lw $t1,temp6
  mul $t0,$t0,$t1
  sw $t0,temp7
# calcul de 2*(3+2)+1
  lw $t0,temp7
  lw $t1,temp4
  add $t0,$t0,$t1
  sw $t0,temp8
# affichage du résultat
  li $v0,4      # appel système pour afficher une chaine
  la $a0,str    # chargement de l'adresse de la chaine
  syscall       # affichage de la chaine
  li $v0,1      # appel système pour afficher un entier
  lw $a0, temp8 # chargement de l'entier à afficher
  syscall       # affichage
# exit
  li $v0,10     # on sort proprement du programme
  syscall

  .data
temp1: .word 2 # 2 est stocké en mémoire à l'adresse dont le label est temp2
temp2: .word 3
temp3: .word 2
temp4: .word 1
temp5: .word 0
temp6: .word 0
temp7: .word 0
temp8: .word 0
str: .asciiz "le résultat est "


Au lieu d'utiliser des labels pour chaque emplacement mémoire, on aurait aussi pu procéder de la manière suivante, sachant qu'un entier est stocké sur 4 octets :

  .text
main:
  la $t0, temp    # chargement de l'adresse du premier temporaire dans t0
# calcul de 3+2
  lw $t1,4($t0)   # 3 se trouve à l'adresse du premier temporaire + 4 octets
  lw $t2,8($t0)   # 2 se trouve à l'adresse du premier temporaire + 8 octets
  add $t1,$t1,$t2 # t1 = t1 + t2
  sw $t1,16($t0)  # écriture du résultat en mémoire à l'adresse de départ + 16 octets
# transfert du résultat ( E -> ( E ) )
  lw $t1,16($t0)
  sw $t1,20($t0)
# calcul de 2*(3+2)
  lw $t1,($t0)
  lw $t2,20($t0)
  mul $t1,$t1,$t2
  sw $t1,24($t0)
# calcul de 2*(3+2)+1
  lw $t1,24($t0)
  lw $t2,12($t0)
  add $t1,$t1,$t2
  sw $t1,28($t0)
# affichage du résultat
  li $v0,4       # appel système pour afficher une chaine
  la $a0,str     # chargement de l'adresse de la chaine
  syscall        # affichage de la chaine
  li $v0,1       # appel système pour afficher un entier
  lw $a0,28($t0) # chargement de l'entier à afficher
  syscall        # affichage
# exit
  li $v0,10      # on sort proprement du programme
  syscall

  .data
temp: .word 2, 3, 2, 1, 0, 0, 0, 0
str: .asciiz "le résultat est "