1 |
b38ffff3
|
ploc
|
(********************************************************************)
|
2 |
|
|
(* *)
|
3 |
|
|
(* The LustreC compiler toolset / The LustreC Development Team *)
|
4 |
|
|
(* Copyright 2012 - -- ONERA - CNRS - INPT - LIFL *)
|
5 |
|
|
(* *)
|
6 |
|
|
(* LustreC is free software, distributed WITHOUT ANY WARRANTY *)
|
7 |
|
|
(* under the terms of the GNU Lesser General Public License *)
|
8 |
|
|
(* version 2.1. *)
|
9 |
|
|
(* *)
|
10 |
|
|
(* This file was originally from the Prelude compiler *)
|
11 |
|
|
(* *)
|
12 |
|
|
(********************************************************************)
|
13 |
0cbf0839
|
ploc
|
|
14 |
|
|
|
15 |
|
|
(** Simple modular syntactic causality analysis. Can reject correct
|
16 |
|
|
programs, especially if the program is not flattened first. *)
|
17 |
|
|
open Utils
|
18 |
|
|
open LustreSpec
|
19 |
|
|
open Corelang
|
20 |
|
|
open Graph
|
21 |
|
|
open Format
|
22 |
|
|
|
23 |
|
|
exception Cycle of ident list
|
24 |
|
|
|
25 |
|
|
module IdentDepGraph = Graph.Imperative.Digraph.ConcreteBidirectional (IdentModule)
|
26 |
|
|
|
27 |
|
|
(* Dependency of mem variables on mem variables is cut off
|
28 |
|
|
by duplication of some mem vars into local node vars.
|
29 |
|
|
Thus, cylic dependency errors may only arise between no-mem vars.
|
30 |
9aaee7f9
|
xthirioux
|
non-mem variables are:
|
31 |
|
|
- inputs: not needed for causality/scheduling, needed only for detecting useless vars
|
32 |
|
|
- read mems (fake vars): same remark as above.
|
33 |
|
|
- outputs: decoupled from mems, if necessary
|
34 |
|
|
- locals
|
35 |
|
|
- instance vars (fake vars): simplify causality analysis
|
36 |
|
|
|
37 |
|
|
global constants are not part of the dependency graph.
|
38 |
|
|
|
39 |
0cbf0839
|
ploc
|
no_mem' = combinational(no_mem, mem);
|
40 |
|
|
=> (mem -> no_mem' -> no_mem)
|
41 |
|
|
|
42 |
|
|
mem' = pre(no_mem, mem);
|
43 |
|
|
=> (mem' -> no_mem), (mem -> mem')
|
44 |
|
|
|
45 |
|
|
Global roadmap:
|
46 |
|
|
- compute two dep graphs g (non-mem/non-mem&mem) and g' (mem/mem)
|
47 |
|
|
- check cycles in g (a cycle means a dependency error)
|
48 |
|
|
- break cycles in g' (it's legal !):
|
49 |
|
|
- check cycles in g'
|
50 |
|
|
- if any, introduce aux var to break cycle, then start afresh
|
51 |
|
|
- insert g' into g
|
52 |
|
|
- return g
|
53 |
|
|
*)
|
54 |
|
|
|
55 |
d4101ea0
|
xthirioux
|
(* Tests whether [v] is a root of graph [g], i.e. a source *)
|
56 |
|
|
let is_graph_root v g =
|
57 |
|
|
IdentDepGraph.in_degree g v = 0
|
58 |
|
|
|
59 |
|
|
(* Computes the set of graph roots, i.e. the sources of acyclic graph [g] *)
|
60 |
|
|
let graph_roots g =
|
61 |
|
|
IdentDepGraph.fold_vertex
|
62 |
|
|
(fun v roots -> if is_graph_root v g then v::roots else roots)
|
63 |
|
|
g []
|
64 |
|
|
|
65 |
0cbf0839
|
ploc
|
let add_edges src tgt g =
|
66 |
|
|
(*List.iter (fun s -> List.iter (fun t -> Format.eprintf "add %s -> %s@." s t) tgt) src;*)
|
67 |
|
|
List.iter
|
68 |
|
|
(fun s ->
|
69 |
|
|
List.iter
|
70 |
|
|
(fun t -> IdentDepGraph.add_edge g s t)
|
71 |
|
|
tgt)
|
72 |
|
|
src;
|
73 |
|
|
g
|
74 |
|
|
|
75 |
|
|
let add_vertices vtc g =
|
76 |
|
|
(*List.iter (fun t -> Format.eprintf "add %s@." t) vtc;*)
|
77 |
|
|
List.iter (fun v -> IdentDepGraph.add_vertex g v) vtc;
|
78 |
|
|
g
|
79 |
|
|
|
80 |
|
|
let new_graph () =
|
81 |
|
|
IdentDepGraph.create ()
|
82 |
|
|
|
83 |
|
|
module ExprDep = struct
|
84 |
|
|
|
85 |
|
|
let instance_var_cpt = ref 0
|
86 |
|
|
|
87 |
9aaee7f9
|
xthirioux
|
(* read vars represent input/mem read-only vars,
|
88 |
|
|
they are not part of the program/schedule,
|
89 |
|
|
as they are not assigned,
|
90 |
|
|
but used to compute useless inputs/mems.
|
91 |
|
|
a mem read var represents a mem at the beginning of a cycle *)
|
92 |
|
|
let mk_read_var id =
|
93 |
|
|
sprintf "#%s" id
|
94 |
|
|
|
95 |
|
|
(* instance vars represent node instance calls,
|
96 |
|
|
they are not part of the program/schedule,
|
97 |
|
|
but used to simplify causality analysis
|
98 |
|
|
*)
|
99 |
0cbf0839
|
ploc
|
let mk_instance_var id =
|
100 |
|
|
incr instance_var_cpt; sprintf "!%s_%d" id !instance_var_cpt
|
101 |
|
|
|
102 |
9aaee7f9
|
xthirioux
|
let is_read_var v = v.[0] = '#'
|
103 |
0cbf0839
|
ploc
|
|
104 |
|
|
let is_instance_var v = v.[0] = '!'
|
105 |
|
|
|
106 |
9aaee7f9
|
xthirioux
|
let is_ghost_var v = is_instance_var v || is_read_var v
|
107 |
|
|
|
108 |
|
|
let undo_read_var id =
|
109 |
|
|
assert (is_read_var id);
|
110 |
|
|
String.sub id 1 (String.length id - 1)
|
111 |
0cbf0839
|
ploc
|
|
112 |
|
|
let eq_memory_variables mems eq =
|
113 |
|
|
let rec match_mem lhs rhs mems =
|
114 |
|
|
match rhs.expr_desc with
|
115 |
|
|
| Expr_fby _
|
116 |
|
|
| Expr_pre _ -> List.fold_right ISet.add lhs mems
|
117 |
d9d34564
|
ploc
|
| Expr_tuple tl ->
|
118 |
|
|
let lhs' = (transpose_list [lhs]) in
|
119 |
|
|
List.fold_right2 match_mem lhs' tl mems
|
120 |
0cbf0839
|
ploc
|
| _ -> mems in
|
121 |
|
|
match_mem eq.eq_lhs eq.eq_rhs mems
|
122 |
|
|
|
123 |
|
|
let node_memory_variables nd =
|
124 |
1eda3e78
|
xthirioux
|
List.fold_left eq_memory_variables ISet.empty (get_node_eqs nd)
|
125 |
0cbf0839
|
ploc
|
|
126 |
9aaee7f9
|
xthirioux
|
let node_input_variables nd =
|
127 |
|
|
List.fold_left (fun inputs v -> ISet.add v.var_id inputs) ISet.empty nd.node_inputs
|
128 |
d96d54ac
|
xthirioux
|
|
129 |
|
|
let node_local_variables nd =
|
130 |
|
|
List.fold_left (fun locals v -> ISet.add v.var_id locals) ISet.empty nd.node_locals
|
131 |
9aaee7f9
|
xthirioux
|
|
132 |
|
|
let node_output_variables nd =
|
133 |
|
|
List.fold_left (fun outputs v -> ISet.add v.var_id outputs) ISet.empty nd.node_outputs
|
134 |
|
|
|
135 |
1837ce98
|
xthirioux
|
let node_auxiliary_variables nd =
|
136 |
|
|
ISet.diff (node_local_variables nd) (node_memory_variables nd)
|
137 |
|
|
|
138 |
9aaee7f9
|
xthirioux
|
let node_variables nd =
|
139 |
|
|
let inputs = node_input_variables nd in
|
140 |
|
|
let inoutputs = List.fold_left (fun inoutputs v -> ISet.add v.var_id inoutputs) inputs nd.node_outputs in
|
141 |
|
|
List.fold_left (fun vars v -> ISet.add v.var_id vars) inoutputs nd.node_locals
|
142 |
84d9893e
|
xthirioux
|
|
143 |
0cbf0839
|
ploc
|
(* computes the equivalence relation relating variables
|
144 |
|
|
in the same equation lhs, under the form of a table
|
145 |
|
|
of class representatives *)
|
146 |
|
|
let node_eq_equiv nd =
|
147 |
|
|
let eq_equiv = Hashtbl.create 23 in
|
148 |
|
|
List.iter (fun eq ->
|
149 |
|
|
let first = List.hd eq.eq_lhs in
|
150 |
|
|
List.iter (fun v -> Hashtbl.add eq_equiv v first) eq.eq_lhs
|
151 |
|
|
)
|
152 |
1eda3e78
|
xthirioux
|
(get_node_eqs nd);
|
153 |
0cbf0839
|
ploc
|
eq_equiv
|
154 |
|
|
|
155 |
|
|
(* Create a tuple of right dimension, according to [expr] type, *)
|
156 |
|
|
(* filled with variable [v] *)
|
157 |
|
|
let adjust_tuple v expr =
|
158 |
|
|
match expr.expr_type.Types.tdesc with
|
159 |
|
|
| Types.Ttuple tl -> duplicate v (List.length tl)
|
160 |
|
|
| _ -> [v]
|
161 |
|
|
|
162 |
84d9893e
|
xthirioux
|
|
163 |
0cbf0839
|
ploc
|
(* Add dependencies from lhs to rhs in [g, g'], *)
|
164 |
|
|
(* no-mem/no-mem and mem/no-mem in g *)
|
165 |
|
|
(* mem/mem in g' *)
|
166 |
9aaee7f9
|
xthirioux
|
(* match (lhs_is_mem, ISet.mem x mems) with
|
167 |
|
|
| (false, true ) -> (add_edges [x] lhs g,
|
168 |
|
|
g')
|
169 |
|
|
| (false, false) -> (add_edges lhs [x] g,
|
170 |
|
|
g')
|
171 |
|
|
| (true , false) -> (add_edges lhs [x] g,
|
172 |
|
|
g')
|
173 |
|
|
| (true , true ) -> (g,
|
174 |
|
|
add_edges [x] lhs g')
|
175 |
|
|
*)
|
176 |
|
|
let add_eq_dependencies mems inputs node_vars eq (g, g') =
|
177 |
84d9893e
|
xthirioux
|
let add_var lhs_is_mem lhs x (g, g') =
|
178 |
9aaee7f9
|
xthirioux
|
if is_instance_var x || ISet.mem x node_vars then
|
179 |
|
|
if ISet.mem x mems
|
180 |
|
|
then
|
181 |
|
|
let g = add_edges lhs [mk_read_var x] g in
|
182 |
|
|
if lhs_is_mem
|
183 |
|
|
then
|
184 |
|
|
(g, add_edges [x] lhs g')
|
185 |
|
|
else
|
186 |
|
|
(add_edges [x] lhs g, g')
|
187 |
|
|
else
|
188 |
|
|
let x = if ISet.mem x inputs then mk_read_var x else x in
|
189 |
|
|
(add_edges lhs [x] g, g')
|
190 |
84d9893e
|
xthirioux
|
else (g, g') in
|
191 |
|
|
(* Add dependencies from [lhs] to rhs clock [ck]. *)
|
192 |
|
|
let rec add_clock lhs_is_mem lhs ck g =
|
193 |
|
|
(*Format.eprintf "add_clock %a@." Clocks.print_ck ck;*)
|
194 |
|
|
match (Clocks.repr ck).Clocks.cdesc with
|
195 |
|
|
| Clocks.Con (ck', cr, _) -> add_var lhs_is_mem lhs (Clocks.const_of_carrier cr) (add_clock lhs_is_mem lhs ck' g)
|
196 |
|
|
| Clocks.Ccarrying (_, ck') -> add_clock lhs_is_mem lhs ck' g
|
197 |
d9d34564
|
ploc
|
| _ -> g
|
198 |
|
|
in
|
199 |
84d9893e
|
xthirioux
|
let rec add_dep lhs_is_mem lhs rhs g =
|
200 |
d9d34564
|
ploc
|
(* Add mashup dependencies for a user-defined node instance [lhs] = [f]([e]) *)
|
201 |
|
|
(* i.e every input is connected to every output, through a ghost var *)
|
202 |
0cbf0839
|
ploc
|
let mashup_appl_dependencies f e g =
|
203 |
|
|
let f_var = mk_instance_var (sprintf "%s_%d" f eq.eq_loc.Location.loc_start.Lexing.pos_lnum) in
|
204 |
|
|
List.fold_right (fun rhs -> add_dep lhs_is_mem (adjust_tuple f_var rhs) rhs)
|
205 |
d9d34564
|
ploc
|
(expr_list_of_expr e) (add_var lhs_is_mem lhs f_var g)
|
206 |
|
|
in
|
207 |
0cbf0839
|
ploc
|
match rhs.expr_desc with
|
208 |
|
|
| Expr_const _ -> g
|
209 |
|
|
| Expr_fby (e1, e2) -> add_dep true lhs e2 (add_dep false lhs e1 g)
|
210 |
|
|
| Expr_pre e -> add_dep true lhs e g
|
211 |
84d9893e
|
xthirioux
|
| Expr_ident x -> add_var lhs_is_mem lhs x (add_clock lhs_is_mem lhs rhs.expr_clock g)
|
212 |
0cbf0839
|
ploc
|
| Expr_access (e1, _)
|
213 |
|
|
| Expr_power (e1, _) -> add_dep lhs_is_mem lhs e1 g
|
214 |
|
|
| Expr_array a -> List.fold_right (add_dep lhs_is_mem lhs) a g
|
215 |
9aaee7f9
|
xthirioux
|
| Expr_tuple t ->
|
216 |
|
|
(*
|
217 |
d9d34564
|
ploc
|
if List.length t <> List.length lhs then (
|
218 |
|
|
match lhs with
|
219 |
|
|
| [l] -> List.fold_right (fun r -> add_dep lhs_is_mem [l] r) t g
|
220 |
|
|
| _ ->
|
221 |
|
|
Format.eprintf "Incompatible tuple assign: %a (%i) vs %a (%i)@.@?"
|
222 |
|
|
(Utils.fprintf_list ~sep:"," (Format.pp_print_string)) lhs
|
223 |
|
|
(List.length lhs)
|
224 |
|
|
Printers.pp_expr rhs
|
225 |
|
|
(List.length t)
|
226 |
|
|
;
|
227 |
|
|
assert false
|
228 |
9aaee7f9
|
xthirioux
|
)
|
229 |
d9d34564
|
ploc
|
else
|
230 |
9aaee7f9
|
xthirioux
|
*)
|
231 |
d9d34564
|
ploc
|
List.fold_right2 (fun l r -> add_dep lhs_is_mem [l] r) lhs t g
|
232 |
84d9893e
|
xthirioux
|
| Expr_merge (c, hl) -> add_var lhs_is_mem lhs c (List.fold_right (fun (_, h) -> add_dep lhs_is_mem lhs h) hl g)
|
233 |
0cbf0839
|
ploc
|
| Expr_ite (c, t, e) -> add_dep lhs_is_mem lhs c (add_dep lhs_is_mem lhs t (add_dep lhs_is_mem lhs e g))
|
234 |
|
|
| Expr_arrow (e1, e2) -> add_dep lhs_is_mem lhs e2 (add_dep lhs_is_mem lhs e1 g)
|
235 |
84d9893e
|
xthirioux
|
| Expr_when (e, c, _) -> add_dep lhs_is_mem lhs e (add_var lhs_is_mem lhs c g)
|
236 |
0cbf0839
|
ploc
|
| Expr_appl (f, e, None) ->
|
237 |
|
|
if Basic_library.is_internal_fun f
|
238 |
d9d34564
|
ploc
|
(* tuple component-wise dependency for internal operators *)
|
239 |
0cbf0839
|
ploc
|
then
|
240 |
|
|
List.fold_right (add_dep lhs_is_mem lhs) (expr_list_of_expr e) g
|
241 |
d9d34564
|
ploc
|
(* mashed up dependency for user-defined operators *)
|
242 |
0cbf0839
|
ploc
|
else
|
243 |
|
|
mashup_appl_dependencies f e g
|
244 |
|
|
| Expr_appl (f, e, Some (r, _)) ->
|
245 |
84d9893e
|
xthirioux
|
mashup_appl_dependencies f e (add_var lhs_is_mem lhs r g)
|
246 |
0cbf0839
|
ploc
|
in
|
247 |
9aaee7f9
|
xthirioux
|
let g =
|
248 |
|
|
List.fold_left
|
249 |
|
|
(fun g lhs -> if ISet.mem lhs mems then add_vertices [lhs; mk_read_var lhs] g else add_vertices [lhs] g) g eq.eq_lhs in
|
250 |
|
|
add_dep false eq.eq_lhs eq.eq_rhs (g, g')
|
251 |
0cbf0839
|
ploc
|
|
252 |
|
|
|
253 |
|
|
(* Returns the dependence graph for node [n] *)
|
254 |
9aaee7f9
|
xthirioux
|
let dependence_graph mems inputs node_vars n =
|
255 |
0cbf0839
|
ploc
|
instance_var_cpt := 0;
|
256 |
|
|
let g = new_graph (), new_graph () in
|
257 |
|
|
(* Basic dependencies *)
|
258 |
1eda3e78
|
xthirioux
|
let g = List.fold_right (add_eq_dependencies mems inputs node_vars) (get_node_eqs n) g in
|
259 |
0cbf0839
|
ploc
|
g
|
260 |
|
|
|
261 |
|
|
end
|
262 |
|
|
|
263 |
|
|
module NodeDep = struct
|
264 |
|
|
|
265 |
|
|
module ExprModule =
|
266 |
|
|
struct
|
267 |
|
|
type t = expr
|
268 |
|
|
let compare = compare
|
269 |
|
|
let hash n = Hashtbl.hash n
|
270 |
|
|
let equal n1 n2 = n1 = n2
|
271 |
|
|
end
|
272 |
|
|
|
273 |
|
|
module ESet = Set.Make(ExprModule)
|
274 |
|
|
|
275 |
|
|
let rec get_expr_calls prednode expr =
|
276 |
|
|
match expr.expr_desc with
|
277 |
|
|
| Expr_const _
|
278 |
|
|
| Expr_ident _ -> ESet.empty
|
279 |
|
|
| Expr_access (e, _)
|
280 |
|
|
| Expr_power (e, _) -> get_expr_calls prednode e
|
281 |
|
|
| Expr_array t
|
282 |
|
|
| Expr_tuple t -> List.fold_right (fun x set -> ESet.union (get_expr_calls prednode x) set) t ESet.empty
|
283 |
|
|
| Expr_merge (_,hl) -> List.fold_right (fun (_,h) set -> ESet.union (get_expr_calls prednode h) set) hl ESet.empty
|
284 |
|
|
| Expr_fby (e1,e2)
|
285 |
|
|
| Expr_arrow (e1,e2) -> ESet.union (get_expr_calls prednode e1) (get_expr_calls prednode e2)
|
286 |
|
|
| Expr_ite (c, t, e) -> ESet.union (get_expr_calls prednode c) (ESet.union (get_expr_calls prednode t) (get_expr_calls prednode e))
|
287 |
|
|
| Expr_pre e
|
288 |
0038002e
|
ploc
|
| Expr_when (e,_,_) -> get_expr_calls prednode e
|
289 |
0cbf0839
|
ploc
|
| Expr_appl (id,e, _) ->
|
290 |
|
|
if not (Basic_library.is_internal_fun id) && prednode id
|
291 |
|
|
then ESet.add expr (get_expr_calls prednode e)
|
292 |
|
|
else (get_expr_calls prednode e)
|
293 |
|
|
|
294 |
|
|
let get_callee expr =
|
295 |
|
|
match expr.expr_desc with
|
296 |
e8c0f452
|
xthirioux
|
| Expr_appl (id, args, _) -> Some (id, expr_list_of_expr args)
|
297 |
|
|
| _ -> None
|
298 |
0cbf0839
|
ploc
|
|
299 |
|
|
let get_calls prednode eqs =
|
300 |
|
|
let deps =
|
301 |
|
|
List.fold_left
|
302 |
|
|
(fun accu eq -> ESet.union accu (get_expr_calls prednode eq.eq_rhs))
|
303 |
|
|
ESet.empty
|
304 |
|
|
eqs in
|
305 |
|
|
ESet.elements deps
|
306 |
|
|
|
307 |
|
|
let dependence_graph prog =
|
308 |
|
|
let g = new_graph () in
|
309 |
|
|
let g = List.fold_right
|
310 |
|
|
(fun td accu -> (* for each node we add its dependencies *)
|
311 |
|
|
match td.top_decl_desc with
|
312 |
|
|
| Node nd ->
|
313 |
|
|
(*Format.eprintf "Computing deps of node %s@.@?" nd.node_id; *)
|
314 |
|
|
let accu = add_vertices [nd.node_id] accu in
|
315 |
1eda3e78
|
xthirioux
|
let deps = List.map (fun e -> fst (desome (get_callee e))) (get_calls (fun _ -> true) (get_node_eqs nd)) in
|
316 |
0cbf0839
|
ploc
|
(*Format.eprintf "%a@.@?" (Utils.fprintf_list ~sep:"@." Format.pp_print_string) deps; *)
|
317 |
|
|
add_edges [nd.node_id] deps accu
|
318 |
|
|
| _ -> assert false (* should not happen *)
|
319 |
|
|
|
320 |
|
|
) prog g in
|
321 |
|
|
g
|
322 |
|
|
|
323 |
|
|
let rec filter_static_inputs inputs args =
|
324 |
|
|
match inputs, args with
|
325 |
|
|
| [] , [] -> []
|
326 |
|
|
| v::vq, a::aq -> if v.var_dec_const then (dimension_of_expr a) :: filter_static_inputs vq aq else filter_static_inputs vq aq
|
327 |
|
|
| _ -> assert false
|
328 |
|
|
|
329 |
|
|
let compute_generic_calls prog =
|
330 |
|
|
List.iter
|
331 |
|
|
(fun td ->
|
332 |
|
|
match td.top_decl_desc with
|
333 |
|
|
| Node nd ->
|
334 |
|
|
let prednode n = is_generic_node (Hashtbl.find node_table n) in
|
335 |
1eda3e78
|
xthirioux
|
nd.node_gencalls <- get_calls prednode (get_node_eqs nd)
|
336 |
0cbf0839
|
ploc
|
| _ -> ()
|
337 |
|
|
|
338 |
|
|
) prog
|
339 |
|
|
|
340 |
|
|
end
|
341 |
|
|
|
342 |
|
|
module CycleDetection = struct
|
343 |
|
|
|
344 |
|
|
(* ---- Look for cycles in a dependency graph *)
|
345 |
|
|
module Cycles = Graph.Components.Make (IdentDepGraph)
|
346 |
|
|
|
347 |
|
|
let mk_copy_var n id =
|
348 |
1eda3e78
|
xthirioux
|
let used name =
|
349 |
|
|
(List.exists (fun v -> v.var_id = name) n.node_locals)
|
350 |
|
|
|| (List.exists (fun v -> v.var_id = name) n.node_inputs)
|
351 |
|
|
|| (List.exists (fun v -> v.var_id = name) n.node_outputs)
|
352 |
|
|
in mk_new_name used id
|
353 |
0cbf0839
|
ploc
|
|
354 |
|
|
let mk_copy_eq n var =
|
355 |
0038002e
|
ploc
|
let var_decl = get_node_var var n in
|
356 |
0cbf0839
|
ploc
|
let cp_var = mk_copy_var n var in
|
357 |
|
|
let expr =
|
358 |
|
|
{ expr_tag = Utils.new_tag ();
|
359 |
|
|
expr_desc = Expr_ident var;
|
360 |
|
|
expr_type = var_decl.var_type;
|
361 |
|
|
expr_clock = var_decl.var_clock;
|
362 |
|
|
expr_delay = Delay.new_var ();
|
363 |
|
|
expr_annot = None;
|
364 |
|
|
expr_loc = var_decl.var_loc } in
|
365 |
|
|
{ var_decl with var_id = cp_var },
|
366 |
|
|
mkeq var_decl.var_loc ([cp_var], expr)
|
367 |
|
|
|
368 |
|
|
let wrong_partition g partition =
|
369 |
|
|
match partition with
|
370 |
|
|
| [id] -> IdentDepGraph.mem_edge g id id
|
371 |
|
|
| _::_::_ -> true
|
372 |
|
|
| [] -> assert false
|
373 |
|
|
|
374 |
|
|
(* Checks that the dependency graph [g] does not contain a cycle. Raises
|
375 |
|
|
[Cycle partition] if the succession of dependencies [partition] forms a cycle *)
|
376 |
|
|
let check_cycles g =
|
377 |
|
|
let scc_l = Cycles.scc_list g in
|
378 |
|
|
List.iter (fun partition ->
|
379 |
|
|
if wrong_partition g partition then
|
380 |
|
|
raise (Cycle partition)
|
381 |
|
|
else ()
|
382 |
|
|
) scc_l
|
383 |
|
|
|
384 |
|
|
(* Creates the sub-graph of [g] restricted to vertices and edges in partition *)
|
385 |
|
|
let copy_partition g partition =
|
386 |
|
|
let copy_g = IdentDepGraph.create () in
|
387 |
|
|
IdentDepGraph.iter_edges
|
388 |
|
|
(fun src tgt ->
|
389 |
|
|
if List.mem src partition && List.mem tgt partition
|
390 |
|
|
then IdentDepGraph.add_edge copy_g src tgt)
|
391 |
|
|
g
|
392 |
|
|
|
393 |
|
|
|
394 |
|
|
(* Breaks dependency cycles in a graph [g] by inserting aux variables.
|
395 |
|
|
[head] is a head of a non-trivial scc of [g].
|
396 |
|
|
In Lustre, this is legal only for mem/mem cycles *)
|
397 |
|
|
let break_cycle head cp_head g =
|
398 |
|
|
let succs = IdentDepGraph.succ g head in
|
399 |
|
|
IdentDepGraph.add_edge g head cp_head;
|
400 |
9aaee7f9
|
xthirioux
|
IdentDepGraph.add_edge g cp_head (ExprDep.mk_read_var head);
|
401 |
0cbf0839
|
ploc
|
List.iter
|
402 |
|
|
(fun s ->
|
403 |
|
|
IdentDepGraph.remove_edge g head s;
|
404 |
|
|
IdentDepGraph.add_edge g s cp_head)
|
405 |
|
|
succs
|
406 |
|
|
|
407 |
|
|
(* Breaks cycles of the dependency graph [g] of memory variables [mems]
|
408 |
|
|
belonging in node [node]. Returns:
|
409 |
|
|
- a list of new auxiliary variable declarations
|
410 |
|
|
- a list of new equations
|
411 |
|
|
- a modified acyclic version of [g]
|
412 |
|
|
*)
|
413 |
|
|
let break_cycles node mems g =
|
414 |
1eda3e78
|
xthirioux
|
let (mem_eqs, non_mem_eqs) = List.partition (fun eq -> List.exists (fun v -> ISet.mem v mems) eq.eq_lhs) (get_node_eqs node) in
|
415 |
0cbf0839
|
ploc
|
let rec break vdecls mem_eqs g =
|
416 |
|
|
let scc_l = Cycles.scc_list g in
|
417 |
|
|
let wrong = List.filter (wrong_partition g) scc_l in
|
418 |
|
|
match wrong with
|
419 |
|
|
| [] -> (vdecls, non_mem_eqs@mem_eqs, g)
|
420 |
|
|
| [head]::_ ->
|
421 |
|
|
begin
|
422 |
|
|
IdentDepGraph.remove_edge g head head;
|
423 |
|
|
break vdecls mem_eqs g
|
424 |
|
|
end
|
425 |
|
|
| (head::part)::_ ->
|
426 |
|
|
begin
|
427 |
|
|
let vdecl_cp_head, cp_eq = mk_copy_eq node head in
|
428 |
|
|
let pvar v = List.mem v part in
|
429 |
|
|
let fvar v = if v = head then vdecl_cp_head.var_id else v in
|
430 |
|
|
let mem_eqs' = List.map (eq_replace_rhs_var pvar fvar) mem_eqs in
|
431 |
|
|
break_cycle head vdecl_cp_head.var_id g;
|
432 |
|
|
break (vdecl_cp_head::vdecls) (cp_eq::mem_eqs') g
|
433 |
|
|
end
|
434 |
|
|
| _ -> assert false
|
435 |
|
|
in break [] mem_eqs g
|
436 |
|
|
|
437 |
|
|
end
|
438 |
|
|
|
439 |
7a737ed5
|
xthirioux
|
(* Module used to compute static disjunction of variables based upon their clocks. *)
|
440 |
|
|
module Disjunction =
|
441 |
|
|
struct
|
442 |
7ecdb0aa
|
xthirioux
|
module ClockedIdentModule =
|
443 |
|
|
struct
|
444 |
|
|
type t = var_decl
|
445 |
|
|
let root_branch vdecl = Clocks.root vdecl.var_clock, Clocks.branch vdecl.var_clock
|
446 |
8fa083d5
|
xthirioux
|
let compare v1 v2 = compare (root_branch v2, v2.var_id) (root_branch v1, v1.var_id)
|
447 |
7ecdb0aa
|
xthirioux
|
end
|
448 |
|
|
|
449 |
|
|
module CISet = Set.Make(ClockedIdentModule)
|
450 |
7a737ed5
|
xthirioux
|
|
451 |
7ecdb0aa
|
xthirioux
|
(* map: var |-> list of disjoint vars, sorted in increasing branch length order,
|
452 |
|
|
maybe removing shorter branches *)
|
453 |
34a5a072
|
xthirioux
|
type disjoint_map = (ident, CISet.t) Hashtbl.t
|
454 |
7a737ed5
|
xthirioux
|
|
455 |
2cf39a8e
|
xthirioux
|
let pp_ciset fmt t =
|
456 |
|
|
begin
|
457 |
|
|
Format.fprintf fmt "{@ ";
|
458 |
|
|
CISet.iter (fun s -> Format.fprintf fmt "%a@ " Printers.pp_var_name s) t;
|
459 |
|
|
Format.fprintf fmt "}@."
|
460 |
|
|
end
|
461 |
|
|
|
462 |
97498b53
|
xthirioux
|
let clock_disjoint_map vdecls =
|
463 |
7a737ed5
|
xthirioux
|
let map = Hashtbl.create 23 in
|
464 |
|
|
begin
|
465 |
7ecdb0aa
|
xthirioux
|
List.iter
|
466 |
|
|
(fun v1 -> let disj_v1 =
|
467 |
|
|
List.fold_left
|
468 |
34a5a072
|
xthirioux
|
(fun res v2 -> if Clocks.disjoint v1.var_clock v2.var_clock then CISet.add v2 res else res)
|
469 |
|
|
CISet.empty
|
470 |
7ecdb0aa
|
xthirioux
|
vdecls in
|
471 |
|
|
(* disjoint vdecls are stored in increasing branch length order *)
|
472 |
|
|
Hashtbl.add map v1.var_id disj_v1)
|
473 |
|
|
vdecls;
|
474 |
34a5a072
|
xthirioux
|
(map : disjoint_map)
|
475 |
7a737ed5
|
xthirioux
|
end
|
476 |
97498b53
|
xthirioux
|
|
477 |
01f1a1f4
|
xthirioux
|
(* merge variables [v] and [v'] in disjunction [map]. Then:
|
478 |
1837ce98
|
xthirioux
|
- the mapping v' becomes v' |-> (map v) inter (map v')
|
479 |
|
|
- the mapping v |-> ... then disappears
|
480 |
|
|
- other mappings become x |-> (map x) \ (if v in x then v else v')
|
481 |
7ecdb0aa
|
xthirioux
|
*)
|
482 |
01f1a1f4
|
xthirioux
|
let merge_in_disjoint_map map v v' =
|
483 |
7ecdb0aa
|
xthirioux
|
begin
|
484 |
34a5a072
|
xthirioux
|
Hashtbl.replace map v'.var_id (CISet.inter (Hashtbl.find map v.var_id) (Hashtbl.find map v'.var_id));
|
485 |
|
|
Hashtbl.remove map v.var_id;
|
486 |
|
|
Hashtbl.iter (fun x map_x -> Hashtbl.replace map x (CISet.remove (if CISet.mem v map_x then v else v') map_x)) map;
|
487 |
7ecdb0aa
|
xthirioux
|
end
|
488 |
|
|
|
489 |
01f1a1f4
|
xthirioux
|
(* replace variable [v] by [v'] in disjunction [map].
|
490 |
|
|
[v'] is a dead variable. Then:
|
491 |
|
|
- the mapping v' becomes v' |-> (map v)
|
492 |
|
|
- the mapping v |-> ... then disappears
|
493 |
|
|
- all mappings become x |-> ((map x) \ { v}) union ({v'} if v in map x)
|
494 |
|
|
*)
|
495 |
|
|
let replace_in_disjoint_map map v v' =
|
496 |
|
|
begin
|
497 |
|
|
Hashtbl.replace map v'.var_id (Hashtbl.find map v.var_id);
|
498 |
|
|
Hashtbl.remove map v.var_id;
|
499 |
|
|
Hashtbl.iter (fun x mapx -> Hashtbl.replace map x (if CISet.mem v mapx then CISet.add v' (CISet.remove v mapx) else CISet.remove v' mapx)) map;
|
500 |
|
|
end
|
501 |
|
|
|
502 |
|
|
(* remove variable [v] in disjunction [map]. Then:
|
503 |
|
|
- the mapping v |-> ... then disappears
|
504 |
|
|
- all mappings become x |-> (map x) \ { v}
|
505 |
|
|
*)
|
506 |
|
|
let remove_in_disjoint_map map v =
|
507 |
|
|
begin
|
508 |
|
|
Hashtbl.remove map v.var_id;
|
509 |
|
|
Hashtbl.iter (fun x mapx -> Hashtbl.replace map x (CISet.remove v mapx)) map;
|
510 |
|
|
end
|
511 |
|
|
|
512 |
97498b53
|
xthirioux
|
let pp_disjoint_map fmt map =
|
513 |
|
|
begin
|
514 |
|
|
Format.fprintf fmt "{ /* disjoint map */@.";
|
515 |
34a5a072
|
xthirioux
|
Hashtbl.iter (fun k v -> Format.fprintf fmt "%s # { %a }@." k (Utils.fprintf_list ~sep:", " Printers.pp_var_name) (CISet.elements v)) map;
|
516 |
97498b53
|
xthirioux
|
Format.fprintf fmt "}@."
|
517 |
|
|
end
|
518 |
7a737ed5
|
xthirioux
|
end
|
519 |
|
|
|
520 |
0cbf0839
|
ploc
|
let pp_dep_graph fmt g =
|
521 |
|
|
begin
|
522 |
|
|
Format.fprintf fmt "{ /* graph */@.";
|
523 |
|
|
IdentDepGraph.iter_edges (fun s t -> Format.fprintf fmt "%s -> %s@." s t) g;
|
524 |
|
|
Format.fprintf fmt "}@."
|
525 |
|
|
end
|
526 |
|
|
|
527 |
|
|
let pp_error fmt trace =
|
528 |
|
|
fprintf fmt "@.Causality error, cyclic data dependencies: %a@."
|
529 |
|
|
(fprintf_list ~sep:"->" pp_print_string) trace
|
530 |
|
|
|
531 |
|
|
(* Merges elements of graph [g2] into graph [g1] *)
|
532 |
97498b53
|
xthirioux
|
let merge_with g1 g2 =
|
533 |
01f1a1f4
|
xthirioux
|
begin
|
534 |
0cbf0839
|
ploc
|
IdentDepGraph.iter_vertex (fun v -> IdentDepGraph.add_vertex g1 v) g2;
|
535 |
|
|
IdentDepGraph.iter_edges (fun s t -> IdentDepGraph.add_edge g1 s t) g2
|
536 |
01f1a1f4
|
xthirioux
|
end
|
537 |
|
|
|
538 |
|
|
let add_external_dependency outputs mems g =
|
539 |
29ced7be
|
xthirioux
|
let caller ="!!_world" in
|
540 |
01f1a1f4
|
xthirioux
|
begin
|
541 |
|
|
IdentDepGraph.add_vertex g caller;
|
542 |
|
|
ISet.iter (fun o -> IdentDepGraph.add_edge g caller o) outputs;
|
543 |
|
|
ISet.iter (fun m -> IdentDepGraph.add_edge g caller m) mems;
|
544 |
|
|
end
|
545 |
0cbf0839
|
ploc
|
|
546 |
|
|
let global_dependency node =
|
547 |
|
|
let mems = ExprDep.node_memory_variables node in
|
548 |
9aaee7f9
|
xthirioux
|
let inputs = ExprDep.node_input_variables node in
|
549 |
01f1a1f4
|
xthirioux
|
let outputs = ExprDep.node_output_variables node in
|
550 |
9aaee7f9
|
xthirioux
|
let node_vars = ExprDep.node_variables node in
|
551 |
|
|
let (g_non_mems, g_mems) = ExprDep.dependence_graph mems inputs node_vars node in
|
552 |
0cbf0839
|
ploc
|
(*Format.eprintf "g_non_mems: %a" pp_dep_graph g_non_mems;
|
553 |
|
|
Format.eprintf "g_mems: %a" pp_dep_graph g_mems;*)
|
554 |
|
|
CycleDetection.check_cycles g_non_mems;
|
555 |
|
|
let (vdecls', eqs', g_mems') = CycleDetection.break_cycles node mems g_mems in
|
556 |
|
|
(*Format.eprintf "g_mems': %a" pp_dep_graph g_mems';*)
|
557 |
01f1a1f4
|
xthirioux
|
begin
|
558 |
|
|
merge_with g_non_mems g_mems';
|
559 |
|
|
add_external_dependency outputs mems g_non_mems;
|
560 |
1eda3e78
|
xthirioux
|
{ node with node_stmts = List.map (fun eq -> Eq eq) eqs'; node_locals = vdecls'@node.node_locals },
|
561 |
01f1a1f4
|
xthirioux
|
g_non_mems
|
562 |
|
|
end
|
563 |
0cbf0839
|
ploc
|
|
564 |
|
|
(* Local Variables: *)
|
565 |
|
|
(* compile-command:"make -C .." *)
|
566 |
|
|
(* End: *)
|