1
|
open Lustre_types
|
2
|
open Utils
|
3
|
open Seal_utils
|
4
|
open Zustre_data (* Access to Z3 context *)
|
5
|
|
6
|
|
7
|
(* Switched system extraction: expression are memoized *)
|
8
|
(*let expr_mem = Hashtbl.create 13*)
|
9
|
|
10
|
let add_init defs vid =
|
11
|
Hashtbl.add defs vid [[], IsInit]
|
12
|
|
13
|
|
14
|
(**************************************************************)
|
15
|
(* Convert from Lustre expressions to Z3 expressions and back *)
|
16
|
(* All (free) variables have to be declared in the Z3 context *)
|
17
|
(**************************************************************)
|
18
|
|
19
|
let is_init_name = "__is_init"
|
20
|
|
21
|
let const_defs = Hashtbl.create 13
|
22
|
let is_const id = Hashtbl.mem const_defs id
|
23
|
let is_enum_const id = Hashtbl.mem Zustre_data.const_tags id
|
24
|
let get_const id = Hashtbl.find const_defs id
|
25
|
|
26
|
(* expressions are only basic constructs here, no more ite, tuples,
|
27
|
arrows, fby, ... *)
|
28
|
|
29
|
(* Set of hash to support memoization *)
|
30
|
let expr_hash: (expr * Utils.tag) list ref = ref []
|
31
|
let ze_hash: (Z3.Expr.expr, Utils.tag) Hashtbl.t = Hashtbl.create 13
|
32
|
let e_hash: (Utils.tag, Z3.Expr.expr) Hashtbl.t = Hashtbl.create 13
|
33
|
let pp_hash pp_key pp_v fmt h = Hashtbl.iter (fun key v -> Format.fprintf fmt "%a -> %a@ " pp_key key pp_v v) h
|
34
|
let pp_e_map fmt = List.iter (fun (e,t) -> Format.fprintf fmt "%i -> %a@ " t Printers.pp_expr e) !expr_hash
|
35
|
let pp_ze_hash fmt = pp_hash
|
36
|
(fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))
|
37
|
Format.pp_print_int
|
38
|
fmt
|
39
|
ze_hash
|
40
|
let pp_e_hash fmt = pp_hash
|
41
|
Format.pp_print_int
|
42
|
(fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))
|
43
|
fmt
|
44
|
e_hash
|
45
|
let mem_expr e =
|
46
|
(* Format.eprintf "Searching for %a in map: @[<v 0>%t@]"
|
47
|
* Printers.pp_expr e
|
48
|
* pp_e_map; *)
|
49
|
let res = List.exists (fun (e',_) -> Corelang.is_eq_expr e e') !expr_hash in
|
50
|
(* Format.eprintf "found?%b@." res; *)
|
51
|
res
|
52
|
|
53
|
let mem_zexpr ze =
|
54
|
Hashtbl.mem ze_hash ze
|
55
|
let get_zexpr e =
|
56
|
let eref, uid = List.find (fun (e',_) -> Corelang.is_eq_expr e e') !expr_hash in
|
57
|
(* Format.eprintf "found expr=%a id=%i@." Printers.pp_expr eref eref.expr_tag; *)
|
58
|
Hashtbl.find e_hash uid
|
59
|
let get_expr ze =
|
60
|
let uid = Hashtbl.find ze_hash ze in
|
61
|
let e,_ = List.find (fun (e,t) -> t = uid) !expr_hash in
|
62
|
e
|
63
|
|
64
|
let neg_ze z3e = Z3.Boolean.mk_not !ctx z3e
|
65
|
let is_init_z3e =
|
66
|
Z3.Expr.mk_const_s !ctx is_init_name Zustre_common.bool_sort
|
67
|
|
68
|
let get_zid (ze:Z3.Expr.expr) : Utils.tag =
|
69
|
try
|
70
|
if Z3.Expr.equal ze is_init_z3e then -1 else
|
71
|
if Z3.Expr.equal ze (neg_ze is_init_z3e) then -2 else
|
72
|
Hashtbl.find ze_hash ze
|
73
|
with _ -> (Format.eprintf "Looking for ze %s in Hash %a"
|
74
|
(Z3.Expr.to_string ze)
|
75
|
(fun fmt hash -> Hashtbl.iter (fun ze uid -> Format.fprintf fmt "%s -> %i@ " (Z3.Expr.to_string ze) uid) hash ) ze_hash;
|
76
|
assert false)
|
77
|
let add_expr =
|
78
|
let cpt = ref 0 in
|
79
|
fun e ze ->
|
80
|
incr cpt;
|
81
|
let uid = !cpt in
|
82
|
expr_hash := (e, uid)::!expr_hash;
|
83
|
Hashtbl.add e_hash uid ze;
|
84
|
Hashtbl.add ze_hash ze uid
|
85
|
|
86
|
|
87
|
let expr_to_z3_expr, zexpr_to_expr =
|
88
|
(* List to store converted expression. *)
|
89
|
(* let hash = ref [] in
|
90
|
* let comp_expr e (e', _) = Corelang.is_eq_expr e e' in
|
91
|
* let comp_zexpr ze (_, ze') = Z3.Expr.equal ze ze' in *)
|
92
|
|
93
|
let rec e2ze e =
|
94
|
if mem_expr e then (
|
95
|
get_zexpr e
|
96
|
)
|
97
|
else (
|
98
|
let res =
|
99
|
match e.expr_desc with
|
100
|
| Expr_const c ->
|
101
|
let z3e = Zustre_common.horn_const_to_expr c in
|
102
|
add_expr e z3e;
|
103
|
z3e
|
104
|
| Expr_ident id -> (
|
105
|
if is_const id then (
|
106
|
let c = get_const id in
|
107
|
let z3e = Zustre_common.horn_const_to_expr c in
|
108
|
add_expr e z3e;
|
109
|
z3e
|
110
|
)
|
111
|
else if is_enum_const id then (
|
112
|
let z3e = Zustre_common.horn_tag_to_expr id in
|
113
|
add_expr e z3e;
|
114
|
z3e
|
115
|
)
|
116
|
else (
|
117
|
let fdecl_id = Zustre_common.get_fdecl id in
|
118
|
let z3e = Z3.Expr.mk_const_f !ctx fdecl_id in
|
119
|
add_expr e z3e;
|
120
|
z3e
|
121
|
)
|
122
|
)
|
123
|
| Expr_appl (id,args, None) (* no reset *) ->
|
124
|
let z3e = Zustre_common.horn_basic_app id e2ze (Corelang.expr_list_of_expr args) in
|
125
|
add_expr e z3e;
|
126
|
z3e
|
127
|
| Expr_tuple [e] ->
|
128
|
let z3e = e2ze e in
|
129
|
add_expr e z3e;
|
130
|
z3e
|
131
|
| _ -> ( match e.expr_desc with Expr_tuple _ -> Format.eprintf "tuple e2ze(%a)@.@?" Printers.pp_expr e
|
132
|
| _ -> Format.eprintf "e2ze(%a)@.@?" Printers.pp_expr e)
|
133
|
; assert false
|
134
|
in
|
135
|
res
|
136
|
)
|
137
|
in
|
138
|
let rec ze2e ze =
|
139
|
let ze_name ze =
|
140
|
let fd = Z3.Expr.get_func_decl ze in
|
141
|
Z3.Symbol.to_string (Z3.FuncDecl.get_name fd)
|
142
|
in
|
143
|
if mem_zexpr ze then
|
144
|
None, Some (get_expr ze)
|
145
|
else
|
146
|
let open Corelang in
|
147
|
let fd = Z3.Expr.get_func_decl ze in
|
148
|
let zel = Z3.Expr.get_args ze in
|
149
|
match Z3.Symbol.to_string (Z3.FuncDecl.get_name fd), zel with
|
150
|
(* | var, [] -> (* should be in env *) get_e *)
|
151
|
|
152
|
(* Extracting IsInit status *)
|
153
|
| "not", [ze] when ze_name ze = is_init_name ->
|
154
|
Some false, None
|
155
|
| name, [] when name = is_init_name -> Some true, None
|
156
|
(* Other constructs are converted to a lustre expression *)
|
157
|
| op, _ -> (
|
158
|
|
159
|
|
160
|
if Z3.Expr.is_numeral ze then
|
161
|
let e =
|
162
|
if Z3.Arithmetic.is_real ze then
|
163
|
let num = Num.num_of_ratio (Z3.Arithmetic.Real.get_ratio ze) in
|
164
|
let s = Z3.Arithmetic.Real.numeral_to_string ze in
|
165
|
mkexpr
|
166
|
Location.dummy_loc
|
167
|
(Expr_const
|
168
|
(Const_real
|
169
|
(Real.create_num num s)))
|
170
|
else if Z3.Arithmetic.is_int ze then
|
171
|
mkexpr
|
172
|
Location.dummy_loc
|
173
|
(Expr_const
|
174
|
(Const_int
|
175
|
(Big_int.int_of_big_int (Z3.Arithmetic.Integer.get_big_int ze))))
|
176
|
else if Z3.Expr.is_const ze then
|
177
|
match Z3.Expr.to_string ze with
|
178
|
| "true" -> mkexpr Location.dummy_loc
|
179
|
(Expr_const (Const_tag (tag_true)))
|
180
|
| "false" ->
|
181
|
mkexpr Location.dummy_loc
|
182
|
(Expr_const (Const_tag (tag_false)))
|
183
|
| _ -> assert false
|
184
|
else
|
185
|
(
|
186
|
Format.eprintf "Const err: %s %b@." (Z3.Expr.to_string ze) (Z3.Expr.is_const ze);
|
187
|
assert false (* a numeral but no int nor real *)
|
188
|
)
|
189
|
in
|
190
|
None, Some e
|
191
|
else
|
192
|
match op with
|
193
|
| "not" | "=" | "-" | "*" | "/"
|
194
|
| ">=" | "<=" | ">" | "<"
|
195
|
->
|
196
|
let args = List.map (fun ze -> Utils.desome (snd (ze2e ze))) zel in
|
197
|
None, Some (mkpredef_call Location.dummy_loc op args)
|
198
|
| "+" -> ( (* Special treatment of + for 2+ args *)
|
199
|
let args = List.map (fun ze -> Utils.desome (snd (ze2e ze))) zel in
|
200
|
let e = match args with
|
201
|
[] -> assert false
|
202
|
| [hd] -> hd
|
203
|
| e1::e2::tl ->
|
204
|
let first_binary_and = mkpredef_call Location.dummy_loc op [e1;e2] in
|
205
|
if tl = [] then first_binary_and else
|
206
|
List.fold_left (fun e e_new ->
|
207
|
mkpredef_call Location.dummy_loc op [e;e_new]
|
208
|
) first_binary_and tl
|
209
|
|
210
|
in
|
211
|
None, Some e
|
212
|
)
|
213
|
| "and" | "or" -> (
|
214
|
(* Special case since it can contain is_init pred *)
|
215
|
let args = List.map (fun ze -> ze2e ze) zel in
|
216
|
let op = if op = "and" then "&&" else if op = "or" then "||" else assert false in
|
217
|
match args with
|
218
|
| [] -> assert false
|
219
|
| [hd] -> hd
|
220
|
| hd::tl ->
|
221
|
List.fold_left
|
222
|
(fun (is_init_opt1, expr_opt1) (is_init_opt2, expr_opt2) ->
|
223
|
(match is_init_opt1, is_init_opt2 with
|
224
|
None, x | x, None -> x
|
225
|
| Some _, Some _ -> assert false),
|
226
|
(match expr_opt1, expr_opt2 with
|
227
|
| None, x | x, None -> x
|
228
|
| Some e1, Some e2 ->
|
229
|
Some (mkpredef_call Location.dummy_loc op [e1; e2])
|
230
|
))
|
231
|
hd tl
|
232
|
)
|
233
|
| op ->
|
234
|
let args = List.map (fun ze -> (snd (ze2e ze))) zel in
|
235
|
Format.eprintf "deal with op %s (nb args: %i). Expr is %s@." op (List.length args) (Z3.Expr.to_string ze); assert false
|
236
|
)
|
237
|
in
|
238
|
(fun e -> e2ze e), (fun ze -> ze2e ze)
|
239
|
|
240
|
|
241
|
let zexpr_to_guard_list ze =
|
242
|
let init_opt, expr_opt = zexpr_to_expr ze in
|
243
|
(match init_opt with
|
244
|
| None -> []
|
245
|
|Some b -> [IsInit, b]
|
246
|
) @ (match expr_opt with
|
247
|
| None -> []
|
248
|
| Some e -> [Expr e, true]
|
249
|
)
|
250
|
|
251
|
|
252
|
let simplify_neg_guard l =
|
253
|
List.map (fun (g,posneg) ->
|
254
|
match g with
|
255
|
| IsInit -> g, posneg
|
256
|
| Expr g ->
|
257
|
if posneg then
|
258
|
Expr (Corelang.push_negations g),
|
259
|
true
|
260
|
else
|
261
|
(* Pushing the negation in the expression *)
|
262
|
Expr(Corelang.push_negations ~neg:true g),
|
263
|
true
|
264
|
) l
|
265
|
|
266
|
(* TODO:
|
267
|
individuellement demander si g1 => g2. Si c'est le cas, on peut ne garder que g1 dans la liste
|
268
|
*)
|
269
|
(*****************************************************************)
|
270
|
(* Checking sat(isfiability) of an expression and simplifying it *)
|
271
|
(* All (free) variables have to be declared in the Z3 context *)
|
272
|
(*****************************************************************)
|
273
|
(*
|
274
|
let goal_simplify zl =
|
275
|
let goal = Z3.Goal.mk_goal !ctx false false false in
|
276
|
Z3.Goal.add goal zl;
|
277
|
let goal' = Z3.Goal.simplify goal None in
|
278
|
(* Format.eprintf "Goal before: %s@.Goal after : %s@.Sat? %s@."
|
279
|
* (Z3.Goal.to_string goal)
|
280
|
* (Z3.Goal.to_string goal')
|
281
|
* (Z3.Solver.string_of_status status_res)
|
282
|
* ; *)
|
283
|
let ze = Z3.Goal.as_expr goal' in
|
284
|
(* Format.eprintf "as an expr: %s@." (Z3.Expr.to_string ze); *)
|
285
|
zexpr_to_guard_list ze
|
286
|
*)
|
287
|
|
288
|
let implies =
|
289
|
let ze_implies_hash : ((Utils.tag * Utils.tag), bool) Hashtbl.t = Hashtbl.create 13 in
|
290
|
fun ze1 ze2 ->
|
291
|
let ze1_uid = get_zid ze1 in
|
292
|
let ze2_uid = get_zid ze2 in
|
293
|
if Hashtbl.mem ze_implies_hash (ze1_uid, ze2_uid) then
|
294
|
Hashtbl.find ze_implies_hash (ze1_uid, ze2_uid)
|
295
|
else
|
296
|
begin
|
297
|
if !seal_debug then (
|
298
|
report ~level:6 (fun fmt -> Format.fprintf fmt "Checking implication: %s => %s?@ "
|
299
|
(Z3.Expr.to_string ze1) (Z3.Expr.to_string ze2)
|
300
|
));
|
301
|
let solver = Z3.Solver.mk_simple_solver !ctx in
|
302
|
let tgt = Z3.Boolean.mk_not !ctx (Z3.Boolean.mk_implies !ctx ze1 ze2) in
|
303
|
let res =
|
304
|
try
|
305
|
let status_res = Z3.Solver.check solver [tgt] in
|
306
|
match status_res with
|
307
|
| Z3.Solver.UNSATISFIABLE -> if !seal_debug then
|
308
|
report ~level:6 (fun fmt -> Format.fprintf fmt "Valid!@ ");
|
309
|
true
|
310
|
| _ -> if !seal_debug then report ~level:6 (fun fmt -> Format.fprintf fmt "not proved valid@ ");
|
311
|
false
|
312
|
with Zustre_common.UnknownFunction(id, msg) -> (
|
313
|
report ~level:1 msg;
|
314
|
false
|
315
|
)
|
316
|
in
|
317
|
Hashtbl.add ze_implies_hash (ze1_uid,ze2_uid) res ;
|
318
|
res
|
319
|
end
|
320
|
|
321
|
let rec simplify zl =
|
322
|
match zl with
|
323
|
| [] | [_] -> zl
|
324
|
| hd::tl -> (
|
325
|
(* Forall e in tl, checking whether hd => e or e => hd, to keep hd
|
326
|
in the first case and e in the second one *)
|
327
|
let tl = simplify tl in
|
328
|
let keep_hd, tl =
|
329
|
List.fold_left (fun (keep_hd, accu) e ->
|
330
|
if implies hd e then
|
331
|
true, accu (* throwing away e *)
|
332
|
else if implies e hd then
|
333
|
false, e::accu (* throwing away hd *)
|
334
|
else
|
335
|
keep_hd, e::accu (* keeping both *)
|
336
|
) (true,[]) tl
|
337
|
in
|
338
|
(* Format.eprintf "keep_hd?%b hd=%s, tl=[%a]@."
|
339
|
* keep_hd
|
340
|
* (Z3.Expr.to_string hd)
|
341
|
* (Utils.fprintf_list ~sep:"; " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) tl
|
342
|
* ; *)
|
343
|
if keep_hd then
|
344
|
hd::tl
|
345
|
else
|
346
|
tl
|
347
|
)
|
348
|
|
349
|
let check_sat ?(just_check=false) (l: elem_boolexpr guard) : bool * (elem_boolexpr guard) =
|
350
|
(* Syntactic simplification *)
|
351
|
if false then
|
352
|
Format.eprintf "Before simplify: %a@." (pp_guard_list pp_elem) l;
|
353
|
let l = simplify_neg_guard l in
|
354
|
if false then (
|
355
|
Format.eprintf "After simplify: %a@." (pp_guard_list pp_elem) l;
|
356
|
Format.eprintf "@[<v 2>Z3 check sat: [%a]@ " (pp_guard_list pp_elem)l;
|
357
|
);
|
358
|
|
359
|
let solver = Z3.Solver.mk_simple_solver !ctx in
|
360
|
try (
|
361
|
let zl =
|
362
|
List.map (fun (e, posneg) ->
|
363
|
let ze =
|
364
|
match e with
|
365
|
| IsInit -> is_init_z3e
|
366
|
| Expr e -> expr_to_z3_expr e
|
367
|
in
|
368
|
if posneg then
|
369
|
ze
|
370
|
else
|
371
|
neg_ze ze
|
372
|
) l
|
373
|
in
|
374
|
if false then Format.eprintf "Z3 exprs1: [%a]@ " (fprintf_list ~sep:",@ " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) zl;
|
375
|
let zl = simplify zl in
|
376
|
if false then Format.eprintf "Z3 exprs2: [%a]@ " (fprintf_list ~sep:",@ " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) zl;
|
377
|
(* Format.eprintf "Calling Z3@."; *)
|
378
|
let status_res = Z3.Solver.check solver zl in
|
379
|
(* Format.eprintf "Z3 done@."; *)
|
380
|
if false then Format.eprintf "Z3 status: %s@ @]@. " (Z3.Solver.string_of_status status_res);
|
381
|
match status_res with
|
382
|
| Z3.Solver.UNSATISFIABLE -> false, []
|
383
|
| _ -> (
|
384
|
if false && just_check then
|
385
|
true, l
|
386
|
else
|
387
|
(* TODO: may be reactivate but it may create new expressions *)
|
388
|
(* let l = goal_simplify zl in *)
|
389
|
let l = List.fold_left
|
390
|
(fun accu ze -> accu @ (zexpr_to_guard_list ze))
|
391
|
[]
|
392
|
zl
|
393
|
in
|
394
|
(* Format.eprintf "@.@[<v 2>Check_Sat:@ before: %a@ after:
|
395
|
%a@. Goal precise? %b/%b@]@.@. " * pp_guard_list l
|
396
|
pp_guard_list l' * (Z3.Goal.is_precise goal) *
|
397
|
(Z3.Goal.is_precise goal'); *)
|
398
|
|
399
|
|
400
|
true, l
|
401
|
|
402
|
|
403
|
)
|
404
|
|
405
|
)
|
406
|
with Zustre_common.UnknownFunction(id, msg) -> (
|
407
|
report ~level:1 msg;
|
408
|
true, l (* keeping everything. *)
|
409
|
)
|
410
|
|
411
|
|
412
|
|
413
|
|
414
|
(**************************************************************)
|
415
|
|
416
|
|
417
|
let clean_sys sys =
|
418
|
List.fold_left (fun accu (guards, updates) ->
|
419
|
let sat, guards' = check_sat (List.map (fun (g, pn) -> Expr g, pn) guards) in
|
420
|
(*Format.eprintf "Guard: %a@.Guard cleaned: %a@.Sat? %b@."
|
421
|
(fprintf_list ~sep:"@ " (pp_guard_expr Printers.pp_expr)) guards
|
422
|
(fprintf_list ~sep:"@ " (pp_guard_expr Printers.pp_expr)) guards'
|
423
|
sat
|
424
|
|
425
|
;*)
|
426
|
if sat then
|
427
|
(List.map (fun (e, b) -> (deelem e, b)) guards', updates)::accu
|
428
|
else
|
429
|
accu
|
430
|
)
|
431
|
[] sys
|
432
|
|
433
|
(* Most costly function: has the be efficiently implemented. All
|
434
|
registered guards are initially produced by the call to
|
435
|
combine_guards. We csan normalize the guards to ease the
|
436
|
comparisons.
|
437
|
|
438
|
We assume that gl1 and gl2 are already both satisfiable and in a
|
439
|
kind of reduced form. Let lshort and llong be the short and long
|
440
|
list of gl1, gl2. We check whether each element elong of llong is
|
441
|
satisfiable with lshort. If no, stop. If yes, we search to reduce
|
442
|
the list. If elong => eshort_i, we can remove eshort_i from
|
443
|
lshort. we can continue with this lshort shortened, lshort'; it is
|
444
|
not necessary to add yet elong in lshort' since we already know
|
445
|
rthat elong is somehow reduced with respect to other elements of
|
446
|
llong. If eshort_i => elong, then we keep ehosrt_i in lshort and do
|
447
|
not store elong.
|
448
|
|
449
|
After iterating through llong, we have a shortened version of
|
450
|
lshort + some elements of llong that have to be remembered. We add
|
451
|
them to this new consolidated list.
|
452
|
|
453
|
*)
|
454
|
|
455
|
(* combine_guards ~fresh:Some(e,b) gl1 gl2 returns ok, gl with ok=true
|
456
|
when (e=b) ang gl1 and gl2 is satisfiable and gl is a consilidated
|
457
|
version of it. *)
|
458
|
let combine_guards ?(fresh=None) gl1 gl2 =
|
459
|
(* Filtering out trivial cases. More semantics ones would have to be
|
460
|
addressed later *)
|
461
|
let check_sat e = (* temp function before we clean the original one *)
|
462
|
(* Format.eprintf "CheckSAT? %a@." (pp_guard_list pp_elem) e; *)
|
463
|
let ok, _ = check_sat e in
|
464
|
(* Format.eprintf "CheckSAT DONE@."; *)
|
465
|
ok
|
466
|
in
|
467
|
let implies (e1,pn1) (e2,pn2) =
|
468
|
let e2z e pn =
|
469
|
match e with
|
470
|
| IsInit -> if pn then is_init_z3e else neg_ze is_init_z3e
|
471
|
| Expr e -> expr_to_z3_expr (if pn then e else (Corelang.push_negations ~neg:true e))
|
472
|
in
|
473
|
implies (e2z e1 pn1) (e2z e2 pn2)
|
474
|
in
|
475
|
let lshort, llong =
|
476
|
if List.length gl1 > List.length gl2 then gl2, gl1 else gl1, gl2
|
477
|
in
|
478
|
let merge long short =
|
479
|
let short, long_sel, ok =
|
480
|
List.fold_left (fun (short,long_sel, ok) long_e ->
|
481
|
if not ok then
|
482
|
[],[], false (* Propagating unsat case *)
|
483
|
else if check_sat (long_e::short) then
|
484
|
let short, keep_long_e =
|
485
|
List.fold_left (fun (accu_short, keep_long_e) eshort_i ->
|
486
|
if not keep_long_e then (* shorten the algo *)
|
487
|
eshort_i :: accu_short, false
|
488
|
else (* keep_long_e = true in the following *)
|
489
|
if implies eshort_i long_e then
|
490
|
(* First case is trying to remove long_e!
|
491
|
|
492
|
Since short is already normalized, we can remove
|
493
|
long_e. If later long_e is stronger than another
|
494
|
element of short, then necessarily eshort_i =>
|
495
|
long_e ->
|
496
|
that_other_element_of_short. Contradiction. *)
|
497
|
eshort_i::accu_short, false
|
498
|
else if implies long_e eshort_i then
|
499
|
(* removing eshort_i, keeping long_e. *)
|
500
|
accu_short, true
|
501
|
else (* Not comparable, keeping both *)
|
502
|
eshort_i::accu_short, true
|
503
|
)
|
504
|
([],true) (* Initially we assume that we will keep long_e *)
|
505
|
short
|
506
|
in
|
507
|
if keep_long_e then
|
508
|
short, long_e::long_sel, true
|
509
|
else
|
510
|
short, long_sel, true
|
511
|
else
|
512
|
[],[],false
|
513
|
) (short, [], true) long
|
514
|
in
|
515
|
ok, long_sel@short
|
516
|
in
|
517
|
let ok, l = match fresh with
|
518
|
| None -> true, []
|
519
|
| Some g -> merge [g] []
|
520
|
in
|
521
|
if not ok then
|
522
|
false, []
|
523
|
else
|
524
|
let ok, lshort = merge lshort l in
|
525
|
if not ok then
|
526
|
false, []
|
527
|
else
|
528
|
merge llong lshort
|
529
|
|
530
|
|
531
|
(* Encode "If gel1=posneg then gel2":
|
532
|
- Compute the combination of guarded exprs in gel1 and gel2:
|
533
|
- Each guarded_expr in gel1 is transformed as a guard: the
|
534
|
expression is associated to posneg.
|
535
|
- Existing guards in gel2 are concatenated to that list of guards
|
536
|
- We keep expr in the ge of gel2 as the legitimate expression
|
537
|
*)
|
538
|
let concatenate_ge gel1 posneg gel2 =
|
539
|
let l, all_invalid =
|
540
|
List.fold_left (
|
541
|
fun (accu, all_invalid) (g2,e2) ->
|
542
|
List.fold_left (
|
543
|
fun (accu, all_invalid) (g1,e1) ->
|
544
|
(* Format.eprintf "@[<v 2>Combining guards: (%a=%b) AND [%a] AND [%a]@ "
|
545
|
* pp_elem e1
|
546
|
* posneg
|
547
|
* pp_guard_list g1
|
548
|
* pp_guard_list g2; *)
|
549
|
|
550
|
let ok, gl = combine_guards ~fresh:(Some(e1,posneg)) g1 g2 in
|
551
|
(* Format.eprintf "@]@ Result is [%a]@ "
|
552
|
* pp_guard_list gl; *)
|
553
|
|
554
|
if ok then
|
555
|
(gl, e2)::accu, false
|
556
|
else (
|
557
|
accu, all_invalid
|
558
|
)
|
559
|
) (accu, all_invalid) gel1
|
560
|
) ([], true) gel2
|
561
|
in
|
562
|
not all_invalid, l
|
563
|
|
564
|
(* Transform the guard expressions ge = [gl1, e1; gl2, e2;...] as
|
565
|
[gl1, e1=id; gl2, e2=id; ...] *)
|
566
|
let mk_ge_eq_id ge id =
|
567
|
List.map
|
568
|
(fun (gl, g_e) ->
|
569
|
gl,
|
570
|
if id = "true" then
|
571
|
g_e
|
572
|
else
|
573
|
match g_e with
|
574
|
| Expr g_e ->
|
575
|
if id = "false" then
|
576
|
Expr (Corelang.push_negations ~neg:true g_e)
|
577
|
else
|
578
|
let loc = g_e.expr_loc in
|
579
|
Expr(Corelang.mk_eq loc
|
580
|
g_e
|
581
|
(Corelang.expr_of_ident id loc))
|
582
|
| _ -> assert false
|
583
|
) ge
|
584
|
|
585
|
(* Rewrite the expression expr, replacing any occurence of a variable
|
586
|
by its definition.
|
587
|
*)
|
588
|
let rec rewrite defs expr : elem_guarded_expr list =
|
589
|
let rewrite = rewrite defs in
|
590
|
let res =
|
591
|
match expr.expr_desc with
|
592
|
| Expr_appl (id, args, None) ->
|
593
|
let args = rewrite args in
|
594
|
List.map (fun (guards, e) ->
|
595
|
let new_e =
|
596
|
Corelang.mkexpr
|
597
|
expr.expr_loc
|
598
|
(Expr_appl(id, deelem e, None))
|
599
|
in
|
600
|
guards,
|
601
|
Expr (Corelang.partial_eval new_e)
|
602
|
) args
|
603
|
| Expr_const _ -> [[], Expr expr]
|
604
|
| Expr_ident id ->
|
605
|
if Hashtbl.mem defs id then
|
606
|
Hashtbl.find defs id
|
607
|
else
|
608
|
(* id should be an input *)
|
609
|
[[], Expr expr]
|
610
|
| Expr_ite (g, e1, e2) ->
|
611
|
let g = rewrite g and
|
612
|
e1 = rewrite e1 and
|
613
|
e2 = rewrite e2 in
|
614
|
let ok_then, g_then = concatenate_ge g true e1 in
|
615
|
let ok_else, g_else = concatenate_ge g false e2 in
|
616
|
(if ok_then then g_then else [])@
|
617
|
(if ok_else then g_else else [])
|
618
|
| Expr_merge (g_id, branches) ->
|
619
|
if Hashtbl.mem defs g_id then
|
620
|
let g = Hashtbl.find defs g_id in
|
621
|
(* Format.eprintf "Expr_merge %s = %a@." g_id (pp_mdefs pp_elem) g ; *)
|
622
|
List.fold_left (fun accu (id, e) ->
|
623
|
let g = mk_ge_eq_id g id in
|
624
|
let e = rewrite e in
|
625
|
let ok, g_eq_id = concatenate_ge g true e in
|
626
|
if ok then g_eq_id@accu else accu
|
627
|
) [] branches
|
628
|
else
|
629
|
assert false (* g should be defined already *)
|
630
|
| Expr_when (e, id, l) ->
|
631
|
let e = rewrite e in
|
632
|
let id_def = Hashtbl.find defs id in
|
633
|
let clock = mk_ge_eq_id id_def l in
|
634
|
let ok, new_ge = concatenate_ge clock true e in
|
635
|
if ok then new_ge else []
|
636
|
| Expr_arrow _ -> [[], IsInit] (* At this point the only arrow should be true -> false *)
|
637
|
| Expr_tuple el ->
|
638
|
(* Each expr is associated to its flatten guarded expr list *)
|
639
|
let gell = List.map rewrite el in
|
640
|
(* Computing all combinations: we obtain a list of guarded tuple *)
|
641
|
let rec aux gell : (elem_boolexpr guard * expr list) list =
|
642
|
match gell with
|
643
|
| [] -> assert false (* Not happening *)
|
644
|
| [gel] -> List.map (fun (g,e) -> g, [deelem e]) gel
|
645
|
| gel::getl ->
|
646
|
let getl = aux getl in
|
647
|
List.fold_left (
|
648
|
fun accu (g,e) ->
|
649
|
List.fold_left (
|
650
|
fun accu (gl, minituple) ->
|
651
|
let is_compat, guard_comb = combine_guards g gl in
|
652
|
if is_compat then
|
653
|
let new_gt : elem_boolexpr guard * expr list =
|
654
|
(guard_comb, (deelem e)::minituple) in
|
655
|
new_gt::accu
|
656
|
else
|
657
|
accu
|
658
|
|
659
|
) accu getl
|
660
|
) [] gel
|
661
|
in
|
662
|
let gtuples = aux gell in
|
663
|
(* Rebuilding the valid type: guarded expr list (with tuple exprs) *)
|
664
|
List.map
|
665
|
(fun (g,tuple) -> g, Expr (Corelang.mkexpr expr.expr_loc (Expr_tuple tuple)))
|
666
|
gtuples
|
667
|
| Expr_fby _
|
668
|
| Expr_appl _
|
669
|
(* Should be removed by normalization and inlining *)
|
670
|
-> Format.eprintf "Pb expr: %a@.@?" Printers.pp_expr expr; assert false
|
671
|
| Expr_array _ | Expr_access _ | Expr_power _
|
672
|
(* Arrays not handled here yet *)
|
673
|
-> assert false
|
674
|
| Expr_pre _ -> (* Not rewriting mem assign *)
|
675
|
assert false
|
676
|
in
|
677
|
(* Format.eprintf "Rewriting %a as [@[<v 0>%a@]]@ "
|
678
|
* Printers.pp_expr expr
|
679
|
* (Utils.fprintf_list ~sep:"@ "
|
680
|
* (pp_guard_expr pp_elem)) res; *)
|
681
|
res
|
682
|
|
683
|
and add_def defs vid expr =
|
684
|
(* Format.eprintf "Add_def: %s = %a@."
|
685
|
* vid
|
686
|
* Printers.pp_expr expr; *)
|
687
|
let vid_defs = rewrite defs expr in
|
688
|
(* Format.eprintf "-> @[<v 0>%a@]@."
|
689
|
* (Utils.fprintf_list ~sep:"@ "
|
690
|
* (pp_guard_expr pp_elem)) vid_defs; *)
|
691
|
report ~level:6 (fun fmt -> Format.fprintf fmt "Add_def: %s = %a@. -> @[<v 0>%a@]@."
|
692
|
vid
|
693
|
Printers.pp_expr expr
|
694
|
|
695
|
(
|
696
|
(Utils.fprintf_list ~sep:"@ "
|
697
|
(pp_guard_expr pp_elem))) vid_defs);
|
698
|
Hashtbl.add defs vid vid_defs;
|
699
|
vid_defs
|
700
|
|
701
|
(* Takes a list of guarded exprs (ge) and a guard
|
702
|
returns the same list of ge splited into the ones where the guard is true and the ones where it is false. In both lists the associated ge do not mention that guard anymore.
|
703
|
|
704
|
When a given ge doesn't mention positively or negatively such guards, it is duplicated in both lists *)
|
705
|
let split_mdefs elem (mdefs: elem_guarded_expr list) =
|
706
|
List.fold_left (
|
707
|
fun (selected, left_out)
|
708
|
((guards, expr) as ge) ->
|
709
|
(* select the element of guards that match the argument elem *)
|
710
|
let sel, others_guards = List.partition (select_elem elem) guards in
|
711
|
match sel with
|
712
|
(* we extract the element from the list and add it to the
|
713
|
appropriate list *)
|
714
|
| [_, sel_status] ->
|
715
|
if sel_status then
|
716
|
(others_guards,expr)::selected, left_out
|
717
|
else selected, (others_guards,expr)::left_out
|
718
|
| [] -> (* no such guard exists, we have to duplicate the
|
719
|
guard_expr in both lists *)
|
720
|
ge::selected, ge::left_out
|
721
|
| _ -> (
|
722
|
Format.eprintf "@.Spliting list on elem %a.@.List:%a@."
|
723
|
pp_elem elem
|
724
|
(pp_mdefs pp_elem) mdefs;
|
725
|
assert false (* more then one element selected. Should
|
726
|
not happen , or trival dead code like if
|
727
|
x then if not x then dead code *)
|
728
|
)
|
729
|
) ([],[]) mdefs
|
730
|
|
731
|
let split_mem_defs
|
732
|
(elem: element)
|
733
|
(mem_defs: (ident * elem_guarded_expr list) list)
|
734
|
:
|
735
|
((ident * elem_guarded_expr mdef_t) list) * ((ident * elem_guarded_expr mdef_t) list)
|
736
|
|
737
|
=
|
738
|
List.fold_right (fun (m,mdefs)
|
739
|
(accu_pos, accu_neg) ->
|
740
|
let pos, neg =
|
741
|
split_mdefs elem mdefs
|
742
|
in
|
743
|
(m, pos)::accu_pos,
|
744
|
(m, neg)::accu_neg
|
745
|
) mem_defs ([],[])
|
746
|
|
747
|
|
748
|
(* Split a list of mem_defs into init and step lists of guarded
|
749
|
expressions per memory. *)
|
750
|
let split_init mem_defs =
|
751
|
split_mem_defs IsInit mem_defs
|
752
|
|
753
|
(* Previous version of the function: way too costly
|
754
|
let pick_guard mem_defs : expr option =
|
755
|
let gel = List.flatten (List.map snd mem_defs) in
|
756
|
let gl = List.flatten (List.map fst gel) in
|
757
|
let all_guards =
|
758
|
List.map (
|
759
|
(* selecting guards and getting rid of boolean *)
|
760
|
fun (e,b) ->
|
761
|
match e with
|
762
|
| Expr e -> e
|
763
|
| _ -> assert false
|
764
|
(* should have been filtered out
|
765
|
yet *)
|
766
|
) gl
|
767
|
in
|
768
|
(* TODO , one could sort by occurence and provided the most common
|
769
|
one *)
|
770
|
try
|
771
|
Some (List.hd all_guards)
|
772
|
with _ -> None
|
773
|
*)
|
774
|
|
775
|
(* Returning the first non empty guard expression *)
|
776
|
let rec pick_guard mem_defs : expr option =
|
777
|
match mem_defs with
|
778
|
| [] -> None
|
779
|
| (_, gel)::tl -> (
|
780
|
let found =
|
781
|
List.fold_left (fun found (g,_) ->
|
782
|
if found = None then
|
783
|
match g with
|
784
|
| [] -> None
|
785
|
| (Expr e, _)::_ -> Some e
|
786
|
| (IsInit, _)::_ -> assert false (* should be removed already *)
|
787
|
else
|
788
|
found
|
789
|
) None gel
|
790
|
in
|
791
|
if found = None then pick_guard tl else found
|
792
|
)
|
793
|
|
794
|
|
795
|
(* Transform a list of variable * guarded exprs into a list of guarded pairs (variable, expressions)
|
796
|
*)
|
797
|
let rec build_switch_sys
|
798
|
(mem_defs : (Utils.ident * elem_guarded_expr list) list )
|
799
|
prefix
|
800
|
:
|
801
|
((expr * bool) list * (ident * expr) list ) list =
|
802
|
if !seal_debug then
|
803
|
report ~level:4 (fun fmt -> Format.fprintf fmt "@[<v 2>Build_switch with@ %a@]@."
|
804
|
pp_all_defs
|
805
|
mem_defs);
|
806
|
(* if all mem_defs have empty guards, we are done, return prefix,
|
807
|
mem_defs expr.
|
808
|
|
809
|
otherwise pick a guard in one of the mem, eg (g, b) then for each
|
810
|
other mem, one need to select the same guard g with the same
|
811
|
status b, *)
|
812
|
let res =
|
813
|
if List.for_all (fun (m,mdefs) ->
|
814
|
(* All defs are unguarded *)
|
815
|
match mdefs with
|
816
|
| [[], _] -> true (* Regular unguarded expression *)
|
817
|
| [] -> true (* A unbalanced definition of the memory. Here
|
818
|
we have m_{k+1} -> m_k *)
|
819
|
| _ -> false
|
820
|
) mem_defs
|
821
|
then
|
822
|
[prefix ,
|
823
|
List.map (fun (m,gel) ->
|
824
|
match gel with
|
825
|
| [_,e] ->
|
826
|
let e =
|
827
|
match e with
|
828
|
| Expr e -> e
|
829
|
| _ -> assert false (* No IsInit expression *)
|
830
|
in
|
831
|
m,e
|
832
|
| [] -> m, Corelang.expr_of_ident m Location.dummy_loc
|
833
|
| _ -> assert false
|
834
|
) mem_defs]
|
835
|
else
|
836
|
(* Picking a guard *)
|
837
|
let elem_opt : expr option = pick_guard mem_defs in
|
838
|
match elem_opt with
|
839
|
None -> (
|
840
|
Format.eprintf "Issues picking guard in mem_defs: %a@." pp_all_defs mem_defs;
|
841
|
assert false (* Otherwise the first case should have matched *)
|
842
|
)
|
843
|
| Some elem -> (
|
844
|
report ~level:4 (fun fmt -> Format.fprintf fmt "selecting guard %a@." Printers.pp_expr elem);
|
845
|
let pos, neg =
|
846
|
split_mem_defs
|
847
|
(Expr elem)
|
848
|
mem_defs
|
849
|
in
|
850
|
report ~level:4 (fun fmt -> Format.fprintf fmt "split by guard done@.");
|
851
|
|
852
|
(* Format.eprintf "Selected item %a in@.%a@.POS=%a@.NEG=%a@."
|
853
|
Printers.pp_expr elem
|
854
|
pp_all_defs mem_defs
|
855
|
pp_all_defs pos
|
856
|
pp_all_defs neg
|
857
|
;
|
858
|
*)
|
859
|
(* Special cases to avoid useless computations: true, false conditions *)
|
860
|
match elem.expr_desc with
|
861
|
(*| Expr_ident "true" -> build_switch_sys pos prefix *)
|
862
|
| Expr_const (Const_tag tag) when tag = tag_true
|
863
|
-> build_switch_sys pos prefix
|
864
|
(*| Expr_ident "false" -> build_switch_sys neg prefix *)
|
865
|
| Expr_const (Const_tag tag) when tag = tag_false
|
866
|
-> build_switch_sys neg prefix
|
867
|
| _ -> (* Regular case *)
|
868
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Building both children branches@.");
|
869
|
(* let _ = (
|
870
|
* Format.eprintf "Expr is %a@." Printers.pp_expr elem;
|
871
|
* match elem.expr_desc with
|
872
|
* | Expr_const _ -> Format.eprintf "a const@."
|
873
|
*
|
874
|
* | Expr_ident _ -> Format.eprintf "an ident@."
|
875
|
* | _ -> Format.eprintf "something else@."
|
876
|
* )
|
877
|
* in *)
|
878
|
let clean l =
|
879
|
let l = List.map (fun (e,b) -> (Expr e), b) l in
|
880
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Checking satisfiability of %a@."
|
881
|
(pp_guard_list pp_elem) l
|
882
|
);
|
883
|
let ok, l = check_sat l in
|
884
|
let l = List.map (fun (e,b) -> deelem e, b) l in
|
885
|
ok, l
|
886
|
in
|
887
|
let pos_prefix = (elem, true)::prefix in
|
888
|
let neg_prefix = (elem, false)::prefix in
|
889
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Cleaning branches ...@.");
|
890
|
let ok_pos, pos_prefix = clean pos_prefix in
|
891
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Cleaning branche pos done@.");
|
892
|
let ok_neg, neg_prefix = clean neg_prefix in
|
893
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Cleaning branche neg done@.");
|
894
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Cleaning branches done@.");
|
895
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Enforcing %a@." Printers.pp_expr elem);
|
896
|
let ok_l = if ok_pos then build_switch_sys pos pos_prefix else [] in
|
897
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Enforcing not(%a)@." Printers.pp_expr elem);
|
898
|
let nok_l = if ok_neg then build_switch_sys neg neg_prefix else [] in
|
899
|
ok_l @ nok_l
|
900
|
)
|
901
|
in
|
902
|
if !seal_debug then (
|
903
|
report ~level:4 (fun fmt ->
|
904
|
Format.fprintf fmt
|
905
|
"@[<v 2>===> @[%t@ @]@]@ @]@ "
|
906
|
(fun fmt -> List.iter (fun (gl,up) ->
|
907
|
Format.fprintf fmt "[@[%a@]] -> (%a)@ "
|
908
|
(pp_guard_list Printers.pp_expr) gl pp_up up) res);
|
909
|
|
910
|
));
|
911
|
res
|
912
|
|
913
|
|
914
|
|
915
|
(* Take a normalized node and extract a list of switches: (cond,
|
916
|
update) meaning "if cond then update" where update shall define all
|
917
|
node memories. Everything as to be expressed over inputs or memories, intermediate variables are removed through inlining *)
|
918
|
let node_as_switched_sys consts (mems:var_decl list) nd =
|
919
|
Z3.Params.update_param_value !ctx "timeout" "10000";
|
920
|
|
921
|
|
922
|
(* rescheduling node: has been scheduled already, no need to protect
|
923
|
the call to schedule_node *)
|
924
|
let nd_report = Scheduling.schedule_node nd in
|
925
|
let schedule = nd_report.Scheduling_type.schedule in
|
926
|
let eqs, auts = Corelang.get_node_eqs nd in
|
927
|
assert (auts = []); (* Automata should be expanded by now *)
|
928
|
let sorted_eqs, unused = Scheduling.sort_equations_from_schedule eqs schedule in
|
929
|
let defs : (ident, elem_guarded_expr list) Hashtbl.t = Hashtbl.create 13 in
|
930
|
let add_def = add_def defs in
|
931
|
|
932
|
let vars = Corelang.get_node_vars nd in
|
933
|
(* Filtering out unused vars *)
|
934
|
let vars = List.filter (fun v -> not (List.mem v.var_id unused)) vars in
|
935
|
(* Registering all locals variables as Z3 predicates. Will be use to
|
936
|
simplify the expansion *)
|
937
|
Zustre_common.decl_sorts ();
|
938
|
let _ =
|
939
|
List.iter (fun v ->
|
940
|
let fdecl = Z3.FuncDecl.mk_func_decl_s
|
941
|
!ctx
|
942
|
v.var_id
|
943
|
[]
|
944
|
(Zustre_common.type_to_sort v.var_type)
|
945
|
in
|
946
|
ignore (Zustre_common.register_fdecl v.var_id fdecl)
|
947
|
) vars
|
948
|
in
|
949
|
let _ =
|
950
|
List.iter (fun c -> Hashtbl.add const_defs c.const_id c.const_value) consts
|
951
|
in
|
952
|
|
953
|
report ~level:4 (fun fmt -> Format.fprintf fmt "Computing definitions for equations@.%a@."
|
954
|
Printers.pp_node_eqs sorted_eqs
|
955
|
);
|
956
|
|
957
|
|
958
|
(* Registering node equations: identifying mem definitions and
|
959
|
storing others in the "defs" hashtbl.
|
960
|
|
961
|
Each assign is stored in a hash tbl as list of guarded
|
962
|
expressions. The memory definition is also "rewritten" as such a
|
963
|
list of guarded assigns. *)
|
964
|
report ~level:1 (fun fmt -> Format.fprintf fmt "registering all definitions in guarded form ...@.");
|
965
|
let mem_defs, output_defs =
|
966
|
List.fold_left (fun (accu_mems, accu_outputs) eq ->
|
967
|
match eq.eq_lhs with
|
968
|
| [vid] ->
|
969
|
(* Only focus on memory definitions *)
|
970
|
if List.exists (fun v -> v.var_id = vid) mems then
|
971
|
(
|
972
|
match eq.eq_rhs.expr_desc with
|
973
|
| Expr_pre def_m ->
|
974
|
report ~level:3 (fun fmt ->
|
975
|
Format.fprintf fmt "Preparing mem %s@." vid);
|
976
|
let def_vid = rewrite defs def_m in
|
977
|
report ~level:4 (fun fmt ->
|
978
|
Format.fprintf fmt "%s = %a@." vid
|
979
|
(
|
980
|
(Utils.fprintf_list ~sep:"@ "
|
981
|
(pp_guard_expr pp_elem)))
|
982
|
def_vid);
|
983
|
(vid, def_vid)::accu_mems, accu_outputs
|
984
|
| _ -> assert false
|
985
|
)
|
986
|
else if List.exists (fun v -> v.var_id = vid) nd.node_outputs then (
|
987
|
report ~level:3 (fun fmt ->
|
988
|
Format.fprintf fmt "Output variable %s@." vid);
|
989
|
let def_vid = add_def vid eq.eq_rhs in
|
990
|
accu_mems, (vid, def_vid)::accu_outputs
|
991
|
|
992
|
)
|
993
|
else
|
994
|
(
|
995
|
report ~level:3 (fun fmt ->
|
996
|
Format.fprintf fmt "Registering variable %s@." vid);
|
997
|
let _ = add_def vid eq.eq_rhs in
|
998
|
accu_mems, accu_outputs
|
999
|
)
|
1000
|
| _ -> assert false (* should have been removed by normalization *)
|
1001
|
) ([], []) sorted_eqs
|
1002
|
in
|
1003
|
report ~level:1 (fun fmt -> Format.fprintf fmt "registering all definitions done@.");
|
1004
|
|
1005
|
|
1006
|
report ~level:2 (fun fmt -> Format.fprintf fmt "Printing out (guarded) memories definitions (may takes time)@.");
|
1007
|
(* Printing memories definitions *)
|
1008
|
report ~level:3
|
1009
|
(fun fmt ->
|
1010
|
Format.fprintf fmt
|
1011
|
"@[<v 0>%a@]@."
|
1012
|
(Utils.fprintf_list ~sep:"@ "
|
1013
|
(fun fmt (m,mdefs) ->
|
1014
|
Format.fprintf fmt
|
1015
|
"%s -> [@[<v 0>%a@] ]@ "
|
1016
|
m
|
1017
|
(Utils.fprintf_list ~sep:"@ "
|
1018
|
(pp_guard_expr pp_elem)) mdefs
|
1019
|
))
|
1020
|
mem_defs);
|
1021
|
(* Format.eprintf "Split init@."; *)
|
1022
|
let init_defs, update_defs =
|
1023
|
split_init mem_defs
|
1024
|
in
|
1025
|
let init_out, update_out =
|
1026
|
split_init output_defs
|
1027
|
in
|
1028
|
report ~level:3
|
1029
|
(fun fmt ->
|
1030
|
Format.fprintf fmt
|
1031
|
"@[<v 0>Init:@ %a@]@."
|
1032
|
(Utils.fprintf_list ~sep:"@ "
|
1033
|
(fun fmt (m,mdefs) ->
|
1034
|
Format.fprintf fmt
|
1035
|
"%s -> @[<v 0>[%a@] ]@ "
|
1036
|
m
|
1037
|
(Utils.fprintf_list ~sep:"@ "
|
1038
|
(pp_guard_expr pp_elem)) mdefs
|
1039
|
))
|
1040
|
init_defs);
|
1041
|
report ~level:3
|
1042
|
(fun fmt ->
|
1043
|
Format.fprintf fmt
|
1044
|
"@[<v 0>Step:@ %a@]@."
|
1045
|
(Utils.fprintf_list ~sep:"@ "
|
1046
|
(fun fmt (m,mdefs) ->
|
1047
|
Format.fprintf fmt
|
1048
|
"%s -> @[<v 0>[%a@] ]@ "
|
1049
|
m
|
1050
|
(Utils.fprintf_list ~sep:"@ "
|
1051
|
(pp_guard_expr pp_elem)) mdefs
|
1052
|
))
|
1053
|
update_defs);
|
1054
|
(* Format.eprintf "Build init@."; *)
|
1055
|
|
1056
|
report ~level:1 (fun fmt -> Format.fprintf fmt "init/step as a switched system ...@.");
|
1057
|
let sw_init=
|
1058
|
build_switch_sys init_defs []
|
1059
|
in
|
1060
|
(* Format.eprintf "Build step@."; *)
|
1061
|
let sw_sys =
|
1062
|
build_switch_sys update_defs []
|
1063
|
in
|
1064
|
report ~level:1 (fun fmt -> Format.fprintf fmt "init/step as a switched system ... done@.");
|
1065
|
|
1066
|
report ~level:1 (fun fmt -> Format.fprintf fmt "output function as a switched system ...@.");
|
1067
|
let init_out =
|
1068
|
build_switch_sys init_out []
|
1069
|
in
|
1070
|
(* report ~level:1 (fun fmt -> Format.fprintf fmt "Build step out@."); *)
|
1071
|
|
1072
|
let update_out =
|
1073
|
build_switch_sys update_out []
|
1074
|
in
|
1075
|
report ~level:1 (fun fmt -> Format.fprintf fmt "output function as a switched system ... done@.");
|
1076
|
|
1077
|
report ~level:1 (fun fmt -> Format.fprintf fmt "removing dead branches and merging remaining ...@.");
|
1078
|
|
1079
|
let sw_init = clean_sys sw_init in
|
1080
|
let sw_sys = clean_sys sw_sys in
|
1081
|
let init_out = clean_sys init_out in
|
1082
|
let update_out = clean_sys update_out in
|
1083
|
report ~level:1 (fun fmt -> Format.fprintf fmt "removing dead branches and merging remaining ... done@.");
|
1084
|
|
1085
|
(* Some additional checks *)
|
1086
|
|
1087
|
if false then
|
1088
|
begin
|
1089
|
Format.eprintf "@.@.CHECKING!!!!!!!!!!!@.";
|
1090
|
Format.eprintf "Any duplicate expression in guards?@.";
|
1091
|
|
1092
|
let sw_sys =
|
1093
|
List.map (fun (gl, up) ->
|
1094
|
let gl = List.sort (fun (e,b) (e',b') ->
|
1095
|
let res = compare e.expr_tag e'.expr_tag in
|
1096
|
if res = 0 then (Format.eprintf "Same exprs?@.%a@.%a@.@."
|
1097
|
Printers.pp_expr e
|
1098
|
Printers.pp_expr e'
|
1099
|
);
|
1100
|
res
|
1101
|
) gl in
|
1102
|
gl, up
|
1103
|
) sw_sys
|
1104
|
in
|
1105
|
Format.eprintf "Another check for duplicates in guard list@.";
|
1106
|
List.iter (fun (gl, _) ->
|
1107
|
let rec aux hd l =
|
1108
|
match l with
|
1109
|
[] -> ()
|
1110
|
| (e,b)::tl -> let others = hd@tl in
|
1111
|
List.iter (fun (e',_) -> if Corelang.is_eq_expr e e' then
|
1112
|
(Format.eprintf "Same exprs?@.%a@.%a@.@."
|
1113
|
Printers.pp_expr e
|
1114
|
Printers.pp_expr e'
|
1115
|
)) others;
|
1116
|
aux ((e,b)::hd) tl
|
1117
|
in
|
1118
|
aux [] gl
|
1119
|
) sw_sys;
|
1120
|
Format.eprintf "Checking duplicates in updates@.";
|
1121
|
let rec check_dup_up accu l =
|
1122
|
match l with
|
1123
|
| [] -> ()
|
1124
|
| ((gl, up) as hd)::tl ->
|
1125
|
let others = accu@tl in
|
1126
|
List.iter (fun (gl',up') -> if up = up' then
|
1127
|
Format.eprintf "Same updates?@.%a@.%a@.%a@.%a@.@."
|
1128
|
|
1129
|
pp_gl_short gl
|
1130
|
pp_up up
|
1131
|
pp_gl_short gl'
|
1132
|
pp_up up'
|
1133
|
|
1134
|
) others;
|
1135
|
|
1136
|
|
1137
|
|
1138
|
check_dup_up (hd::accu) tl
|
1139
|
|
1140
|
in
|
1141
|
check_dup_up [] sw_sys;
|
1142
|
let _ (* sw_sys *) =
|
1143
|
List.sort (fun (gl1, _) (gl2, _) ->
|
1144
|
let glid gl = List.map (fun (e,_) -> e.expr_tag) gl in
|
1145
|
|
1146
|
let res = compare (glid gl1) (glid gl2) in
|
1147
|
if res = 0 then Format.eprintf "Same guards?@.%a@.%a@.@."
|
1148
|
pp_gl_short gl1 pp_gl_short gl2
|
1149
|
;
|
1150
|
res
|
1151
|
|
1152
|
) sw_sys
|
1153
|
|
1154
|
in
|
1155
|
()
|
1156
|
end;
|
1157
|
|
1158
|
|
1159
|
(* Iter through the elements and gather them by updates *)
|
1160
|
let process sys =
|
1161
|
(* The map will associate to each update up the pair (set, set
|
1162
|
list) where set is the share guards and set list a list of
|
1163
|
disjunctive guards. Each set represents a conjunction of
|
1164
|
expressions. *)
|
1165
|
report ~level:3 (fun fmt -> Format.fprintf fmt "%t@."
|
1166
|
(fun fmt -> List.iter (fun (gl,up) ->
|
1167
|
Format.fprintf fmt "[@[%a@]] -> (%a)@ "
|
1168
|
(pp_guard_list Printers.pp_expr) gl pp_up up) sw_init));
|
1169
|
|
1170
|
(* We perform multiple pass to avoid errors *)
|
1171
|
let map =
|
1172
|
List.fold_left (fun map (gl,up) ->
|
1173
|
(* creating a new set to describe gl *)
|
1174
|
let new_set =
|
1175
|
List.fold_left
|
1176
|
(fun set g -> Guards.add g set)
|
1177
|
Guards.empty
|
1178
|
gl
|
1179
|
in
|
1180
|
(* updating the map with up -> new_set *)
|
1181
|
if UpMap.mem up map then
|
1182
|
let guard_set = UpMap.find up map in
|
1183
|
UpMap.add up (new_set::guard_set) map
|
1184
|
else
|
1185
|
UpMap.add up [new_set] map
|
1186
|
) UpMap.empty sys
|
1187
|
in
|
1188
|
(* Processing the set of guards leading to the same update: return
|
1189
|
conj, disj with conf is a set of guards, and disj a DNF, ie a
|
1190
|
list of set of guards *)
|
1191
|
let map =
|
1192
|
UpMap.map (
|
1193
|
fun guards ->
|
1194
|
match guards with
|
1195
|
| [] -> Guards.empty, [] (* Nothing *)
|
1196
|
| [s]-> s, [] (* basic case *)
|
1197
|
| hd::tl ->
|
1198
|
let shared = List.fold_left (fun shared s -> Guards.inter shared s) hd tl in
|
1199
|
let remaining = List.map (fun s -> Guards.diff s shared) guards in
|
1200
|
(* If one of them is empty, we can remove the others, otherwise keep them *)
|
1201
|
if List.exists Guards.is_empty remaining then
|
1202
|
shared, []
|
1203
|
else
|
1204
|
shared, remaining
|
1205
|
) map
|
1206
|
in
|
1207
|
let rec mk_binop op l = match l with
|
1208
|
[] -> assert false
|
1209
|
| [e] -> e
|
1210
|
| hd::tl -> Corelang.mkpredef_call hd.expr_loc op [hd; mk_binop op tl]
|
1211
|
in
|
1212
|
let gl_as_expr gl =
|
1213
|
let gl = Guards.elements gl in
|
1214
|
let export (e,b) = if b then e else Corelang.push_negations ~neg:true e in
|
1215
|
match gl with
|
1216
|
[] -> []
|
1217
|
| [e] -> [export e]
|
1218
|
| _ ->
|
1219
|
[mk_binop "&&"
|
1220
|
(List.map export gl)]
|
1221
|
in
|
1222
|
let rec clean_disj disj =
|
1223
|
match disj with
|
1224
|
| [] -> []
|
1225
|
| [_] -> assert false (* A disjunction was a single case can be ignored *)
|
1226
|
| _::_::_ -> (
|
1227
|
(* First basic version: producing a DNF One can later, (1)
|
1228
|
simplify it with z3, or (2) build the compact tree with
|
1229
|
maximum shared subexpression (and simplify it with z3) *)
|
1230
|
let elems = List.fold_left (fun accu gl -> (gl_as_expr gl) @ accu) [] disj in
|
1231
|
let or_expr = mk_binop "||" elems in
|
1232
|
[or_expr]
|
1233
|
|
1234
|
|
1235
|
(* TODO disj*)
|
1236
|
(* get the item that occurs in most case *)
|
1237
|
(* List.fold_left (fun accu s ->
|
1238
|
* List.fold_left (fun accu (e,b) ->
|
1239
|
* if List.mem_assoc (e.expr_tag, b)
|
1240
|
* ) accu (Guards.elements s)
|
1241
|
* ) [] disj *)
|
1242
|
|
1243
|
)
|
1244
|
in
|
1245
|
if !seal_debug then Format.eprintf "Map: %i elements@ " (UpMap.cardinal map);
|
1246
|
UpMap.fold (fun up (common, disj) accu ->
|
1247
|
if !seal_debug then
|
1248
|
report ~level:6 (fun fmt -> Format.fprintf fmt
|
1249
|
"Guards:@.shared: [%a]@.disj: [@[<v 0>%a@ ]@]@.Updates: %a@."
|
1250
|
Guards.pp_short common
|
1251
|
(fprintf_list ~sep:";@ " Guards.pp_long) disj
|
1252
|
UpMap.pp up);
|
1253
|
let disj = clean_disj disj in
|
1254
|
let guard_expr = (gl_as_expr common)@disj in
|
1255
|
|
1256
|
((match guard_expr with
|
1257
|
| [] -> None
|
1258
|
| _ -> Some (mk_binop "&&" guard_expr)), up)::accu
|
1259
|
) map []
|
1260
|
|
1261
|
in
|
1262
|
|
1263
|
|
1264
|
|
1265
|
report ~level:3 (fun fmt -> Format.fprintf fmt "Process sw_init:@.");
|
1266
|
let sw_init = process sw_init in
|
1267
|
report ~level:3 (fun fmt -> Format.fprintf fmt "Process sw_sys:@.");
|
1268
|
let sw_sys = process sw_sys in
|
1269
|
report ~level:3 (fun fmt -> Format.fprintf fmt "Process init_out:@.");
|
1270
|
let init_out = process init_out in
|
1271
|
report ~level:3 (fun fmt -> Format.fprintf fmt "Process update_out:@.");
|
1272
|
let update_out = process update_out in
|
1273
|
sw_init , sw_sys, init_out, update_out
|