Génération de code Mips

1  Environnement de programmation

Comme d'habitude, une archive zyvas.tar, de laquelle manque le fichier que vous devez écrire spim.ml. Que vous écrivez en complétant un fichier de départ squelette.ml. Donc dès maintenant et avant de réflechir :

2  Mise au point

Les moyens de mise au point sont la visualisation du code spim produit (comportement par défaut). Attention ce code d'est pas exécutable par le simulateur spim, car il comporte encore des temporaires En revanche, l'option ``-spill'' qui applique une transformation ultérieure qui alloue les temporaires dans la pile et produit du code exécutable. Il est possible de changer la répartition des registres en passant l'option -4 à zyvas le code produit est alors plus lisible.

Par exemple si on considère le source a.p :
program
function
 square(x : integer) : integer ;
begin
  square := x*x
end ;

begin
   writeln
(square(3))
end.
On obtiendra (une fois le module Spim écrit), et par « ./zyvas -4 a.p > a.spi », le code a.spi suivant :
square:
    subu $sp$spsquare_f
    move $107
$ra
    move
 $108$s0
    move
 $105$a0
    mul
  $109$105$105
    move $104
$109
square_end
:
    move $v0$104
    move $ra$107
    move $s0$108
    addu $sp$spsquare_f
    j   $ra
main
:
    subu $sp$spmain_f
    move $110
$ra
    move
 $111$s0
    li
  $112, 3
    move $a0$112
    jal square
    move $106
$v0
    move $a0
$106
    jal println_int
main_end
:
    move $ra$110
    move $s0$111
    addu $sp$spmain_f
    j   $ra
Code non exécutable à cause, entre autres, des $XXX qui trainent. En revanche, par « ./zyvas -4 -spill a.p > a.spill, on aura le code a.spill qui lui est exécutable par le simulateur spim.

Vous pouvez aussi lancer la procédure de test automatique par « make ok », ou par « make ok ZYVAOPTS="-4" »

3  Ce qu'il faut faire

Voici l'extrait pertinent du fichier d'interface spim.mli.
(*****************************)
(* Point d'entrée principal  *)
(*****************************)


type procedure = {
  frame : Frame.frame ;   (* frame de la procédure *)
  code : Ass.instr list ; (* Code proprement dit *)
  (* Instruction a enlever si la fonction n'alloue pas en pile (oublier) *)

  remove_ifzero : string list ;
  }


type program = {
      prelude : Ass.instr;         (* A emettre avant tout le reste *)
      main : procedure;            (* Code de main *)
      procedures : procedure list(* Code des proce'dures *)
    }

val program : Code.code  Trans.program -> program

Le squelette est déjà assez complet, il reste à écrire la sélection des instructions (fonction emit_stm) et la selection des appels de fonction (fonction emit_call). (selection des appels de fonction).

La fonction emit_fun, dont la mission est d'émettre le code du corps d'une fonction (avec prologue et épilogue), est complètement donnée. Toutefois, elle fait l'hypothèse que tous les arguments sont passés en registres. Plus précisément, emit_fun échoue si la fonction à compiler prend plus d'arguments qu'il y a de registres consacrés aux arguments (quatre en mode normal, un seul en mode « -4 »). Dans un premier temps, vous pourrez adopter le même comportement pour votre fonction emit_call.

4  Allez-y

Pour vous aider, vous pouvez aller chercher dans le poly les descriptions suivantes : Vous aurez réussi quand « make ok » n'échouera plus que sur le test « args.p ».
Voir la solution si besoin est.

5  Allez-y encore

Traiter le cas général, avec passage des argument en excès sur la pile.

Pour vous aider, vous pouvez aller chercher dans le poly les descriptions suivantes :
Voir la solution si besoin est.

Ce document a été traduit de LATEX par HEVEA.