| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- open Printf
- open Types
- open Util
- let rec add_depth depth node =
- match node with
- | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
- (* XXX: traversal should traverse into Dim nodes *)
- let dims = List.map (add_depth depth) dims in
- GlobalDec (ArrayDims (ctype, dims), name, Depth depth :: ann)
- | Param (ArrayDims (ctype, dims), name, ann) ->
- let dims = List.map (add_depth depth) dims in
- Param (ArrayDims (ctype, dims), name, Depth depth :: ann)
- | GlobalDec _
- | GlobalDef _
- | Param _
- | Dim _
- | VarDec _
- | Var _
- | FunCall _
- | Assign _
- | For _
- | VarUse _
- | FunUse _
- | VarLet _ ->
- annotate (Depth depth) node |> traverse_unit (add_depth depth)
- | FunDec _ ->
- annotate (Depth depth) node
- | FunDef (export, ret_type, name, params, body, ann) ->
- let params = List.map (add_depth (depth + 1)) params in
- let body = add_depth (depth + 1) body in
- FunDef (export, ret_type, name, params, body, Depth depth :: ann)
- | _ ->
- traverse_unit (add_depth depth) node
- type identifier_type = Funcname | Varname
- let typename = function Varname -> "variable" | Funcname -> "function"
- let tblfind tbl name = try Some (Hashtbl.find tbl name) with Not_found -> None
- let add_to_scope scope name dec namety err =
- match tblfind scope name with
- | Some orig when depthof orig >= depthof dec ->
- err := NodeMsg (orig, "\rPreviously declared here:") ::
- NodeMsg (dec, sprintf "Error: cannot redeclare %s \"%s\""
- (typename namety) name) :: !err
- | Some _ ->
- Hashtbl.replace scope name dec
- | None ->
- Hashtbl.add scope name dec
- let check_in_scope scope name errnode namety err =
- match tblfind scope name with
- | Some dec -> dec
- | None ->
- let msg = sprintf "undefined %s \"%s\"" (typename namety) name in
- err := NodeMsg (errnode, msg) :: !err;
- DummyNode
- let prt_vars vars =
- let prt name dec = prerr_string (name ^ ", ") in
- Hashtbl.iter prt vars;
- prerr_endline "(end)"
- let analyse do_rename program =
- let err = ref [] in
- (* Add functions at the current depth to the function scope, do not traverse
- * into nested functions *)
- let rec collect_funs funs node =
- match node with
- | FunDec (_, name, _, _)
- | FunDef (_, _, name, _, _, _) ->
- (* TODO: don't copy function body to save memory *)
- add_to_scope funs name node Funcname err;
- node
- | _ -> traverse_unit (collect_funs funs) node
- in
- (* Traverse through statements in the current scope, checking and replacing
- * variable occurrences. Add newly declared variables to the variable scope
- * on-the-fly. *)
- let rec traverse scope node =
- let trav = traverse scope in
- let trav_dims = function
- | ArrayDims (ctype, dims) ->
- ArrayDims (ctype, List.map trav dims)
- | ctype -> ctype
- in
- let vars, funs, repl = scope in
- let check_rename node rename =
- let name = nameof node in
- let shadows_higher_scope =
- do_rename && (Hashtbl.mem vars name || Hashtbl.mem repl name)
- in
- (* Trigger duplication error and make sure following duplication errors
- * will refer to a non-generated variable name *)
- add_to_scope vars name node Varname err;
- if shadows_higher_scope then begin
- let newname = fresh_id name in
- Hashtbl.replace repl name newname;
- let newnode = rename newname in
- add_to_scope vars newname newnode Varname err;
- newnode
- end else
- node
- in
- let add_dims = function
- | ArrayDims (ctype, dims) ->
- let rec add = function
- | [] -> []
- | (Dim (name, ann) as dim) :: tl ->
- check_rename dim (fun name -> Dim (name, ann)) :: add tl
- | _ -> raise InvalidNode
- in
- ArrayDims (ctype, add dims)
- | ctype -> ctype
- in
- match node with
- | Program (decls, ann) ->
- Program (Block decls |> trav |> trav_funs scope |> block_body, ann)
- | FunDec _ | FunDef _ -> node
- | GlobalDef (export, ctype, name, init, ann) ->
- let node = GlobalDef (export, trav_dims ctype, name, optdo trav init, ann) in
- add_to_scope vars name node Varname err;
- node
- | VarDec (ctype, name, init, ann) ->
- let ctype = trav_dims ctype in
- let init = optdo trav init in
- let node = VarDec (ctype, name, init, ann) in
- check_rename node (fun name -> VarDec (ctype, name, init, ann))
- | GlobalDec (ArrayDims (ctype, dims), name, ann) ->
- let rec replace_dims i = function
- | [] -> []
- | Dim (dimname, ann) :: tl ->
- let newname = generate_array_dim name i in
- Hashtbl.add repl dimname newname;
- Dim (newname, ann) :: replace_dims (i + 1) tl
- | _ -> raise InvalidNode
- in
- let ctype = ArrayDims (ctype, replace_dims 0 dims) in
- let node = GlobalDec (add_dims ctype, name, ann) in
- add_to_scope vars name node Varname err;
- node
- | GlobalDec (ctype, name, ann) ->
- add_to_scope vars name node Varname err;
- node
- | Param (ctype, name, ann) ->
- let ctype = add_dims ctype in
- let node = Param (ctype, name, ann) in
- check_rename node (fun name -> Param (ctype, name, ann))
- | For (name, start, stop, step, body, ann) ->
- let start, stop, step = trav start, trav stop, trav step in
- (* For-loops are a special case: the loop counter defines a new scope
- * which is allowed to shadow existing local variables, and allows for
- * nested loops with the same induction variables. Replace the loop
- * counter with a fresh variable to enforce this behaviour, and avoid
- * having to replace variables during desigaring. *)
- (* FIXME: only create new variable if necessary *)
- let newname = fresh_id name in
- let node = For (newname, start, stop, step, body, ann) in
- Hashtbl.add vars name node;
- Hashtbl.add vars newname node;
- Hashtbl.add repl name newname;
- let node = For (newname, start, stop, step, trav body, ann) in
- Hashtbl.remove repl name;
- Hashtbl.remove vars newname;
- Hashtbl.remove vars name;
- node
- (* Perform renaming *)
- | Var (name, dims, ann) when Hashtbl.mem repl name ->
- trav (Var (Hashtbl.find repl name, dims, ann))
- | Assign (name, dims, value, ann) when Hashtbl.mem repl name ->
- trav (Assign (Hashtbl.find repl name, dims, value, ann) )
- (* Replace variables or function calls with use-nodes which contain the
- * entire declaration *)
- | Var (name, dims, ann) ->
- let dec = check_in_scope vars name node Varname err in
- VarUse (dec, optmap trav dims, ann)
- | FunCall (name, args, ann) ->
- let dec = check_in_scope funs name node Funcname err in
- FunUse (dec, List.map trav args, ann)
- (* Assign statements are replaced with VarLet nodes, which stores the
- * declaration of the assigned variable *)
- | Assign (name, dims, value, ann) ->
- begin
- match check_in_scope vars name node Varname err with
- | For _ ->
- err := NodeMsg (node, "cannot assign to induction variable") :: !err;
- node
- | dec ->
- VarLet (dec, optmap trav dims, trav value, ann)
- end
- (* Also support intermediary nodes because context analysis is re-run later
- * on to propagate new declaration properties *)
- | VarUse (dec, dims, ann) ->
- VarUse (Hashtbl.find vars (nameof dec), optmap trav dims, ann)
- | FunUse (dec, args, ann) ->
- FunUse (Hashtbl.find funs (nameof dec), List.map trav args, ann)
- | VarLet (dec, dims, value, ann) ->
- VarLet (Hashtbl.find vars (nameof dec), optmap trav dims, trav value, ann)
- | Allocate (dec, dims, ann) ->
- Allocate (Hashtbl.find vars (nameof dec), List.map trav dims, ann)
- (* Do not traverse into external function declarations, since their
- * parameters must not be added to the namespace *)
- | FunDec _ -> node
- | _ -> traverse_unit trav node
- and trav_funs scope = function
- (* Copy scope when entering a function body *)
- | FunDef (export, ret_type, name, params, body, ann) ->
- let vars, funs, repl = scope in
- let locfuns = Hashtbl.copy funs in
- let locscope = (Hashtbl.copy vars, locfuns, Hashtbl.copy repl) in
- let params = List.map (traverse locscope) params in
- let body =
- collect_funs locfuns body |> traverse locscope |> trav_funs locscope
- in
- FunDef (export, ret_type, name, params, body, ann)
- | node -> traverse_unit (trav_funs scope) node
- in
- let vars = Hashtbl.create 32 in
- let funs = Hashtbl.create 16 in
- let repl = Hashtbl.create 16 in
- add_depth 0 program |>
- collect_funs funs |>
- traverse (vars, funs, repl) |>
- quit_on_error (List.rev !err)
- let phase = function
- | Ast node -> Ast (analyse true node)
- | _ -> raise InvalidInput
|