(* Production de code pour le langage Arith *)

open Format
open X86_64
open Ast

(* Exception à lever quand une variable (locale ou globale) est mal utilisée *)
exception VarUndef of string

(* Taille de la frame, en octets (chaque variable locale occupe 8 octets) *)
let frame_size = ref 0

(* Les variables globales sont stockées dans une table de hachage *)
let (genv : (string, unit) Hashtbl.t) = Hashtbl.create 17

(* On utilise une table d'association dont les clés sont les variables locales
   (des chaînes de caractères) et où la valeur associée est la position
   par rapport à $fp (en octets) *)
module StrMap = Map.Make(String)

(* Compilation d'une expression *)
let compile_expr =
  (* Fonction récursive locale à compile_expr utilisée pour générer le code
     machine de l'arbre de syntaxe abstraite associé à une valeur de type
     Ast.expr ; à l'issue de l'exécution de ce code, la valeur doit se trouver
     en sommet de pile *)
  let rec comprec env next = function
    | Cst i ->
        movq (imm i) (reg rax)
    | Var x ->
        begin
          try
            let ofs = - (StrMap.find x env) in
            movq (ind ~ofs rbp) (reg rax)
          with Not_found ->
            if not (Hashtbl.mem genv x) then raise (VarUndef x);
            movq (lab x) (reg rax)
        end
    | Binop (Div, e1, e2)-> (* un cas particulier pour la division *)
        comprec env next e1 ++
        pushq (reg rax) ++
        comprec env next e2 ++
        movq (reg rax) (reg rbx) ++
        movq (imm 0) (reg rdx) ++
        popq rax ++
        idivq (reg rbx)
    | Binop (o, e1, e2)->
        let op = match o with
          | Add -> addq
          | Sub -> subq
          | Mul -> imulq
          | Div -> assert false
        in
        comprec env next e1 ++
        pushq (reg rax) ++
        comprec env next e2 ++
        movq (reg rax) (reg rbx) ++
        popq rax ++
        op (reg rbx) (reg rax)
    | Letin (x, e1, e2) ->
        if !frame_size = next then frame_size := 8 + !frame_size;
        comprec env next e1 ++
        movq (reg rax) (ind ~ofs:(-next) rbp) ++
        comprec (StrMap.add x next env) (next + 8) e2
  in
  comprec StrMap.empty 0

(* Compilation d'une instruction *)
let compile_instr = function
  | Set (x, e) ->
      let code =
        compile_expr e ++
        movq (reg rax) (lab x)
      in
      Hashtbl.replace genv x ();
      code
  | Print e ->
      compile_expr e ++
      movq (reg rax) (reg rdi) ++
      call "print_int"


(* Compile le programme p et enregistre le code dans le fichier ofile *)
let compile_program p ofile =
  let code = List.map compile_instr p in
  let code = List.fold_right (++) code nop in
  if !frame_size mod 16 = 8 then frame_size := 8 + !frame_size;
  let p =
    { text =
        globl "main" ++ label "main" ++
        pushq !%rbp ++
        movq !%rsp !%rbp ++
        subq (imm !frame_size) (reg rsp) ++ (* allocation des variables *)
        code ++
        movq (imm 0) (reg rax) ++ (* exit *)
        movq !%rbp !%rsp ++
        popq rbp ++
        ret ++
        label "print_int" ++
        pushq !%rbp ++ (* assure notamment l'alignement *)
        movq (reg rdi) (reg rsi) ++
        leaq (lab ".Sprint_int") rdi ++
        movq (imm 0) (reg rax) ++
        call "printf" ++
        popq rbp ++
        ret;
      data =
        Hashtbl.fold (fun x _ l -> label x ++ dquad [1] ++ l) genv
          (label ".Sprint_int" ++ string "%d\n")
    }
  in
  let f = open_out ofile in
  let fmt = formatter_of_out_channel f in
  X86_64.print_program fmt p;
  (* on "flush" le buffer afin de s'assurer que tout y a été écrit
     avant de le fermer *)
  fprintf fmt "@?";
  close_out f

This document was generated using caml2html