Une fois cette archive décompressée (par exemple avec tar zxvf rtl-ocaml.tar.gz), vous obtenez un répertoire mini-c/ contenant plusieurs modules OCaml :
Pp | Utilitaires d'impression |
Ops | Opérations x86-64 utilisées pendant la sélection d'instructions |
Label | Étiquettes |
Register | Registres |
Rtltree | Register Transfer Language (RTL) |
Rtlinterp | Interprète de code RTL (pour tester) |
Ces modules sont complets. Cliquer sur le nom du module pour accéder à sa documentation. On prendra le temps de bien comprendre le code fourni.
On pourra récupérer ce nouveau minic.ml qui ajoute au compilateur une option --interp-rtl pour exécuter le code RTL (avec la fonction Rtlinterp.program fournie). Par ailleurs, ce nouveau programme principal affiche le code RTL (avec la fonction Rtltree.print_file fournie) dès lors que l'option --debug est passée.
On suggère très fortement de procéder construction par construction, en testant systématiquement à chaque fois.
let graph = ref Label.M.emptyIl est utile d'introduire une fonction pour ajouter une nouvelle instruction dans le graphe, à une étiquette fraîche qui est renvoyée :
let generate i = let l = Label.fresh () in graph := Label.M.add l i !graph; l
Pour les expressions, écrire une fonction de la forme
let rec expr e destr destl = ...où e est l'expression à traduire, destr le registre de destination de la valeur de cette expression et destl l'étiquette où il faut transférer ensuite le contrôle.
Pour les instructions, écrire une fonction de la forme
let rec stmt s destl retr exitl = ...où s est l'instruction à traduire et destl l'étiquette où il faut transférer ensuite le contrôle. Lorsque l'instruction est return, la valeur renvoyée doit être mise dans le registre retr et le contrôle doit être passé à l'étiquette exitl.
Écrire une fonction deffun qui traduit une fonction mini-C vers le type Rtltree.deffun des fonctions RTL. Il suffit de créer un pseudo-registre pour le résultat et une étiquette pour la sortie, avant d'appeler la fonction précédente sur le corps de la fonction mini-C.
Écrire enfin une fonction qui traduit un programme mini-C vers le type Rtltree.file, en traduisant chacune des fonctions.
Tester avec le programme :
int main() { return 42; }On doit obtenir quelque chose comme :
=== RTL ================================================== #1 main() entry : L2 exit : L1 locals: L2: mov $42 #1 --> L1
Les nouvelles instructions RTL à utiliser sont : Emunop, Embinop.
Tester avec des programmes comme
int main() { return 40 - (-2); }
Tester avec des programmes comme
int main() { int x, y; y = 40; x = 2; return y + x; }
Pour traduire les instructions if et while, il peut être utile d'introduire une fonction
let rec condition e truel false = ...qui traduit une expression e en RTL et transfert le contrôle à l'étiquette truel si sa valeur est non nulle et à l'étiquette falsel sinon.
Ajouter également le traitement des constructions && et ||, en respectant leur évaluation paresseuse.
Les nouvelles instructions RTL à utiliser sont : Emubranch, Embbranch, Egoto.
Tester avec des programmes comme
int main() { if (1 <= 2) return 3; else return 4; }
Les nouvelles instructions RTL à utiliser sont : Ecall.
Tester avec des programmes comme
int fact(int n) { if (n <= 1) return 1; return n * fact(n-1); } int main() { return fact(42); }
Les nouvelles instructions RTL à utiliser sont : Eload, Estore.
Tester avec des programmes comme
struct S { int a; int b; }; int main() { struct S *p; p = malloc(sizeof(struct S)); p->a = 40; p->b = 2; return p->a + p->b; }
> ./run -i "mon/chemin/vers/mini-c --interp-rtl" Test de mon/chemin/vers/mini-c --interp-rtl Interprétation Execution normale ----------------- ........................................................... Execution conduisant à un échec ------------------------------- ... Compilation: Compilation : 62/62 : 100% Comportement du code : 62/62 : 100%