1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
(* Wasm_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)
open! Stdlib
open Code
type closure =
{ functions : (Var.t * int) list
; free_variables : Var.t list
; mutable id : int option
}
module SCC = Strongly_connected_components.Make (Var)
let iter_closures ~f instrs =
let rec iter_closures_rec f instr_acc clos_acc instrs =
let push_closures clos_acc instr_acc =
if Var.Map.is_empty clos_acc
then instr_acc
else
let l = f clos_acc in
List.rev_map
~f:(fun g ->
let params, cont, cloc = Var.Map.find g clos_acc in
Let (g, Closure (params, cont, cloc)))
l
@ instr_acc
in
match instrs with
| [] -> List.rev (push_closures clos_acc instr_acc)
| Let (g, Closure (params, cont, cloc)) :: rem ->
iter_closures_rec f instr_acc (Var.Map.add g (params, cont, cloc) clos_acc) rem
| i :: rem ->
iter_closures_rec f (i :: push_closures clos_acc instr_acc) Var.Map.empty rem
in
iter_closures_rec f [] Var.Map.empty instrs
let collect_free_vars program var_depth depth pc closures =
let vars = ref Var.Set.empty in
let add_if_free_variable x =
let idx = Var.idx x in
let d = var_depth.(idx) in
assert (d >= 0);
if d < depth then vars := Var.Set.add x !vars
in
Code.preorder_traverse
{ fold = Code.fold_children }
(fun pc () ->
let block = Code.Addr.Map.find pc program.blocks in
Freevars.iter_block_free_vars add_if_free_variable block;
List.iter block.body ~f:(fun i ->
match i with
| Let (f, Closure _) -> (
match Var.Map.find_opt f closures with
| Some { functions = (g, _) :: _; free_variables; _ } when Var.equal f g ->
List.iter ~f:add_if_free_variable free_variables
| Some _ | None -> ())
| _ -> ()))
pc
program.blocks
();
!vars
let mark_bound_variables var_depth block depth =
Freevars.iter_block_bound_vars (fun x -> var_depth.(Var.idx x) <- depth) block;
List.iter block.body ~f:(fun i ->
match i with
| Let (_, Closure (params, _, _)) ->
List.iter params ~f:(fun x -> var_depth.(Var.idx x) <- depth + 1)
| _ -> ())
let rec traverse var_depth closures program pc depth =
Code.preorder_traverse
{ fold = Code.fold_children }
(fun pc (program : Code.program) ->
let block = Code.Addr.Map.find pc program.blocks in
mark_bound_variables var_depth block depth;
let program =
List.fold_left
~f:(fun program i ->
match i with
| Let (_, Closure (_, (pc', _), _)) ->
traverse var_depth closures program pc' (depth + 1)
| _ -> program)
~init:program
block.body
in
let body =
iter_closures block.body ~f:(fun l ->
let free_vars =
Var.Map.fold
(fun f (_, (pc', _), _) free_vars ->
Var.Map.add
f
(collect_free_vars program var_depth (depth + 1) pc' !closures)
free_vars)
l
Var.Map.empty
in
let domain = Var.Map.fold (fun f _ s -> Var.Set.add f s) l Var.Set.empty in
let graph = Var.Map.map (fun s -> Var.Set.inter s domain) free_vars in
let components = SCC.connected_components_sorted_from_roots_to_leaf graph in
let l =
Array.map
~f:(fun component ->
let fun_lst =
match component with
| SCC.No_loop x -> [ x ]
| SCC.Has_loop l -> l
in
let free_variables =
Var.Set.elements
(List.fold_left
~f:(fun fv x -> Var.Set.remove x fv)
~init:
(List.fold_left
~f:(fun fv x -> Var.Set.union fv (Var.Map.find x free_vars))
~init:Var.Set.empty
fun_lst)
fun_lst)
in
let functions =
let arities =
Var.Map.fold
(fun f (params, _, _) m -> Var.Map.add f (List.length params) m)
l
Var.Map.empty
in
List.map ~f:(fun f -> f, Var.Map.find f arities) fun_lst
in
List.iter
~f:(fun (f, _) ->
closures :=
Var.Map.add f { functions; free_variables; id = None } !closures)
functions;
fun_lst)
components
in
List.concat (List.rev (Array.to_list l)))
in
{ program with blocks = Code.Addr.Map.add pc { block with body } program.blocks })
pc
program.blocks
program
let f p =
let t = Timer.make () in
let nv = Var.count () in
let var_depth = Array.make nv (-1) in
let closures = ref Var.Map.empty in
let p = traverse var_depth closures p p.start 0 in
if Debug.find "times" () then Format.eprintf " closure conversion: %a@." Timer.print t;
p, !closures
|