Traduction des appels

Voici le cours correspondant. La solution est ici.

Présentation

Les sources sont dans l’archive td6-rtl2ertl.tar.gz, on construit l’exécutable avec la commande make.

Il s’agit de passer du language RTL défini dans RTL.mli au langage ERTL défini dans ERTL.mli, en explicitant la convention d’appel. Lisez bien les commentaires introductifs de ERTL.mli.

Comme la semaine dernière, la traduction est séparée en deux modules : rtl2ertl.ml est le module externe qui définit les fonctions de manipulation de base, qui sont passées au module paramétré rtl2ertlI.ml que vous allez devoir implémenter.

La signature du module dans rtl2ertlI.mli donne à la fois les fonctions définies dans rtl2ertl.ml que vous devrez utiliser, et les fonctions utilisées dans rtl2ertl.ml que vous allez lui fournir.

La traduction des appels nécessite de modifier d’une part l’instruction d’appel d’une fonction, d’autre part d’insérer un prologue et un épilogue dans le corps de chaque fonction pour tenir compte de la convention d’appel.

Voici les trois éléments qu’il faut implémenter :

let translate_call (odestr : Register.t option) (callee : Primitive.callee) (actuals : Register.t list) (l : Label.t) : ERTL.instruction = ...

let prologue : Label.t = ...

let epilogue : Label.t = ...

Voilà les deux fonctions qui permettent de créer un nouveau pseudo-registre ou un nouveau label, comme la semaine dernière :

let allocate () :  Register.t = ...

let generate (instr : ERTL.instruction) : Label.t = ...

Les labels et les pseudo-registres de RTL restent valables en ERTL. On utilise si possible les mêmes, mais les fonctions allocate et generate permettent d’en générer de nouveaux si besoin.

D’autres variables sont nécessaires pour traduire les appels, créer le prologue et l’épilogue, elles sont données comme paramètres du foncteur :

Pour commencer

Cette fois, aucun code de démarrage ne vous est fourni ! A vous de créer le fichier rtl2ertlI.ml avec les définitions de fonctions nécessaires pour satisfaire la signature du module rtl2ertlI.mli. Cela devrait permettre de compiler le compilateur sans erreurs.

Note : Pour l’instant le corps d’une fonction peut se contenter de lancer une exception.

Voir la solution.

Fonctions auxiliaires

Vous aurez besoin dans la suite d’implémenter tout ou partie des fonctions suivantes, au renommage près. Vous pouvez choisir de les implémenter d’abord ou de passer tout-de-suite à la section suivante.

Pseudo-registres et registres matériels

Le language ERTL fournit deux instructions pour passer d’un pseudo-registre à un registre matériel et inversement : IGetHwReg et ISetHwReg.

Vous pouvez avoir besoin d’implémenter les fonctions suivantes :

let sethwreg ((desthwr, sourcer) : MIPS.register * Register.t) (l : Label.t) : Label.t = ...

let sethwregs (moves : (MIPS.register * Register.t) list) (l : Label.t) : Label.t = ...

let osethwreg ((desthwr, osourcer) : MIPS.register * Register.t option) (l : Label.t) : Label.t = ...

let gethwreg ((destr, sourcehwr) : Register.t * MIPS.register) (l : Label.t) : Label.t = ...

let gethwregs (moves : (Register.t * MIPS.register) list) (l : Label.t) : Label.t = ...

let ogethwreg ((odestr, sourcehwr) : Register.t option * MIPS.register) (l : Label.t) : Label.t = ...

Voir la solution.

Pseudo-registres et emplacements de pile

Le language ERTL fournit deux instructions pour passer d’un pseudo-registre à un emplacement de pile et inversement : IGetStack et ISetStack.

Les emplacements de pile sont distingués suivant qu’il s’agit pour la fonction d’emplacements de pile entrants ou sont stockés certains de ses paramètres (ERTL.SlotIncoming) ou d’emplacements de pile sortants ou sont stockés certains paramètres des fonctions qu’elle-même appelle (ERTL.SlotOutgoing).

Les emplacements sont représentés par un décalage entier depuis le début de la zone correspondante, ce qui amène naturellement à écrire les fonctions suivantes :

let offsets (rs : Register.t list) : ((Register.t * int32) list) = ...

let setstackslots (sourcers : Register.t list) -> (l : Label.t) : Label.t = ...

let getstackslots (destrs : Register.t list) -> (l : Label.t) : Label.t = ...

Voir la solution.

Traduire la convention d’appel

Concrètement, on veut qu’un appel se déroule comme suit :

Cela implique de savoir passer une valeur d’un pseudo-registre à un registre matériel ou un emplacement de pile et inversement.

Dans un premier temps, on vous demande de vous concentrer sur la structuration de votre code. Vous allez donc écrire le code de translate_call, du prologue et de l’epilogue en utilisant des fonctions auxiliaires de votre choix, par exemple celles proposées dans la section précédente.

Afin de pouvoir tester au plus tôt votre implémentation, vous pouvez commencer par traiter seulement les appels avec 4 paramètres au plus, qui ne nécessitent pas d’utiliser la pile.

Traduction des appels

Donnez un corps à la fonction translate_call.

Note : Pour des raisons pratiques, la fonction translate_call doit renvoyer une instruction au lieu d’un label. Vous aurez peut-être besoin d’utiliser l’instruction IGoto ...

Les différents registres matériels utilisés sont définis dans MIPS.mli. Les fonctions définies dans misc.mli peuvent vous être utiles.

Voir la solution.

Traduction du prologue et de l’épilogue

Donnez une valeur aux variables prologue et epilogue.

Là-encore, les fonctions définies dans misc.mli peuvent vous être utiles.

Voir la solution.

Vous pouvez tester votre traduction vers le language ERTL avec make ertl.

La solution complète est le fichier rtl2ertlI.ml.


Ce document a été traduit de LATEX par HEVEA