package herdtools7

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

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
249
module 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
OCaml

Innovation. Community. Security.