Advertisement
Guest User

Untitled

a guest
Jan 10th, 2017
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
F# 10.61 KB | None | 0 0
  1. (* File MicroC/Comp.fs
  2.    A compiler from micro-C, a sublanguage of the C language, to an
  3.    abstract machine.  Direct (forwards) compilation without
  4.    optimization of jumps to jumps, tail-calls etc.
  5.    sestoft@itu.dk * 2009-09-23, 2011-11-10
  6.  
  7.    A value is an integer; it may represent an integer or a pointer,
  8.    where a pointer is just an address in the store (of a variable or
  9.    pointer or the base address of an array).  
  10.  
  11.    The compile-time environment maps a global variable to a fixed
  12.    store address, and maps a local variable to an offset into the
  13.    current stack frame, relative to its bottom.  The run-time store
  14.    maps a location to an integer.  This freely permits pointer
  15.    arithmetics, as in real C.  A compile-time function environment
  16.    maps a function name to a code label.  In the generated code,
  17.    labels are replaced by absolute code addresses.
  18.  
  19.    Expressions can have side effects.  A function takes a list of
  20.    typed arguments and may optionally return a result.
  21.  
  22.    Arrays can be one-dimensional and constant-size only.  For
  23.    simplicity, we represent an array as a variable which holds the
  24.    address of the first array element.  This is consistent with the
  25.    way array-type parameters are handled in C, but not with the way
  26.    array-type variables are handled.  Actually, this was how B (the
  27.    predecessor of C) represented array variables.
  28.  
  29.    The store behaves as a stack, so all data except global variables
  30.    are stack allocated: variables, function parameters and arrays.  
  31. *)
  32.  
  33. module Comp
  34.  
  35. open System.IO
  36. open Absyn
  37. open Machine
  38.  
  39. (* ------------------------------------------------------------------- *)
  40.  
  41. (* Simple environment operations *)
  42.  
  43. type 'data env = (string * 'data) list
  44.  
  45. let rec lookup env x =
  46.     match env with
  47.     | []         -> failwith (x + " not found")
  48.     | (y, v)::yr -> if x=y then v else lookup yr x
  49.  
  50. (* A global variable has an absolute address, a local one has an offset: *)
  51.  
  52. type var =
  53.      | Glovar of int                   (* absolute address in stack           *)
  54.      | Locvar of int                   (* address relative to bottom of frame *)
  55.  
  56. (* The variable environment keeps track of global and local variables, and
  57.    keeps track of next available offset for local variables *)
  58.  
  59. type varEnv = (var * typ) env * int
  60.  
  61. (* The function environment maps function name to label and parameter decs *)
  62.  
  63. type paramdecs = (typ * string) list
  64. type funEnv = (label * typ option * paramdecs) env
  65.  
  66. (* Bind declared variable in env and generate code to allocate it: *)
  67.  
  68. let allocate (kind : int -> var) (typ, x) (varEnv : varEnv) : varEnv * instr list =
  69.     let (env, fdepth) = varEnv
  70.     match typ with
  71.     | TypA (TypA _, _) ->
  72.       raise (Failure "allocate: array of arrays not permitted")
  73.     | TypA (t, Some i) ->
  74.       let newEnv = ((x, (kind (fdepth+i), typ)) :: env, fdepth+i+1)
  75.       let code = [INCSP i; GETSP; CSTI (i-1); SUB]
  76.       (newEnv, code)
  77.     | _ ->
  78.       let newEnv = ((x, (kind (fdepth), typ)) :: env, fdepth+1)
  79.       let code = [INCSP 1]
  80.       (newEnv, code)
  81.  
  82. (* Bind declared parameters in env: *)
  83.  
  84. let bindParam (env, fdepth) (typ, x)  : varEnv =
  85.     ((x, (Locvar fdepth, typ)) :: env , fdepth+1)
  86.  
  87. let bindParams paras ((env, fdepth) : varEnv) : varEnv =
  88.     List.fold bindParam (env, fdepth) paras;
  89.  
  90. (* ------------------------------------------------------------------- *)
  91.  
  92. (* Build environments for global variables and functions *)
  93.  
  94. let makeGlobalEnvs (topdecs : topdec list) : varEnv * funEnv * instr list =
  95.     let rec addv decs varEnv funEnv =
  96.         match decs with
  97.         | []         -> (varEnv, funEnv, [])
  98.         | dec::decr  ->
  99.           match dec with
  100.           | Vardec (typ, var) ->
  101.             let (varEnv1, code1)        = allocate Glovar (typ, var) varEnv
  102.             let (varEnvr, funEnvr, coder) = addv decr varEnv1 funEnv
  103.             (varEnvr, funEnvr, code1 @ coder)
  104.           | Fundec (tyOpt, f, xs, body) ->
  105.             addv decr varEnv ((f, (newLabel(), tyOpt, xs)) :: funEnv)
  106.     addv topdecs ([], 0) []
  107.  
  108. (* ------------------------------------------------------------------- *)
  109.  
  110. (* Compiling micro-C statements:
  111.    * stmt    is the statement to compile
  112.    * varenv  is the local and global variable environment
  113.    * funEnv  is the global function environment
  114. *)
  115.  
  116. let rec cStmt stmt (varEnv : varEnv) (funEnv : funEnv) : instr list =
  117.     match stmt with
  118.     | If(e, stmt1, stmt2) ->
  119.       let labelse = newLabel()
  120.       let labend  = newLabel()
  121.       cExpr e varEnv funEnv @ [IFZERO labelse]
  122.       @ cStmt stmt1 varEnv funEnv @ [GOTO labend]
  123.       @ [Label labelse] @ cStmt stmt2 varEnv funEnv
  124.       @ [Label labend]          
  125.     | While(e, body) ->
  126.       let labbegin = newLabel()
  127.       let labtest  = newLabel()
  128.       [GOTO labtest; Label labbegin] @ cStmt body varEnv funEnv
  129.       @ [Label labtest] @ cExpr e varEnv funEnv @ [IFNZRO labbegin]
  130.     | Expr e ->
  131.       cExpr e varEnv funEnv @ [INCSP -1]
  132.     | Block stmts ->
  133.       let rec loop stmts varEnv =
  134.           match stmts with
  135.           | []     -> (snd varEnv, [])
  136.           | s1::sr ->
  137.             let (varEnv1, code1) = cStmtOrDec s1 varEnv funEnv
  138.             let (fdepthr, coder) = loop sr varEnv1
  139.             (fdepthr, code1 @ coder)
  140.       let (fdepthend, code) = loop stmts varEnv
  141.       code @ [INCSP(snd varEnv - fdepthend)]
  142.     | Return None ->
  143.       [RET (snd varEnv - 1)]
  144.     | Return (Some e) ->
  145.       cExpr e varEnv funEnv @ [RET (snd varEnv)]
  146.  
  147. and cStmtOrDec stmtOrDec (varEnv : varEnv) (funEnv : funEnv) : varEnv * instr list =
  148.     match stmtOrDec with
  149.     | Stmt stmt    -> (varEnv, cStmt stmt varEnv funEnv)
  150.     | Dec (typ, x) -> allocate Locvar (typ, x) varEnv
  151.  
  152. (* Compiling micro-C expressions:
  153.  
  154.    * e       is the expression to compile
  155.    * varEnv  is the local and gloval variable environment
  156.    * funEnv  is the global function environment
  157.  
  158.    Net effect principle: if the compilation (cExpr e varEnv funEnv) of
  159.    expression e returns the instruction sequence instrs, then the
  160.    execution of instrs will leave the rvalue of expression e on the
  161.    stack top (and thus extend the current stack frame with one element).  
  162. *)
  163.  
  164. and cExpr (e : expr) (varEnv : varEnv) (funEnv : funEnv) : instr list =
  165.     match e with
  166.     | Access acc     -> cAccess acc varEnv funEnv @ [LDI]
  167.     | Assign(acc, e) -> cAccess acc varEnv funEnv @ cExpr e varEnv funEnv @ [STI]
  168.     | CstI i         -> [CSTI i]
  169.     | Addr acc       -> cAccess acc varEnv funEnv
  170.     | Range b s e -> if (s != 0) then [CSTI b] @ [CSTI s] @ [CSTI e] @ [RANGE] else raise (Failure "step must not be 0")
  171.     // cExpr b varEnv funEnv @ cExpr s varEnv @ cExpr e varEnv funEnv @ [RANGE] else raise (Failure "step must not be 0")
  172.     // cExpr [b..s..e] varEnv funEnv @ [RANGE] else raise (Failure "step must not be 0")
  173.     | Prim1(ope, e1) ->
  174.       cExpr e1 varEnv funEnv
  175.       @ (match ope with
  176.          | "!"      -> [NOT]
  177.          | "printi" -> [PRINTI]
  178.          | "printc" -> [PRINTC]
  179.          | "arrlen" -> [ARRLEN] //Added by Mathies Wiencke.
  180.          | _        -> raise (Failure "unknown primitive 1"))
  181.     | Prim2(ope, e1, e2) ->
  182.       cExpr e1 varEnv funEnv
  183.       @ cExpr e2 varEnv funEnv
  184.       @ (match ope with
  185.          | "*"   -> [MUL]
  186.          | "+"   -> [ADD]
  187.          | "-"   -> [SUB]
  188.          | "/"   -> [DIV]
  189.          | "%"   -> [MOD]
  190.          | "=="  -> [EQ]
  191.          | "!="  -> [EQ; NOT]
  192.          | "<"   -> [LT]
  193.          | ">="  -> [LT; NOT]
  194.          | ">"   -> [SWAP; LT]
  195.          | "<="  -> [SWAP; LT; NOT]
  196.          | _     -> raise (Failure "unknown primitive 2"))
  197.     | Andalso(e1, e2) ->
  198.       let labend   = newLabel()
  199.       let labfalse = newLabel()
  200.       cExpr e1 varEnv funEnv
  201.       @ [IFZERO labfalse]
  202.       @ cExpr e2 varEnv funEnv
  203.       @ [GOTO labend; Label labfalse; CSTI 0; Label labend]            
  204.     | Orelse(e1, e2) ->
  205.       let labend  = newLabel()
  206.       let labtrue = newLabel()
  207.       cExpr e1 varEnv funEnv
  208.       @ [IFNZRO labtrue]
  209.       @ cExpr e2 varEnv funEnv
  210.       @ [GOTO labend; Label labtrue; CSTI 1; Label labend]
  211.     | Call(f, es) -> callfun f es varEnv funEnv
  212.  
  213. (* Generate code to access variable, dereference pointer or index array.
  214.    The effect of the compiled code is to leave an lvalue on the stack.   *)
  215.  
  216. and cAccess access varEnv funEnv : instr list =
  217.     match access with
  218.     | AccVar x ->
  219.       match lookup (fst varEnv) x with
  220.       | Glovar addr, _ -> [CSTI addr]
  221.       | Locvar addr, _ -> [GETBP; CSTI addr; ADD]
  222.     | AccDeref e -> cExpr e varEnv funEnv
  223.     | AccIndex(acc, idx) -> cAccess acc varEnv funEnv
  224.                             @ [LDI] @ cExpr idx varEnv funEnv @ [ADD]
  225.  
  226. (* Generate code to evaluate a list es of expressions: *)
  227.  
  228. and cExprs es varEnv funEnv : instr list =
  229.     List.concat(List.map (fun e -> cExpr e varEnv funEnv) es)
  230.  
  231. (* Generate code to evaluate arguments es and then call function f: *)
  232.    
  233. and callfun f es varEnv funEnv : instr list =
  234.     let (labf, tyOpt, paramdecs) = lookup funEnv f
  235.     let argc = List.length es
  236.     if argc = List.length paramdecs then
  237.       cExprs es varEnv funEnv @ [CALL(argc, labf)]
  238.     else
  239.       raise (Failure (f + ": parameter/argument mismatch"))
  240.  
  241.  
  242. (* Compile a complete micro-C program: globals, call to main, functions *)
  243.  
  244. let cProgram (Prog topdecs) : instr list =
  245.     let _ = resetLabels ()
  246.     let ((globalVarEnv, _), funEnv, globalInit) = makeGlobalEnvs topdecs
  247.     let compilefun (tyOpt, f, xs, body) =
  248.         let (labf, _, paras) = lookup funEnv f
  249.         let (envf, fdepthf) = bindParams paras (globalVarEnv, 0)
  250.         let code = cStmt body (envf, fdepthf) funEnv
  251.         [Label labf] @ code @ [RET (List.length paras-1)]
  252.     let functions =
  253.         List.choose (function
  254.                          | Fundec (rTy, name, argTy, body)
  255.                                     -> Some (compilefun (rTy, name, argTy, body))
  256.                          | Vardec _ -> None)
  257.                     topdecs
  258.     let (mainlab, _, mainparams) = lookup funEnv "main"
  259.     let argc = List.length mainparams
  260.     globalInit
  261.     @ [LDARGS; CALL(argc, mainlab); STOP]
  262.     @ List.concat functions
  263.  
  264. (* Compile a complete micro-C and write the resulting instruction list
  265.    to file fname; also, return the program as a list of instructions.
  266.  *)
  267.  
  268. let intsToFile (inss : int list) (fname : string) =
  269.     File.WriteAllText(fname, String.concat " " (List.map string inss))
  270.  
  271. let compileToFile program fname =
  272.     let instrs   = cProgram program
  273.     let bytecode = code2ints instrs
  274.     intsToFile bytecode fname
  275.     instrs
  276.  
  277. (* Example programs are found in the files ex1.c, ex2.c, etc *)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement