package herdtools7
Source file TopoSort.ml
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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249module type OrderedHashedType = sig include Set.OrderedType include Hashtbl.HashedType with type t := t val to_string : t -> string end module Make (O : OrderedHashedType) = struct type succs = O.t -> O.t list module OSet = Set.Make (O) (* Compatibility layer around Map *) module OMap = struct module M = Map.Make (O) (* Straight out of the stdlib version 5.1, will be overriden by anything if included in Map.Make *) let of_list bs = List.fold_left (fun m (k, v) -> M.add k v m) M.empty bs [@@warning "-32"] include M end module OTbl = Hashtbl.Make (O) module OStack = struct type t = { stack : O.t Stack.t; mutable set : OSet.t } let create () = { stack = Stack.create (); set = OSet.empty } let push o t = t.set <- OSet.add o t.set; Stack.push o t.stack let mem w t = OSet.mem w t.set let pop t = let o = Stack.pop t.stack in t.set <- OSet.remove o t.set; o let pop_until = let rec aux o acc t = let o' = pop t in let acc = o' :: acc in if O.compare o' o = 0 then acc else aux o acc t in fun o t -> aux o [] t end type data = { (* order from which nodes are discovered *) index : int; (* lowest index of nodes in the stack reachable from a node. *) lowlink : int; } (** Book-keeping for each node *) type state = { data : data OTbl.t; stack : OStack.t; index : int ref } (** All book-keeping *) let set_lowlink s v v_data lowlink = let v_data = { v_data with lowlink } in OTbl.replace s.data v v_data; v_data let rec each_successor succs s fold v (acc, v_data) w = let () = if false then Format.eprintf "each_successor of v=%s w=%s@." (O.to_string v) (O.to_string w) in match OTbl.find_opt s.data w with | None -> let acc, w_data = strong_connected succs s fold acc w in if v_data.lowlink > w_data.lowlink then (acc, set_lowlink s v v_data w_data.lowlink) else (acc, v_data) | Some w_data -> if OStack.mem w s.stack && v_data.lowlink > w_data.index then (acc, set_lowlink s v v_data w_data.index) else (acc, v_data) and strong_connected succs s fold acc v = (* Assumption that [s.data] does not contain [v]. *) let () = if false then Format.eprintf "strong_connected %s@." (O.to_string v) in let index = !(s.index) in let v_data = { index; lowlink = index } in let () = OTbl.add s.data v v_data and () = OStack.push v s.stack and () = incr s.index in let acc, v_data = List.fold_left (each_successor succs s fold v) (acc, v_data) (succs v) in let acc = if v_data.index = v_data.lowlink then let connected_component = OStack.pop_until v s.stack in fold connected_component acc else acc in (acc, v_data) let fold_strong_connected ?(size_hint = 16) fold nodes succs acc = let s = { data = OTbl.create size_hint; index = ref 0; stack = OStack.create () } in List.fold_left (fun acc v -> if OTbl.mem s.data v then acc else strong_connected succs s fold acc v |> fst) acc nodes let sort_connected nodes succs = fold_strong_connected List.cons nodes succs [] |> List.rev let index_connected = let indexer nodes (indexes, i) = let indexes = List.fold_left (fun indexes node -> OMap.add node i indexes) indexes nodes in (indexes, succ i) in fun nodes succs -> let indexes, _i = fold_strong_connected indexer nodes succs (OMap.empty, 0) in OMap.bindings indexes module Properties = struct let order_respected (nodes, succs) = let indexes = index_connected nodes succs |> OMap.of_list in let () = if false then ( let open Format in eprintf "@[<hov 2>Indexes:@ "; OMap.iter (fun v i -> eprintf "@[<h>%s:%d@]@ " (O.to_string v) i) indexes; eprintf "@]@.") in List.for_all (fun v -> List.for_all (fun w -> match (OMap.find_opt v indexes, OMap.find_opt w indexes) with | Some i_v, Some i_w -> i_v >= i_w | _ -> false) (succs v)) nodes end end module ASTFold = struct (* Compatibility layer around String. *) module O = struct let hash : string -> int = Hashtbl.hash [@@warning "-32"] include String let to_string s = s end module OSet = ASTUtils.ISet module TS = Make (O) module Tbl = TS.OTbl type t = { nodes : string list; (** [succs(a)] returns the identifiers on which [a] depends. *) succs : string -> string list; decls : AST.decl list Tbl.t; } let tbl_add_set tbl key values = match Tbl.find_opt tbl key with | None -> Tbl.add tbl key values | Some prev -> Tbl.replace tbl key (OSet.union values prev) let tbl_add_list tbl key values = match Tbl.find_opt tbl key with | None -> Tbl.add tbl key values | Some prev -> Tbl.replace tbl key (List.rev_append values prev) let def d = let open AST in match d.desc with | D_Func { name; _ } | D_GlobalStorage { name; _ } | D_TypeDecl (name, _, _) -> name | D_Pragma _ -> assert false let use d = ASTUtils.use_decl d OSet.empty let extra_def d = let open AST in match d.desc with | D_TypeDecl (_, { desc = T_Enum names; _ }, _) -> names | _ -> [] let build ast : t = let add_one (succ_tbl, decl_tbl) d = let v = def d and u = use d in tbl_add_set succ_tbl v u; tbl_add_list decl_tbl v [ d ]; List.iter (fun v' -> tbl_add_set succ_tbl v' (OSet.singleton v); tbl_add_list decl_tbl v' []) (extra_def d); v in let succ_tbl, decls = (Tbl.create 16, Tbl.create 16) in let nodes = List.map (add_one (succ_tbl, decls)) ast in let () = Tbl.filter_map_inplace (fun _v d -> OSet.filter (Tbl.mem decls) d |> Option.some) succ_tbl in let () = if false then ( let open Format in eprintf "@[<v 2>Dependencies:@ "; Tbl.iter (fun v -> eprintf "@[<h>%s <-- %a@]@ " v OSet.pp_print) succ_tbl; eprintf "@]@.") in let succs s = Tbl.find succ_tbl s |> OSet.elements in { nodes; succs; decls } type step = Single of AST.decl | Recursive of AST.decl list let fold fold ast = let { nodes; succs; decls } = build ast in let folder nodes acc = let ds = ASTUtils.list_concat_map (Tbl.find decls) nodes in match ds with | [] -> acc (* Can happen for phantom dependencies. *) | [ d ] -> fold (Single d) acc | _ -> fold (Recursive ds) acc in let size_hint = Tbl.length decls in TS.fold_strong_connected ~size_hint folder nodes succs end