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