(* Produção de código para a linguagem Arith *)

open Format
open X86_64
open Ast

(* Excepção por levantar quando uma variável (local ou global) é mal utilizada *)
exception VarUndef of string

(* Tamanho da frame, em byte (cada variável local ocupa 8 bytes) *)
let frame_size = ref 0

(* As variáveis globais são guardadas numa tabela de dispersão *)
let (genv : (string, unit) Hashtbl.t) = Hashtbl.create 17

(* Utilizamos uma tabela associativa cujas chaves são as variáveis locais
   (strings) e cujo valor associado é a posição relativamente
   a $fp (em bytes) *)
module StrMap = Map.Make(String)

(* Compilação de uma expressão *)
let compile_expr =
  (* Função recursiva local a compile_expr usada para gerar o código
     máquina da árvore de sintaxe abstracta associada a um valor de tipo
     Ast.expr ; no fim da execução deste código, o valor deve estar
     no topo da pilha *)
  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)-> (* um caso particular para divisão *)
        comprec env next e1 ++
        pushq 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 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

(* Compilação de uma instrução *)
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"


(* Compilação do programa p e grava o código no ficheiro ofile *)
let compile_program p ofile =
  let code = List.map compile_instr p in
  let code = List.fold_right (++) code nop in
  let p =
    { text =
        glabel "main" ++
        subq (imm !frame_size) (reg rsp) ++ (* aloca a frame *)
        leaq (ind ~ofs:(!frame_size - 8) rsp) rbp ++ (* $fp = ... *)
        code ++
        addq (imm !frame_size) (reg rsp) ++ (* desaloca a frame *)
        movq (imm 0) (reg rax) ++ (* exit *)
        ret ++
        label "print_int" ++
        movq (reg rdi) (reg rsi) ++
        movq (ilab ".Sprint_int") (reg rdi) ++
        movq (imm 0) (reg rax) ++
        call "printf" ++
        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;
  (* "flush" final para garantir que tudo foi escrito no ficheiro
     antes de o fechar *)
  fprintf fmt "@?";
  close_out f

This document was generated using caml2html