| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- open Printf
- open Types
- open Util
- let rec var_init = function
- (* Move global initialisations to __init function *)
- | Program (decls, ann) ->
- let decls = flatten_blocks (List.map var_init decls) in
- let rec trav assigns = function
- | [] -> (assigns, [])
- | (Assign _ as hd) :: tl
- | (Allocate _ as hd) :: tl -> trav (assigns @ [hd]) tl
- | hd :: tl ->
- let (assigns, decls) = trav assigns tl in
- (assigns, (hd :: decls))
- in
- let (assigns, decls) = trav [] decls in (
- match assigns with
- | [] -> Program (decls, ann)
- | assigns ->
- let init_func = FunDef (true, Void, "__init", [], Block assigns, []) in
- Program (init_func :: decls, ann)
- )
- (* Global variable initialisation:
- * Add an assign statement and the Program node will remove it later on *)
- | GlobalDef (export, ctype, name, Some init, ann) ->
- Block [GlobalDef (export, ctype, name, None, ann);
- Assign (name, None, init, ann)]
- (* Global array definition:
- * - Create a new global variable for each dimension and initialise it to
- * the given expression
- * - create __allocate statement in __init *)
- | GlobalDef (export, Array (ctype, dims), name, None, ann) as dec ->
- let rec create_dimvars i = function
- | [] -> []
- | hd :: tl ->
- let dimname = name ^ "$" ^ string_of_int i in
- let var = Var (dimname, None, ann) in
- var :: (create_dimvars (i + 1) tl)
- in
- let dimvars = create_dimvars 1 dims in
- let create_globaldef dim = function
- | Var (dimname, None, ann) ->
- var_init (GlobalDef (export, Int, dimname, Some dim, ann))
- | _ -> raise InvalidNode
- in
- let vardecs = List.map2 create_globaldef dims dimvars in
- let alloc = [Allocate (dec, dimvars, ann)] in
- Block (vardecs @
- [GlobalDef (export, Array (ctype, dimvars), name, None, ann)] @
- alloc)
- (* Split local variable initialisations in declaration and assignment *)
- | FunDef (export, ret_type, name, params, body, ann) ->
- let inits = ref [] in
- let rec extract_inits = function
- (* Translate scalar array initialisation to ArrayScalar node,
- * for easy replacement later on *)
- | VarDec (Array _ as vtype, name, Some (Const _ as v), ann) ->
- let init = Some (ArrayInit (ArrayScalar v, vtype)) in
- extract_inits (VarDec (vtype, name, init, ann))
- (* Wrap ArrayConst in ArrayInit to pass dimensions *)
- | VarDec (Array _ as vtype, name, Some (ArrayConst _ as v), ann) ->
- let init = Some (ArrayInit (v, vtype)) in
- extract_inits (VarDec (vtype, name, init, ann))
- | VarDec (ctype, name, init, ann) as dec ->
- (* array definition: create __allocate statement *)
- let alloc = match ctype with
- | Array (_, dims) -> [Allocate (dec, dims, ann)]
- | _ -> []
- in
- (* initialisation: create assign statement *)
- let add = match init with
- | Some value -> alloc @ [Assign (name, None, value, ann)]
- | None -> alloc
- in
- inits := !inits @ add;
- VarDec (ctype, name, None, ann)
- | LocalFuns funs -> LocalFuns (List.map var_init funs)
- | node -> transform_children extract_inits node
- in
- let rec place_inits = function
- (* initialisations need to be placed after local functions *)
- | (LocalFuns _ as hd) :: tl -> hd :: !inits @ tl
- | hd :: tl -> hd :: (place_inits tl)
- | [] -> []
- in
- let params = flatten_blocks (List.map var_init params) in
- let body = flatten_blocks (place_inits (block_body (extract_inits body))) in
- FunDef (export, ret_type, name, params, Block body, ann)
- | node -> transform_children var_init node
- let rec replace_var var replacement node =
- let trav = (replace_var var replacement) in
- match node with
- | Var (name, None, ann) when name = var ->
- Var (replacement, None, ann)
- | For (counter, start, stop, step, body, ann) when counter = var ->
- For (replacement, trav start, trav stop, trav step, trav body, ann)
- | node ->
- transform_children trav node
- let for_to_while node =
- let rec traverse new_vars = function
- | FunDef (export, ret_type, name, params, body, ann) ->
- let new_vars = ref [] in
- let body = traverse new_vars body in
- let create_vardec name = VarDec (Int, name, None, []) in
- let new_vardecs = List.map create_vardec !new_vars in
- let _body = new_vardecs @ (flatten_blocks (block_body body)) in
- FunDef (export, ret_type, name, params, Block _body, ann)
- (* Transform for-loops to while-loops *)
- | For (counter, start, stop, step, body, ann) ->
- let _i = fresh_var counter in
- let _stop = fresh_const "stop" in
- let _step = fresh_const "step" in
- new_vars := !new_vars @ [_i; _stop; _step];
- let vi = Var (_i, None, []) in
- let vstop = Var (_stop, None, annof stop) in
- let vstep = Var (_step, None, annof step) in
- let cond = Cond (
- Binop (Gt, vstep, Const (IntVal 0, []), []),
- Binop (Lt, vi, vstop, []),
- Binop (Gt, vi, vstop, []),
- []
- ) in
- Block [
- Assign (_i, None, start, annof start);
- Assign (_stop, None, stop, annof stop);
- Assign (_step, None, step, annof step);
- traverse new_vars (While (cond, (Block (
- block_body (replace_var counter _i body) @
- [Assign (_i, None, Binop (Add, vi, vstep, []), [])]
- )), ann));
- ]
- (* DISABLED, while-loops are explicittly supported by the assembly phase
- (* Transform while-loops to do-while loops in if-statements *)
- | While (cond, body, ann) ->
- let cond = traverse new_vars cond in
- let body = traverse new_vars body in
- Block [If (cond, Block [DoWhile (cond, body, ann)], ann)]
- *)
- | node -> transform_children (traverse new_vars) node
- in
- traverse (ref []) node
- let rec array_init = function
- (* Transform scalar assignment into nested for-loops *)
- | Assign (name, None, ArrayInit (ArrayScalar value, Array (_, dims)), ann) ->
- let rec add_loop indices = function
- | [] ->
- Assign (name, Some indices, value, ann)
- | dim :: rest ->
- let counter = fresh_var "i" in
- let body = Block [add_loop (indices @ [Var (counter, None, [])]) rest] in
- For (counter, Const (IntVal 0, []), dim, Const (IntVal 1, []), body, [])
- in
- add_loop [] dims
- (* Transform array constant inisialisation into separate assign statements
- * for all entries in the constant array *)
- (* TODO: only allow when array dimensions are constant? *)
- | Assign (name, None, ArrayInit (ArrayConst _ as value, Array (_, dims)), ann) ->
- let ndims = list_size dims in
- let rec make_assigns depth i indices = function
- | [] -> []
- | hd :: tl ->
- let assigns = traverse depth (i :: indices) hd in
- make_assigns depth (i + 1) indices tl @ assigns
- and traverse depth indices = function
- | ArrayConst (values, _) ->
- make_assigns (depth + 1) 0 indices values
- | value when depth = ndims ->
- let indices = List.map (fun i -> Const (IntVal i, [])) indices in
- [Assign (name, Some (List.rev indices), value, ann)]
- | node ->
- let msg = sprintf
- "dimension mismatch: expected %d nesting levels, got %d"
- ndims depth
- in
- raise (NodeError (node, msg))
- in
- Block (List.rev (traverse 0 [] value))
- | node -> transform_children array_init node
- (* Generate new variables for array dimensions in function bodies, to avoid
- * re-evalutation after array dimension reduction. For example:
- *
- * int dims = 0;
- *
- * int dim() {
- * dims = dims 1; // Side effect => dims() should be called once
- * return 10;
- * }
- *
- * void foo() {
- * int[10, dim()] arr;
- * arr[0, 1] = 1;
- * }
- *
- * After dimension reduction, this would become:
- * void foo() {
- * int[] arr;
- * arr = allocate(10, dim());
- * arr[1 * dim() + 0] = 1;
- * }
- *
- * This behaviour is of course incorrect. To avoid dim() from being evaluated
- * twice, the snippet above is transformed into (note the $$ which will help
- * later during constant propagation):
- * void foo() {
- * int a$dim$$1 = 10;
- * int a$dim$$2 = dim();
- * int[a$dim$$1, a$dim$$2] arr;
- * arr[1, 2] = 1;
- * }
- *
- * ... which then becomes:
- * void foo() {
- * int a$dim$$1;
- * int a$dim$$2;
- * int[a$dim$$1, a$dim$$2] arr;
- * a$dim$1 = 10;
- * a$dim$2 = dim();
- * arr = __allocate(a$dim$1 * a$dim$2);
- * arr[1 * a$dim$2 * 0] = 1;
- * }
- * *)
- let rec array_dims = function
- | VarDec (Array (ctype, dims), name, init, ann) ->
- let make_dimname i _ = name ^ "$dim$$" ^ string_of_int (i + 1) in
- let dimnames = mapi make_dimname dims in
- let make_dimvar d n = Var (n, None, annof d) in
- let dimvars = List.map2 make_dimvar dims dimnames in
- let make_dimdec dimname dim = VarDec (Int, dimname, Some dim, []) in
- let dimdecs = List.map2 make_dimdec dimnames dims in
- Block (dimdecs @ [VarDec (Array (ctype, dimvars), name, init, ann)])
- | node -> transform_children array_dims node
- let rec phase input =
- log_line 1 "- Desugaring";
- match input with
- | Ast node -> Ast (for_to_while (array_init (var_init (array_dims node))))
- | _ -> raise (InvalidInput "desugar")
|