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 *)
|
8
|
type element = IsInit | Expr of expr
|
9
|
type guard = (element * bool) list
|
10
|
type guarded_expr = guard * element
|
11
|
type mdef_t = guarded_expr list
|
12
|
|
13
|
let pp_elem fmt e =
|
14
|
match e with
|
15
|
| IsInit -> Format.fprintf fmt "init"
|
16
|
| Expr e -> Printers.pp_expr fmt e
|
17
|
|
18
|
let pp_guard_list fmt gl =
|
19
|
(fprintf_list ~sep:"; "
|
20
|
(fun fmt (e,b) -> if b then pp_elem fmt e else Format.fprintf fmt "not(%a)" pp_elem e)) fmt gl
|
21
|
|
22
|
let pp_guard_expr fmt (gl,e) =
|
23
|
Format.fprintf fmt "%a -> %a"
|
24
|
pp_guard_list gl
|
25
|
pp_elem e
|
26
|
|
27
|
let pp_mdefs fmt gel = fprintf_list ~sep:"@ " pp_guard_expr fmt gel
|
28
|
|
29
|
|
30
|
let add_init defs vid =
|
31
|
Hashtbl.add defs vid [[], IsInit]
|
32
|
|
33
|
let deelem e = match e with
|
34
|
Expr e -> e
|
35
|
| IsInit -> assert false (* Wasn't expecting isinit here: we are building values! *)
|
36
|
|
37
|
let is_eq_elem elem elem' =
|
38
|
match elem, elem' with
|
39
|
| IsInit, IsInit -> true
|
40
|
| Expr e, Expr e' ->
|
41
|
Corelang.is_eq_expr e e'
|
42
|
| _ -> false
|
43
|
|
44
|
let select_elem elem (gelem, _) =
|
45
|
is_eq_elem elem gelem
|
46
|
|
47
|
|
48
|
(**************************************************************)
|
49
|
(* Convert from Lustre expressions to Z3 expressions and back *)
|
50
|
(* All (free) variables have to be declared in the Z3 context *)
|
51
|
(**************************************************************)
|
52
|
|
53
|
let is_init_name = "__is_init"
|
54
|
|
55
|
let const_defs = Hashtbl.create 13
|
56
|
let is_const id = Hashtbl.mem const_defs id
|
57
|
let get_const id = Hashtbl.find const_defs id
|
58
|
|
59
|
(* expressions are only basic constructs here, no more ite, tuples,
|
60
|
arrows, fby, ... *)
|
61
|
let expr_to_z3_expr, zexpr_to_expr =
|
62
|
(* List to store converted expression. *)
|
63
|
let hash = ref [] in
|
64
|
let comp_expr e (e', _) = Corelang.is_eq_expr e e' in
|
65
|
let comp_zexpr ze (_, ze') = Z3.Expr.equal ze ze' in
|
66
|
let mem_expr e = List.exists (comp_expr e) !hash in
|
67
|
let mem_zexpr ze = List.exists (comp_zexpr ze) !hash in
|
68
|
let get_zexpr e =
|
69
|
let (_, ze) = List.find (comp_expr e) !hash in
|
70
|
ze
|
71
|
in
|
72
|
let get_expr ze =
|
73
|
let (e, _) = List.find (comp_zexpr ze) !hash in
|
74
|
e
|
75
|
in
|
76
|
let add_expr e ze = hash := (e, ze)::!hash in
|
77
|
let rec e2ze e =
|
78
|
if mem_expr e then (
|
79
|
get_zexpr e
|
80
|
)
|
81
|
else (
|
82
|
let res =
|
83
|
match e.expr_desc with
|
84
|
| Expr_const c ->
|
85
|
let z3e = Zustre_common.horn_const_to_expr c in
|
86
|
add_expr e z3e;
|
87
|
z3e
|
88
|
| Expr_ident id -> (
|
89
|
if is_const id then (
|
90
|
let c = get_const id in
|
91
|
let z3e = Zustre_common.horn_const_to_expr c in
|
92
|
add_expr e z3e;
|
93
|
z3e
|
94
|
)
|
95
|
else (
|
96
|
let fdecl_id = Zustre_common.get_fdecl id in
|
97
|
let z3e = Z3.Expr.mk_const_f !ctx fdecl_id in
|
98
|
add_expr e z3e;
|
99
|
z3e
|
100
|
)
|
101
|
)
|
102
|
| Expr_appl (id,args, None) (* no reset *) ->
|
103
|
let z3e = Zustre_common.horn_basic_app id e2ze (Corelang.expr_list_of_expr args) in
|
104
|
add_expr e z3e;
|
105
|
z3e
|
106
|
| Expr_tuple [e] ->
|
107
|
let z3e = e2ze e in
|
108
|
add_expr e z3e;
|
109
|
z3e
|
110
|
| _ -> ( match e.expr_desc with Expr_tuple _ -> Format.eprintf "tuple e2ze(%a)@.@?" Printers.pp_expr e
|
111
|
| _ -> Format.eprintf "e2ze(%a)@.@?" Printers.pp_expr e)
|
112
|
; assert false
|
113
|
in
|
114
|
res
|
115
|
)
|
116
|
in
|
117
|
let rec ze2e ze =
|
118
|
let ze_name ze =
|
119
|
let fd = Z3.Expr.get_func_decl ze in
|
120
|
Z3.Symbol.to_string (Z3.FuncDecl.get_name fd)
|
121
|
in
|
122
|
if mem_zexpr ze then
|
123
|
None, Some (get_expr ze)
|
124
|
else
|
125
|
let open Corelang in
|
126
|
let fd = Z3.Expr.get_func_decl ze in
|
127
|
let zel = Z3.Expr.get_args ze in
|
128
|
match Z3.Symbol.to_string (Z3.FuncDecl.get_name fd), zel with
|
129
|
(* | var, [] -> (* should be in env *) get_e *)
|
130
|
|
131
|
(* Extracting IsInit status *)
|
132
|
| "not", [ze] when ze_name ze = is_init_name ->
|
133
|
Some false, None
|
134
|
| name, [] when name = is_init_name -> Some true, None
|
135
|
(* Other constructs are converted to a lustre expression *)
|
136
|
| op, _ -> (
|
137
|
|
138
|
|
139
|
if Z3.Expr.is_numeral ze then
|
140
|
let e =
|
141
|
if Z3.Arithmetic.is_real ze then
|
142
|
let num = Num.num_of_ratio (Z3.Arithmetic.Real.get_ratio ze) in
|
143
|
let s = Z3.Arithmetic.Real.numeral_to_string ze in
|
144
|
mkexpr
|
145
|
Location.dummy_loc
|
146
|
(Expr_const
|
147
|
(Const_real
|
148
|
(num, 0, s)))
|
149
|
else if Z3.Arithmetic.is_int ze then
|
150
|
mkexpr
|
151
|
Location.dummy_loc
|
152
|
(Expr_const
|
153
|
(Const_int
|
154
|
(Big_int.int_of_big_int (Z3.Arithmetic.Integer.get_big_int ze))))
|
155
|
else if Z3.Expr.is_const ze then
|
156
|
match Z3.Expr.to_string ze with
|
157
|
| "true" -> mkexpr Location.dummy_loc
|
158
|
(Expr_const (Const_tag (tag_true)))
|
159
|
| "false" ->
|
160
|
mkexpr Location.dummy_loc
|
161
|
(Expr_const (Const_tag (tag_false)))
|
162
|
| _ -> assert false
|
163
|
else
|
164
|
(
|
165
|
Format.eprintf "Const err: %s %b@." (Z3.Expr.to_string ze) (Z3.Expr.is_const ze);
|
166
|
assert false (* a numeral but no int nor real *)
|
167
|
)
|
168
|
in
|
169
|
None, Some e
|
170
|
else
|
171
|
match op with
|
172
|
| "not" | "=" | "-" | "*" | "/"
|
173
|
| ">=" | "<=" | ">" | "<"
|
174
|
->
|
175
|
let args = List.map (fun ze -> Utils.desome (snd (ze2e ze))) zel in
|
176
|
None, Some (mkpredef_call Location.dummy_loc op args)
|
177
|
| "+" -> ( (* Special treatment of + for 2+ args *)
|
178
|
let args = List.map (fun ze -> Utils.desome (snd (ze2e ze))) zel in
|
179
|
let e = match args with
|
180
|
[] -> assert false
|
181
|
| [hd] -> hd
|
182
|
| e1::e2::tl ->
|
183
|
let first_binary_and = mkpredef_call Location.dummy_loc op [e1;e2] in
|
184
|
if tl = [] then first_binary_and else
|
185
|
List.fold_left (fun e e_new ->
|
186
|
mkpredef_call Location.dummy_loc op [e;e_new]
|
187
|
) first_binary_and tl
|
188
|
|
189
|
in
|
190
|
None, Some e
|
191
|
)
|
192
|
| "and" | "or" -> (
|
193
|
(* Special case since it can contain is_init pred *)
|
194
|
let args = List.map (fun ze -> ze2e ze) zel in
|
195
|
let op = if op = "and" then "&&" else if op = "or" then "||" else assert false in
|
196
|
match args with
|
197
|
| [] -> assert false
|
198
|
| [hd] -> hd
|
199
|
| hd::tl ->
|
200
|
List.fold_left
|
201
|
(fun (is_init_opt1, expr_opt1) (is_init_opt2, expr_opt2) ->
|
202
|
(match is_init_opt1, is_init_opt2 with
|
203
|
None, x | x, None -> x
|
204
|
| Some _, Some _ -> assert false),
|
205
|
(match expr_opt1, expr_opt2 with
|
206
|
| None, x | x, None -> x
|
207
|
| Some e1, Some e2 ->
|
208
|
Some (mkpredef_call Location.dummy_loc op [e1; e2])
|
209
|
))
|
210
|
hd tl
|
211
|
)
|
212
|
| op ->
|
213
|
let args = List.map (fun ze -> (snd (ze2e ze))) zel in
|
214
|
Format.eprintf "deal with op %s (nb args: %i). Expr is %s@." op (List.length args) (Z3.Expr.to_string ze); assert false
|
215
|
)
|
216
|
in
|
217
|
(fun e -> e2ze e), (fun ze -> ze2e ze)
|
218
|
|
219
|
let is_init_z3e = Z3.Expr.mk_const_s !ctx is_init_name Zustre_common.bool_sort
|
220
|
let neg_ze z3e = Z3.Boolean.mk_not !ctx z3e
|
221
|
|
222
|
let zexpr_to_guard_list ze =
|
223
|
let init_opt, expr_opt = zexpr_to_expr ze in
|
224
|
(match init_opt with
|
225
|
| None -> []
|
226
|
|Some b -> [IsInit, b]
|
227
|
) @ (match expr_opt with
|
228
|
| None -> []
|
229
|
| Some e -> [Expr e, true]
|
230
|
)
|
231
|
let simplify_neg_guard l =
|
232
|
List.map (fun (g,posneg) ->
|
233
|
match g with
|
234
|
| IsInit -> g, posneg
|
235
|
| Expr g ->
|
236
|
if posneg then
|
237
|
Expr (Corelang.push_negations g),
|
238
|
true
|
239
|
else
|
240
|
(* Pushing the negation in the expression *)
|
241
|
Expr(Corelang.push_negations ~neg:true g),
|
242
|
true
|
243
|
) l
|
244
|
|
245
|
(* TODO:
|
246
|
individuellement demander si g1 => g2. Si c'est le cas, on peut ne garder que g1 dans la liste
|
247
|
*)
|
248
|
(*****************************************************************)
|
249
|
(* Checking sat(isfiability) of an expression and simplifying it *)
|
250
|
(* All (free) variables have to be declared in the Z3 context *)
|
251
|
(*****************************************************************)
|
252
|
let goal_simplify zl =
|
253
|
let goal = Z3.Goal.mk_goal !ctx false false false in
|
254
|
Z3.Goal.add goal zl;
|
255
|
let goal' = Z3.Goal.simplify goal None in
|
256
|
(* Format.eprintf "Goal before: %s@.Goal after : %s@.Sat? %s@."
|
257
|
* (Z3.Goal.to_string goal)
|
258
|
* (Z3.Goal.to_string goal')
|
259
|
* (Z3.Solver.string_of_status status_res)
|
260
|
* ; *)
|
261
|
let ze = Z3.Goal.as_expr goal' in
|
262
|
(* Format.eprintf "as an expr: %s@." (Z3.Expr.to_string ze); *)
|
263
|
ze
|
264
|
|
265
|
let implies e1 e2 =
|
266
|
(* Format.eprintf "Checking implication: %s => %s? "
|
267
|
* (Z3.Expr.to_string e1) (Z3.Expr.to_string e2)
|
268
|
* ; *)
|
269
|
let solver = Z3.Solver.mk_simple_solver !ctx in
|
270
|
let tgt = Z3.Boolean.mk_not !ctx (Z3.Boolean.mk_implies !ctx e1 e2) in
|
271
|
try
|
272
|
let status_res = Z3.Solver.check solver [tgt] in
|
273
|
match status_res with
|
274
|
| Z3.Solver.UNSATISFIABLE -> (* Format.eprintf "Valid!@."; *)
|
275
|
true
|
276
|
| _ -> (* Format.eprintf "not proved valid@."; *)
|
277
|
false
|
278
|
with Zustre_common.UnknownFunction(id, msg) -> (
|
279
|
report ~level:1 msg;
|
280
|
false
|
281
|
)
|
282
|
|
283
|
let rec simplify zl =
|
284
|
match zl with
|
285
|
| [] | [_] -> zl
|
286
|
| hd::tl -> (
|
287
|
(* Forall e in tl, checking whether hd => e or e => hd, to keep hd
|
288
|
in the first case and e in the second one *)
|
289
|
let tl = simplify tl in
|
290
|
let keep_hd, tl =
|
291
|
List.fold_left (fun (keep_hd, accu) e ->
|
292
|
if implies hd e then
|
293
|
true, accu (* throwing away e *)
|
294
|
else if implies e hd then
|
295
|
false, e::accu (* throwing away hd *)
|
296
|
else
|
297
|
keep_hd, e::accu (* keeping both *)
|
298
|
) (true,[]) tl
|
299
|
in
|
300
|
(* Format.eprintf "keep_hd?%b hd=%s, tl=[%a]@."
|
301
|
* keep_hd
|
302
|
* (Z3.Expr.to_string hd)
|
303
|
* (Utils.fprintf_list ~sep:"; " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) tl
|
304
|
* ; *)
|
305
|
if keep_hd then
|
306
|
hd::tl
|
307
|
else
|
308
|
tl
|
309
|
)
|
310
|
|
311
|
let check_sat ?(just_check=false) (l:guard) : bool * guard =
|
312
|
(* Syntactic simplification *)
|
313
|
(* Format.eprintf "Before simplify: %a@." pp_guard_list l; *)
|
314
|
let l = simplify_neg_guard l in
|
315
|
(* Format.eprintf "After simplify: %a@." pp_guard_list l; *)
|
316
|
(* Format.eprintf "@[<v 2>Z3 check sat: [%a]@ " pp_guard_list l; *)
|
317
|
let solver = Z3.Solver.mk_simple_solver !ctx in
|
318
|
try (
|
319
|
let zl =
|
320
|
List.map (fun (e, posneg) ->
|
321
|
let ze =
|
322
|
match e with
|
323
|
| IsInit -> is_init_z3e
|
324
|
| Expr e -> expr_to_z3_expr e
|
325
|
in
|
326
|
if posneg then
|
327
|
ze
|
328
|
else
|
329
|
neg_ze ze
|
330
|
) l
|
331
|
in
|
332
|
(* Format.eprintf "Z3 exprs1: [%a]@ " (fprintf_list ~sep:",@ " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) zl; *)
|
333
|
let zl = simplify zl in
|
334
|
(* Format.eprintf "Z3 exprs2: [%a]@ " (fprintf_list ~sep:",@ " (fun fmt e -> Format.fprintf fmt "%s" (Z3.Expr.to_string e))) zl; *)
|
335
|
let status_res = Z3.Solver.check solver zl in
|
336
|
(* Format.eprintf "Z3 status: %s@ @]@. " (Z3.Solver.string_of_status status_res); *)
|
337
|
match status_res with
|
338
|
| Z3.Solver.UNSATISFIABLE -> false, []
|
339
|
| _ -> (
|
340
|
if false && just_check then
|
341
|
true, l
|
342
|
else
|
343
|
let zl = goal_simplify zl in
|
344
|
let l = zexpr_to_guard_list zl in
|
345
|
(* Format.eprintf "@.@[<v 2>Check_Sat:@ before: %a@ after:
|
346
|
%a@. Goal precise? %b/%b@]@.@. " * pp_guard_list l
|
347
|
pp_guard_list l' * (Z3.Goal.is_precise goal) *
|
348
|
(Z3.Goal.is_precise goal'); *)
|
349
|
|
350
|
|
351
|
true, l
|
352
|
|
353
|
|
354
|
)
|
355
|
|
356
|
)
|
357
|
with Zustre_common.UnknownFunction(id, msg) -> (
|
358
|
report ~level:1 msg;
|
359
|
true, l (* keeping everything. *)
|
360
|
)
|
361
|
|
362
|
|
363
|
|
364
|
|
365
|
(**************************************************************)
|
366
|
|
367
|
|
368
|
let clean_sys sys =
|
369
|
List.fold_left (fun accu (guards, updates) ->
|
370
|
(*let guards' = clean_guard guards in*)
|
371
|
let sat, guards' = check_sat (List.map (fun (g, pn) -> Expr g, pn) guards) in
|
372
|
(*Format.eprintf "Guard: %a@.Guard cleaned: %a@.Sat? %b@."
|
373
|
(fprintf_list ~sep:"@ " (pp_guard_expr Printers.pp_expr)) guards
|
374
|
(fprintf_list ~sep:"@ " (pp_guard_expr Printers.pp_expr)) guards'
|
375
|
sat
|
376
|
|
377
|
;*)
|
378
|
if sat then
|
379
|
(List.map (fun (e, b) -> (deelem e, b)) guards', updates)::accu
|
380
|
else
|
381
|
accu
|
382
|
)
|
383
|
[] sys
|
384
|
|
385
|
let combine_guards ?(fresh=None) gl1 gl2 =
|
386
|
(* Filtering out trivial cases. More semantics ones would have to be
|
387
|
addressed later *)
|
388
|
let check (gexpr, posneg) l =
|
389
|
(* Format.eprintf "Checking %a=%b in %a@ "
|
390
|
* pp_elem gexpr
|
391
|
* posneg
|
392
|
* pp_guard_list l
|
393
|
* ; *)
|
394
|
(* Check if gepxr is part of l *)
|
395
|
let sel_fun = select_elem gexpr in
|
396
|
let ok, res =
|
397
|
if List.exists sel_fun l then (
|
398
|
(* Checking the guard value posneg *)
|
399
|
let _, status = List.find sel_fun l in
|
400
|
(* Format.eprintf "Found %a in %a. @ Checking status (%b).@ "
|
401
|
* pp_elem gexpr
|
402
|
* pp_guard_list l
|
403
|
* (status=posneg)
|
404
|
* ; *)
|
405
|
status=posneg, l
|
406
|
)
|
407
|
else (
|
408
|
(* Format.eprintf "Not found.@ "; *)
|
409
|
(* Valid: no overlap *)
|
410
|
(* Individual checkat *)
|
411
|
let ok, e = check_sat ~just_check:true [gexpr, posneg] in
|
412
|
(* let ok, e = true, [gexpr, posneg] in (* TODO solve the issue with check_sat *) *)
|
413
|
(* Format.eprintf "Check_sat? %b@ " ok; *)
|
414
|
if ok then
|
415
|
let l = e@l in
|
416
|
let ok, l = check_sat ~just_check:true l in
|
417
|
(* let ok, l = true, l in (* TODO solve the issue with check_sat *) *)
|
418
|
ok, l
|
419
|
else
|
420
|
false, []
|
421
|
)
|
422
|
in
|
423
|
(* Format.eprintf "Check sat res: %a@ "
|
424
|
* pp_guard_list res; *)
|
425
|
ok, res
|
426
|
in
|
427
|
let ok, gl =
|
428
|
List.fold_left (
|
429
|
fun (ok, l) g ->
|
430
|
(* Bypass for negative output *)
|
431
|
if not ok then false, []
|
432
|
else
|
433
|
check g l
|
434
|
) (true, gl2) gl1
|
435
|
in
|
436
|
if ok then
|
437
|
match fresh with
|
438
|
None -> true, gl
|
439
|
| Some fresh_g -> (
|
440
|
(* Checking the fresh element *)
|
441
|
check fresh_g gl
|
442
|
)
|
443
|
else
|
444
|
false, []
|
445
|
|
446
|
(* DEBUG
|
447
|
let combine_guards ?(fresh=None) gl1 gl2 =
|
448
|
true,
|
449
|
(match fresh with None -> [] | Some (gexpr, posneg) -> [gexpr, posneg])@gl1@gl2
|
450
|
*)
|
451
|
(* Encode "If gel1=posneg then gel2":
|
452
|
- Compute the combination of guarded exprs in gel1 and gel2:
|
453
|
- Each guarded_expr in gel1 is transformed as a guard: the
|
454
|
expression is associated to posneg.
|
455
|
- Existing guards in gel2 are concatenated to that list of guards
|
456
|
- We keep expr in the ge of gel2 as the legitimate expression
|
457
|
*)
|
458
|
let concatenate_ge gel1 posneg gel2 =
|
459
|
List.fold_left (
|
460
|
fun accu (g2,e2) ->
|
461
|
List.fold_left (
|
462
|
fun accu (g1,e1) ->
|
463
|
(* Format.eprintf "@[<v 2>Combining guards: (%a=%b) AND [%a] AND [%a]@ "
|
464
|
* pp_elem e1
|
465
|
* posneg
|
466
|
* pp_guard_list g1
|
467
|
* pp_guard_list g2; *)
|
468
|
|
469
|
let ok, gl = combine_guards ~fresh:(Some(e1,posneg)) g1 g2 in
|
470
|
(* Format.eprintf "@]@ Result is [%a]@ "
|
471
|
* pp_guard_list gl; *)
|
472
|
|
473
|
if ok then
|
474
|
(gl, e2)::accu
|
475
|
else (
|
476
|
|
477
|
accu
|
478
|
)
|
479
|
) accu gel1
|
480
|
) [] gel2
|
481
|
|
482
|
let rec rewrite defs expr : guarded_expr list =
|
483
|
let rewrite = rewrite defs in
|
484
|
let res =
|
485
|
match expr.expr_desc with
|
486
|
| Expr_appl (id, args, None) ->
|
487
|
let args = rewrite args in
|
488
|
List.map (fun (guards, e) ->
|
489
|
guards,
|
490
|
Expr {expr with expr_desc = Expr_appl(id, deelem e, None)}
|
491
|
) args
|
492
|
| Expr_const _ -> [[], Expr expr]
|
493
|
| Expr_ident id ->
|
494
|
if Hashtbl.mem defs id then
|
495
|
Hashtbl.find defs id
|
496
|
else
|
497
|
(* id should be an input *)
|
498
|
[[], Expr expr]
|
499
|
| Expr_ite (g, e1, e2) ->
|
500
|
let g = rewrite g and
|
501
|
e1 = rewrite e1 and
|
502
|
e2 = rewrite e2 in
|
503
|
(concatenate_ge g true e1)@
|
504
|
(concatenate_ge g false e2)
|
505
|
| Expr_merge (g, branches) ->
|
506
|
assert false (* TODO: deal with merges *)
|
507
|
|
508
|
| Expr_when (e, _, _) -> rewrite e
|
509
|
| Expr_arrow _ -> [[], IsInit] (* At this point the only arrow should be true -> false *)
|
510
|
| Expr_tuple el ->
|
511
|
(* Each expr is associated to its flatten guarded expr list *)
|
512
|
let gell = List.map rewrite el in
|
513
|
(* Computing all combinations: we obtain a list of guarded tuple *)
|
514
|
let rec aux gell : (guard * expr list) list =
|
515
|
match gell with
|
516
|
| [] -> assert false (* Not happening *)
|
517
|
| [gel] -> List.map (fun (g,e) -> g, [deelem e]) gel
|
518
|
| gel::getl ->
|
519
|
let getl = aux getl in
|
520
|
List.fold_left (
|
521
|
fun accu (g,e) ->
|
522
|
List.fold_left (
|
523
|
fun accu (gl, minituple) ->
|
524
|
let is_compat, guard_comb = combine_guards g gl in
|
525
|
if is_compat then
|
526
|
let new_gt : guard * expr list = (guard_comb, (deelem e)::minituple) in
|
527
|
new_gt::accu
|
528
|
else
|
529
|
accu
|
530
|
|
531
|
) accu getl
|
532
|
) [] gel
|
533
|
in
|
534
|
let gtuples = aux gell in
|
535
|
(* Rebuilding the valid type: guarded expr list (with tuple exprs) *)
|
536
|
List.map (fun (g,tuple) -> g, Expr {expr with expr_desc = Expr_tuple tuple}) gtuples
|
537
|
| Expr_fby _
|
538
|
| Expr_appl _
|
539
|
(* Should be removed by mormalization and inlining *)
|
540
|
-> Format.eprintf "Pb expr: %a@.@?" Printers.pp_expr expr; assert false
|
541
|
| Expr_array _ | Expr_access _ | Expr_power _
|
542
|
(* Arrays not handled here yet *)
|
543
|
-> assert false
|
544
|
| Expr_pre _ -> (* Not rewriting mem assign *)
|
545
|
assert false
|
546
|
in
|
547
|
(* Format.eprintf "Rewriting %a as [@[<v 0>%a@]]@ "
|
548
|
* Printers.pp_expr expr
|
549
|
* (Utils.fprintf_list ~sep:"@ "
|
550
|
* pp_guard_expr) res; *)
|
551
|
res
|
552
|
and add_def defs vid expr =
|
553
|
let vid_defs = rewrite defs expr in
|
554
|
(* Format.eprintf "Add_def: %s = %a@. -> @[<v 0>%a@]@."
|
555
|
* vid
|
556
|
* Printers.pp_expr expr
|
557
|
* (
|
558
|
* (Utils.fprintf_list ~sep:"@ "
|
559
|
* pp_guard_expr)) vid_defs; *)
|
560
|
Hashtbl.add defs vid vid_defs
|
561
|
|
562
|
(* Takes a list of guarded exprs (ge) and a guard
|
563
|
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.
|
564
|
|
565
|
When a given ge doesn't mention positively or negatively such guards, it is duplicated in both lists *)
|
566
|
let split_mdefs elem (mdefs: guarded_expr list) =
|
567
|
List.fold_left (
|
568
|
fun (selected, left_out)
|
569
|
((guards, expr) as ge) ->
|
570
|
(* select the element of guards that match the argument elem *)
|
571
|
let sel, others_guards = List.partition (select_elem elem) guards in
|
572
|
match sel with
|
573
|
(* we extract the element from the list and add it to the
|
574
|
appropriate list *)
|
575
|
| [_, sel_status] ->
|
576
|
if sel_status then
|
577
|
(others_guards,expr)::selected, left_out
|
578
|
else selected, (others_guards,expr)::left_out
|
579
|
| [] -> (* no such guard exists, we have to duplicate the
|
580
|
guard_expr in both lists *)
|
581
|
ge::selected, ge::left_out
|
582
|
| _ -> (
|
583
|
Format.eprintf "@.Spliting list on elem %a.@.List:%a@."
|
584
|
pp_elem elem
|
585
|
pp_mdefs mdefs;
|
586
|
assert false (* more then one element selected. Should
|
587
|
not happen , or trival dead code like if
|
588
|
x then if not x then dead code *)
|
589
|
)
|
590
|
) ([],[]) mdefs
|
591
|
|
592
|
let split_mem_defs
|
593
|
(elem: element)
|
594
|
(mem_defs: (ident * guarded_expr list) list)
|
595
|
:
|
596
|
((ident * mdef_t) list) * ((ident * mdef_t) list)
|
597
|
|
598
|
=
|
599
|
List.fold_right (fun (m,mdefs)
|
600
|
(accu_pos, accu_neg) ->
|
601
|
let pos, neg =
|
602
|
split_mdefs elem mdefs
|
603
|
in
|
604
|
(m, pos)::accu_pos,
|
605
|
(m, neg)::accu_neg
|
606
|
) mem_defs ([],[])
|
607
|
|
608
|
|
609
|
(* Split a list of mem_defs into init and step lists of guarded
|
610
|
expressions per memory. *)
|
611
|
let split_init mem_defs =
|
612
|
split_mem_defs IsInit mem_defs
|
613
|
|
614
|
let pick_guard mem_defs : expr option =
|
615
|
let gel = List.flatten (List.map snd mem_defs) in
|
616
|
let gl = List.flatten (List.map fst gel) in
|
617
|
let all_guards =
|
618
|
List.map (
|
619
|
(* selecting guards and getting rid of boolean *)
|
620
|
fun (e,b) ->
|
621
|
match e with
|
622
|
| Expr e -> e
|
623
|
| _ -> assert false
|
624
|
(* should have been filtered out
|
625
|
yet *)
|
626
|
) gl
|
627
|
in
|
628
|
(* TODO , one could sort by occurence and provided the most common
|
629
|
one *)
|
630
|
try
|
631
|
Some (List.hd all_guards)
|
632
|
with _ -> None
|
633
|
(* Transform a list of variable * guarded exprs into a list of guarded pairs (variable, expressions)
|
634
|
*)
|
635
|
let rec build_switch_sys
|
636
|
(mem_defs : (Utils.ident * guarded_expr list) list )
|
637
|
prefix
|
638
|
:
|
639
|
((expr * bool) list * (ident * expr) list ) list =
|
640
|
(* if all mem_defs have empty guards, we are done, return prefix,
|
641
|
mem_defs expr.
|
642
|
|
643
|
otherwise pick a guard in one of the mem, eg (g, b) then for each
|
644
|
other mem, one need to select the same guard g with the same
|
645
|
status b, *)
|
646
|
if List.for_all (fun (m,mdefs) ->
|
647
|
(* All defs are unguarded *)
|
648
|
match mdefs with
|
649
|
| [[], _] -> true
|
650
|
| _ -> false
|
651
|
) mem_defs
|
652
|
then
|
653
|
[prefix ,
|
654
|
List.map (fun (m,gel) ->
|
655
|
match gel with
|
656
|
| [_,e] ->
|
657
|
let e =
|
658
|
match e with
|
659
|
| Expr e -> e
|
660
|
| _ -> assert false (* No IsInit expression *)
|
661
|
in
|
662
|
m,e
|
663
|
| _ -> assert false
|
664
|
) mem_defs]
|
665
|
else
|
666
|
(* Picking a guard *)
|
667
|
let elem_opt : expr option = pick_guard mem_defs in
|
668
|
match elem_opt with
|
669
|
None -> []
|
670
|
| Some elem -> (
|
671
|
let pos, neg =
|
672
|
split_mem_defs
|
673
|
(Expr elem)
|
674
|
mem_defs
|
675
|
in
|
676
|
(* Special cases to avoid useless computations: true, false conditions *)
|
677
|
match elem.expr_desc with
|
678
|
| Expr_ident "true" -> build_switch_sys pos prefix
|
679
|
| Expr_const (Const_tag tag) when tag = Corelang.tag_true
|
680
|
-> build_switch_sys pos prefix
|
681
|
| Expr_ident "false" -> build_switch_sys neg prefix
|
682
|
| Expr_const (Const_tag tag) when tag = Corelang.tag_false
|
683
|
-> build_switch_sys neg prefix
|
684
|
| _ -> (* Regular case *)
|
685
|
(* let _ = (
|
686
|
* Format.eprintf "Expr is %a@." Printers.pp_expr elem;
|
687
|
* match elem.expr_desc with
|
688
|
* | Expr_const _ -> Format.eprintf "a const@."
|
689
|
*
|
690
|
* | Expr_ident _ -> Format.eprintf "an ident@."
|
691
|
* | _ -> Format.eprintf "something else@."
|
692
|
* )
|
693
|
* in *)
|
694
|
let clean l =
|
695
|
let l = List.map (fun (e,b) -> (Expr e), b) l in
|
696
|
let ok, l = check_sat l in
|
697
|
let l = List.map (fun (e,b) -> deelem e, b) l in
|
698
|
ok, l
|
699
|
in
|
700
|
let pos_prefix = (elem, true)::prefix in
|
701
|
let ok_pos, pos_prefix = clean pos_prefix in
|
702
|
let neg_prefix = (elem, false)::prefix in
|
703
|
let ok_neg, neg_prefix = clean neg_prefix in
|
704
|
|
705
|
(if ok_pos then build_switch_sys pos pos_prefix else [])
|
706
|
@
|
707
|
(if ok_neg then build_switch_sys neg neg_prefix else [])
|
708
|
)
|
709
|
|
710
|
|
711
|
|
712
|
(* Take a normalized node and extract a list of switches: (cond,
|
713
|
update) meaning "if cond then update" where update shall define all
|
714
|
node memories. Everything as to be expressed over inputs or memories, intermediate variables are removed through inlining *)
|
715
|
let node_as_switched_sys consts (mems:var_decl list) nd =
|
716
|
(* rescheduling node: has been scheduled already, no need to protect
|
717
|
the call to schedule_node *)
|
718
|
let nd_report = Scheduling.schedule_node nd in
|
719
|
let schedule = nd_report.Scheduling_type.schedule in
|
720
|
let eqs, auts = Corelang.get_node_eqs nd in
|
721
|
assert (auts = []); (* Automata should be expanded by now *)
|
722
|
let sorted_eqs = Scheduling.sort_equations_from_schedule eqs schedule in
|
723
|
let defs : (ident, guarded_expr list) Hashtbl.t = Hashtbl.create 13 in
|
724
|
let add_def = add_def defs in
|
725
|
|
726
|
let vars = Corelang.get_node_vars nd in
|
727
|
(* Registering all locals variables as Z3 predicates. Will be use to
|
728
|
simplify the expansion *)
|
729
|
let _ =
|
730
|
List.iter (fun v ->
|
731
|
let fdecl = Z3.FuncDecl.mk_func_decl_s
|
732
|
!ctx
|
733
|
v.var_id
|
734
|
[]
|
735
|
(Zustre_common.type_to_sort v.var_type)
|
736
|
in
|
737
|
ignore (Zustre_common.register_fdecl v.var_id fdecl)
|
738
|
) vars
|
739
|
in
|
740
|
let _ =
|
741
|
List.iter (fun c -> Hashtbl.add const_defs c.const_id c.const_value) consts
|
742
|
in
|
743
|
|
744
|
|
745
|
(* Registering node equations: identifying mem definitions and
|
746
|
storing others in the "defs" hashtbl. *)
|
747
|
let mem_defs =
|
748
|
List.fold_left (fun accu eq ->
|
749
|
match eq.eq_lhs with
|
750
|
| [vid] ->
|
751
|
(* Only focus on non memory definitions *)
|
752
|
if not (List.exists (fun v -> v.var_id = vid) mems) then (
|
753
|
report ~level:3 (fun fmt ->
|
754
|
Format.fprintf fmt "Registering variable %s@." vid);
|
755
|
add_def vid eq.eq_rhs;
|
756
|
accu
|
757
|
)
|
758
|
else
|
759
|
(
|
760
|
match eq.eq_rhs.expr_desc with
|
761
|
| Expr_pre def_m ->
|
762
|
report ~level:3 (fun fmt ->
|
763
|
Format.fprintf fmt "Preparing mem %s@." vid);
|
764
|
(vid, rewrite defs def_m)::accu
|
765
|
| _ -> assert false
|
766
|
)
|
767
|
| _ -> assert false (* should have been removed by normalization *)
|
768
|
) [] sorted_eqs
|
769
|
in
|
770
|
|
771
|
|
772
|
report ~level:2 (fun fmt -> Format.fprintf fmt "Printing out (guarded) memories definitions (may takes time)@.");
|
773
|
(* Printing memories definitions *)
|
774
|
report ~level:3
|
775
|
(fun fmt ->
|
776
|
Format.fprintf fmt
|
777
|
"@[<v 0>%a@]@ "
|
778
|
(Utils.fprintf_list ~sep:"@ "
|
779
|
(fun fmt (m,mdefs) ->
|
780
|
Format.fprintf fmt
|
781
|
"%s -> [@[<v 0>%a@] ]@ "
|
782
|
m
|
783
|
(Utils.fprintf_list ~sep:"@ "
|
784
|
pp_guard_expr) mdefs
|
785
|
))
|
786
|
mem_defs);
|
787
|
(* Format.eprintf "Split init@."; *)
|
788
|
let init_defs, update_defs =
|
789
|
split_init mem_defs
|
790
|
in
|
791
|
report ~level:3
|
792
|
(fun fmt ->
|
793
|
Format.fprintf fmt
|
794
|
"@[<v 0>Init:@ %a@ Step@ %a@]@ "
|
795
|
(Utils.fprintf_list ~sep:"@ "
|
796
|
(fun fmt (m,mdefs) ->
|
797
|
Format.fprintf fmt
|
798
|
"%s -> @[<v 0>[%a@] ]@ "
|
799
|
m
|
800
|
(Utils.fprintf_list ~sep:"@ "
|
801
|
pp_guard_expr) mdefs
|
802
|
))
|
803
|
init_defs
|
804
|
(Utils.fprintf_list ~sep:"@ "
|
805
|
(fun fmt (m,mdefs) ->
|
806
|
Format.fprintf fmt
|
807
|
"%s -> @[<v 0>[%a@] ]@ "
|
808
|
m
|
809
|
(Utils.fprintf_list ~sep:"@ "
|
810
|
pp_guard_expr) mdefs
|
811
|
))
|
812
|
update_defs);
|
813
|
(* Format.eprintf "Build init@."; *)
|
814
|
|
815
|
let sw_init=
|
816
|
build_switch_sys init_defs []
|
817
|
in
|
818
|
(* Format.eprintf "Build step@."; *)
|
819
|
let sw_sys =
|
820
|
build_switch_sys update_defs []
|
821
|
in
|
822
|
clean_sys sw_init, clean_sys sw_sys
|
823
|
|