(*
TP05: Génération de code
*)
(*Partie 1 : Compilation des expressions arithmétiques simples
*)
type expr =
| Const of int (* constante *)
| Add of expr*expr (* addition *)
| Mul of expr*expr (* multiplication *)
| IfTE of expr*expr*expr*expr (* if e1=e2 then e3 else e4 *)
| Call of string*expr list (* appel de fonction *)
| Var of int (* i-ème argument d'une fonction *)
(* definitions: (f,n,b) est la définition de la fonction f, à n
arguments, de corps b *)
type def = string*int*expr
(* un programme est une liste de définitions de fonction,
* suivie d'une expression à évaluer. *)
type prog = def list*expr
(* EXEMPLES *)
(* exemple basique *)
let basic: prog = [],Const 42
(* exemple d'expression: (9+5)*(4-1) *)
let trente: expr = Mul(Add(Const 9,Const 5),Add(Const 4,Const(-1)))
(* le programme pour calculer trente : pas de définition de fonction *)
let prog_trente: prog = [],trente
(* autre exemple: factorielle *)
let fact: def = ("fact", 1,
IfTE(Var 1,Const 0, Const 1,
Mul(Var 1,Call("fact",[Add(Var 1,Const(-1))]))))
(* exemple de programme: on utilise factorielle *)
let prog_fact: prog = [fact],Add(Const 18,Call("fact",[Const 4]))
(* vous ne pouvez pas encore utiliser ces exemples évidemment... *)
(** Les Registres *)
(* les 32 registres sont identifiés par les chaînes des entiers de 0 à 31 *)
type registre = string
(* pour obtenir faciliment le registre numéro [i] *)
let reg i = "R" ^ string_of_int i
(* quelques registres *)
let r_zero: registre = reg 0 (* y a toujours 0 dedans *)
let r_result: registre = reg 1 (* par défaut, contient le dernier résultat calculé *)
let r_fp: registre = reg 29 (* Ici, on va dire qu'on s'en sert que pointeur de frame *)
let r_sp: registre = reg 30 (* traditionnellement, le pointeur de pile *)
let r_return: registre = reg 31 (* pour savoir où continuer après un appel de fonction *)
(* fonction de compilation vers un fichier donné *)
let compile fichier =
(* fonctions d'affichage vers le fichier donné en argument *)
let f = open_out fichier in
let print s = output_string f ("\t"^s^"\n") in
(* empiler la valeur dans le registre [r] sur la pile *)
let push r =
print ("PSH "^r^" "^r_sp^" -4")
in
(* dépiler, en lisant dans un registre *)
let pop r =
failwith "à faire..."
in
(* génération du code correspondant à une expression *)
let rec gen_expr = function
| Const k -> print ("ORI " ^ r_result ^ " " ^ r_zero ^ " " ^(string_of_int k))
| Add(e1,e2) ->
failwith "à faire"
| Mul(e1,e2) ->
failwith "copier-coller ce qui est au-dessus"
| IfTE(e1,e2,e3,e4) ->
failwith "à faire"
| Var n ->
failwith "à faire encore, un peu après"
| Call(f,l) ->
failwith "à faire aussi plus tard"
in
(** À partir d'ici, c'est de l'enrobage pour que ça marche *)
(* juste pour pas avoir le '\t' au début de la ligne *)
let print_lab s = output_string f s; output_char f '\n' in
(* génération du code des définitions *)
let gen_def (f,n,e) =
(* préambule *)
print ""; (* on va à la ligne pour être propres :) *)
print_lab (f^":"); (* le nom de la définition *)
gen_expr e; (* Le calcul de la fonction, enfin *)
(* postambule: on remet tout où il faut *)
print ("RET "^r_return);
(* saut à l'adresse de retour *)
in
(* génération du code du programme final *)
let gen_prog (l,e) =
(* initialisation *)
print ""; (* on va à la ligne pour être propres (comme d'hab) *)
print_lab "start:";
print "BEQ 0 main";
print "SYSCALL 1 0 6"; (* trois lignes magiques pour que ça marche: abra... *)
print "SYSCALL 1 0 6"; (* ...cada... *)
print "SYSCALL 1 0 6"; (* ...briais! (demandez à l'auteur de emuf pourquoi) *)
(* génération du code des définitions *)
List.iter gen_def l;
(* génération du code de l'expression *)
print_lab "main:";
gen_expr e;
(* avant de finir, on affiche le resultat *)
print "SYSCALL R1 0 7"; (* afficher r.1 *)
print "ORIU R1 0 10"; (* mettre '\n' dans r.1 (pour le retour à la ligne) *)
print "SYSCALL 1 0 6"; (* afficher le caractère *)
(* et on fait exit *)
print "RET 0";
close_out f;
Sys.command ("cat "^fichier) (* affiche le fichier généré *)
in
gen_prog
(* premier test de base *)
let _ = compile "basic.s" basic
(* compile le programme trente dans le fichier "trente.s" *)
let _ = compile "trente.s" prog_trente
(* compile le programme fact_5 dans le fichier "fact_5.s" *)
let _ = compile "fact.s" prog_fact
(* Partie 2 : Ordonnancement optimal
*)
(* ne pas commencer ici tant qu'on n'a pas terminé tout au-dessus *)
let reorder_expr: expr -> expr = fun _ -> failwith "a faire: reorder_expr"
let reorder: prog -> prog = fun _ -> failwith "a faire: reorder_expr"
(* test de l'ordonnanceur sur l'expression (1+(3+(7+(12+(19+0))))) *)
let cinq_add = (Add(Const 1,Add(Const 3,Add(Const 7,Add(Const 12,Add(Const 19,Const 0))))))
let _ = compile "/tmp/cinqadd.s" ([],cinq_add)
let _ = compile "/tmp/cinqadd-opt.s" (reorder ([],cinq_add))
(* Partie 3 : Récursion terminale
*)
(* idem: ne pas commencer ici tant qu'on n'a pas terminé tout au-dessus *)
(* annotation d'un programme : vous devez ignorer les annotations
d'appels de fonctions déjà présentes, et les remplacer par des
annotations correctes *)
let annot_expr: int -> expr -> expr = fun _ -> failwith "TODO (annot_expr)"
let annot_prog: prog -> prog = fun _ -> failwith "TODO (annot_prog)"
(* test: factorielle récursive terminale *)
(* vous pouvez décommenter la suite après avoir rajouté un '*int*' à la
* définition du [Call] *)
(*
let fact_no_rt =
["fact",2,IfTE(Var 2,Const 0, Var 1,
Call("fact",-1,[Mul(Var 1,Var 2);
Add(Var 2, Const(-1))]))],
Call("fact",-1,[Const 1; Const 5])
let fact_rt = annot_prog fact_no_rt
(* compilation et exécution *)
let _ = compile "fact_rt_5.s" fact_rt_5
*)
(* Partie 4 : Expressions avec fonctions imbriquées: lambda-lifting
*)
type eexpr =
| EConst of int
| EAdd of eexpr*eexpr
| EMul of eexpr*eexpr
| EIfTE of eexpr*eexpr*eexpr*eexpr
| ECall of string*eexpr list
(* let f x1 ... xn = e in e' *)
| ELet of string*string list*eexpr*eexpr
| EVar of string
let lift: eexpr -> prog = fun _ -> failwith "TODO (lift)"
(* factorielle, avec une fonction imbriquée, récursive terminale *)
(*
let fact_rt_6 = ELet("fact",["n"],
ELet("aux", ["acc"; "n"],
EIfTE(EVar "n",EConst 0, EVar "acc",
ECall("aux",[EMul(EVar "acc",EVar "n");
EAdd(EVar "n", EConst(-1))])),
ECall("aux",[EConst 1; EVar "n"])),
ECall("fact",[EConst 6]))
(* déroulage et annotation *)
let fact_rt_6_deroulee = annot_prog (lift fact_rt_6)
(* on peut aussi réordonnancer si vous voulez *)
(* compilation et exécution *)
let _ = compile "fact_rt_6.s" fact_rt_6_deroulee
*)