Otro otro ANF

A(dministrative?)-Normal Form

En Lecture 4, Ben Lerner definió ANF y dio varios ejemplos de como convertir expresiones a ANF.

En clase discutimos alternativas a la funcion anf de Dr. Lerner, en particular que podiamos crear un nuevo tipo para no confundir expresiones con expresiones en ANF. Joe Gibbs presenta una version de ANF que usa unos nuevos tipos, pero me parece un poco complicada. Voy a tratar de hacer una un poco mas sencilla.

Sabemos que queremos distinguir expresiones immediatas de las demas, asi que podemos definir un tipo.

type immexpr =
  | ImmNum of int64
  | ImmId of string

Ahora podemos definir otro tipo aexp de expresiones en ANF. A diferencia de las expr, las aexpr solo pueden construir expresiones en ANF.

type aexpr =
  | AImm of immexpr
  | APrim2 of prim2 * immexpr * immexpr
  | ALet of string * aexpr * aexpr

Normalizando

La funcion anf debe entoces recibir un tag expr y producir un aexpr. Vamos a llamar esta anf. Recibe tag expr y una funcion que recibe immexpr y devuleve aexpr, aqui llamana expr_with_holes esta funcion es como un callback, y va a recibir el inmediato que contiene el resultado de cualquier subexpression complicada. La llamaremos para obtener el valor de la subexpression.

let rec anf (e : tag expr) (expr_with_holes : (immexpr -> aexpr))
 : aexpr =
  match e with
  | ENumber (n, _) -> (expr_with_holes (ImmNum n))
  | EId (b, _) -> (expr_with_holes (ImmId b))

El lio de anf es para poder convertir subexpresiones complicadas en valores inmediatos, para compilar mas facil. Por ejemplo, un EPrim2 puede tener subexpresiones complicadas en left y right, pero las vamos a convertir en inmediatos, e insertarlos en un APrim2:

  | EPrim2 (op, left, right, tag) ->
     anf left (fun limm ->
       anf right (fun rimm ->
           let varname = "foo" ^ (string_of_int tag) in
         ALet (varname,
           APrim2 (op, limm, rimm),
           (expr_with_holes (ImmId varname)))))

Compilando

Nos hara falta una funcion para convertir un inmediato a un arg:

  let imm_to_arg imm env =
    match imm with
    | ImmNum n -> Const n
    | ImmId v -> let slot = lookup v env
                 in RegOffset (RSP, ~-8*slot)

Ahora podemos compilar un inmediato, moviendolo a RAX:

let rec compile_aexpr (e : aexpr) (env : env) : instruction list =
  match e with
  | AImm imm ->
     [ IMov (Reg RAX, imm_to_arg imm env) ]

Y los APrim2 tienen argumentos inmediatos que podemos sumar

  | APrim2 (Plus, left, right) ->
     [ IMov (Reg RAX, imm_to_arg left) ;
       IAdd (Reg RAX, imm_to_arg right) ]