Project

General

Profile

Download (23.6 KB) Statistics
| Branch: | Tag: | Revision:
1
(********************************************************************)
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

    
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

    
22

    
23
type identified_call = eq * tag
24
type error =
25
  | DataCycle of ident list list (* multiple failed partitions at once *) 
26
  | NodeCycle of ident list
27

    
28
exception Error of error
29

    
30

    
31
module IdentDepGraph = Graph.Imperative.Digraph.ConcreteBidirectional (IdentModule)
32
(*module DotGraph = Graphviz.Dot (IdentDepGraph)*)
33
module Bfs = Traverse.Bfs (IdentDepGraph)
34
  
35
(* Dependency of mem variables on mem variables is cut off 
36
   by duplication of some mem vars into local node vars.
37
   Thus, cylic dependency errors may only arise between no-mem vars.
38
   non-mem variables are:
39
   - constants/inputs: not needed for causality/scheduling, needed only for detecting useless vars
40
   - read mems (fake vars): same remark as above.
41
   - outputs: decoupled from mems, if necessary
42
   - locals
43
   - instance vars (fake vars): simplify causality analysis
44
   
45
   global constants are not part of the dependency graph.
46
   
47
   no_mem' = combinational(no_mem, mem);
48
   => (mem -> no_mem' -> no_mem)
49

    
50
   mem' = pre(no_mem, mem);
51
   => (mem' -> no_mem), (mem -> mem')
52
   
53
   Global roadmap:
54
   - compute two dep graphs g (non-mem/non-mem&mem) and g' (mem/mem)
55
   - check cycles in g (a cycle means a dependency error)
56
   - break cycles in g' (it's legal !):
57
     - check cycles in g'
58
     - if any, introduce aux var to break cycle, then start afresh
59
   - insert g' into g
60
   - return g
61
*)
62

    
63
(* Tests whether [v] is a root of graph [g], i.e. a source *)
64
let is_graph_root v g =
65
  IdentDepGraph.in_degree g v = 0
66

    
67
(* Computes the set of graph roots, i.e. the sources of acyclic graph [g] *)
68
let graph_roots g =
69
  IdentDepGraph.fold_vertex
70
    (fun v roots -> if is_graph_root v g then v::roots else roots)
71
    g []
72

    
73
let add_edges src tgt g =
74
 (*List.iter (fun s -> List.iter (fun t -> Format.eprintf "add %s -> %s@." s t) tgt) src;*)
75
  List.iter
76
    (fun s ->
77
      List.iter
78
	(fun t -> IdentDepGraph.add_edge g s t)
79
	tgt)
80
    src;
81
  g
82

    
83
let add_vertices vtc g =
84
 (*List.iter (fun t -> Format.eprintf "add %s@." t) vtc;*)
85
  List.iter (fun v -> IdentDepGraph.add_vertex g v) vtc;
86
  g
87

    
88
let new_graph () =
89
  IdentDepGraph.create ()
90

    
91
    
92
module ExprDep = struct
93
  let get_node_eqs nd =
94
    let eqs, auts = get_node_eqs nd in
95
    if auts != [] then assert false (* No call on causality on a Lustre model
96
				       with automaton. They should be expanded by now. *);
97
    eqs
98
      
99
  let instance_var_cpt = ref 0
100

    
101
(* read vars represent input/mem read-only vars,
102
   they are not part of the program/schedule,
103
   as they are not assigned,
104
   but used to compute useless inputs/mems.
105
   a mem read var represents a mem at the beginning of a cycle  *)
106
  let mk_read_var id =
107
    Format.sprintf "#%s" id
108

    
109
(* instance vars represent node instance calls,
110
   they are not part of the program/schedule,
111
   but used to simplify causality analysis
112
*)
113
  let mk_instance_var id =
114
    incr instance_var_cpt; Format.sprintf "!%s_%d" id !instance_var_cpt
115

    
116
  let is_read_var v = v.[0] = '#'
117

    
118
  let is_instance_var v = v.[0] = '!'
119

    
120
  let is_ghost_var v = is_instance_var v || is_read_var v
121

    
122
  let undo_read_var id =
123
    assert (is_read_var id);
124
    String.sub id 1 (String.length id - 1)
125

    
126
  let undo_instance_var id =
127
    assert (is_instance_var id);
128
    String.sub id 1 (String.length id - 1)
129

    
130
  let eq_memory_variables mems eq =
