1 |
e2068500
|
Temesghen Kahsai
|
(********************************************************************)
|
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 LustreSpec
|
14 |
|
|
open Corelang
|
15 |
|
|
open Graph
|
16 |
|
|
open Causality
|
17 |
|
|
|
18 |
|
|
type context =
|
19 |
|
|
{
|
20 |
|
|
mutable evaluated : Disjunction.CISet.t;
|
21 |
|
|
dep_graph : IdentDepGraph.t;
|
22 |
|
|
disjoint : (ident, Disjunction.CISet.t) Hashtbl.t;
|
23 |
|
|
policy : (ident, var_decl) Hashtbl.t;
|
24 |
|
|
}
|
25 |
|
|
|
26 |
|
|
(* computes the in-degree for each local variable of node [n], according to dep graph [g].
|
27 |
|
|
*)
|
28 |
|
|
let compute_fanin n g =
|
29 |
|
|
let locals = ISet.diff (ExprDep.node_local_variables n) (ExprDep.node_memory_variables n) in
|
30 |
|
|
let inputs = ExprDep.node_input_variables n in
|
31 |
|
|
let fanin = Hashtbl.create 23 in
|
32 |
|
|
begin
|
33 |
|
|
IdentDepGraph.iter_vertex
|
34 |
|
|
(fun v ->
|
35 |
|
|
if ISet.mem v locals
|
36 |
|
|
then Hashtbl.add fanin v (IdentDepGraph.in_degree g v) else
|
37 |
|
|
if ExprDep.is_read_var v && not (ISet.mem v inputs)
|
38 |
|
|
then Hashtbl.add fanin (ExprDep.undo_read_var v) (IdentDepGraph.in_degree g v)) g;
|
39 |
|
|
fanin
|
40 |
|
|
end
|
41 |
|
|
|
42 |
|
|
let pp_fanin fmt fanin =
|
43 |
|
|
begin
|
44 |
|
|
Format.fprintf fmt "{ /* locals fanin: */@.";
|
45 |
|
|
Hashtbl.iter (fun s t -> Format.fprintf fmt "%s -> %d@." s t) fanin;
|
46 |
|
|
Format.fprintf fmt "}@."
|
47 |
|
|
end
|
48 |
|
|
|
49 |
|
|
(* computes the cone of influence of a given [var] wrt a dependency graph [g].
|
50 |
|
|
*)
|
51 |
|
|
let cone_of_influence g var =
|
52 |
|
|
(*Format.printf "coi: %s@." var;*)
|
53 |
|
|
let frontier = ref (ISet.add var ISet.empty) in
|
54 |
|
|
let coi = ref ISet.empty in
|
55 |
|
|
while not (ISet.is_empty !frontier)
|
56 |
|
|
do
|
57 |
|
|
let head = ISet.min_elt !frontier in
|
58 |
|
|
(*Format.printf "head: %s@." head;*)
|
59 |
|
|
frontier := ISet.remove head !frontier;
|
60 |
|
|
if ExprDep.is_read_var head then coi := ISet.add (ExprDep.undo_read_var head) !coi;
|
61 |
|
|
List.iter (fun s -> frontier := ISet.add s !frontier) (IdentDepGraph.succ g head);
|
62 |
|
|
done;
|
63 |
|
|
!coi
|
64 |
|
|
|
65 |
|
|
let compute_unused_variables n g =
|
66 |
|
|
let inputs = ExprDep.node_input_variables n in
|
67 |
|
|
let mems = ExprDep.node_memory_variables n in
|
68 |
|
|
let outputs = ExprDep.node_output_variables n in
|
69 |
|
|
ISet.fold
|
70 |
|
|
(fun var unused -> ISet.diff unused (cone_of_influence g var))
|
71 |
|
|
(ISet.union outputs mems)
|
72 |
|
|
(ISet.union inputs mems)
|
73 |
|
|
|
74 |
|
|
(* computes the set of potentially reusable variables.
|
75 |
|
|
We don't reuse input variables, due to possible aliasing *)
|
76 |
|
|
let node_reusable_variables node =
|
77 |
|
|
let mems = ExprDep.node_memory_variables node in
|
78 |
|
|
List.fold_left
|
79 |
|
|
(fun acc l ->
|
80 |
|
|
if ISet.mem l.var_id mems then acc else Disjunction.CISet.add l acc)
|
81 |
|
|
Disjunction.CISet.empty
|
82 |
|
|
node.node_locals
|
83 |
|
|
|
84 |
|
|
let kill_root ctx head =
|
85 |
|
|
IdentDepGraph.iter_succ (IdentDepGraph.remove_edge ctx.dep_graph head.var_id) ctx.dep_graph head.var_id
|
86 |
|
|
|
87 |
|
|
(* Recursively removes useless variables,
|
88 |
|
|
i.e. [ctx.evaluated] variables that are current roots of the dep graph [ctx.dep_graph]
|
89 |
|
|
- [evaluated] is the set of already evaluated variables,
|
90 |
|
|
wrt the scheduling
|
91 |
|
|
- does only remove edges, not variables themselves
|
92 |
|
|
*)
|
93 |
|
|
let remove_roots ctx =
|
94 |
|
|
let rem = ref true in
|
95 |
|
|
let remaining = ref ctx.evaluated in
|
96 |
|
|
while !rem
|
97 |
|
|
do
|
98 |
|
|
rem := false;
|
99 |
|
|
let all_roots = graph_roots ctx.dep_graph in
|
100 |
|
|
let frontier_roots = Disjunction.CISet.filter (fun v -> List.mem v.var_id all_roots) !remaining in
|
101 |
|
|
if not (Disjunction.CISet.is_empty frontier_roots) then
|
102 |
|
|
begin
|
103 |
|
|
rem := true;
|
104 |
|
|
Disjunction.CISet.iter (kill_root ctx) frontier_roots;
|
105 |
|
|
remaining := Disjunction.CISet.diff !remaining frontier_roots
|
106 |
|
|
end
|
107 |
|
|
done
|
108 |
|
|
|
109 |
|
|
(* checks whether a variable is aliasable,
|
110 |
|
|
depending on its (address) type *)
|
111 |
|
|
let is_aliasable var =
|
112 |
|
|
Types.is_address_type var.var_type
|
113 |
|
|
|
114 |
|
|
(* checks whether a variable [v] is an input of the [var] equation, with an address type.
|
115 |
|
|
if so, [var] could not safely reuse/alias [v], should [v] be dead in the caller node,
|
116 |
|
|
because [v] may not be dead in the callee node when [var] is assigned *)
|
117 |
|
|
let is_aliasable_input node var =
|
118 |
|
|
let eq_var = get_node_eq var node in
|
119 |
|
|
let inputs_var =
|
120 |
|
|
match NodeDep.get_callee eq_var.eq_rhs with
|
121 |
|
|
| None -> []
|
122 |
|
|
| Some (_, args) -> List.fold_right (fun e r -> match e.expr_desc with Expr_ident id -> id::r | _ -> r) args [] in
|
123 |
|
|
fun v -> is_aliasable v && List.mem v.var_id inputs_var
|
124 |
d50b0dc0
|
Temesghen Kahsai
|
(*
|
125 |
|
|
let res =
|
126 |
|
|
is_aliasable v && List.mem v.var_id inputs_var
|
127 |
|
|
in (Format.eprintf "aliasable %s by %s = %B@." var v.var_id res; res)
|
128 |
|
|
*)
|
129 |
e2068500
|
Temesghen Kahsai
|
(* replace variable [v] by [v'] in graph [g].
|
130 |
|
|
[v'] is a dead variable
|
131 |
|
|
*)
|
132 |
|
|
let replace_in_dep_graph v v' g =
|
133 |
|
|
begin
|
134 |
|
|
IdentDepGraph.add_vertex g v';
|
135 |
|
|
IdentDepGraph.iter_succ (fun s -> IdentDepGraph.add_edge g v' s) g v;
|
136 |
|
|
IdentDepGraph.iter_pred (fun p -> IdentDepGraph.add_edge g p v') g v;
|
137 |
|
|
IdentDepGraph.remove_vertex g v
|
138 |
|
|
end
|
139 |
|
|
|
140 |
|
|
let pp_reuse_policy fmt policy =
|
141 |
|
|
begin
|
142 |
|
|
Format.fprintf fmt "{ /* reuse policy */@.";
|
143 |
|
|
Hashtbl.iter (fun s t -> Format.fprintf fmt "%s -> %s@." s t.var_id) policy;
|
144 |
|
|
Format.fprintf fmt "}@."
|
145 |
|
|
end
|
146 |
|
|
|
147 |
|
|
let pp_context fmt ctx =
|
148 |
|
|
begin
|
149 |
|
|
Format.fprintf fmt "{ /*BEGIN context */@.";
|
150 |
|
|
Format.fprintf fmt "eval=%a;@." Disjunction.pp_ciset ctx.evaluated;
|
151 |
|
|
Format.fprintf fmt "graph=%a;@." pp_dep_graph ctx.dep_graph;
|
152 |
|
|
Format.fprintf fmt "disjoint=%a;@." Disjunction.pp_disjoint_map ctx.disjoint;
|
153 |
|
|
Format.fprintf fmt "policy=%a;@." pp_reuse_policy ctx.policy;
|
154 |
|
|
Format.fprintf fmt "/* END context */ }@.";
|
155 |
|
|
end
|
156 |
|
|
|
157 |
|
|
(* computes the reusable dependencies of variable [var] in graph [g],
|
158 |
|
|
once [var] has been evaluated
|
159 |
|
|
- [locals] is the set of potentially reusable variables
|
160 |
|
|
- [evaluated] is the set of evaluated variables
|
161 |
|
|
- [quasi] is the set of quasi-reusable variables
|
162 |
|
|
- [reusable] is the set of dead/reusable dependencies of [var] in graph [g]
|
163 |
|
|
- [policy] is the reuse map (which domain is [evaluated])
|
164 |
|
|
*)
|
165 |
|
|
let compute_dependencies heads ctx =
|
166 |
|
|
begin
|
167 |
|
|
(*Log.report ~level:6 (fun fmt -> Format.fprintf fmt "compute_reusable_dependencies %a %a %a@." Disjunction.pp_ciset locals Printers.pp_var_name var pp_context ctx);*)
|
168 |
|
|
List.iter (kill_root ctx) heads;
|
169 |
|
|
remove_roots ctx;
|
170 |
|
|
end
|
171 |
|
|
|
172 |
|
|
let compute_evaluated heads ctx =
|
173 |
|
|
begin
|
174 |
|
|
List.iter (fun head -> ctx.evaluated <- Disjunction.CISet.add head ctx.evaluated) heads;
|
175 |
|
|
end
|
176 |
|
|
|
177 |
|
|
(* tests whether a variable [v] may be (re)used instead of [var]. The conditions are:
|
178 |
d50b0dc0
|
Temesghen Kahsai
|
- [v] has been really used ([v] is its own representative)
|
179 |
e2068500
|
Temesghen Kahsai
|
- same type
|
180 |
|
|
- [v] is not an aliasable input of the equation defining [var]
|
181 |
|
|
- [v] is not one of the current heads (which contain [var])
|
182 |
d50b0dc0
|
Temesghen Kahsai
|
- [v] is not currently in use
|
183 |
e2068500
|
Temesghen Kahsai
|
*)
|
184 |
|
|
let eligible node ctx heads var v =
|
185 |
d50b0dc0
|
Temesghen Kahsai
|
Hashtbl.find ctx.policy v.var_id == v
|
186 |
|
|
&& Typing.eq_ground (Types.unclock_type var.var_type) (Types.unclock_type v.var_type)
|
187 |
e2068500
|
Temesghen Kahsai
|
&& not (is_aliasable_input node var.var_id v)
|
188 |
|
|
&& not (List.exists (fun h -> h.var_id = v.var_id) heads)
|
189 |
d50b0dc0
|
Temesghen Kahsai
|
&& (*let repr_v = Hashtbl.find ctx.policy v.var_id*)
|
190 |
|
|
not (Disjunction.CISet.exists (fun p -> IdentDepGraph.mem_edge ctx.dep_graph p.var_id v.var_id) ctx.evaluated)
|
191 |
e2068500
|
Temesghen Kahsai
|
|
192 |
|
|
let compute_reuse node ctx heads var =
|
193 |
|
|
let disjoint = Hashtbl.find ctx.disjoint var.var_id in
|
194 |
|
|
let locally_reusable v =
|
195 |
|
|
IdentDepGraph.fold_pred (fun p r -> r && Disjunction.CISet.exists (fun d -> p = d.var_id) disjoint) ctx.dep_graph v.var_id true in
|
196 |
|
|
let eligibles = Disjunction.CISet.filter (eligible node ctx heads var) ctx.evaluated in
|
197 |
d50b0dc0
|
Temesghen Kahsai
|
Log.report ~level:7 (fun fmt -> Format.fprintf fmt "eligibles:%a@." Disjunction.pp_ciset eligibles);
|
198 |
e2068500
|
Temesghen Kahsai
|
let quasi_dead, live = Disjunction.CISet.partition locally_reusable eligibles in
|
199 |
d50b0dc0
|
Temesghen Kahsai
|
Log.report ~level:7 (fun fmt -> Format.fprintf fmt "live:%a@." Disjunction.pp_ciset live);
|
200 |
e2068500
|
Temesghen Kahsai
|
try
|
201 |
|
|
let disjoint_live = Disjunction.CISet.inter disjoint live in
|
202 |
d50b0dc0
|
Temesghen Kahsai
|
Log.report ~level:7 (fun fmt -> Format.fprintf fmt "disjoint live:%a@." Disjunction.pp_ciset disjoint_live);
|
203 |
e2068500
|
Temesghen Kahsai
|
let reuse = Disjunction.CISet.max_elt disjoint_live in
|
204 |
d50b0dc0
|
Temesghen Kahsai
|
(*let reuse' = Hashtbl.find ctx.policy reuse.var_id in*)
|
205 |
e2068500
|
Temesghen Kahsai
|
begin
|
206 |
|
|
IdentDepGraph.add_edge ctx.dep_graph var.var_id reuse.var_id;
|
207 |
d50b0dc0
|
Temesghen Kahsai
|
(*if reuse != reuse' then IdentDepGraph.add_edge ctx.dep_graph reuse.var_id reuse'.var_id;*)
|
208 |
|
|
Hashtbl.add ctx.policy var.var_id reuse;
|
209 |
e2068500
|
Temesghen Kahsai
|
ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated;
|
210 |
|
|
(*Format.eprintf "%s reused by live@." var.var_id;*)
|
211 |
|
|
end
|
212 |
|
|
with Not_found ->
|
213 |
|
|
try
|
214 |
|
|
let dead = Disjunction.CISet.filter (fun v -> is_graph_root v.var_id ctx.dep_graph) quasi_dead in
|
215 |
d50b0dc0
|
Temesghen Kahsai
|
Log.report ~level:7 (fun fmt -> Format.fprintf fmt "dead:%a@." Disjunction.pp_ciset dead);
|
216 |
e2068500
|
Temesghen Kahsai
|
let reuse = Disjunction.CISet.choose dead in
|
217 |
d50b0dc0
|
Temesghen Kahsai
|
(*let reuse' = Hashtbl.find ctx.policy reuse.var_id in*)
|
218 |
e2068500
|
Temesghen Kahsai
|
begin
|
219 |
|
|
IdentDepGraph.add_edge ctx.dep_graph var.var_id reuse.var_id;
|
220 |
d50b0dc0
|
Temesghen Kahsai
|
(*if reuse != reuse' then IdentDepGraph.add_edge ctx.dep_graph reuse.var_id reuse'.var_id;*)
|
221 |
|
|
Hashtbl.add ctx.policy var.var_id reuse;
|
222 |
e2068500
|
Temesghen Kahsai
|
ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated;
|
223 |
|
|
(*Format.eprintf "%s reused by dead %a@." var.var_id Disjunction.pp_ciset dead;*)
|
224 |
|
|
end
|
225 |
|
|
with Not_found ->
|
226 |
|
|
begin
|
227 |
|
|
Hashtbl.add ctx.policy var.var_id var;
|
228 |
|
|
ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated;
|
229 |
|
|
end
|
230 |
|
|
|
231 |
|
|
let compute_reuse_policy node schedule disjoint g =
|
232 |
|
|
let sort = ref schedule in
|
233 |
|
|
let ctx = { evaluated = Disjunction.CISet.empty;
|
234 |
|
|
dep_graph = g;
|
235 |
|
|
disjoint = disjoint;
|
236 |
|
|
policy = Hashtbl.create 23; } in
|
237 |
|
|
while !sort <> []
|
238 |
|
|
do
|
239 |
|
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "new context:%a@." pp_context ctx);
|
240 |
|
|
let heads = List.map (fun v -> get_node_var v node) (List.hd !sort) in
|
241 |
|
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "NEW HEADS:");
|
242 |
d50b0dc0
|
Temesghen Kahsai
|
List.iter (fun head -> Log.report ~level:6 (fun fmt -> Format.fprintf fmt "%s (%a)" head.var_id Printers.pp_node_eq (get_node_eq head.var_id node))) heads;
|
243 |
e2068500
|
Temesghen Kahsai
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "@.");
|
244 |
|
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "COMPUTE_DEPENDENCIES@.");
|
245 |
|
|
compute_dependencies heads ctx;
|
246 |
|
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "new context:%a@." pp_context ctx);
|
247 |
|
|
Log.report ~level:6 (fun fmt -> Format.fprintf fmt "COMPUTE_REUSE@.");
|
248 |
|
|
List.iter (compute_reuse node ctx heads) heads;
|
249 |
|
|
(*compute_evaluated heads ctx;*)
|
250 |
|
|
List.iter (fun head -> Log.report ~level:6 (fun fmt -> Format.fprintf fmt "reuse %s instead of %s@." (Hashtbl.find ctx.policy head.var_id).var_id head.var_id)) heads;
|
251 |
|
|
sort := List.tl !sort;
|
252 |
|
|
done;
|
253 |
|
|
IdentDepGraph.clear ctx.dep_graph;
|
254 |
|
|
ctx.policy
|
255 |
|
|
|
256 |
|
|
(* Reuse policy:
|
257 |
|
|
- could reuse variables with the same type exactly only (simple).
|
258 |
|
|
- reusing variables with different types would involve:
|
259 |
|
|
- either dirty castings
|
260 |
|
|
- or complex inclusion expression (for instance: array <-> array cell, struct <-> struct field) to be able to reuse only some parts of structured data.
|
261 |
|
|
... it seems too complex and potentially unsafe
|
262 |
|
|
- for node instance calls: output variables could NOT reuse aliasable input variables,
|
263 |
|
|
even if inputs become dead, because the correctness would depend on the scheduling
|
264 |
|
|
of the callee (so, the compiling strategy could NOT be modular anymore).
|
265 |
|
|
- once a policy is set, we need to:
|
266 |
|
|
- replace each variable by its reuse alias.
|
267 |
|
|
- simplify resulting equations, as we may now have:
|
268 |
|
|
x = x; --> ; for scalar vars
|
269 |
|
|
or:
|
270 |
|
|
x = &{ f1 = x->f1; f2 = t; } --> x->f2 = t; for struct vars
|
271 |
|
|
*)
|
272 |
|
|
|
273 |
|
|
|
274 |
|
|
(* the reuse policy seeks to use less local variables
|
275 |
|
|
by replacing local variables, applying the rules
|
276 |
|
|
in the following order:
|
277 |
|
|
1) use another clock disjoint still live variable,
|
278 |
|
|
with the greatest possible disjoint clock
|
279 |
|
|
2) reuse a dead variable
|
280 |
|
|
For the sake of safety, we replace variables by others:
|
281 |
|
|
- with the same type
|
282 |
|
|
- not aliasable (i.e. address type)
|
283 |
|
|
*)
|
284 |
|
|
|
285 |
|
|
(* Local Variables: *)
|
286 |
|
|
(* compile-command:"make -C .." *)
|
287 |
|
|
(* End: *)
|