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)
module OMap = struct
module M = Map.Make (O)
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 = {
index : int;
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 =
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
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 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
| [ 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