131
    let rec match_mem lhs rhs mems =
132
      match rhs.expr_desc with
133
      | Expr_fby _
134
      | Expr_pre _    -> List.fold_right ISet.add lhs mems
135
      | Expr_tuple tl -> 
136
	 let lhs' = (transpose_list [lhs]) in
137
	 List.fold_right2 match_mem lhs' tl mems
138
      | _             -> mems in
139
    match_mem eq.eq_lhs eq.eq_rhs mems
140

    
141
  let node_memory_variables nd =
142
    List.fold_left eq_memory_variables ISet.empty (get_node_eqs nd)
143

    
144
  let node_input_variables nd =
145
    List.fold_left (fun inputs v -> ISet.add v.var_id inputs) ISet.empty nd.node_inputs
146

    
147
  let node_local_variables nd =
148
    List.fold_left (fun locals v -> ISet.add v.var_id locals) ISet.empty nd.node_locals
149

    
150
  let node_constant_variables nd =
151
    List.fold_left (fun locals v -> if v.var_dec_const then ISet.add v.var_id locals else locals) ISet.empty nd.node_locals
152

    
153
  let node_output_variables nd =
154
    List.fold_left (fun outputs v -> ISet.add v.var_id outputs) ISet.empty nd.node_outputs
155

    
156
  let node_auxiliary_variables nd =
157
    ISet.diff (node_local_variables nd) (node_memory_variables nd)
158

    
159
  let node_variables nd =
160
    let inputs = node_input_variables nd in
161
    let inoutputs = List.fold_left (fun inoutputs v -> ISet.add v.var_id inoutputs) inputs nd.node_outputs in
162
    List.fold_left (fun vars v -> ISet.add v.var_id vars) inoutputs nd.node_locals
163

    
164
(* computes the equivalence relation relating variables 
165
   in the same equation lhs, under the form of a table 
166
   of class representatives *)
167
  let node_eq_equiv nd =
168
    let eq_equiv = Hashtbl.create 23 in
169
    List.iter (fun eq ->
170
      let first = List.hd eq.eq_lhs in
171
      List.iter (fun v -> Hashtbl.add eq_equiv v first) eq.eq_lhs
172
    )
173
      (get_node_eqs nd);
174
    eq_equiv
175

    
176
(* Create a tuple of right dimension, according to [expr] type, *)
177
(* filled with variable [v] *)
178
  let adjust_tuple v expr =
179
    match expr.expr_type.Types.tdesc with
180
    | Types.Ttuple tl -> duplicate v (List.length tl)
181
    | _         -> [v]
