Project

General

Profile

Download (11.8 KB) Statistics
| Branch: | Tag: | Revision:
1
(********************************************************************)
2
(*                                                                  *)
3
(*  The LustreC compiler toolset   /  The LustreC Development Team  *)
4
(*  Copyright 2012 -    --   ONERA - CNRS - INPT                    *)
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
(********************************************************************)
11

    
12
open Utils
13
open Lustre_types
14
open Corelang
15
open Causality
16

    
17
type context = {
18
  mutable evaluated : Disjunction.CISet.t;
19
  dep_graph : IdentDepGraph.t;
20
  disjoint : (ident, Disjunction.CISet.t) Hashtbl.t;
21
  policy : (ident, var_decl) Hashtbl.t;
22
}
23

    
24
type fanin = (ident, tag) Hashtbl.t
25

    
26
(* computes the in-degree for each local variable of node [n], according to dep
27
   graph [g]. *)
28
let compute_fanin n g =
29
  let locals =
30
    ISet.diff (ExprDep.node_local_variables n) (ExprDep.node_memory_variables n)
31
  in
32
  let inputs = ExprDep.node_input_variables n in
33
  let fanin = Hashtbl.create 23 in
34
  IdentDepGraph.iter_vertex
35
    (fun v ->
36
      if ISet.mem v locals then
37
        Hashtbl.add fanin v (IdentDepGraph.in_degree g v)
38
      else if ExprDep.is_read_var v && not (ISet.mem v inputs) then
39
        Hashtbl.add fanin (ExprDep.undo_read_var v)
40
          (IdentDepGraph.in_degree g v))
41
    g;
42
  fanin
43

    
44
let pp_fanin fmt fanin =
45
  Format.fprintf fmt "@[<v 0>@[<v 2>{ /* locals fanin: */";
46
  Hashtbl.iter (fun s t -> Format.fprintf fmt "@ %s -> %d" s t) fanin;
47
  Format.fprintf fmt "@]@ }@]"
48

    
49
(* computes the cone of influence of a given [var] wrt a dependency graph [g]. *)
50
let cone_of_influence g var =
51
  (*Format.printf "DEBUG coi: %s@." var;*)
52
  let frontier = ref (ISet.add var ISet.empty) in
53
  let explored = ref ISet.empty in
54
  let coi = ref ISet.empty in
55
  while not (ISet.is_empty !frontier) do
56
    let head = ISet.min_elt !frontier in
57
    (*Format.printf "DEBUG head: %s@." head;*)
58
    frontier := ISet.remove head !frontier;
59
    explored := ISet.add head !explored;
60
    if ExprDep.is_read_var head then
61
      coi := ISet.add (ExprDep.undo_read_var head) !coi;
62
    List.iter
63
      (fun s ->
64
        if not (ISet.mem s !explored) then frontier := ISet.add s !frontier)
65
      (IdentDepGraph.succ g head)
66
  done;
67
  !coi
68

    
69
let compute_unused_variables n g =
70
  let inputs = ExprDep.node_input_variables n in
71
  let mems = ExprDep.node_memory_variables n in
72
  let outputs = ExprDep.node_output_variables n in
73
  ISet.fold
74
    (fun var unused -> ISet.diff unused (cone_of_influence g var))
75
    (ISet.union outputs mems) (ISet.union inputs mems)
76

    
77
(* computes the set of potentially reusable variables. We don't reuse input
78
   variables, due to possible aliasing *)
79
let node_reusable_variables node =
80
  let mems = ExprDep.node_memory_variables node in
81
  List.fold_left
82
    (fun acc l ->
83
      if ISet.mem l.var_id mems then acc else Disjunction.CISet.add l acc)
84
    Disjunction.CISet.empty node.node_locals
85

    
86
let kill_instance_variables ctx inst =
87
  IdentDepGraph.remove_vertex ctx.dep_graph inst
88

    
89
let kill_root ctx head =
90
  IdentDepGraph.iter_succ
91
    (IdentDepGraph.remove_edge ctx.dep_graph head.var_id)
92
    ctx.dep_graph head.var_id
93

    
94
(* Recursively removes useless variables, i.e. [ctx.evaluated] variables that
95
   are current roots of the dep graph [ctx.dep_graph] - [evaluated] is the set
96
   of already evaluated variables, wrt the scheduling - does only remove edges,
97
   not variables themselves - yet, instance variables are removed *)
98
let remove_roots ctx =
99
  let rem = ref true in
100
  let remaining = ref ctx.evaluated in
101
  while !rem do
102
    rem := false;
103
    let all_roots = graph_roots ctx.dep_graph in
104
    let inst_roots, var_roots =
105
      List.partition
106
        (fun v -> ExprDep.is_instance_var v && v <> Causality.world)
107
        all_roots
108
    in
109
    let frontier_roots =
110
      Disjunction.CISet.filter (fun v -> List.mem v.var_id var_roots) !remaining
