% -*- mode: Noweb; noweb-code-mode: caml-mode -*- % $Id: codegen.nw,v 1.25 2004-06-28 13:29:35 lindig Exp $ % --------------------------------------------------------------------------- \section{Code Generation} \label{sec:codegen} % --------------------------------------------------------------------------- This module has two interface functions: one for generating the [[C--]] file header, and one for generating the bodies of [[C--]] functions. The compiler diver will call the first function to generate the file header and pass in a list of imports. The imports correspond to the external functions that may be called by Tiger programs. <>= val output_file_header : string list -> unit val emit : Tree.stm list -> unit @ <>= module E = Error module S = Symbol module T = Tree let pf = Printf.printf let spf = Printf.sprintf let join_map f l = String.concat "," (List.map f l) @ In the file header we declare our [[C--]] program to be little endian, import all of the external functions, and export the [[tiger_main]] function. In addition, we must declare the [[alloc_ptr]] global to be consistent with the other [[C--]] sources in the standard library and garbage collector. We also import the [[space_end]] pointer from the garbage collector to support inlined allocations. <>= let output_file_header imports = let pr_import x = pf "import bits32 \"tig_%s\" as %s;\n" x x in pf "target byteorder little;\n"; List.iter pr_import imports; pf "export tiger_main;\n\n"; pf "bits32 alloc_ptr;\n"; pf "import space_end;\n\n" @ The heart of the code generator is the [[emit]] function that converts a list of [[Tree]] statements (Section~\ref{sec:tree}) into [[C--]]. The [[emit]] function uses three private functions for converting statements, and expressions either in a value context or a boolean context. <>= let emit exl = <> <> <> in let code = List.map stm exl in List.iter (fun x -> pf " %s\n" x) code @ Generating [[C--]] code from our intermediate representation is relatively easy. However, when generating code for [[Tree]] statements, there is one small subtlety; we always wrap try blocks inside of a span with a key value of 2, and a data pointer of 1. This ``pointer'' marks the activation as having an exception handler that can be unwound to. The span data for exceptions is not examined, but we need something to put into the span, and it cannot be zero, because then we could not distinguish it from an error result from [[GetDescriptor]] in the runtime system. <>= let rec stm = function T.LABEL l -> spf "%s:" (S.name l) | T.CONT(l,ls) -> spf "continuation %s(%s):" (S.name l) (join_map S.name ls) | T.JUMP e -> spf "goto %s;" (valexp e) | T.CJUMP(ex, l1, l2) -> spf "if(%s) {goto %s;} else {goto %s;}" (boolexp ex) (S.name l1) (S.name l2) | T.MOVE(e1, e2) -> spf "%s = %s;" (valexp e2) (valexp e1) | T.EXP(T.CALL _ as e) -> spf "%s;" (valexp e) | T.EXP e -> spf "/* eliminated: %s */" (valexp e) | T.TRY l -> spf "span 2 1 { /* %s */" (S.name l) | T.TRYEND l -> spf "} /* end %s */" (S.name l) | T.RET e -> spf "return(%s);" (valexp e) | T.SEQ _ -> E.internal "SEQ node found in code gen" @ Generating [[C--]] code for [[Tree]] expressions is also quite simple. The [[CALL]] nodes require a little bit of work to get all of the annotations correct, but there are not any surprises. <>= and valexp = function T.BINOP(bop, e1, e2) -> spf "%%%s(%s, %s)" (T.cmm_binop bop) (valexp e1) (valexp e2) | T.RELOP _ as e -> spf "%%sx32(%%bit(%s))" (boolexp e) | T.MEM(e,ptr) -> spf "bits32[%s]" (valexp e) | T.TEMP(t,ptr) -> spf "%s" (S.name t) | T.NAME l -> (S.name l) | T.CONST i -> string_of_int i | T.CALL(l,el,cc,k,ptr) -> let cc = match cc with None -> "" | Some s -> spf "foreign \"%s\" " s and k = match k with None -> "" | Some l -> spf "also %s to %s" (if Option.use_unwind() then "unwinds" else "cuts") (S.name l) in spf "%s %s(%s) also aborts %s" cc (valexp l) (join_map valexp el) k | T.ESEQ _ -> E.internal "ESEQ node found in code gen" @ <>= and boolexp = function | T.RELOP(rop, e1, e2) -> spf "%%%s(%s, %s)" (T.cmm_relop rop) (valexp e1) (valexp e2) | e -> spf "%%ne(%s, 0)" (valexp e) @