182

    
183

    
184
  (* Add dependencies from lhs to rhs in [g, g'], *)
185
  (* no-mem/no-mem and mem/no-mem in g            *)
186
  (* mem/mem in g'                                *)
187
  (*     match (lhs_is_mem, ISet.mem x mems) with
188
	 | (false, true ) -> (add_edges [x] lhs g,
189
	 g')
190
	 | (false, false) -> (add_edges lhs [x] g,
191
	 g')
192
	 | (true , false) -> (add_edges lhs [x] g,
193
	 g')
194
	 | (true , true ) -> (g,
195
	 add_edges [x] lhs g')
196
  *)
197
  let add_eq_dependencies mems inputs node_vars eq (g, g') =
198
    let add_var lhs_is_mem lhs x (g, g') =
199
      if is_instance_var x || ISet.mem x node_vars then
200
	if ISet.mem x mems
201
	then
202
	  let g = add_edges lhs [mk_read_var x] g in
203
	  if lhs_is_mem
204
	  then
205
	    (g, add_edges [x] lhs g')
206
	  else
207
	    (add_edges [x] lhs g, g')
208
	else
209
	  let x = if ISet.mem x inputs then mk_read_var x else x in
210
	  (add_edges lhs [x] g, g')
211
      else (add_edges lhs [mk_read_var x] g, g') (* x is a global constant, treated as a read var *)
212
    in
213
  (* Add dependencies from [lhs] to rhs clock [ck]. *)
214
    let rec add_clock lhs_is_mem lhs ck g =
215
    (*Format.eprintf "add_clock %a@." Clocks.print_ck ck;*)
216
      match (Clocks.repr ck).Clocks.cdesc with
217
      | Clocks.Con (ck', cr, _)   -> add_var lhs_is_mem lhs (Clocks.const_of_carrier cr) (add_clock lhs_is_mem lhs ck' g)
218
      | Clocks.Ccarrying (_, ck') -> add_clock lhs_is_mem lhs ck' g
219
      | _                         -> g 
220
    in
221
    let rec add_dep lhs_is_mem lhs rhs g =
222
    (* Add mashup dependencies for a user-defined node instance [lhs] = [f]([e]) *)
223
    (* i.e every input is connected to every output, through a ghost var *)
224
      let mashup_appl_dependencies f e g =
225
	let f_var = mk_instance_var (Format.sprintf "%s_%d" f eq.eq_loc.Location.loc_start.Lexing.pos_lnum) in
226
	List.fold_right (fun rhs -> add_dep lhs_is_mem (adjust_tuple f_var rhs) rhs)
227
	  (expr_list_of_expr e) (add_var lhs_is_mem lhs f_var g) 
228
      in
229
      match rhs.expr_desc with
230
      | Expr_const _    -> g
231
      | Expr_fby (e1, e2)  -> add_dep true lhs e2 (add_dep false lhs e1 g)
232
      | Expr_pre e      -> add_dep true lhs e g
233
      | Expr_ident x -> add_var lhs_is_mem lhs x (add_clock lhs_is_mem lhs rhs.expr_clock g)
234
      | Expr_access (e1, d)
235
      | Expr_power (e1, d) -> add_dep lhs_is_mem lhs e1 (add_dep lhs_is_mem lhs (expr_of_dimension d) g)
236
      | Expr_array a -> List.fold_right (add_dep lhs_is_mem lhs) a g
237
      | Expr_tuple t -> List.fold_right2 (fun l r -> add_dep lhs_is_mem [l] r) lhs t g
238
      | 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)
239
      | 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))
240
      | Expr_arrow (e1, e2)  -> add_dep lhs_is_mem lhs e2 (add_dep lhs_is_mem lhs e1 g)
241
      | Expr_when  (e, c, _)  -> add_dep lhs_is_mem lhs e (add_var lhs_is_mem lhs c g)
242
      | Expr_appl (f, e, None) ->
243
	 if Basic_library.is_expr_internal_fun rhs
244
      (* tuple component-wise dependency for internal operators *)
245
	 then
246
	   List.fold_right (add_dep lhs_is_mem lhs) (expr_list_of_expr e) g
247
      (* mashed up dependency for user-defined operators *)
248
	 else
249
	   mashup_appl_dependencies f e g
250
      | Expr_appl (f, e, Some c) ->
251
	 mashup_appl_dependencies f e (add_dep lhs_is_mem lhs c g)
252
    in
253
    let g =
254
      List.fold_left
255
	(fun g lhs ->
256
	  if ISet.mem lhs mems then
257
	    add_vertices [lhs; mk_read_var lhs] g
258
	  else
259
	    add_vertices [lhs] g
260
	)
261
	g eq.eq_lhs
262
    in
263
    add_dep false eq.eq_lhs eq.eq_rhs (g, g')
264
      
265

    
266
  (* Returns the dependence graph for node [n] *)
267
  let dependence_graph mems inputs node_vars n =
268
    instance_var_cpt := 0;
269
    let g = new_graph (), new_graph () in
270
    (* Basic dependencies *)
271
    let g = List.fold_right (add_eq_dependencies mems inputs node_vars) (get_node_eqs n) g in
272
    (* TODO Xavier: un essai ci dessous. Ca n'a pas l'air de résoudre le pb. Il
273
       faut imposer que les outputs dépendent des asserts pour identifier que les
274
       fcn calls des asserts sont évalués avant le noeuds *)
275
    
276
    (* (\* In order to introduce dependencies between assert expressions and the node, *)
277
    (*    we build an artificial dependency between node output and each assert *)
278
    (*    expr. While these are not valid equations, they should properly propage in *)
279
    (*    function add_eq_dependencies *\) *)
280
    (* let g = *)
281
    (*   let output_vars_as_lhs = ISet.elements (node_output_variables n) in *)
282
    (*   List.fold_left (fun g ae -> *)
283
    (*     let fake_eq = mkeq Location.dummy_loc (output_vars_as_lhs, ae.assert_expr) in *)
284
    (*   add_eq_dependencies mems inputs node_vars fake_eq g *)
285
    (* ) g n.node_asserts in  *)
286
    g
287

    
288
end
289

    
290
module NodeDep = struct
291

    
292
  module ExprModule =
293
  struct
294
    type t = expr
295
    let compare = compare
296
    let hash n = Hashtbl.hash n
297
    let equal n1 n2 = n1 = n2
298
  end
299

    
300
  module ESet = Set.Make(ExprModule)
301

    
302
  let rec get_expr_calls prednode expr = 
303
    match expr.expr_desc with
304
    | Expr_const _ 
305
    | Expr_ident _ -> ESet.empty
306
    | Expr_access (e, _)
307
    | Expr_power (e, _) -> get_expr_calls prednode e
308
    | Expr_array t
309
    | Expr_tuple t -> List.fold_right (fun x set -> ESet.union (get_expr_calls prednode x) set) t ESet.empty
310
    | Expr_merge (_,hl) -> List.fold_right (fun (_,h) set -> ESet.union (get_expr_calls prednode h) set) hl ESet.empty
311
    | Expr_fby (e1,e2)
312
    | Expr_arrow (e1,e2) -> ESet.union (get_expr_calls prednode e1) (get_expr_calls prednode e2)
313
    | Expr_ite   (c, t, e) -> ESet.union (get_expr_calls prednode c) (ESet.union (get_expr_calls prednode t) (get_expr_calls prednode e))
314
    | Expr_pre e 
315
    | Expr_when (e,_,_) -> get_expr_calls prednode e
316
    | Expr_appl (id,e, _) ->
317
       if not (Basic_library.is_expr_internal_fun expr) && prednode id
318
       then ESet.add expr (get_expr_calls prednode e)
319
       else (get_expr_calls prednode e)
320

    
321
  let get_callee expr =
322
    match expr.expr_desc with
323
    | Expr_appl (id, args, _) -> Some (id, expr_list_of_expr args)
324
    | _ -> None
325

    
326
  let get_calls prednode nd =
327
    let accu f init objl = List.fold_left (fun accu o -> ESet.union accu (f o)) init objl in
328
    let get_eq_calls eq = get_expr_calls prednode eq.eq_rhs in
329
    let rec get_stmt_calls s =
330
      match s with Eq eq -> get_eq_calls eq | Aut aut -> get_aut_calls aut 
331
    and get_aut_calls aut =
332
      let get_handler_calls h =
333
	let get_cond_calls c = accu (fun (_,e,_,_) -> get_expr_calls prednode e) ESet.empty c in
334
	let until = get_cond_calls h.hand_until in
335
	let unless = get_cond_calls h.hand_unless in
336
	let calls = ESet.union until unless in 
337
	let calls = accu get_stmt_calls calls h.hand_stmts in
338
	let calls = accu (fun a -> get_expr_calls prednode a.assert_expr) calls h.hand_asserts in
339
	(* let calls = accu xx calls h.hand_annots in *) (* TODO: search for calls in eexpr *)
340
	calls
341
      in
342
      accu get_handler_calls ESet.empty aut.aut_handlers
343
    in
344
    let eqs, auts = get_node_eqs nd in
345
    let deps = accu get_eq_calls ESet.empty eqs in
346
    let deps = accu get_aut_calls deps auts in
347
    ESet.elements deps
348

    
349
  let dependence_graph prog =
350
    let g = new_graph () in
351
    let g = List.fold_right 
352
      (fun td accu -> (* for each node we add its dependencies *)
353
	match td.top_decl_desc with 
354
	| Node nd ->
355
	  (*Format.eprintf "Computing deps of node %s@.@?" nd.node_id; *)
356
	   let accu = add_vertices [nd.node_id] accu in
357
	   let deps = List.map
358
	     (fun e -> fst (desome (get_callee e)))
359
	     (get_calls (fun _ -> true) nd) 
360
	   in
361
	     (* Adding assert expressions deps *)
362
	   let deps_asserts =
363
	     let prednode = (fun _ -> true) in (* what is this about? *)
364
	     List.map
365
	       (fun e -> fst (desome (get_callee e)))
366
 	       (ESet.elements
367
		  (List.fold_left
368
		     (fun accu assert_expr -> ESet.union accu (get_expr_calls prednode assert_expr))
369
		     ESet.empty
370
		     (List.map (fun _assert -> _assert.assert_expr) nd.node_asserts)
371
		  )
372
	       )
373
      	   in
374
	   (*Format.eprintf "%a@.@?" (Utils.fprintf_list ~sep:"@." Format.pp_print_string) deps; *)
375
	   add_edges [nd.node_id] (deps@deps_asserts) accu
376
	| _ -> assert false (* should not happen *)
377
	   
378
      ) prog g in
379
    g   
380

    
381
  (* keep subgraph of [gr] consisting of nodes accessible from node [v] *)
382
  let slice_graph gr v =
383
    begin
384
      let gr' = new_graph () in
385
      IdentDepGraph.add_vertex gr' v;
386
      Bfs.iter_component (fun v -> IdentDepGraph.iter_succ (fun s -> IdentDepGraph.add_vertex gr' s; IdentDepGraph.add_edge gr' v s) gr v) gr v;
387
      gr'
388
    end
389
      
390
  let rec filter_static_inputs inputs args =
391
    match inputs, args with
392
    | []   , [] -> []
393
    | v::vq, a::aq -> if v.var_dec_const && Types.is_dimension_type v.var_type then (dimension_of_expr a) :: filter_static_inputs vq aq else filter_static_inputs vq aq
394
    | _ -> assert false
395

    
396
  let compute_generic_calls prog =
397
    List.iter
398
      (fun td ->
399
	match td.top_decl_desc with 
400
	| Node nd ->
401
	   let prednode n = is_generic_node (Hashtbl.find node_table n) in
402
	   nd.node_gencalls <- get_calls prednode nd
403
	| _ -> ()
404
	   
405
      ) prog
406

    
407
end
408

    
409

    
410
module CycleDetection = struct
411

    
412
  (* ---- Look for cycles in a dependency graph *)
413
  module Cycles = Graph.Components.Make (IdentDepGraph)
414

    
415
  let mk_copy_var n id =
416
    let used name =
417
      (List.exists (fun v -> v.var_id = name) n.node_locals)
418
      || (List.exists (fun v -> v.var_id = name) n.node_inputs)
419
      || (List.exists (fun v -> v.var_id = name) n.node_outputs)
420
    in mk_new_name used id
421

    
422
  let mk_copy_eq n var =
423
    let var_decl = get_node_var var n in
424
    let cp_var = mk_copy_var n var in
425
    let expr =
426
      { expr_tag = Utils.new_tag ();
427
	expr_desc = Expr_ident var;
428
	expr_type = var_decl.var_type;
429
	expr_clock = var_decl.var_clock;
430
	expr_delay = Delay.new_var ();
431
	expr_annot = None;
432
	expr_loc = var_decl.var_loc } in
433
    { var_decl with var_id = cp_var; var_orig = false },
434
    mkeq var_decl.var_loc ([cp_var], expr)
435

    
436
  let wrong_partition g partition =
437
    match partition with
438
    | [id]    -> IdentDepGraph.mem_edge g id id
439
    | _::_::_ -> true
440
    | []      -> assert false
441

    
442
  (* Checks that the dependency graph [g] does not contain a cycle. Raises
443
     [Cycle partition] if the succession of dependencies [partition] forms a cycle *)
444
  let check_cycles g =
445
    let scc_l = Cycles.scc_list g in
446
    let algebraic_loops = List.filter (wrong_partition g) scc_l in
447
    if List.length algebraic_loops > 0 then
448
      raise (Error (DataCycle algebraic_loops))
449
	(* We extract a hint to resolve the cycle: for each variable in the cycle
450
	   which is defined by a call, we return the name of the node call and
451
	   its specific id *)
452

    
453
  (* Creates the sub-graph of [g] restricted to vertices and edges in partition *)
454
  let copy_partition g partition =
455
    let copy_g = IdentDepGraph.create () in
456
    IdentDepGraph.iter_edges
457
      (fun src tgt ->
458
	if List.mem src partition && List.mem tgt partition
459
	then IdentDepGraph.add_edge copy_g src tgt)
460
      g
461

    
462
      
463
  (* Breaks dependency cycles in a graph [g] by inserting aux variables.
464
     [head] is a head of a non-trivial scc of [g]. 
465
     In Lustre, this is legal only for mem/mem cycles *)
466
  let break_cycle head cp_head g =
467
    let succs = IdentDepGraph.succ g head in
468
    IdentDepGraph.add_edge g head cp_head;
469
    IdentDepGraph.add_edge g cp_head (ExprDep.mk_read_var head);
470
    List.iter
471
      (fun s ->
472
	IdentDepGraph.remove_edge g head s;
473
	IdentDepGraph.add_edge    g s cp_head)
474
      succs
475

    
476
  (* Breaks cycles of the dependency graph [g] of memory variables [mems]
477
     belonging in node [node]. Returns:
478
     - a list of new auxiliary variable declarations
479
     - a list of new equations
480
     - a modified acyclic version of [g]
481
  *)
482
  let break_cycles node mems g =
483
    let eqs , auts = get_node_eqs node in
484
    assert (auts = []); (* TODO: check: For the moment we assume that auts are expanded by now *)
485
    let (mem_eqs, non_mem_eqs) = List.partition (fun eq -> List.exists (fun v -> ISet.mem v mems) eq.eq_lhs) eqs in
486
    let rec break vdecls mem_eqs g =
487
      let scc_l = Cycles.scc_list g in
488
      let wrong = List.filter (wrong_partition g) scc_l in
489
      match wrong with
490
      | []              -> (vdecls, non_mem_eqs@mem_eqs, g)
491
      | [head]::_       ->
492
	 begin
493
	   IdentDepGraph.remove_edge g head head;
494
	   break vdecls mem_eqs g
495
	 end
496
      | (head::part)::_ -> 
497
	 begin
498
	   let vdecl_cp_head, cp_eq = mk_copy_eq node head in
499
	   let pvar v = List.mem v part in
500
	   let fvar v = if v = head then vdecl_cp_head.var_id else v in
501
	   let mem_eqs' = List.map (eq_replace_rhs_var pvar fvar) mem_eqs in
502
	   break_cycle head vdecl_cp_head.var_id g;
503
	   break (vdecl_cp_head::vdecls) (cp_eq::mem_eqs') g
504
	 end
505
      | _               -> assert false
506
    in break [] mem_eqs g
507

    
508
end
509

    
510
(* Module used to compute static disjunction of variables based upon their clocks. *)
511
module Disjunction =
512
struct
513
  module ClockedIdentModule =
514
  struct
515
    type t = var_decl
516
    let root_branch vdecl = Clocks.root vdecl.var_clock, Clocks.branch vdecl.var_clock
517
    let compare v1 v2 = compare (root_branch v2, v2.var_id) (root_branch v1, v1.var_id)
518
  end
519

    
520
  module CISet = Set.Make(ClockedIdentModule)
521

    
522
  (* map: var |-> list of disjoint vars, sorted in increasing branch length order,
523
     maybe removing shorter branches *)
524
  type disjoint_map = (ident, CISet.t) Hashtbl.t
525

    
526
  let pp_ciset fmt t =
527
    begin
528
      Format.fprintf fmt "{@ ";
529
      CISet.iter (fun s -> Format.fprintf fmt "%a@ " Printers.pp_var_name s) t;
530
      Format.fprintf fmt "}@."
531
    end
532

    
533
  let clock_disjoint_map vdecls =
534
    let map = Hashtbl.create 23 in
535
    begin
536
      List.iter
537
	(fun v1 -> let disj_v1 =
538
		     List.fold_left
539
		       (fun res v2 -> if Clocks.disjoint v1.var_clock v2.var_clock then CISet.add v2 res else res)
540
		       CISet.empty
541
		       vdecls in
542
		   (* disjoint vdecls are stored in increasing branch length order *)
543
		   Hashtbl.add map v1.var_id disj_v1)
544
	vdecls;
545
      (map : disjoint_map)
546
    end
547

    
548
  (* merge variables [v] and [v'] in disjunction [map]. Then:
549
     - the mapping v' becomes v' |-> (map v) inter (map v')
550
     - the mapping v |-> ... then disappears
551
     - other mappings become x |-> (map x) \ (if v in x then v else v')
552
  *)
553
  let merge_in_disjoint_map map v v' =
554
    begin
555
      Hashtbl.replace map v'.var_id (CISet.inter (Hashtbl.find map v.var_id) (Hashtbl.find map v'.var_id));
556
      Hashtbl.remove map v.var_id;
557
      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;
558
    end
559

    
560
  (* replace variable [v] by [v'] in disjunction [map].
561
     [v'] is a dead variable. Then:
562
     - the mapping v' becomes v' |-> (map v)
563
     - the mapping v |-> ... then disappears
564
     - all mappings become x |-> ((map x) \ { v}) union ({v'} if v in map x)
565
  *)
566
  let replace_in_disjoint_map map v v' =
567
    begin
568
      Hashtbl.replace map v'.var_id (Hashtbl.find map v.var_id);
569
      Hashtbl.remove  map v.var_id;
570
      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;
571
    end
572

    
573
  (* remove variable [v] in disjunction [map]. Then:
574
     - the mapping v |-> ... then disappears
575
     - all mappings become x |-> (map x) \ { v}
576
  *)
577
  let remove_in_disjoint_map map v =
578
    begin
579
      Hashtbl.remove map v.var_id;
580
      Hashtbl.iter (fun x mapx -> Hashtbl.replace map x (CISet.remove v mapx)) map;
581
    end
582

    
583
  let pp_disjoint_map fmt map =
584
    begin
585
      Format.fprintf fmt "{ /* disjoint map */@.";
586
      Hashtbl.iter (fun k v -> Format.fprintf fmt "%s # { %a }@." k (Utils.fprintf_list ~sep:", " Printers.pp_var_name) (CISet.elements v)) map;
587
      Format.fprintf fmt "}@."
588
    end
589
end
590

    
591
  
592
let pp_dep_graph fmt g =
593
  begin
594
    Format.fprintf fmt "{ /* graph */@.";
595
    IdentDepGraph.iter_edges (fun s t -> Format.fprintf fmt "%s -> %s@." s t) g;
596
    Format.fprintf fmt "}@."
597
  end
598

    
599
let pp_error fmt err =
600
  match err with
601
  | NodeCycle trace ->
602
     Format.fprintf fmt "Causality error, cyclic node calls:@   @[<v 0>%a@]@ "
603
       (fprintf_list ~sep:",@ " Format.pp_print_string) trace
604
  | DataCycle traces -> (
605
     Format.fprintf fmt "Causality error, cyclic data dependencies:@   @[<v 0>%a@]@ "
606
       (fprintf_list ~sep:";@ "
607
       (fun fmt trace ->
608
	 Format.fprintf fmt "@[<v 0>{%a}@]"
609
	   (fprintf_list ~sep:",@ " Format.pp_print_string)
610
	   trace
611
       )) traces
612
  )
613
     
614
(* Merges elements of graph [g2] into graph [g1] *)
615
let merge_with g1 g2 =
616
  begin
617
    IdentDepGraph.iter_vertex (fun v -> IdentDepGraph.add_vertex g1 v) g2;
618
    IdentDepGraph.iter_edges (fun s t -> IdentDepGraph.add_edge g1 s t) g2
619
  end
620

    
621
let world = "!!_world"
622

    
623
let add_external_dependency outputs mems g =
624
  begin
625
    IdentDepGraph.add_vertex g world;
626
    ISet.iter (fun o -> IdentDepGraph.add_edge g world o) outputs;
627
    ISet.iter (fun m -> IdentDepGraph.add_edge g world m) mems;
628
  end
629

    
630
let global_dependency node =
631
  let mems = ExprDep.node_memory_variables node in
632
  let inputs =
633
    ISet.union
634
      (ExprDep.node_input_variables node)
635
      (ExprDep.node_constant_variables node) in
636
  let outputs = ExprDep.node_output_variables node in
637
  let node_vars = ExprDep.node_variables node in
638
  let (g_non_mems, g_mems) = ExprDep.dependence_graph mems inputs node_vars node in
639
  (*Format.eprintf "g_non_mems: %a" pp_dep_graph g_non_mems;
640
    Format.eprintf "g_mems: %a" pp_dep_graph g_mems;*)
641
  try
642
    CycleDetection.check_cycles g_non_mems;
643
    let (vdecls', eqs', g_mems') = CycleDetection.break_cycles node mems g_mems in
644
    (*Format.eprintf "g_mems': %a" pp_dep_graph g_mems';*)
645
    begin
646
      merge_with g_non_mems g_mems';
647
      add_external_dependency outputs mems g_non_mems;
648
      { node with node_stmts = List.map (fun eq -> Eq eq) eqs'; node_locals = vdecls'@node.node_locals }, 
649
      g_non_mems
650
    end
651
  with Error (DataCycle _ as exc) -> (
652
      raise (Error (exc))
653
  )
654

    
655
(* Local Variables: *)
656
(* compile-command:"make -C .." *)
657
(* End: *)
(9-9/66)