111
    in
112
    if not (Disjunction.CISet.is_empty frontier_roots && inst_roots = []) then (
113
      rem := true;
114
      List.iter (kill_instance_variables ctx) inst_roots;
115
      Disjunction.CISet.iter (kill_root ctx) frontier_roots;
116
      remaining := Disjunction.CISet.diff !remaining frontier_roots)
117
  done
118

    
119
(* checks whether a variable is aliasable, depending on its (address) type *)
120
let is_aliasable var =
121
  (not (!Options.mpfr && Types.is_real_type var.var_type))
122
  && Types.is_address_type var.var_type
123

    
124
(* checks whether a variable [v] is an input of the [var] equation, with an
125
   address type. if so, [var] could not safely reuse/alias [v], should [v] be
126
   dead in the caller node, because [v] may not be dead in the callee node when
127
   [var] is assigned *)
128
let is_aliasable_input node var =
129
  let eq_var = get_node_eq var node in
130
  let inputs_var =
131
    match NodeDep.get_callee eq_var.eq_rhs with
132
    | None ->
133
      []
134
    | Some (_, args) ->
135
      List.fold_right
136
        (fun e r -> match e.expr_desc with Expr_ident id -> id :: r | _ -> r)
137
        args []
138
  in
139
  fun v -> is_aliasable v && List.mem v.var_id inputs_var
140

    
141
(* replace variable [v] by [v'] in graph [g]. [v'] is a dead variable *)
142
let replace_in_dep_graph v v' g =
143
  IdentDepGraph.add_vertex g v';
144
  IdentDepGraph.iter_succ (fun s -> IdentDepGraph.add_edge g v' s) g v;
145
  IdentDepGraph.iter_pred (fun p -> IdentDepGraph.add_edge g p v') g v;
146
  IdentDepGraph.remove_vertex g v
147

    
148
let pp_reuse_policy fmt policy =
149
  Format.(
150
    fprintf fmt "@[<v 2>{ /* reuse policy */%t@] }" (fun fmt ->
151
        Hashtbl.iter (fun s t -> fprintf fmt "@,%s -> %s" s t.var_id) policy))
152

    
153
let pp_context fmt ctx =
154
  Format.fprintf fmt
155
    "@[<v 2>{ /*BEGIN context */@,\
156
     eval     = %a;@,\
157
     graph    = %a;@,\
158
     disjoint = %a;@,\
159
     policy   = %a;@,\
160
     /* END context */ }@]" Disjunction.pp_ciset ctx.evaluated pp_dep_graph
161
    ctx.dep_graph Disjunction.pp_disjoint_map ctx.disjoint pp_reuse_policy
162
    ctx.policy
163

    
164
(* computes the reusable dependencies of variable [var] in graph [g], once [var]
165
   has been evaluated - [locals] is the set of potentially reusable variables -
166
   [evaluated] is the set of evaluated variables - [quasi] is the set of
167
   quasi-reusable variables - [reusable] is the set of dead/reusable
168
   dependencies of [var] in graph [g] - [policy] is the reuse map (which domain
169
   is [evaluated]) *)
170
let compute_dependencies heads ctx =
171
  (*Log.report ~level:6 (fun fmt -> Format.fprintf fmt
172
    "compute_reusable_dependencies %a %a %a@." Disjunction.pp_ciset locals
173
    Printers.pp_var_name var pp_context ctx);*)
174
  List.iter (kill_root ctx) heads;
175
  remove_roots ctx
176

    
177
let compute_evaluated heads ctx =
178
  List.iter
179
    (fun head -> ctx.evaluated <- Disjunction.CISet.add head ctx.evaluated)
180
    heads
181

    
182
(* tests whether a variable [v] may be (re)used instead of [var]. The conditions
183
   are: - [v] has been really used ([v] is its own representative) - same type -
184
   [v] is not an aliasable input of the equation defining [var] - [v] is not one
185
   of the current heads (which contain [var]) - [v] is not currently in use *)
186
let eligible node ctx heads var v =
187
  Hashtbl.find ctx.policy v.var_id == v
188
  && Typing.eq_ground
189
       (Types.unclock_type var.var_type)
190
       (Types.unclock_type v.var_type)
191
  && (not (is_aliasable_input node var.var_id v))
192
  && (not (List.exists (fun h -> h.var_id = v.var_id) heads))
193
  && (*let repr_v = Hashtbl.find ctx.policy v.var_id*)
194
  not
195
    (Disjunction.CISet.exists
196
       (fun p -> IdentDepGraph.mem_edge ctx.dep_graph p.var_id v.var_id)
197
       ctx.evaluated)
198

    
199
let compute_reuse node ctx heads var =
200
  let disjoint = Hashtbl.find ctx.disjoint var.var_id in
201
  let locally_reusable v =
202
    IdentDepGraph.fold_pred
