Coccinelle release-1.0.0-rc11
[bpt/coccinelle.git] / ctl / ctl_engine.ml
1 (*
2 * Copyright 2012, INRIA
3 * Julia Lawall, Gilles Muller
4 * Copyright 2010-2011, INRIA, University of Copenhagen
5 * Julia Lawall, Rene Rydhof Hansen, Gilles Muller, Nicolas Palix
6 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
7 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
8 * This file is part of Coccinelle.
9 *
10 * Coccinelle is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, according to version 2 of the License.
13 *
14 * Coccinelle is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
21 *
22 * The authors reserve the right to distribute this or future versions of
23 * Coccinelle under other licenses.
24 *)
25
26
27 (*external c_counter : unit -> int = "c_counter"*)
28 let timeout = 800
29 (* Optimize triples_conj by first extracting the intersection of the two sets,
30 which can certainly be in the intersection *)
31 let pTRIPLES_CONJ_OPT = ref true
32 (* For complement, make NegState for the negation of a single state *)
33 let pTRIPLES_COMPLEMENT_OPT = ref true
34 (* For complement, do something special for the case where the environment
35 and witnesses are empty *)
36 let pTRIPLES_COMPLEMENT_SIMPLE_OPT = ref true
37 (* "Double negate" the arguments of the path operators *)
38 let pDOUBLE_NEGATE_OPT = ref true
39 (* Only do pre_forall/pre_exists on new elements in fixpoint iteration *)
40 let pNEW_INFO_OPT = ref true
41 (* Filter the result of the label function to drop entries that aren't
42 compatible with any of the available environments *)
43 let pREQUIRED_ENV_OPT = ref true
44 (* Memoize the raw result of the label function *)
45 let pSATLABEL_MEMO_OPT = ref true
46 (* Filter results according to the required states *)
47 let pREQUIRED_STATES_OPT = ref true
48 (* Drop negative witnesses at Uncheck *)
49 let pUNCHECK_OPT = ref true
50 let pANY_NEG_OPT = ref true
51 let pLazyOpt = ref true
52
53 (* Nico: This stack is use for graphical traces *)
54 let graph_stack = ref ([] : string list)
55 let graph_hash = (Hashtbl.create 101)
56
57 (*
58 let pTRIPLES_CONJ_OPT = ref false
59 let pTRIPLES_COMPLEMENT_OPT = ref false
60 let pTRIPLES_COMPLEMENT_SIMPLE_OPT = ref false
61 let pDOUBLE_NEGATE_OPT = ref false
62 let pNEW_INFO_OPT = ref false
63 let pREQUIRED_ENV_OPT = ref false
64 let pSATLABEL_MEMO_OPT = ref false
65 let pREQUIRED_STATES_OPT = ref false
66 let pUNCHECK_OPT = ref false
67 let pANY_NEG_OPT = ref false
68 let pLazyOpt = ref false
69 *)
70
71
72 let step_count = ref 0
73 exception Steps
74 let inc_step _ =
75 if not (!step_count = 0)
76 then
77 begin
78 step_count := !step_count - 1;
79 if !step_count = 0 then raise Steps
80 end
81
82 let inc cell = cell := !cell + 1
83
84 let satEU_calls = ref 0
85 let satAW_calls = ref 0
86 let satAU_calls = ref 0
87 let satEF_calls = ref 0
88 let satAF_calls = ref 0
89 let satEG_calls = ref 0
90 let satAG_calls = ref 0
91
92 let triples = ref 0
93
94 let ctr = ref 0
95 let new_let _ =
96 let c = !ctr in
97 ctr := c + 1;
98 Printf.sprintf "_fresh_r_%d" c
99
100 (* **********************************************************************
101 *
102 * Implementation of a Witness Tree model checking engine for CTL-FVex
103 *
104 *
105 * **********************************************************************)
106
107 (* ********************************************************************** *)
108 (* Module: SUBST (substitutions: meta. vars and values) *)
109 (* ********************************************************************** *)
110
111 module type SUBST =
112 sig
113 type value
114 type mvar
115 val eq_mvar: mvar -> mvar -> bool
116 val eq_val: value -> value -> bool
117 val merge_val: value -> value -> value
118 val print_mvar : mvar -> unit
119 val print_value : value -> unit
120 end
121 ;;
122
123 (* ********************************************************************** *)
124 (* Module: GRAPH (control flow graphs / model) *)
125 (* ********************************************************************** *)
126
127 module type GRAPH =
128 sig
129 type node
130 type cfg
131 val predecessors: cfg -> node -> node list
132 val successors: cfg -> node -> node list
133 val extract_is_loop : cfg -> node -> bool
134 val print_node : node -> unit
135 val size : cfg -> int
136 val print_graph : cfg -> string option ->
137 (node * string) list -> (node * string) list -> string -> unit
138 end
139 ;;
140
141 module OGRAPHEXT_GRAPH =
142 struct
143 type node = int;;
144 type cfg = (string,unit) Ograph_extended.ograph_mutable;;
145 let predecessors cfg n = List.map fst ((cfg#predecessors n)#tolist);;
146 let print_node i = Format.print_string (Common.i_to_s i)
147 end
148 ;;
149
150 (* ********************************************************************** *)
151 (* Module: PREDICATE (predicates for CTL formulae) *)
152 (* ********************************************************************** *)
153
154 module type PREDICATE =
155 sig
156 type t
157 val print_predicate : t -> unit
158 end
159
160
161 (* ********************************************************************** *)
162
163 (* ---------------------------------------------------------------------- *)
164 (* Misc. useful generic functions *)
165 (* ---------------------------------------------------------------------- *)
166
167 let get_graph_files () = !graph_stack
168 let get_graph_comp_files outfile = Hashtbl.find_all graph_hash outfile
169
170 let head = List.hd
171
172 let tail l =
173 match l with
174 [] -> []
175 | (x::xs) -> xs
176 ;;
177
178 let foldl = List.fold_left;;
179
180 let foldl1 f xs = foldl f (head xs) (tail xs)
181
182 type 'a esc = ESC of 'a | CONT of 'a
183
184 let foldr = List.fold_right;;
185
186 let concat = List.concat;;
187
188 let map = List.map;;
189
190 let filter = List.filter;;
191
192 let partition = List.partition;;
193
194 let concatmap f l = List.concat (List.map f l);;
195
196 let maybe f g opt =
197 match opt with
198 | None -> g
199 | Some x -> f x
200 ;;
201
202 let some_map f opts = map (maybe (fun x -> Some (f x)) None) opts
203
204 let some_tolist_alt opts = concatmap (maybe (fun x -> [x]) []) opts
205
206 let rec some_tolist opts =
207 match opts with
208 | [] -> []
209 | (Some x)::rest -> x::(some_tolist rest)
210 | _::rest -> some_tolist rest
211 ;;
212
213 let rec groupBy eq l =
214 match l with
215 [] -> []
216 | (x::xs) ->
217 let (xs1,xs2) = partition (fun x' -> eq x x') xs in
218 (x::xs1)::(groupBy eq xs2)
219 ;;
220
221 let group l = groupBy (=) l;;
222
223 let rec memBy eq x l =
224 match l with
225 [] -> false
226 | (y::ys) -> if (eq x y) then true else (memBy eq x ys)
227 ;;
228
229 let rec nubBy eq ls =
230 match ls with
231 [] -> []
232 | (x::xs) when (memBy eq x xs) -> nubBy eq xs
233 | (x::xs) -> x::(nubBy eq xs)
234 ;;
235
236 let rec nub ls =
237 match ls with
238 [] -> []
239 | (x::xs) when (List.mem x xs) -> nub xs
240 | (x::xs) -> x::(nub xs)
241 ;;
242
243 let state_compare (s1,_,_) (s2,_,_) = compare s1 s2
244
245 let setifyBy eq xs = nubBy eq xs;;
246
247 let setify xs = nub xs;;
248
249 let inner_setify xs = List.sort compare (nub xs);;
250
251 let unionBy compare eq xs = function
252 [] -> xs
253 | ys ->
254 let rec loop = function
255 [] -> ys
256 | x::xs -> if memBy eq x ys then loop xs else x::(loop xs) in
257 List.sort compare (loop xs)
258 ;;
259
260 let union xs ys = unionBy state_compare (=) xs ys;;
261
262 let setdiff xs ys = filter (fun x -> not (List.mem x ys)) xs;;
263
264 let subseteqBy eq xs ys = List.for_all (fun x -> memBy eq x ys) xs;;
265
266 let subseteq xs ys = List.for_all (fun x -> List.mem x ys) xs;;
267 let supseteq xs ys = subseteq ys xs
268
269 let setequalBy eq xs ys = (subseteqBy eq xs ys) & (subseteqBy eq ys xs);;
270
271 let setequal xs ys = (subseteq xs ys) & (subseteq ys xs);;
272
273 (* Fix point calculation *)
274 let rec fix eq f x =
275 let x' = f x in if (eq x' x) then x' else fix eq f x'
276 ;;
277
278 (* Fix point calculation on set-valued functions *)
279 let setfix f x = (fix subseteq f x) (*if new is a subset of old, stop*)
280 let setgfix f x = (fix supseteq f x) (*if new is a supset of old, stop*)
281
282 let get_states l = nub (List.map (function (s,_,_) -> s) l)
283
284 (* ********************************************************************** *)
285 (* Module: CTL_ENGINE *)
286 (* ********************************************************************** *)
287
288 module CTL_ENGINE =
289 functor (SUB : SUBST) ->
290 functor (G : GRAPH) ->
291 functor (P : PREDICATE) ->
292 struct
293
294 module A = Ast_ctl
295
296 type substitution = (SUB.mvar, SUB.value) Ast_ctl.generic_substitution
297
298 type ('pred,'anno) witness =
299 (G.node, substitution,
300 ('pred, SUB.mvar, 'anno) Ast_ctl.generic_ctl list)
301 Ast_ctl.generic_witnesstree
302
303 type ('pred,'anno) triples =
304 (G.node * substitution * ('pred,'anno) witness list) list
305
306 (* ---------------------------------------------------------------------- *)
307 (* Pretty printing functions *)
308 (* ---------------------------------------------------------------------- *)
309
310 let (print_generic_substitution : substitution -> unit) = fun substxs ->
311 let print_generic_subst = function
312 A.Subst (mvar, v) ->
313 SUB.print_mvar mvar; Format.print_string " --> "; SUB.print_value v
314 | A.NegSubst (mvar, v) ->
315 SUB.print_mvar mvar; Format.print_string " -/-> "; SUB.print_value v in
316 Format.print_string "[";
317 Common.print_between (fun () -> Format.print_string ";" )
318 print_generic_subst substxs;
319 Format.print_string "]"
320
321 let rec (print_generic_witness: ('pred, 'anno) witness -> unit) =
322 function
323 | A.Wit (state, subst, anno, childrens) ->
324 Format.print_string "wit ";
325 G.print_node state;
326 print_generic_substitution subst;
327 (match childrens with
328 [] -> Format.print_string "{}"
329 | _ ->
330 Format.force_newline(); Format.print_string " "; Format.open_box 0;
331 print_generic_witnesstree childrens; Format.close_box())
332 | A.NegWit(wit) ->
333 Format.print_string "!";
334 print_generic_witness wit
335
336 and (print_generic_witnesstree: ('pred,'anno) witness list -> unit) =
337 fun witnesstree ->
338 Format.open_box 1;
339 Format.print_string "{";
340 Common.print_between
341 (fun () -> Format.print_string ";"; Format.force_newline() )
342 print_generic_witness witnesstree;
343 Format.print_string "}";
344 Format.close_box()
345
346 and print_generic_triple (node,subst,tree) =
347 G.print_node node;
348 print_generic_substitution subst;
349 print_generic_witnesstree tree
350
351 and (print_generic_algo : ('pred,'anno) triples -> unit) = fun xs ->
352 Format.print_string "<";
353 Common.print_between
354 (fun () -> Format.print_string ";"; Format.force_newline())
355 print_generic_triple xs;
356 Format.print_string ">"
357 ;;
358
359 let print_state (str : string) (l : ('pred,'anno) triples) =
360 Printf.printf "%s\n" str;
361 List.iter (function x ->
362 print_generic_triple x; Format.print_newline(); flush stdout)
363 (List.sort compare l);
364 Printf.printf "\n"
365
366 let print_required_states = function
367 None -> Printf.printf "no required states\n"
368 | Some states ->
369 Printf.printf "required states: ";
370 List.iter
371 (function x ->
372 G.print_node x; Format.print_string " "; Format.print_flush())
373 states;
374 Printf.printf "\n"
375
376 let mkstates states = function
377 None -> states
378 | Some states -> states
379
380 let print_graph grp required_states res str = function
381 A.Exists (keep,v,phi) -> ()
382 | phi ->
383 if !Flag_ctl.graphical_trace && not !Flag_ctl.checking_reachability
384 then
385 match phi with
386 | A.Exists (keep,v,phi) -> ()
387 | _ ->
388 let label =
389 Printf.sprintf "%s%s"
390 (String.escaped
391 (Common.format_to_string
392 (function _ ->
393 Pretty_print_ctl.pp_ctl
394 (P.print_predicate, SUB.print_mvar)
395 false phi)))
396 str in
397 let file = (match !Flag.currentfile with
398 None -> "graphical_trace"
399 | Some f -> f
400 ) in
401 (if not (List.mem file !graph_stack) then
402 graph_stack := file :: !graph_stack);
403 let filename = Filename.temp_file (file^":") ".dot" in
404 Hashtbl.add graph_hash file filename;
405 G.print_graph grp
406 (if !Flag_ctl.gt_without_label then None else (Some label))
407 (match required_states with
408 None -> []
409 | Some required_states ->
410 (List.map (function s -> (s,"blue")) required_states))
411 (List.map (function (s,_,_) -> (s,"\"#FF8080\"")) res) filename
412
413 let print_graph_c grp required_states res ctr phi =
414 let str = "iter: "^(string_of_int !ctr) in
415 print_graph grp required_states res str phi
416
417 (* ---------------------------------------------------------------------- *)
418 (* *)
419 (* ---------------------------------------------------------------------- *)
420
421
422 (* ************************* *)
423 (* Substitutions *)
424 (* ************************* *)
425
426 let dom_sub sub =
427 match sub with
428 | A.Subst(x,_) -> x
429 | A.NegSubst(x,_) -> x
430 ;;
431
432 let ran_sub sub =
433 match sub with
434 | A.Subst(_,x) -> x
435 | A.NegSubst(_,x) -> x
436 ;;
437
438 let eq_subBy eqx eqv sub sub' =
439 match (sub,sub') with
440 | (A.Subst(x,v),A.Subst(x',v')) -> (eqx x x') && (eqv v v')
441 | (A.NegSubst(x,v),A.NegSubst(x',v')) -> (eqx x x') && (eqv v v')
442 | _ -> false
443 ;;
444
445 (* NOTE: functor *)
446 let eq_sub sub sub' = eq_subBy SUB.eq_mvar SUB.eq_val sub sub'
447
448 let eq_subst th th' = setequalBy eq_sub th th';;
449
450 let merge_subBy eqx (===) (>+<) sub sub' =
451 (* variable part is guaranteed to be the same *)
452 match (sub,sub') with
453 (A.Subst (x,v),A.Subst (x',v')) ->
454 if (v === v')
455 then Some [A.Subst(x, v >+< v')]
456 else None
457 | (A.NegSubst(x,v),A.Subst(x',v')) ->
458 if (not (v === v'))
459 then Some [A.Subst(x',v')]
460 else None
461 | (A.Subst(x,v),A.NegSubst(x',v')) ->
462 if (not (v === v'))
463 then Some [A.Subst(x,v)]
464 else None
465 | (A.NegSubst(x,v),A.NegSubst(x',v')) ->
466 if (v === v')
467 then
468 let merged = v >+< v' in
469 if merged = v && merged = v'
470 then Some [A.NegSubst(x,v >+< v')]
471 else
472 (* positions are compatible, but not identical. keep apart. *)
473 Some [A.NegSubst(x,v);A.NegSubst(x',v')]
474 else Some [A.NegSubst(x,v);A.NegSubst(x',v')]
475 ;;
476
477 (* NOTE: functor *)
478 (* How could we accomadate subterm constraints here??? *)
479 let merge_sub sub sub' =
480 merge_subBy SUB.eq_mvar SUB.eq_val SUB.merge_val sub sub'
481
482 let clean_substBy eq cmp theta = List.sort cmp (nubBy eq theta);;
483
484 (* NOTE: we sort by using the generic "compare" on (meta-)variable
485 * names; we could also require a definition of compare for meta-variables
486 * or substitutions but that seems like overkill for sorting
487 *)
488 let clean_subst theta =
489 let res =
490 clean_substBy eq_sub
491 (fun s s' ->
492 let res = compare (dom_sub s) (dom_sub s') in
493 if res = 0
494 then
495 match (s,s') with
496 (A.Subst(_,_),A.NegSubst(_,_)) -> -1
497 | (A.NegSubst(_,_),A.Subst(_,_)) -> 1
498 | _ -> compare (ran_sub s) (ran_sub s')
499 else res)
500 theta in
501 let rec loop = function
502 [] -> []
503 | (A.Subst(x,v)::A.NegSubst(y,v')::rest) when SUB.eq_mvar x y ->
504 loop (A.Subst(x,v)::rest)
505 | x::xs -> x::(loop xs) in
506 loop res
507
508 let top_subst = [];; (* Always TRUE subst. *)
509
510 (* Split a theta in two parts: one with (only) "x" and one without *)
511 (* NOTE: functor *)
512 let split_subst theta x =
513 partition (fun sub -> SUB.eq_mvar (dom_sub sub) x) theta;;
514
515 exception SUBST_MISMATCH
516 let conj_subst theta theta' =
517 match (theta,theta') with
518 | ([],_) -> Some theta'
519 | (_,[]) -> Some theta
520 | _ ->
521 let rec classify = function
522 [] -> []
523 | [x] -> [(dom_sub x,[x])]
524 | x::xs ->
525 (match classify xs with
526 ((nm,y)::ys) as res ->
527 if dom_sub x = nm
528 then (nm,x::y)::ys
529 else (dom_sub x,[x])::res
530 | _ -> failwith "not possible") in
531 let merge_all theta theta' =
532 foldl
533 (function rest ->
534 function sub ->
535 foldl
536 (function rest ->
537 function sub' ->
538 match (merge_sub sub sub') with
539 Some subs -> subs @ rest
540 | _ -> raise SUBST_MISMATCH)
541 rest theta')
542 [] theta in
543 let rec loop = function
544 ([],ctheta') ->
545 List.concat (List.map (function (_,ths) -> ths) ctheta')
546 | (ctheta,[]) ->
547 List.concat (List.map (function (_,ths) -> ths) ctheta)
548 | ((x,ths)::xs,(y,ths')::ys) ->
549 (match compare x y with
550 0 -> (merge_all ths ths') @ loop (xs,ys)
551 | -1 -> ths @ loop (xs,((y,ths')::ys))
552 | 1 -> ths' @ loop (((x,ths)::xs),ys)
553 | _ -> failwith "not possible") in
554 try Some (clean_subst(loop (classify theta, classify theta')))
555 with SUBST_MISMATCH -> None
556 ;;
557
558 (* theta' must be a subset of theta *)
559 let conj_subst_none theta theta' =
560 match (theta,theta') with
561 | (_,[]) -> Some theta
562 | ([],_) -> None
563 | _ ->
564 let rec classify = function
565 [] -> []
566 | [x] -> [(dom_sub x,[x])]
567 | x::xs ->
568 (match classify xs with
569 ((nm,y)::ys) as res ->
570 if dom_sub x = nm
571 then (nm,x::y)::ys
572 else (dom_sub x,[x])::res
573 | _ -> failwith "not possible") in
574 let merge_all theta theta' =
575 foldl
576 (function rest ->
577 function sub ->
578 foldl
579 (function rest ->
580 function sub' ->
581 match (merge_sub sub sub') with
582 Some subs -> subs @ rest
583 | _ -> raise SUBST_MISMATCH)
584 rest theta')
585 [] theta in
586 let rec loop = function
587 (ctheta,[]) ->
588 List.concat (List.map (function (_,ths) -> ths) ctheta)
589 | ([],ctheta') -> raise SUBST_MISMATCH
590 | ((x,ths)::xs,(y,ths')::ys) ->
591 (match compare x y with
592 0 -> (merge_all ths ths') @ loop (xs,ys)
593 | -1 -> ths @ loop (xs,((y,ths')::ys))
594 | 1 -> raise SUBST_MISMATCH
595 | _ -> failwith "not possible") in
596 try Some (clean_subst(loop (classify theta, classify theta')))
597 with SUBST_MISMATCH -> None
598 ;;
599
600 let negate_sub sub =
601 match sub with
602 | A.Subst(x,v) -> A.NegSubst (x,v)
603 | A.NegSubst(x,v) -> A.Subst(x,v)
604 ;;
605
606 (* Turn a (big) theta into a list of (small) thetas *)
607 let negate_subst theta = (map (fun sub -> [negate_sub sub]) theta);;
608
609
610 (* ************************* *)
611 (* Witnesses *)
612 (* ************************* *)
613
614 (* Always TRUE witness *)
615 let top_wit = ([] : (('pred, 'anno) witness list));;
616
617 let eq_wit wit wit' = wit = wit';;
618
619 let union_wit wit wit' = (*List.sort compare (wit' @ wit) for popl*)
620 let res = unionBy compare (=) wit wit' in
621 let anynegwit = (* if any is neg, then all are *)
622 List.exists (function A.NegWit _ -> true | A.Wit _ -> false) in
623 if anynegwit res
624 then List.filter (function A.NegWit _ -> true | A.Wit _ -> false) res
625 else res
626
627 let negate_wit wit = A.NegWit wit (*
628 match wit with
629 | A.Wit(s,th,anno,ws) -> A.NegWitWit(s,th,anno,ws)
630 | A.NegWitWit(s,th,anno,ws) -> A.Wit(s,th,anno,ws)*)
631 ;;
632
633 let negate_wits wits =
634 List.sort compare (map (fun wit -> [negate_wit wit]) wits);;
635
636 let unwitify trips =
637 let anynegwit = (* if any is neg, then all are *)
638 List.exists (function A.NegWit _ -> true | A.Wit _ -> false) in
639 setify
640 (List.fold_left
641 (function prev ->
642 function (s,th,wit) ->
643 if anynegwit wit then prev else (s,th,top_wit)::prev)
644 [] trips)
645
646 (* ************************* *)
647 (* Triples *)
648 (* ************************* *)
649
650 (* Triples are equal when the constituents are equal *)
651 let eq_trip (s,th,wit) (s',th',wit') =
652 (s = s') && (eq_wit wit wit') && (eq_subst th th');;
653
654 let triples_top states = map (fun s -> (s,top_subst,top_wit)) states;;
655
656 let normalize trips =
657 List.map
658 (function (st,th,wit) -> (st,List.sort compare th,List.sort compare wit))
659 trips
660
661
662 (* conj opt doesn't work ((1,[],{{x=3}}) v (1,[],{{x=4}})) & (1,[],{{x=4}}) =
663 (1,[],{{x=3},{x=4}}), not (1,[],{{x=4}}) *)
664 let triples_conj trips trips' =
665 let (trips,shared,trips') =
666 if false && !pTRIPLES_CONJ_OPT (* see comment above *)
667 then
668 let (shared,trips) =
669 List.partition (function t -> List.mem t trips') trips in
670 let trips' =
671 List.filter (function t -> not(List.mem t shared)) trips' in
672 (trips,shared,trips')
673 else (trips,[],trips') in
674 foldl (* returns a set - setify inlined *)
675 (function rest ->
676 function (s1,th1,wit1) ->
677 foldl
678 (function rest ->
679 function (s2,th2,wit2) ->
680 if (s1 = s2) then
681 (match (conj_subst th1 th2) with
682 Some th ->
683 let t = (s1,th,union_wit wit1 wit2) in
684 if List.mem t rest then rest else t::rest
685 | _ -> rest)
686 else rest)
687 rest trips')
688 shared trips
689 ;;
690
691 (* ignore the state in the right argument. always pretend it is the same as
692 the left one *)
693 (* env on right has to be a subset of env on left *)
694 let triples_conj_none trips trips' =
695 let (trips,shared,trips') =
696 if false && !pTRIPLES_CONJ_OPT (* see comment above *)
697 then
698 let (shared,trips) =
699 List.partition (function t -> List.mem t trips') trips in
700 let trips' =
701 List.filter (function t -> not(List.mem t shared)) trips' in
702 (trips,shared,trips')
703 else (trips,[],trips') in
704 foldl (* returns a set - setify inlined *)
705 (function rest ->
706 function (s1,th1,wit1) ->
707 foldl
708 (function rest ->
709 function (s2,th2,wit2) ->
710 match (conj_subst_none th1 th2) with
711 Some th ->
712 let t = (s1,th,union_wit wit1 wit2) in
713 if List.mem t rest then rest else t::rest
714 | _ -> rest)
715 rest trips')
716 shared trips
717 ;;
718
719 exception AW
720
721 let triples_conj_AW trips trips' =
722 let (trips,shared,trips') =
723 if false && !pTRIPLES_CONJ_OPT
724 then
725 let (shared,trips) =
726 List.partition (function t -> List.mem t trips') trips in
727 let trips' =
728 List.filter (function t -> not(List.mem t shared)) trips' in
729 (trips,shared,trips')
730 else (trips,[],trips') in
731 foldl (* returns a set - setify inlined *)
732 (function rest ->
733 function (s1,th1,wit1) ->
734 foldl
735 (function rest ->
736 function (s2,th2,wit2) ->
737 if (s1 = s2) then
738 (match (conj_subst th1 th2) with
739 Some th ->
740 let t = (s1,th,union_wit wit1 wit2) in
741 if List.mem t rest then rest else t::rest
742 | _ -> raise AW)
743 else rest)
744 rest trips')
745 shared trips
746 ;;
747
748 (* *************************** *)
749 (* NEGATION (NegState style) *)
750 (* *************************** *)
751
752 (* Constructive negation at the state level *)
753 type ('a) state =
754 PosState of 'a
755 | NegState of 'a list
756 ;;
757
758 let compatible_states = function
759 (PosState s1, PosState s2) ->
760 if s1 = s2 then Some (PosState s1) else None
761 | (PosState s1, NegState s2) ->
762 if List.mem s1 s2 then None else Some (PosState s1)
763 | (NegState s1, PosState s2) ->
764 if List.mem s2 s1 then None else Some (PosState s2)
765 | (NegState s1, NegState s2) -> Some (NegState (s1 @ s2))
766 ;;
767
768 (* Conjunction on triples with "special states" *)
769 let triples_state_conj trips trips' =
770 let (trips,shared,trips') =
771 if !pTRIPLES_CONJ_OPT
772 then
773 let (shared,trips) =
774 List.partition (function t -> List.mem t trips') trips in
775 let trips' =
776 List.filter (function t -> not(List.mem t shared)) trips' in
777 (trips,shared,trips')
778 else (trips,[],trips') in
779 foldl
780 (function rest ->
781 function (s1,th1,wit1) ->
782 foldl
783 (function rest ->
784 function (s2,th2,wit2) ->
785 match compatible_states(s1,s2) with
786 Some s ->
787 (match (conj_subst th1 th2) with
788 Some th ->
789 let t = (s,th,union_wit wit1 wit2) in
790 if List.mem t rest then rest else t::rest
791 | _ -> rest)
792 | _ -> rest)
793 rest trips')
794 shared trips
795 ;;
796
797 let triple_negate (s,th,wits) =
798 let negstates = (NegState [s],top_subst,top_wit) in
799 let negths = map (fun th -> (PosState s,th,top_wit)) (negate_subst th) in
800 let negwits = map (fun nwit -> (PosState s,th,nwit)) (negate_wits wits) in
801 negstates :: (negths @ negwits) (* all different *)
802
803 (* FIX ME: it is not necessary to do full conjunction *)
804 let triples_complement states (trips : ('pred, 'anno) triples) =
805 if !pTRIPLES_COMPLEMENT_OPT
806 then
807 (let cleanup (s,th,wit) =
808 match s with
809 PosState s' -> [(s',th,wit)]
810 | NegState ss ->
811 assert (th=top_subst);
812 assert (wit=top_wit);
813 map (fun st -> (st,top_subst,top_wit)) (setdiff states ss) in
814 let (simple,complex) =
815 if !pTRIPLES_COMPLEMENT_SIMPLE_OPT
816 then
817 let (simple,complex) =
818 List.partition (function (s,[],[]) -> true | _ -> false) trips in
819 let simple =
820 [(NegState(List.map (function (s,_,_) -> s) simple),
821 top_subst,top_wit)] in
822 (simple,complex)
823 else ([(NegState [],top_subst,top_wit)],trips) in
824 let rec compl trips =
825 match trips with
826 [] -> simple
827 | (t::ts) -> triples_state_conj (triple_negate t) (compl ts) in
828 let compld = (compl complex) in
829 let compld = concatmap cleanup compld in
830 compld)
831 else
832 let negstates (st,th,wits) =
833 map (function st -> (st,top_subst,top_wit)) (setdiff states [st]) in
834 let negths (st,th,wits) =
835 map (function th -> (st,th,top_wit)) (negate_subst th) in
836 let negwits (st,th,wits) =
837 map (function nwit -> (st,th,nwit)) (negate_wits wits) in
838 match trips with
839 [] -> map (function st -> (st,top_subst,top_wit)) states
840 | x::xs ->
841 setify
842 (foldl
843 (function prev ->
844 function cur ->
845 triples_conj (negstates cur @ negths cur @ negwits cur) prev)
846 (negstates x @ negths x @ negwits x) xs)
847 ;;
848
849 let triple_negate (s,th,wits) =
850 let negths = map (fun th -> (s,th,top_wit)) (negate_subst th) in
851 let negwits = map (fun nwit -> (s,th,nwit)) (negate_wits wits) in
852 ([s], negths @ negwits) (* all different *)
853
854 let print_compl_state str (n,p) =
855 Printf.printf "%s neg: " str;
856 List.iter
857 (function x -> G.print_node x; Format.print_flush(); Printf.printf " ")
858 n;
859 Printf.printf "\n";
860 print_state "pos" p
861
862 let triples_complement states (trips : ('pred, 'anno) triples) =
863 if trips = []
864 then map (function st -> (st,top_subst,top_wit)) states
865 else
866 let cleanup (neg,pos) =
867 let keep_pos =
868 List.filter (function (s,_,_) -> List.mem s neg) pos in
869 (map (fun st -> (st,top_subst,top_wit)) (setdiff states neg)) @
870 keep_pos in
871 let trips = List.sort state_compare trips in
872 let all_negated = List.map triple_negate trips in
873 let merge_one (neg1,pos1) (neg2,pos2) =
874 let (pos1conj,pos1keep) =
875 List.partition (function (s,_,_) -> List.mem s neg2) pos1 in
876 let (pos2conj,pos2keep) =
877 List.partition (function (s,_,_) -> List.mem s neg1) pos2 in
878 (Common.union_set neg1 neg2,
879 (triples_conj pos1conj pos2conj) @ pos1keep @ pos2keep) in
880 let rec inner_loop = function
881 x1::x2::rest -> (merge_one x1 x2) :: (inner_loop rest)
882 | l -> l in
883 let rec outer_loop = function
884 [x] -> x
885 | l -> outer_loop (inner_loop l) in
886 cleanup (outer_loop all_negated)
887
888 (* ********************************** *)
889 (* END OF NEGATION (NegState style) *)
890 (* ********************************** *)
891
892 (* now this is always true, so we could get rid of it *)
893 let something_dropped = ref true
894
895 let triples_union trips trips' =
896 (*unionBy compare eq_trip trips trips';;*)
897 (* returns -1 is t1 > t2, 1 if t2 >= t1, and 0 otherwise *)
898 (*
899 The following does not work. Suppose we have ([x->3],{A}) and ([],{A,B}).
900 Then, the following says that since the first is a more restrictive
901 environment and has fewer witnesses, then it should be dropped. But having
902 fewer witnesses is not necessarily less informative than having more,
903 because fewer witnesses can mean the absence of the witness-causing thing.
904 So the fewer witnesses have to be kept around.
905 subseteq changed to = to make it hopefully work
906 *)
907 if !pNEW_INFO_OPT
908 then
909 begin
910 something_dropped := false;
911 if trips = trips'
912 then (something_dropped := true; trips)
913 else
914 let subsumes (s1,th1,wit1) (s2,th2,wit2) =
915 if s1 = s2
916 then
917 (match conj_subst th1 th2 with
918 Some conj ->
919 if conj = th1
920 then if (*subseteq*) wit1 = wit2 then 1 else 0
921 else
922 if conj = th2
923 then if (*subseteq*) wit2 = wit1 then (-1) else 0
924 else 0
925 | None -> 0)
926 else 0 in
927 let rec first_loop second = function
928 [] -> second
929 | x::xs -> first_loop (second_loop x second) xs
930 and second_loop x = function
931 [] -> [x]
932 | (y::ys) as all ->
933 match subsumes x y with
934 1 -> something_dropped := true; all
935 | (-1) -> second_loop x ys
936 | _ -> y::(second_loop x ys) in
937 first_loop trips trips'
938 end
939 else unionBy compare eq_trip trips trips'
940
941
942 let triples_witness x unchecked not_keep trips =
943 let anyneg = (* if any is neg, then all are *)
944 List.exists (function A.NegSubst _ -> true | A.Subst _ -> false) in
945 let anynegwit = (* if any is neg, then all are *)
946 List.exists (function A.NegWit _ -> true | A.Wit _ -> false) in
947 let allnegwit = (* if any is neg, then all are *)
948 List.for_all (function A.NegWit _ -> true | A.Wit _ -> false) in
949 let negtopos =
950 List.map (function A.NegWit w -> w | A.Wit _ -> failwith "bad wit")in
951 let res =
952 List.fold_left
953 (function prev ->
954 function (s,th,wit) as t ->
955 let (th_x,newth) = split_subst th x in
956 match th_x with
957 [] ->
958 (* one consider whether if not not_keep is true, then we should
959 fail. but it could be that the variable is a used_after and
960 then it is the later rule that should fail and not this one *)
961 if not not_keep && !Flag_ctl.verbose_ctl_engine
962 then
963 (SUB.print_mvar x; Format.print_flush();
964 print_state ": empty witness from" [t]);
965 t::prev
966 | l when anyneg l && !pANY_NEG_OPT -> prev
967 (* see tests/nestseq for how neg bindings can come up even
968 without eg partial matches
969 (* negated substitution only allowed with negwits.
970 just dropped *)
971 if anynegwit wit && allnegwit wit (* nonempty negwit list *)
972 then prev
973 else
974 (print_generic_substitution l; Format.print_newline();
975 failwith"unexpected negative binding with positive witnesses")*)
976 | _ ->
977 let new_triple =
978 if unchecked or not_keep
979 then (s,newth,wit)
980 else
981 if anynegwit wit && allnegwit wit
982 then (s,newth,[A.NegWit(A.Wit(s,th_x,[],negtopos wit))])
983 else (s,newth,[A.Wit(s,th_x,[],wit)]) in
984 new_triple::prev)
985 [] trips in
986 if unchecked || !Flag_ctl.partial_match (* the only way to have a NegWit *)
987 then setify res
988 else List.rev res
989 ;;
990
991
992 (* ---------------------------------------------------------------------- *)
993 (* SAT - Model Checking Algorithm for CTL-FVex *)
994 (* *)
995 (* TODO: Implement _all_ operators (directly) *)
996 (* ---------------------------------------------------------------------- *)
997
998
999 (* ************************************* *)
1000 (* The SAT algorithm and special helpers *)
1001 (* ************************************* *)
1002
1003 let rec pre_exist dir (grp,_,_) y reqst =
1004 let check s =
1005 match reqst with None -> true | Some reqst -> List.mem s reqst in
1006 let exp (s,th,wit) =
1007 concatmap
1008 (fun s' -> if check s' then [(s',th,wit)] else [])
1009 (match dir with
1010 A.FORWARD -> G.predecessors grp s
1011 | A.BACKWARD -> G.successors grp s) in
1012 setify (concatmap exp y)
1013 ;;
1014
1015 exception Empty
1016
1017 let pre_forall dir (grp,_,states) y all reqst =
1018 let check s =
1019 match reqst with
1020 None -> true | Some reqst -> List.mem s reqst in
1021 let pred =
1022 match dir with
1023 A.FORWARD -> G.predecessors | A.BACKWARD -> G.successors in
1024 let succ =
1025 match dir with
1026 A.FORWARD -> G.successors | A.BACKWARD -> G.predecessors in
1027 let neighbors =
1028 List.map
1029 (function p -> (p,succ grp p))
1030 (setify
1031 (concatmap
1032 (function (s,_,_) -> List.filter check (pred grp s)) y)) in
1033 (* would a hash table be more efficient? *)
1034 let all = List.sort state_compare all in
1035 let rec up_nodes child s = function
1036 [] -> []
1037 | (s1,th,wit)::xs ->
1038 (match compare s1 child with
1039 -1 -> up_nodes child s xs
1040 | 0 -> (s,th,wit)::(up_nodes child s xs)
1041 | _ -> []) in
1042 let neighbor_triples =
1043 List.fold_left
1044 (function rest ->
1045 function (s,children) ->
1046 try
1047 (List.map
1048 (function child ->
1049 match up_nodes child s all with [] -> raise Empty | l -> l)
1050 children) :: rest
1051 with Empty -> rest)
1052 [] neighbors in
1053 match neighbor_triples with
1054 [] -> []
1055 | _ ->
1056 (*normalize*)
1057 (foldl1 (@) (List.map (foldl1 triples_conj) neighbor_triples))
1058
1059 let pre_forall_AW dir (grp,_,states) y all reqst =
1060 let check s =
1061 match reqst with
1062 None -> true | Some reqst -> List.mem s reqst in
1063 let pred =
1064 match dir with
1065 A.FORWARD -> G.predecessors | A.BACKWARD -> G.successors in
1066 let succ =
1067 match dir with
1068 A.FORWARD -> G.successors | A.BACKWARD -> G.predecessors in
1069 let neighbors =
1070 List.map
1071 (function p -> (p,succ grp p))
1072 (setify
1073 (concatmap
1074 (function (s,_,_) -> List.filter check (pred grp s)) y)) in
1075 (* would a hash table be more efficient? *)
1076 let all = List.sort state_compare all in
1077 let rec up_nodes child s = function
1078 [] -> []
1079 | (s1,th,wit)::xs ->
1080 (match compare s1 child with
1081 -1 -> up_nodes child s xs
1082 | 0 -> (s,th,wit)::(up_nodes child s xs)
1083 | _ -> []) in
1084 let neighbor_triples =
1085 List.fold_left
1086 (function rest ->
1087 function (s,children) ->
1088 (List.map
1089 (function child ->
1090 match up_nodes child s all with [] -> raise AW | l -> l)
1091 children) :: rest)
1092 [] neighbors in
1093 match neighbor_triples with
1094 [] -> []
1095 | _ -> foldl1 (@) (List.map (foldl1 triples_conj_AW) neighbor_triples)
1096
1097 (* drop_negwits will call setify *)
1098 let satEX dir m s reqst = pre_exist dir m s reqst;;
1099
1100 let satAX dir m s reqst = pre_forall dir m s s reqst
1101 ;;
1102
1103 (* E[phi1 U phi2] == phi2 \/ (phi1 /\ EXE[phi1 U phi2]) *)
1104 let satEU dir ((_,_,states) as m) s1 s2 reqst print_graph =
1105 (*Printf.printf "EU\n";
1106 let ctr = ref 0 in*)
1107 inc satEU_calls;
1108 if s1 = []
1109 then s2
1110 else
1111 (*let ctr = ref 0 in*)
1112 if !pNEW_INFO_OPT
1113 then
1114 let rec f y new_info =
1115 inc_step();
1116 match new_info with
1117 [] -> y
1118 | new_info ->
1119 (*ctr := !ctr + 1;
1120 print_graph y ctr;*)
1121 let first = triples_conj s1 (pre_exist dir m new_info reqst) in
1122 let res = triples_union first y in
1123 let new_info = setdiff res y in
1124 (*Printf.printf "iter %d res %d new_info %d\n"
1125 !ctr (List.length res) (List.length new_info);
1126 print_state "res" res;
1127 print_state "new_info" new_info;
1128 flush stdout;*)
1129 f res new_info in
1130 f s2 s2
1131 else
1132 let f y =
1133 inc_step();
1134 (*ctr := !ctr + 1;
1135 print_graph y ctr;*)
1136 let pre = pre_exist dir m y reqst in
1137 triples_union s2 (triples_conj s1 pre) in
1138 setfix f s2
1139 ;;
1140
1141 (* EF phi == E[true U phi] *)
1142 let satEF dir m s2 reqst =
1143 inc satEF_calls;
1144 (*let ctr = ref 0 in*)
1145 if !pNEW_INFO_OPT
1146 then
1147 let rec f y new_info =
1148 inc_step();
1149 match new_info with
1150 [] -> y
1151 | new_info ->
1152 (*ctr := !ctr + 1;
1153 print_state (Printf.sprintf "iteration %d\n" !ctr) y;*)
1154 let first = pre_exist dir m new_info reqst in
1155 let res = triples_union first y in
1156 let new_info = setdiff res y in
1157 (*Printf.printf "EF %s iter %d res %d new_info %d\n"
1158 (if dir = A.BACKWARD then "reachable" else "real ef")
1159 !ctr (List.length res) (List.length new_info);
1160 print_state "new info" new_info;
1161 flush stdout;*)
1162 f res new_info in
1163 f s2 s2
1164 else
1165 let f y =
1166 inc_step();
1167 let pre = pre_exist dir m y reqst in
1168 triples_union s2 pre in
1169 setfix f s2
1170
1171
1172 type ('pred,'anno) auok =
1173 AUok of ('pred,'anno) triples | AUfailed of ('pred,'anno) triples
1174
1175 (* A[phi1 U phi2] == phi2 \/ (phi1 /\ AXA[phi1 U phi2]) *)
1176 let satAU dir ((cfg,_,states) as m) s1 s2 reqst print_graph =
1177 let ctr = ref 0 in
1178 inc satAU_calls;
1179 if s1 = []
1180 then AUok s2
1181 else
1182 (*let ctr = ref 0 in*)
1183 let pre_forall =
1184 if !Flag_ctl.loop_in_src_code
1185 then pre_forall_AW
1186 else pre_forall in
1187 if !pNEW_INFO_OPT
1188 then
1189 let rec f y newinfo =
1190 inc_step();
1191 match newinfo with
1192 [] -> AUok y
1193 | new_info ->
1194 ctr := !ctr + 1;
1195 (*print_state (Printf.sprintf "iteration %d\n" !ctr) y;
1196 flush stdout;*)
1197 print_graph y ctr;
1198 let pre =
1199 try Some (pre_forall dir m new_info y reqst)
1200 with AW -> None in
1201 match pre with
1202 None -> AUfailed y
1203 | Some pre ->
1204 match triples_conj s1 pre with
1205 [] -> AUok y
1206 | first ->
1207 (*print_state "s1" s1;
1208 print_state "pre" pre;
1209 print_state "first" first;*)
1210 let res = triples_union first y in
1211 let new_info =
1212 if not !something_dropped
1213 then first
1214 else setdiff res y in
1215 (*Printf.printf
1216 "iter %d res %d new_info %d\n"
1217 !ctr (List.length res) (List.length new_info);
1218 flush stdout;*)
1219 f res new_info in
1220 f s2 s2
1221 else
1222 if !Flag_ctl.loop_in_src_code
1223 then AUfailed s2
1224 else
1225 (*let setfix =
1226 fix (function s1 -> function s2 ->
1227 let s1 = List.map (function (s,th,w) -> (s,th,nub w)) s1 in
1228 let s2 = List.map (function (s,th,w) -> (s,th,nub w)) s2 in
1229 subseteq s1 s2) in for popl *)
1230 let f y =
1231 inc_step();
1232 ctr := !ctr + 1;
1233 print_graph y ctr;
1234 let pre = pre_forall dir m y y reqst in
1235 triples_union s2 (triples_conj s1 pre) in
1236 AUok (setfix f s2)
1237 ;;
1238
1239
1240 (* reqst could be the states of s1 *)
1241 (*
1242 let lstates = mkstates states reqst in
1243 let initial_removed =
1244 triples_complement lstates (triples_union s1 s2) in
1245 let initial_base = triples_conj s1 (triples_complement lstates s2) in
1246 let rec loop base removed =
1247 let new_removed =
1248 triples_conj base (pre_exist dir m removed reqst) in
1249 let new_base =
1250 triples_conj base (triples_complement lstates new_removed) in
1251 if supseteq new_base base
1252 then triples_union base s2
1253 else loop new_base new_removed in
1254 loop initial_base initial_removed *)
1255
1256 let satAW dir ((grp,_,states) as m) s1 s2 reqst =
1257 inc satAW_calls;
1258 if s1 = []
1259 then s2
1260 else
1261 (*
1262 This works extremely badly when the region is small and the end of the
1263 region is very ambiguous, eg free(x) ... x
1264 see free.c
1265 if !pNEW_INFO_OPT
1266 then
1267 let get_states l = setify(List.map (function (s,_,_) -> s) l) in
1268 let ostates = Common.union_set (get_states s1) (get_states s2) in
1269 let succ =
1270 (match dir with
1271 A.FORWARD -> G.successors grp
1272 | A.BACKWARD -> G.predecessors grp) in
1273 let states =
1274 List.fold_left Common.union_set ostates (List.map succ ostates) in
1275 let negphi = triples_complement states s1 in
1276 let negpsi = triples_complement states s2 in
1277 triples_complement ostates
1278 (satEU dir m negpsi (triples_conj negphi negpsi) (Some ostates))
1279 else
1280 *)
1281 (*let ctr = ref 0 in*)
1282 let f y =
1283 inc_step();
1284 (*ctr := !ctr + 1;
1285 Printf.printf "iter %d y %d\n" !ctr (List.length y);
1286 print_state "y" y;
1287 flush stdout;*)
1288 let pre = pre_forall dir m y y reqst in
1289 (*print_state "pre" pre;*)
1290 let conj = triples_conj s1 pre in (* or triples_conj_AW *)
1291 triples_union s2 conj in
1292 let drop_wits = List.map (function (s,e,_) -> (s,e,[])) in
1293 (* drop wits on s1 represents that we don't want any witnesses from
1294 the case that infinitely loops, only from the case that gets
1295 out of the loop. s1 is like a guard. To see the problem, consider
1296 an example where both s1 and s2 match some code after the loop.
1297 we only want the witness from s2. *)
1298 setgfix f (triples_union (nub(drop_wits s1)) s2)
1299 ;;
1300
1301 let satAF dir m s reqst =
1302 inc satAF_calls;
1303 if !pNEW_INFO_OPT
1304 then
1305 let rec f y newinfo =
1306 inc_step();
1307 match newinfo with
1308 [] -> y
1309 | new_info ->
1310 let first = pre_forall dir m new_info y reqst in
1311 let res = triples_union first y in
1312 let new_info = setdiff res y in
1313 f res new_info in
1314 f s s
1315 else
1316 let f y =
1317 inc_step();
1318 let pre = pre_forall dir m y y reqst in
1319 triples_union s pre in
1320 setfix f s
1321
1322 let satAG dir ((_,_,states) as m) s reqst =
1323 inc satAG_calls;
1324 let f y =
1325 inc_step();
1326 let pre = pre_forall dir m y y reqst in
1327 triples_conj y pre in
1328 setgfix f s
1329
1330 let satEG dir ((_,_,states) as m) s reqst =
1331 inc satEG_calls;
1332 let f y =
1333 inc_step();
1334 let pre = pre_exist dir m y reqst in
1335 triples_conj y pre in
1336 setgfix f s
1337
1338 (* **************************************************************** *)
1339 (* Inner And - a way of dealing with multiple matches within a node *)
1340 (* **************************************************************** *)
1341 (* applied to the result of matching a node. collect witnesses when the
1342 states and environments are the same *)
1343 (* not a good idea, poses problem for unparsing, because don't realize that
1344 adjacent things come from different matches, leading to loss of newlines etc.
1345 exple struct I { ... - int x; + int y; ...} *)
1346
1347 let inner_and trips = trips (*
1348 let rec loop = function
1349 [] -> ([],[])
1350 | (s,th,w)::trips ->
1351 let (cur,acc) = loop trips in
1352 (match cur with
1353 (s',_,_)::_ when s = s' ->
1354 let rec loop' = function
1355 [] -> [(s,th,w)]
1356 | ((_,th',w') as t')::ts' ->
1357 (match conj_subst th th' with
1358 Some th'' -> (s,th'',union_wit w w')::ts'
1359 | None -> t'::(loop' ts')) in
1360 (loop' cur,acc)
1361 | _ -> ([(s,th,w)],cur@acc)) in
1362 let (cur,acc) =
1363 loop (List.sort state_compare trips) (* is this sort needed? *) in
1364 cur@acc *)
1365
1366 (* *************** *)
1367 (* Partial matches *)
1368 (* *************** *)
1369
1370 let filter_conj states unwanted partial_matches =
1371 let x =
1372 triples_conj (triples_complement states (unwitify unwanted))
1373 partial_matches in
1374 triples_conj (unwitify x) (triples_complement states x)
1375
1376 let strict_triples_conj strict states trips trips' =
1377 let res = triples_conj trips trips' in
1378 if !Flag_ctl.partial_match && strict = A.STRICT
1379 then
1380 let fail_left = filter_conj states trips trips' in
1381 let fail_right = filter_conj states trips' trips in
1382 let ors = triples_union fail_left fail_right in
1383 triples_union res ors
1384 else res
1385
1386 let strict_triples_conj_none strict states trips trips' =
1387 let res = triples_conj_none trips trips' in
1388 if !Flag_ctl.partial_match && strict = A.STRICT
1389 then
1390 let fail_left = filter_conj states trips trips' in
1391 let fail_right = filter_conj states trips' trips in
1392 let ors = triples_union fail_left fail_right in
1393 triples_union res ors
1394 else res
1395
1396 let left_strict_triples_conj strict states trips trips' =
1397 let res = triples_conj trips trips' in
1398 if !Flag_ctl.partial_match && strict = A.STRICT
1399 then
1400 let fail_left = filter_conj states trips trips' in
1401 triples_union res fail_left
1402 else res
1403
1404 let strict_A1 strict op failop dir ((_,_,states) as m) trips required_states =
1405 let res = op dir m trips required_states in
1406 if !Flag_ctl.partial_match && strict = A.STRICT
1407 then
1408 let states = mkstates states required_states in
1409 let fail = filter_conj states res (failop dir m trips required_states) in
1410 triples_union res fail
1411 else res
1412
1413 let strict_A2 strict op failop dir ((_,_,states) as m) trips trips'
1414 required_states =
1415 let res = op dir m trips trips' required_states in
1416 if !Flag_ctl.partial_match && strict = A.STRICT
1417 then
1418 let states = mkstates states required_states in
1419 let fail = filter_conj states res (failop dir m trips' required_states) in
1420 triples_union res fail
1421 else res
1422
1423 let strict_A2au strict op failop dir ((_,_,states) as m) trips trips'
1424 required_states print_graph =
1425 match op dir m trips trips' required_states print_graph with
1426 AUok res ->
1427 if !Flag_ctl.partial_match && strict = A.STRICT
1428 then
1429 let states = mkstates states required_states in
1430 let fail =
1431 filter_conj states res (failop dir m trips' required_states) in
1432 AUok (triples_union res fail)
1433 else AUok res
1434 | AUfailed res -> AUfailed res
1435
1436 (* ********************* *)
1437 (* Environment functions *)
1438 (* ********************* *)
1439
1440 let drop_wits required_states s phi =
1441 match required_states with
1442 None -> s
1443 | Some states -> List.filter (function (s,_,_) -> List.mem s states) s
1444
1445
1446 let print_required required =
1447 List.iter
1448 (function l ->
1449 Format.print_string "{";
1450 List.iter
1451 (function reqd ->
1452 print_generic_substitution reqd; Format.print_newline())
1453 l;
1454 Format.print_string "}";
1455 Format.print_newline())
1456 required
1457
1458 exception Too_long
1459
1460 let extend_required trips required =
1461 if !Flag_ctl.partial_match
1462 then required
1463 else
1464 if !pREQUIRED_ENV_OPT
1465 then
1466 (* make it a set *)
1467 let envs =
1468 List.fold_left
1469 (function rest ->
1470 function (_,t,_) -> if List.mem t rest then rest else t::rest)
1471 [] trips in
1472 let envs = if List.mem [] envs then [] else envs in
1473 match (envs,required) with
1474 ([],_) -> required
1475 | (envs,hd::tl) ->
1476 (try
1477 let hdln = List.length hd + 5 (* let it grow a little bit *) in
1478 let (_,merged) =
1479 let add x (ln,y) =
1480 if List.mem x y
1481 then (ln,y)
1482 else if ln + 1 > hdln then raise Too_long else (ln+1,x::y) in
1483 foldl
1484 (function rest ->
1485 function t ->
1486 foldl
1487 (function rest ->
1488 function r ->
1489 match conj_subst t r with
1490 None -> rest | Some th -> add th rest)
1491 rest hd)
1492 (0,[]) envs in
1493 merged :: tl
1494 with Too_long -> envs :: required)
1495 | (envs,_) -> envs :: required
1496 else required
1497
1498 let drop_required v required =
1499 if !pREQUIRED_ENV_OPT
1500 then
1501 let res =
1502 inner_setify
1503 (List.map
1504 (function l ->
1505 inner_setify
1506 (List.map (List.filter (function sub -> not(dom_sub sub = v))) l))
1507 required) in
1508 (* check whether an entry has become useless *)
1509 List.filter (function l -> not (List.exists (function x -> x = []) l)) res
1510 else required
1511
1512 (* no idea how to write this function ... *)
1513 let memo_label =
1514 (Hashtbl.create(50) : (P.t, (G.node * substitution) list) Hashtbl.t)
1515
1516 let satLabel label required p =
1517 let triples =
1518 if !pSATLABEL_MEMO_OPT
1519 then
1520 try
1521 let states_subs = Hashtbl.find memo_label p in
1522 List.map (function (st,th) -> (st,th,[])) states_subs
1523 with
1524 Not_found ->
1525 let triples = setify(label p) in
1526 Hashtbl.add memo_label p
1527 (List.map (function (st,th,_) -> (st,th)) triples);
1528 triples
1529 else setify(label p) in
1530 (* normalize first; conj_subst relies on sorting *)
1531 let ntriples = normalize triples in
1532 if !pREQUIRED_ENV_OPT
1533 then
1534 foldl
1535 (function rest ->
1536 function ((s,th,_) as t) ->
1537 if List.for_all
1538 (List.exists (function th' -> not(conj_subst th th' = None)))
1539 required
1540 then t::rest
1541 else rest)
1542 [] ntriples
1543 else ntriples
1544
1545 let get_required_states l =
1546 if !pREQUIRED_STATES_OPT && not !Flag_ctl.partial_match
1547 then
1548 Some(inner_setify (List.map (function (s,_,_) -> s) l))
1549 else None
1550
1551 let get_children_required_states dir (grp,_,_) required_states =
1552 if !pREQUIRED_STATES_OPT && not !Flag_ctl.partial_match
1553 then
1554 match required_states with
1555 None -> None
1556 | Some states ->
1557 let fn =
1558 match dir with
1559 A.FORWARD -> G.successors
1560 | A.BACKWARD -> G.predecessors in
1561 Some (inner_setify (List.concat (List.map (fn grp) states)))
1562 else None
1563
1564 let reachable_table =
1565 (Hashtbl.create(50) : (G.node * A.direction, G.node list) Hashtbl.t)
1566
1567 (* like satEF, but specialized for get_reachable *)
1568 let reachsatEF dir (grp,_,_) s2 =
1569 let dirop =
1570 match dir with A.FORWARD -> G.successors | A.BACKWARD -> G.predecessors in
1571 let union = unionBy compare (=) in
1572 let rec f y = function
1573 [] -> y
1574 | new_info ->
1575 let (pre_collected,new_info) =
1576 List.partition (function Common.Left x -> true | _ -> false)
1577 (List.map
1578 (function x ->
1579 try Common.Left (Hashtbl.find reachable_table (x,dir))
1580 with Not_found -> Common.Right x)
1581 new_info) in
1582 let y =
1583 List.fold_left
1584 (function rest ->
1585 function Common.Left x -> union x rest
1586 | _ -> failwith "not possible")
1587 y pre_collected in
1588 let new_info =
1589 List.map
1590 (function Common.Right x -> x | _ -> failwith "not possible")
1591 new_info in
1592 let first = inner_setify (concatmap (dirop grp) new_info) in
1593 let new_info = setdiff first y in
1594 let res = new_info @ y in
1595 f res new_info in
1596 List.rev(f s2 s2) (* put root first *)
1597
1598 let get_reachable dir m required_states =
1599 match required_states with
1600 None -> None
1601 | Some states ->
1602 Some
1603 (List.fold_left
1604 (function rest ->
1605 function cur ->
1606 if List.mem cur rest
1607 then rest
1608 else
1609 Common.union_set
1610 (try Hashtbl.find reachable_table (cur,dir)
1611 with
1612 Not_found ->
1613 let states = reachsatEF dir m [cur] in
1614 Hashtbl.add reachable_table (cur,dir) states;
1615 states)
1616 rest)
1617 [] states)
1618
1619 let ctr = ref 0
1620 let new_var _ =
1621 let c = !ctr in
1622 ctr := !ctr + 1;
1623 Printf.sprintf "_c%d" c
1624
1625 (* **************************** *)
1626 (* End of environment functions *)
1627 (* **************************** *)
1628
1629 type ('code,'value) cell = Frozen of 'code | Thawed of 'value
1630
1631 let rec satloop unchecked required required_states
1632 ((grp,label,states) as m) phi env =
1633 let rec loop unchecked required required_states phi =
1634 let res =
1635 match phi with
1636 A.False -> []
1637 | A.True -> triples_top states
1638 | A.Pred(p) -> satLabel label required p
1639 | A.Uncheck(phi1) ->
1640 let unchecked = if !pUNCHECK_OPT then true else false in
1641 loop unchecked required required_states phi1
1642 | A.Not(phi) ->
1643 let phires = loop unchecked required required_states phi in
1644 (*let phires =
1645 List.map (function (s,th,w) -> (s,th,[])) phires in*)
1646 triples_complement (mkstates states required_states)
1647 phires
1648 | A.Or(phi1,phi2) ->
1649 triples_union
1650 (loop unchecked required required_states phi1)
1651 (loop unchecked required required_states phi2)
1652 | A.SeqOr(phi1,phi2) ->
1653 let res1 = loop unchecked required required_states phi1 in
1654 let res2 = loop unchecked required required_states phi2 in
1655 let res1neg = unwitify res1 in
1656 let pm = !Flag_ctl.partial_match in
1657 (match (pm,res1,res2) with
1658 (false,res1,[]) -> res1
1659 | (false,[],res2) -> res2
1660 | _ ->
1661 triples_union res1
1662 (triples_conj
1663 (triples_complement (mkstates states required_states) res1neg)
1664 res2))
1665 | A.And(strict,phi1,phi2) ->
1666 (* phi1 is considered to be more likely to be [], because of the
1667 definition of asttoctl. Could use heuristics such as the size of
1668 the term *)
1669 let pm = !Flag_ctl.partial_match in
1670 (match (pm,loop unchecked required required_states phi1) with
1671 (false,[]) when !pLazyOpt -> []
1672 | (_,phi1res) ->
1673 let new_required = extend_required phi1res required in
1674 let new_required_states = get_required_states phi1res in
1675 (match (pm,loop unchecked new_required new_required_states phi2)
1676 with
1677 (false,[]) when !pLazyOpt -> []
1678 | (_,phi2res) ->
1679 strict_triples_conj strict
1680 (mkstates states required_states)
1681 phi1res phi2res))
1682 | A.AndAny(dir,strict,phi1,phi2) ->
1683 (* phi2 can appear anywhere that is reachable *)
1684 let pm = !Flag_ctl.partial_match in
1685 (match (pm,loop unchecked required required_states phi1) with
1686 (false,[]) -> []
1687 | (_,phi1res) ->
1688 let new_required = extend_required phi1res required in
1689 let new_required_states = get_required_states phi1res in
1690 let new_required_states =
1691 get_reachable dir m new_required_states in
1692 (match (pm,loop unchecked new_required new_required_states phi2)
1693 with
1694 (false,[]) -> phi1res
1695 | (_,phi2res) ->
1696 (match phi1res with
1697 [] -> (* !Flag_ctl.partial_match must be true *)
1698 if phi2res = []
1699 then []
1700 else
1701 let s = mkstates states required_states in
1702 List.fold_left
1703 (function a -> function b ->
1704 strict_triples_conj strict s a [b])
1705 [List.hd phi2res] (List.tl phi2res)
1706 | [(state,_,_)] ->
1707 let phi2res =
1708 List.map (function (s,e,w) -> [(state,e,w)]) phi2res in
1709 let s = mkstates states required_states in
1710 List.fold_left
1711 (function a -> function b ->
1712 strict_triples_conj strict s a b)
1713 phi1res phi2res
1714 | _ ->
1715 failwith
1716 "only one result allowed for the left arg of AndAny")))
1717 | A.HackForStmt(dir,strict,phi1,phi2) ->
1718 (* phi2 can appear anywhere that is reachable *)
1719 let pm = !Flag_ctl.partial_match in
1720 (match (pm,loop unchecked required required_states phi1) with
1721 (false,[]) -> []
1722 | (_,phi1res) ->
1723 let new_required = extend_required phi1res required in
1724 let new_required_states = get_required_states phi1res in
1725 let new_required_states =
1726 get_reachable dir m new_required_states in
1727 (match (pm,loop unchecked new_required new_required_states phi2)
1728 with
1729 (false,[]) -> phi1res
1730 | (_,phi2res) ->
1731 (* if there is more than one state, something about the
1732 environment has to ensure that the right triples of
1733 phi2 get associated with the triples of phi1.
1734 the asttoctl2 has to ensure that that is the case.
1735 these should thus be structural properties.
1736 env of phi2 has to be a proper subset of env of phi1
1737 to ensure all end up being consistent. no new triples
1738 should be generated. strict_triples_conj_none takes
1739 care of this.
1740 *)
1741 let s = mkstates states required_states in
1742 List.fold_left
1743 (function acc ->
1744 function (st,th,_) as phi2_elem ->
1745 let inverse =
1746 triples_complement [st] [(st,th,[])] in
1747 strict_triples_conj_none strict s acc
1748 (phi2_elem::inverse))
1749 phi1res phi2res))
1750 | A.InnerAnd(phi) ->
1751 inner_and(loop unchecked required required_states phi)
1752 | A.EX(dir,phi) ->
1753 let new_required_states =
1754 get_children_required_states dir m required_states in
1755 satEX dir m (loop unchecked required new_required_states phi)
1756 required_states
1757 | A.AX(dir,strict,phi) ->
1758 let new_required_states =
1759 get_children_required_states dir m required_states in
1760 let res = loop unchecked required new_required_states phi in
1761 strict_A1 strict satAX satEX dir m res required_states
1762 | A.EF(dir,phi) ->
1763 let new_required_states = get_reachable dir m required_states in
1764 satEF dir m (loop unchecked required new_required_states phi)
1765 new_required_states
1766 | A.AF(dir,strict,phi) ->
1767 if !Flag_ctl.loop_in_src_code
1768 then
1769 loop unchecked required required_states
1770 (A.AU(dir,strict,A.True,phi))
1771 else
1772 let new_required_states = get_reachable dir m required_states in
1773 let res = loop unchecked required new_required_states phi in
1774 strict_A1 strict satAF satEF dir m res new_required_states
1775 | A.EG(dir,phi) ->
1776 let new_required_states = get_reachable dir m required_states in
1777 satEG dir m (loop unchecked required new_required_states phi)
1778 new_required_states
1779 | A.AG(dir,strict,phi) ->
1780 let new_required_states = get_reachable dir m required_states in
1781 let res = loop unchecked required new_required_states phi in
1782 strict_A1 strict satAG satEF dir m res new_required_states
1783 | A.EU(dir,phi1,phi2) ->
1784 let new_required_states = get_reachable dir m required_states in
1785 (match loop unchecked required new_required_states phi2 with
1786 [] when !pLazyOpt -> []
1787 | s2 ->
1788 let new_required = extend_required s2 required in
1789 let s1 = loop unchecked new_required new_required_states phi1 in
1790 satEU dir m s1 s2 new_required_states
1791 (fun y ctr -> print_graph_c grp new_required_states y ctr phi))
1792 | A.AW(dir,strict,phi1,phi2) ->
1793 let new_required_states = get_reachable dir m required_states in
1794 (match loop unchecked required new_required_states phi2 with
1795 [] when !pLazyOpt -> []
1796 | s2 ->
1797 let new_required = extend_required s2 required in
1798 let s1 = loop unchecked new_required new_required_states phi1 in
1799 strict_A2 strict satAW satEF dir m s1 s2 new_required_states)
1800 | A.AU(dir,strict,phi1,phi2) ->
1801 (*Printf.printf "using AU\n"; flush stdout;*)
1802 let new_required_states = get_reachable dir m required_states in
1803 (match loop unchecked required new_required_states phi2 with
1804 [] when !pLazyOpt -> []
1805 | s2 ->
1806 let new_required = extend_required s2 required in
1807 let s1 = loop unchecked new_required new_required_states phi1 in
1808 let res =
1809 strict_A2au strict satAU satEF dir m s1 s2 new_required_states
1810 (fun y ctr ->
1811 print_graph_c grp new_required_states y ctr phi) in
1812 match res with
1813 AUok res -> res
1814 | AUfailed tmp_res ->
1815 (* found a loop, have to try AW *)
1816 (* the formula is
1817 A[E[phi1 U phi2] & phi1 W phi2]
1818 the and is nonstrict *)
1819 (* tmp_res is bigger than s2, so perhaps closer to s1 *)
1820 (*Printf.printf "using AW\n"; flush stdout;*)
1821 let s1 =
1822 triples_conj
1823 (satEU dir m s1 tmp_res new_required_states
1824 (* no graph, for the moment *)
1825 (fun y str -> ()))
1826 s1 in
1827 strict_A2 strict satAW satEF dir m s1 s2 new_required_states
1828 )
1829 | A.Implies(phi1,phi2) ->
1830 loop unchecked required required_states (A.Or(A.Not phi1,phi2))
1831 | A.Exists (keep,v,phi) ->
1832 let new_required = drop_required v required in
1833 triples_witness v unchecked (not keep)
1834 (loop unchecked new_required required_states phi)
1835
1836 | A.Let(v,phi1,phi2) ->
1837 (* should only be used when the properties unchecked, required,
1838 and required_states are known to be the same or at least
1839 compatible between all the uses. this is not checked. *)
1840 let res = loop unchecked required required_states phi1 in
1841 satloop unchecked required required_states m phi2 ((v,res) :: env)
1842 | A.LetR(dir,v,phi1,phi2) ->
1843 (* should only be used when the properties unchecked, required,
1844 and required_states are known to be the same or at least
1845 compatible between all the uses. this is not checked. *)
1846 (* doesn't seem to be used any more *)
1847 let new_required_states = get_reachable dir m required_states in
1848 let res = loop unchecked required new_required_states phi1 in
1849 satloop unchecked required required_states m phi2 ((v,res) :: env)
1850 | A.Ref(v) ->
1851 let res = List.assoc v env in
1852 if unchecked
1853 then List.map (function (s,th,_) -> (s,th,[])) res
1854 else res
1855 | A.XX(phi) -> failwith "should have been removed" in
1856 if !Flag_ctl.bench > 0 then triples := !triples + (List.length res);
1857 let res = drop_wits required_states res phi (* ) *) in
1858 print_graph grp required_states res "" phi;
1859 res in
1860
1861 loop unchecked required required_states phi
1862 ;;
1863
1864
1865 (* SAT with tracking *)
1866 let output str =
1867 Printf.printf "%s\n" str
1868
1869 let rec sat_verbose_loop unchecked required required_states annot maxlvl lvl
1870 ((_,label,states) as m) phi env =
1871 let anno res children = (annot lvl phi res children,res) in
1872 let satv unchecked required required_states phi0 env =
1873 sat_verbose_loop unchecked required required_states annot maxlvl (lvl+1)
1874 m phi0 env in
1875 if (lvl > maxlvl) && (maxlvl > -1) then
1876 anno (satloop unchecked required required_states m phi env) []
1877 else
1878 let (child,res) =
1879 match phi with
1880 A.False -> anno [] []
1881 | A.True -> anno (triples_top states) []
1882 | A.Pred(p) ->
1883 output "label";
1884 anno (satLabel label required p) []
1885 | A.Uncheck(phi1) ->
1886 let unchecked = if !pUNCHECK_OPT then true else false in
1887 let (child1,res1) = satv unchecked required required_states phi1 env in
1888 output "uncheck";
1889 anno res1 [child1]
1890 | A.Not(phi1) ->
1891 let (child,res) =
1892 satv unchecked required required_states phi1 env in
1893 output "not";
1894 anno (triples_complement (mkstates states required_states) res) [child]
1895 | A.Or(phi1,phi2) ->
1896 let (child1,res1) =
1897 satv unchecked required required_states phi1 env in
1898 let (child2,res2) =
1899 satv unchecked required required_states phi2 env in
1900 output "or";
1901 anno (triples_union res1 res2) [child1; child2]
1902 | A.SeqOr(phi1,phi2) ->
1903 let (child1,res1) =
1904 satv unchecked required required_states phi1 env in
1905 let (child2,res2) =
1906 satv unchecked required required_states phi2 env in
1907 let res1neg =
1908 List.map (function (s,th,_) -> (s,th,[])) res1 in
1909 output "seqor";
1910 let pm = !Flag_ctl.partial_match in
1911 (match (pm,res1,res2) with
1912 (false,res1,[]) -> anno res1 [child1; child2]
1913 | (false,[],res2) -> anno res2 [child1; child2]
1914 | _ ->
1915 anno (triples_union res1
1916 (triples_conj
1917 (triples_complement (mkstates states required_states)
1918 res1neg)
1919 res2))
1920 [child1; child2])
1921 | A.And(strict,phi1,phi2) ->
1922 let pm = !Flag_ctl.partial_match in
1923 (match (pm,satv unchecked required required_states phi1 env) with
1924 (false,(child1,[])) ->
1925 output "and"; anno [] [child1]
1926 | (_,(child1,res1)) ->
1927 let new_required = extend_required res1 required in
1928 let new_required_states = get_required_states res1 in
1929 (match (pm,satv unchecked new_required new_required_states phi2
1930 env) with
1931 (false,(child2,[])) ->
1932 output "and"; anno [] [child1;child2]
1933 | (_,(child2,res2)) ->
1934 output "and";
1935 let res =
1936 strict_triples_conj strict
1937 (mkstates states required_states)
1938 res1 res2 in
1939 anno res [child1; child2]))
1940 | A.AndAny(dir,strict,phi1,phi2) ->
1941 let pm = !Flag_ctl.partial_match in
1942 (match (pm,satv unchecked required required_states phi1 env) with
1943 (false,(child1,[])) ->
1944 output "and"; anno [] [child1]
1945 | (_,(child1,res1)) ->
1946 let new_required = extend_required res1 required in
1947 let new_required_states = get_required_states res1 in
1948 let new_required_states =
1949 get_reachable dir m new_required_states in
1950 (match (pm,satv unchecked new_required new_required_states phi2
1951 env) with
1952 (false,(child2,[])) ->
1953 output "andany";
1954 anno res1 [child1;child2]
1955 | (_,(child2,res2)) ->
1956 (match res1 with
1957 [] -> (* !Flag_ctl.partial_match must be true *)
1958 if res2 = []
1959 then anno [] [child1; child2]
1960 else
1961 let res =
1962 let s = mkstates states required_states in
1963 List.fold_left
1964 (function a -> function b ->
1965 strict_triples_conj strict s a [b])
1966 [List.hd res2] (List.tl res2) in
1967 anno res [child1; child2]
1968 | [(state,_,_)] ->
1969 let res2 =
1970 List.map (function (s,e,w) -> [(state,e,w)]) res2 in
1971 output "andany";
1972 let res =
1973 let s = mkstates states required_states in
1974 List.fold_left
1975 (function a -> function b ->
1976 strict_triples_conj strict s a b)
1977 res1 res2 in
1978 anno res [child1; child2]
1979 | _ ->
1980 failwith
1981 "only one result allowed for the left arg of AndAny")))
1982 | A.HackForStmt(dir,strict,phi1,phi2) ->
1983 let pm = !Flag_ctl.partial_match in
1984 (match (pm,satv unchecked required required_states phi1 env) with
1985 (false,(child1,[])) ->
1986 output "and"; anno [] [child1]
1987 | (_,(child1,res1)) ->
1988 let new_required = extend_required res1 required in
1989 let new_required_states = get_required_states res1 in
1990 let new_required_states =
1991 get_reachable dir m new_required_states in
1992 (match (pm,satv unchecked new_required new_required_states phi2
1993 env) with
1994 (false,(child2,[])) ->
1995 output "andany";
1996 anno res1 [child1;child2]
1997 | (_,(child2,res2)) ->
1998 let res =
1999 let s = mkstates states required_states in
2000 List.fold_left
2001 (function acc ->
2002 function (st,th,_) as phi2_elem ->
2003 let inverse =
2004 triples_complement [st] [(st,th,[])] in
2005 strict_triples_conj_none strict s acc
2006 (phi2_elem::inverse))
2007 res1 res2 in
2008 anno res [child1; child2]))
2009 | A.InnerAnd(phi1) ->
2010 let (child1,res1) = satv unchecked required required_states phi1 env in
2011 output "uncheck";
2012 anno (inner_and res1) [child1]
2013 | A.EX(dir,phi1) ->
2014 let new_required_states =
2015 get_children_required_states dir m required_states in
2016 let (child,res) =
2017 satv unchecked required new_required_states phi1 env in
2018 output "EX";
2019 anno (satEX dir m res required_states) [child]
2020 | A.AX(dir,strict,phi1) ->
2021 let new_required_states =
2022 get_children_required_states dir m required_states in
2023 let (child,res) =
2024 satv unchecked required new_required_states phi1 env in
2025 output "AX";
2026 let res = strict_A1 strict satAX satEX dir m res required_states in
2027 anno res [child]
2028 | A.EF(dir,phi1) ->
2029 let new_required_states = get_reachable dir m required_states in
2030 let (child,res) =
2031 satv unchecked required new_required_states phi1 env in
2032 output "EF";
2033 anno (satEF dir m res new_required_states) [child]
2034 | A.AF(dir,strict,phi1) ->
2035 if !Flag_ctl.loop_in_src_code
2036 then
2037 satv unchecked required required_states
2038 (A.AU(dir,strict,A.True,phi1))
2039 env
2040 else
2041 (let new_required_states = get_reachable dir m required_states in
2042 let (child,res) =
2043 satv unchecked required new_required_states phi1 env in
2044 output "AF";
2045 let res =
2046 strict_A1 strict satAF satEF dir m res new_required_states in
2047 anno res [child])
2048 | A.EG(dir,phi1) ->
2049 let new_required_states = get_reachable dir m required_states in
2050 let (child,res) =
2051 satv unchecked required new_required_states phi1 env in
2052 output "EG";
2053 anno (satEG dir m res new_required_states) [child]
2054 | A.AG(dir,strict,phi1) ->
2055 let new_required_states = get_reachable dir m required_states in
2056 let (child,res) =
2057 satv unchecked required new_required_states phi1 env in
2058 output "AG";
2059 let res = strict_A1 strict satAG satEF dir m res new_required_states in
2060 anno res [child]
2061
2062 | A.EU(dir,phi1,phi2) ->
2063 let new_required_states = get_reachable dir m required_states in
2064 (match satv unchecked required new_required_states phi2 env with
2065 (child2,[]) ->
2066 output "EU";
2067 anno [] [child2]
2068 | (child2,res2) ->
2069 let new_required = extend_required res2 required in
2070 let (child1,res1) =
2071 satv unchecked new_required new_required_states phi1 env in
2072 output "EU";
2073 anno (satEU dir m res1 res2 new_required_states (fun y str -> ()))
2074 [child1; child2])
2075 | A.AW(dir,strict,phi1,phi2) ->
2076 failwith "should not be used" (*
2077 let new_required_states = get_reachable dir m required_states in
2078 (match satv unchecked required new_required_states phi2 env with
2079 (child2,[]) ->
2080 output (Printf.sprintf "AW %b" unchecked); anno [] [child2]
2081 | (child2,res2) ->
2082 let new_required = extend_required res2 required in
2083 let (child1,res1) =
2084 satv unchecked new_required new_required_states phi1 env in
2085 output (Printf.sprintf "AW %b" unchecked);
2086 let res =
2087 strict_A2 strict satAW satEF dir m res1 res2
2088 new_required_states in
2089 anno res [child1; child2]) *)
2090 | A.AU(dir,strict,phi1,phi2) ->
2091 let new_required_states = get_reachable dir m required_states in
2092 (match satv unchecked required new_required_states phi2 env with
2093 (child2,[]) ->
2094 output "AU"; anno [] [child2]
2095 | (child2,s2) ->
2096 let new_required = extend_required s2 required in
2097 let (child1,s1) =
2098 satv unchecked new_required new_required_states phi1 env in
2099 output "AU";
2100 let res =
2101 strict_A2au strict satAU satEF dir m s1 s2 new_required_states
2102 (fun y str -> ()) in
2103 (match res with
2104 AUok res ->
2105 anno res [child1; child2]
2106 | AUfailed tmp_res ->
2107 (* found a loop, have to try AW *)
2108 (* the formula is
2109 A[E[phi1 U phi2] & phi1 W phi2]
2110 the and is nonstrict *)
2111 (* tmp_res is bigger than s2, so perhaps closer to s1 *)
2112 output "AW";
2113 let s1 =
2114 triples_conj
2115 (satEU dir m s1 tmp_res new_required_states
2116 (* no graph, for the moment *)
2117 (fun y str -> ()))
2118 s1 in
2119 let res =
2120 strict_A2 strict satAW satEF dir m s1 s2 new_required_states in
2121 anno res [child1; child2]))
2122 | A.Implies(phi1,phi2) ->
2123 satv unchecked required required_states
2124 (A.Or(A.Not phi1,phi2))
2125 env
2126 | A.Exists (keep,v,phi1) ->
2127 let new_required = drop_required v required in
2128 let (child,res) =
2129 satv unchecked new_required required_states phi1 env in
2130 output "exists";
2131 anno (triples_witness v unchecked (not keep) res) [child]
2132 | A.Let(v,phi1,phi2) ->
2133 let (child1,res1) =
2134 satv unchecked required required_states phi1 env in
2135 let (child2,res2) =
2136 satv unchecked required required_states phi2 ((v,res1) :: env) in
2137 anno res2 [child1;child2]
2138 | A.LetR(dir,v,phi1,phi2) ->
2139 let new_required_states = get_reachable dir m required_states in
2140 let (child1,res1) =
2141 satv unchecked required new_required_states phi1 env in
2142 let (child2,res2) =
2143 satv unchecked required required_states phi2 ((v,res1) :: env) in
2144 anno res2 [child1;child2]
2145 | A.Ref(v) ->
2146 output "Ref";
2147 let res = List.assoc v env in
2148 let res =
2149 if unchecked
2150 then List.map (function (s,th,_) -> (s,th,[])) res
2151 else res in
2152 anno res []
2153 | A.XX(phi) -> failwith "should have been removed" in
2154 let res1 = drop_wits required_states res phi in
2155 if not(res1 = res)
2156 then
2157 begin
2158 print_required_states required_states;
2159 print_state "after drop_wits" res1 end;
2160 (child,res1)
2161
2162 ;;
2163
2164 let sat_verbose annotate maxlvl lvl m phi =
2165 sat_verbose_loop false [] None annotate maxlvl lvl m phi []
2166
2167 (* Type for annotations collected in a tree *)
2168 type ('a) witAnnoTree = WitAnno of ('a * ('a witAnnoTree) list);;
2169
2170 let sat_annotree annotate m phi =
2171 let tree_anno l phi res chld = WitAnno(annotate l phi res,chld) in
2172 sat_verbose_loop false [] None tree_anno (-1) 0 m phi []
2173 ;;
2174
2175 (*
2176 let sat m phi = satloop m phi []
2177 ;;
2178 *)
2179
2180 let simpleanno l phi res =
2181 let pp s =
2182 Format.print_string ("\n" ^ s ^ "\n------------------------------\n");
2183 print_generic_algo (List.sort compare res);
2184 Format.print_string "\n------------------------------\n\n" in
2185 let pp_dir = function
2186 A.FORWARD -> ()
2187 | A.BACKWARD -> pp "^" in
2188 match phi with
2189 | A.False -> pp "False"
2190 | A.True -> pp "True"
2191 | A.Pred(p) -> pp ("Pred" ^ (Common.dump p))
2192 | A.Not(phi) -> pp "Not"
2193 | A.Exists(_,v,phi) -> pp ("Exists " ^ (Common.dump(v)))
2194 | A.And(_,phi1,phi2) -> pp "And"
2195 | A.AndAny(dir,_,phi1,phi2) -> pp "AndAny"
2196 | A.HackForStmt(dir,_,phi1,phi2) -> pp "HackForStmt"
2197 | A.Or(phi1,phi2) -> pp "Or"
2198 | A.SeqOr(phi1,phi2) -> pp "SeqOr"
2199 | A.Implies(phi1,phi2) -> pp "Implies"
2200 | A.AF(dir,_,phi1) -> pp "AF"; pp_dir dir
2201 | A.AX(dir,_,phi1) -> pp "AX"; pp_dir dir
2202 | A.AG(dir,_,phi1) -> pp "AG"; pp_dir dir
2203 | A.AW(dir,_,phi1,phi2)-> pp "AW"; pp_dir dir
2204 | A.AU(dir,_,phi1,phi2)-> pp "AU"; pp_dir dir
2205 | A.EF(dir,phi1) -> pp "EF"; pp_dir dir
2206 | A.EX(dir,phi1) -> pp "EX"; pp_dir dir
2207 | A.EG(dir,phi1) -> pp "EG"; pp_dir dir
2208 | A.EU(dir,phi1,phi2) -> pp "EU"; pp_dir dir
2209 | A.Let (x,phi1,phi2) -> pp ("Let"^" "^x)
2210 | A.LetR (dir,x,phi1,phi2) -> pp ("LetR"^" "^x); pp_dir dir
2211 | A.Ref(s) -> pp ("Ref("^s^")")
2212 | A.Uncheck(s) -> pp "Uncheck"
2213 | A.InnerAnd(s) -> pp "InnerAnd"
2214 | A.XX(phi1) -> pp "XX"
2215 ;;
2216
2217
2218 (* pad: Rene, you can now use the module pretty_print_ctl.ml to
2219 print a ctl formula more accurately if you want.
2220 Use the print_xxx provided in the different module to call
2221 Pretty_print_ctl.pp_ctl.
2222 *)
2223
2224 let simpleanno2 l phi res =
2225 begin
2226 Pretty_print_ctl.pp_ctl (P.print_predicate, SUB.print_mvar) false phi;
2227 Format.print_newline ();
2228 Format.print_string "----------------------------------------------------";
2229 Format.print_newline ();
2230 print_generic_algo (List.sort compare res);
2231 Format.print_newline ();
2232 Format.print_string "----------------------------------------------------";
2233 Format.print_newline ();
2234 Format.print_newline ();
2235 end
2236
2237
2238 (* ---------------------------------------------------------------------- *)
2239 (* Benchmarking *)
2240 (* ---------------------------------------------------------------------- *)
2241
2242 type optentry = bool ref * string
2243 type options = {label : optentry; unch : optentry;
2244 conj : optentry; compl1 : optentry; compl2 : optentry;
2245 newinfo : optentry;
2246 reqenv : optentry; reqstates : optentry}
2247
2248 let options =
2249 {label = (pSATLABEL_MEMO_OPT,"satlabel_memo_opt");
2250 unch = (pUNCHECK_OPT,"uncheck_opt");
2251 conj = (pTRIPLES_CONJ_OPT,"triples_conj_opt");
2252 compl1 = (pTRIPLES_COMPLEMENT_OPT,"triples_complement_opt");
2253 compl2 = (pTRIPLES_COMPLEMENT_SIMPLE_OPT,"triples_complement_simple_opt");
2254 newinfo = (pNEW_INFO_OPT,"new_info_opt");
2255 reqenv = (pREQUIRED_ENV_OPT,"required_env_opt");
2256 reqstates = (pREQUIRED_STATES_OPT,"required_states_opt")}
2257
2258 let baseline =
2259 [("none ",[]);
2260 ("label ",[options.label]);
2261 ("unch ",[options.unch]);
2262 ("unch and label ",[options.label;options.unch])]
2263
2264 let conjneg =
2265 [("conj ", [options.conj]);
2266 ("compl1 ", [options.compl1]);
2267 ("compl12 ", [options.compl1;options.compl2]);
2268 ("conj/compl12 ", [options.conj;options.compl1;options.compl2]);
2269 ("conj unch satl ", [options.conj;options.unch;options.label]);
2270 (*
2271 ("compl1 unch satl ", [options.compl1;options.unch;options.label]);
2272 ("compl12 unch satl ",
2273 [options.compl1;options.compl2;options.unch;options.label]); *)
2274 ("conj/compl12 unch satl ",
2275 [options.conj;options.compl1;options.compl2;options.unch;options.label])]
2276
2277 let path =
2278 [("newinfo ", [options.newinfo]);
2279 ("newinfo unch satl ", [options.newinfo;options.unch;options.label])]
2280
2281 let required =
2282 [("reqenv ", [options.reqenv]);
2283 ("reqstates ", [options.reqstates]);
2284 ("reqenv/states ", [options.reqenv;options.reqstates]);
2285 (* ("reqenv unch satl ", [options.reqenv;options.unch;options.label]);
2286 ("reqstates unch satl ",
2287 [options.reqstates;options.unch;options.label]);*)
2288 ("reqenv/states unch satl ",
2289 [options.reqenv;options.reqstates;options.unch;options.label])]
2290
2291 let all_options =
2292 [options.label;options.unch;options.conj;options.compl1;options.compl2;
2293 options.newinfo;options.reqenv;options.reqstates]
2294
2295 let all =
2296 [("all ",all_options)]
2297
2298 let all_options_but_path =
2299 [options.label;options.unch;options.conj;options.compl1;options.compl2;
2300 options.reqenv;options.reqstates]
2301
2302 let all_but_path = ("all but path ",all_options_but_path)
2303
2304 let counters =
2305 [(satAW_calls, "satAW", ref 0);
2306 (satAU_calls, "satAU", ref 0);
2307 (satEF_calls, "satEF", ref 0);
2308 (satAF_calls, "satAF", ref 0);
2309 (satEG_calls, "satEG", ref 0);
2310 (satAG_calls, "satAG", ref 0);
2311 (satEU_calls, "satEU", ref 0)]
2312
2313 let perms =
2314 map
2315 (function (opt,x) ->
2316 (opt,x,ref 0.0,ref 0,
2317 List.map (function _ -> (ref 0, ref 0, ref 0)) counters))
2318 [List.hd all;all_but_path]
2319 (*(all@baseline@conjneg@path@required)*)
2320
2321 exception Out
2322
2323 let rec iter fn = function
2324 1 -> fn()
2325 | n -> let _ = fn() in
2326 (Hashtbl.clear reachable_table;
2327 Hashtbl.clear memo_label;
2328 triples := 0;
2329 iter fn (n-1))
2330
2331 let copy_to_stderr fl =
2332 let i = open_in fl in
2333 let rec loop _ =
2334 Printf.fprintf stderr "%s\n" (input_line i);
2335 loop() in
2336 try loop() with _ -> ();
2337 close_in i
2338
2339 let bench_sat (_,_,states) fn =
2340 List.iter (function (opt,_) -> opt := false) all_options;
2341 let answers =
2342 concatmap
2343 (function (name,options,time,trips,counter_info) ->
2344 let iterct = !Flag_ctl.bench in
2345 if !time > float_of_int timeout then time := -100.0;
2346 if not (!time = -100.0)
2347 then
2348 begin
2349 Hashtbl.clear reachable_table;
2350 Hashtbl.clear memo_label;
2351 List.iter (function (opt,_) -> opt := true) options;
2352 List.iter (function (calls,_,save_calls) -> save_calls := !calls)
2353 counters;
2354 triples := 0;
2355 let res =
2356 let bef = Sys.time() in
2357 try
2358 Common.timeout_function timeout
2359 (fun () ->
2360 let bef = Sys.time() in
2361 let res = iter fn iterct in
2362 let aft = Sys.time() in
2363 time := !time +. (aft -. bef);
2364 trips := !trips + !triples;
2365 List.iter2
2366 (function (calls,_,save_calls) ->
2367 function (current_calls,current_cfg,current_max_cfg) ->
2368 current_calls :=
2369 !current_calls + (!calls - !save_calls);
2370 if (!calls - !save_calls) > 0
2371 then
2372 (let st = List.length states in
2373 current_cfg := !current_cfg + st;
2374 if st > !current_max_cfg
2375 then current_max_cfg := st))
2376 counters counter_info;
2377 [res])
2378 with
2379 Common.Timeout ->
2380 begin
2381 let aft = Sys.time() in
2382 time := -100.0;
2383 Printf.fprintf stderr "Timeout at %f on: %s\n"
2384 (aft -. bef) name;
2385 []
2386 end in
2387 List.iter (function (opt,_) -> opt := false) options;
2388 res
2389 end
2390 else [])
2391 perms in
2392 Printf.fprintf stderr "\n";
2393 match answers with
2394 [] -> []
2395 | res::rest ->
2396 (if not(List.for_all (function x -> x = res) rest)
2397 then
2398 (List.iter (print_state "a state") answers;
2399 Printf.printf "something doesn't work\n");
2400 res)
2401
2402 let print_bench _ =
2403 let iterct = !Flag_ctl.bench in
2404 if iterct > 0
2405 then
2406 (List.iter
2407 (function (name,options,time,trips,counter_info) ->
2408 Printf.fprintf stderr "%s Numbers: %f %d "
2409 name (!time /. (float_of_int iterct)) !trips;
2410 List.iter
2411 (function (calls,cfg,max_cfg) ->
2412 Printf.fprintf stderr "%d %d %d " (!calls / iterct) !cfg !max_cfg)
2413 counter_info;
2414 Printf.fprintf stderr "\n")
2415 perms)
2416
2417 (* ---------------------------------------------------------------------- *)
2418 (* preprocessing: ignore irrelevant functions *)
2419
2420 let preprocess (cfg,_,_) label = function
2421 [] -> true (* no information, try everything *)
2422 | l ->
2423 let sz = G.size cfg in
2424 let verbose_output pred = function
2425 [] ->
2426 Printf.printf "did not find:\n";
2427 P.print_predicate pred; Format.print_newline()
2428 | _ ->
2429 Printf.printf "found:\n";
2430 P.print_predicate pred; Format.print_newline();
2431 Printf.printf "but it was not enough\n" in
2432 let get_any verbose x =
2433 let res =
2434 try Hashtbl.find memo_label x
2435 with
2436 Not_found ->
2437 (let triples = label x in
2438 let filtered =
2439 List.map (function (st,th,_) -> (st,th)) triples in
2440 Hashtbl.add memo_label x filtered;
2441 filtered) in
2442 if verbose then verbose_output x res;
2443 not([] = res) in
2444 let get_all l =
2445 (* don't bother testing when there are more patterns than nodes *)
2446 if List.length l > sz-2
2447 then false
2448 else List.for_all (get_any false) l in
2449 if List.exists get_all l
2450 then true
2451 else
2452 (if !Flag_ctl.verbose_match
2453 then
2454 List.iter (List.iter (function x -> let _ = get_any true x in ()))
2455 l;
2456 false)
2457
2458 let filter_partial_matches trips =
2459 if !Flag_ctl.partial_match
2460 then
2461 let anynegwit = (* if any is neg, then all are *)
2462 List.exists (function A.NegWit _ -> true | A.Wit _ -> false) in
2463 let (bad,good) =
2464 List.partition (function (s,th,wit) -> anynegwit wit) trips in
2465 (match bad with
2466 [] -> ()
2467 | _ -> print_state "partial matches" bad; Format.print_newline());
2468 good
2469 else trips
2470
2471 (* ---------------------------------------------------------------------- *)
2472 (* Main entry point for engine *)
2473 let sat m phi reqopt =
2474 try
2475 (match !Flag_ctl.steps with
2476 None -> step_count := 0
2477 | Some x -> step_count := x);
2478 Hashtbl.clear reachable_table;
2479 Hashtbl.clear memo_label;
2480 let (x,label,states) = m in
2481 if (!Flag_ctl.bench > 0) or (preprocess m label reqopt)
2482 then
2483 ((* to drop when Yoann initialized this flag *)
2484 if List.exists (G.extract_is_loop x) states
2485 then Flag_ctl.loop_in_src_code := true;
2486 let m = (x,label,List.sort compare states) in
2487 let res =
2488 if(!Flag_ctl.verbose_ctl_engine)
2489 then
2490 let fn _ = snd (sat_annotree simpleanno2 m phi) in
2491 if !Flag_ctl.bench > 0
2492 then bench_sat m fn
2493 else fn()
2494 else
2495 let fn _ = satloop false [] None m phi [] in
2496 if !Flag_ctl.bench > 0
2497 then bench_sat m fn
2498 else Common.profile_code "ctl" (fun _ -> fn()) in
2499 let res = filter_partial_matches res in
2500 (*
2501 Printf.printf "steps: start %d, stop %d\n"
2502 (match !Flag_ctl.steps with Some x -> x | _ -> 0)
2503 !step_count;
2504 Printf.printf "triples: %d\n" !triples;
2505 print_state "final result" res;
2506 *)
2507 List.sort compare res)
2508 else
2509 (if !Flag_ctl.verbose_ctl_engine
2510 then Common.pr2 "missing something required";
2511 [])
2512 with Steps -> []
2513
2514 (* ********************************************************************** *)
2515 (* End of Module: CTL_ENGINE *)
2516 (* ********************************************************************** *)
2517 end
2518 ;;