203
      (fun p r ->
204
        r && Disjunction.CISet.exists (fun d -> p = d.var_id) disjoint)
205
      ctx.dep_graph v.var_id true
206
  in
207
  let eligibles =
208
    if ISet.mem var.var_id (ExprDep.node_memory_variables node) then
209
      Disjunction.CISet.empty
210
    else Disjunction.CISet.filter (eligible node ctx heads var) ctx.evaluated
211
  in
212
  let quasi_dead, live =
213
    Disjunction.CISet.partition locally_reusable eligibles
214
  in
215
  let disjoint_live = Disjunction.CISet.inter disjoint live in
216
  let dead =
217
    Disjunction.CISet.filter
218
      (fun v -> is_graph_root v.var_id ctx.dep_graph)
219
      quasi_dead
220
  in
221
  Log.report ~level:7 (fun fmt ->
222
      Format.fprintf fmt
223
        "@[<v>eligibles    : %a@,\
224
         live         : %a@,\
225
         disjoint live: %a@,\
226
         dead         : %a@,\
227
         @]"
228
        Disjunction.pp_ciset eligibles Disjunction.pp_ciset live
229
        Disjunction.pp_ciset disjoint_live Disjunction.pp_ciset dead);
230
  (try
231
     let reuse =
232
       match Disjunction.CISet.max_elt_opt disjoint_live with
233
       | Some reuse ->
234
         reuse
235
       | None ->
236
         Disjunction.CISet.choose dead
237
     in
238
     IdentDepGraph.add_edge ctx.dep_graph var.var_id reuse.var_id;
239
     Hashtbl.add ctx.policy var.var_id reuse
240
   with Not_found -> Hashtbl.add ctx.policy var.var_id var);
241
  ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated
242

    
243
let compute_reuse_policy node schedule disjoint g =
244
  let ctx =
245
    {
246
      evaluated = Disjunction.CISet.empty;
247
      dep_graph = g;
248
      disjoint;
249
      policy = Hashtbl.create 23;
250
    }
251
  in
252
  List.iter
253
    (fun heads ->
254
      let heads = List.map (fun v -> get_node_var v node) heads in
255
      Log.report ~level:6 (fun fmt ->
256
          Format.(
257
            fprintf fmt
258
              "@[<v>@[<v 2>new context:@,\
259
               %a@]@,\
260
               NEW HEADS:%a@,\
261
               COMPUTE_DEPENDENCIES@,\
262
               @]"
263
              pp_context ctx
264
              (pp_print_list ~pp_open_box:pp_open_hbox ~pp_sep:pp_print_space
265
                 (fun fmt head ->
266
                   fprintf fmt "%s (%a)" head.var_id Printers.pp_node_eq
267
                     (get_node_eq head.var_id node)))
268
              heads));
269
      compute_dependencies heads ctx;
270
      Log.report ~level:6 (fun fmt ->
271
          Format.fprintf fmt "@[<v>@[<v 2>new context:@,%a@]@,COMPUTE_REUSE@,@]"
272
            pp_context ctx);
273
      List.iter (compute_reuse node ctx heads) heads;
274
      (*compute_evaluated heads ctx;*)
275
      Log.report ~level:6 (fun fmt ->
276
          Format.(
277
            fprintf fmt "@[<v>%a@,@]"
278
              (pp_print_list ~pp_open_box:pp_open_vbox0 (fun fmt head ->
279
                   fprintf fmt "reuse %s instead of %s"
280
                     (Hashtbl.find ctx.policy head.var_id).var_id head.var_id))
281
              heads)))
282
    schedule;
283
  IdentDepGraph.clear ctx.dep_graph;
284
  ctx.policy
285

    
286
(* Reuse policy: - could reuse variables with the same type exactly only
287
   (simple). - reusing variables with different types would involve: - either
288
   dirty castings - or complex inclusion expression (for instance: array <->
289
   array cell, struct <-> struct field) to be able to reuse only some parts of
290
   structured data. ... it seems too complex and potentially unsafe - for node
291
   instance calls: output variables could NOT reuse aliasable input variables,
292
   even if inputs become dead, because the correctness would depend on the
293
   scheduling of the callee (so, the compiling strategy could NOT be modular
294
   anymore). - once a policy is set, we need to: - replace each variable by its
295
   reuse alias. - simplify resulting equations, as we may now have: x = x; --> ;
296
   for scalar vars or: x = &{ f1 = x->f1; f2 = t; } --> x->f2 = t; for struct
297
   vars *)
298

    
299
(* the reuse policy seeks to use less local variables by replacing local
300
   variables, applying the rules in the following order: 1) use another clock
301
   disjoint still live variable, with the greatest possible disjoint clock 2)
302
   reuse a dead variable For the sake of safety, we replace variables by others:
303
   - with the same type - not aliasable (i.e. address type) *)
304

    
305
(* Local Variables: *)
306
(* compile-command:"make -C .." *)
307
(* End: *)
(7-7/10)