From e8e2b37f7340bf793430dce68b94b3c2509e77f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bobot?= <francois.bobot@cea.fr> Date: Mon, 9 Oct 2017 13:43:55 +0200 Subject: [PATCH] [Eva] Traces domain: uses structural equality on expr --- src/kernel_services/abstract_interp/bottom.ml | 14 +- .../abstract_interp/bottom.mli | 1 + .../value/domains/traces/traces_domain.ml | 454 +++++++++++------- src/plugins/value/value_parameters.ml | 27 ++ src/plugins/value/value_parameters.mli | 3 + tests/value/traces/test4.i | 2 + 6 files changed, 336 insertions(+), 165 deletions(-) diff --git a/src/kernel_services/abstract_interp/bottom.ml b/src/kernel_services/abstract_interp/bottom.ml index a20715ef6a7..12c57bbe213 100644 --- a/src/kernel_services/abstract_interp/bottom.ml +++ b/src/kernel_services/abstract_interp/bottom.ml @@ -52,6 +52,12 @@ let equal equal x y = match x, y with | `Value vx, `Value vy -> equal vx vy | _ -> false +let compare compare a b = match a, b with + | `Bottom, `Bottom -> 0 + | `Bottom, _ -> -1 + | _, `Bottom -> 1 + | `Value v, `Value w -> compare v w + let is_included is_included x y = match x, y with | `Bottom, _ -> true | _, `Bottom -> false @@ -75,6 +81,10 @@ let pretty pretty fmt = function | `Bottom -> Format.fprintf fmt "Bottom" | `Value v -> pretty fmt v +let iter f = function + | `Bottom -> () + | `Value v -> f v + let counter = ref 0 @@ -163,7 +173,3 @@ module Top = struct (narrow vnarrow x y :> _ or_top_bottom) end - -let iter f = function - | `Bottom -> () - | `Value v -> f v diff --git a/src/kernel_services/abstract_interp/bottom.mli b/src/kernel_services/abstract_interp/bottom.mli index f883452345f..051fed464a9 100644 --- a/src/kernel_services/abstract_interp/bottom.mli +++ b/src/kernel_services/abstract_interp/bottom.mli @@ -41,6 +41,7 @@ val is_bottom: 'a or_bottom -> bool val non_bottom: 'a or_bottom -> 'a val equal: ('a -> 'a -> bool) -> 'a or_bottom -> 'a or_bottom -> bool +val compare: ('a -> 'a -> int) -> 'a or_bottom -> 'a or_bottom -> int val is_included: ('a -> 'a -> bool) -> 'a or_bottom -> 'a or_bottom -> bool val join: ('a -> 'a -> 'a) -> 'a or_bottom -> 'a or_bottom -> 'a or_bottom val join_list: ('a -> 'a -> 'a) -> 'a or_bottom list -> 'a or_bottom diff --git a/src/plugins/value/domains/traces/traces_domain.ml b/src/plugins/value/domains/traces/traces_domain.ml index 1bdad678c7a..170b2d90411 100644 --- a/src/plugins/value/domains/traces/traces_domain.ml +++ b/src/plugins/value/domains/traces/traces_domain.ml @@ -1,4 +1,4 @@ -2(**************************************************************************) +(**************************************************************************) (* *) (* This file is part of Frama-C. *) (* *) @@ -24,8 +24,6 @@ module OCamlGraph = Graph module Frama_c_File = File open Cil_datatype -let loop_mode_at_enter_loop = true - [@@@ warning "-40-42"] module Node : sig @@ -62,8 +60,8 @@ module Edge = struct let rec pretty fmt = function | Assign(n,loc,_typ,exp) -> Format.fprintf fmt "@[Assign:@ %a = %a -> %a@]" - Lval.pretty loc Exp.pretty exp Node.pretty n - | Assume(n,e,b) -> Format.fprintf fmt "@[Assume:@ %a %b -> %a@]" Exp.pretty e b Node.pretty n + Lval.pretty loc ExpStructEq.pretty exp Node.pretty n + | Assume(n,e,b) -> Format.fprintf fmt "@[Assume:@ %a %b -> %a@]" ExpStructEq.pretty e b Node.pretty n | EnterScope(n,vs) -> Format.fprintf fmt "@[EnterScope:@ %a -> %a@]" (Pretty_utils.pp_list ~sep:"@ " Varinfo.pretty) vs Node.pretty n | LeaveScope(n,vs) -> Format.fprintf fmt "@[LeaveScope:@ %a -> %a@]" @@ -72,10 +70,11 @@ module Edge = struct Format.fprintf fmt "@[CallDeclared:@ %a%s(%a) -> %a@]" (Pretty_utils.pp_opt ~pre:"" ~suf:" =@ " Lval.pretty) lval1 (Kernel_function.get_name kf1) - (Pretty_utils.pp_list ~sep:",@ " Exp.pretty) exp1 + (Pretty_utils.pp_list ~sep:",@ " ExpStructEq.pretty) exp1 Node.pretty n | Msg(n,s) -> Format.fprintf fmt "@[%s -> %a@]" s Node.pretty n - | Loop(n,_,s,g) -> Format.fprintf fmt "@[<hv 2>@[Loop(%a)@] %a @[-> %a@]@]" + | Loop(n,stmt,s,g) -> Format.fprintf fmt "@[<hv 2>@[Loop(%a) %a@] %a @[-> %a@]@]" + Stmt.pretty_sid stmt Node.pretty s (GraphShape.pretty pretty_list) g Node.pretty n and pretty_list fmt l = Pretty_utils.pp_list ~sep:";@ " pretty fmt l @@ -97,11 +96,11 @@ module Edge = struct if c <> 0 then c else let c = Typ.compare typ1 typ2 in if c <> 0 then c else - Exp.compare exp1 exp2 + ExpStructEq.compare exp1 exp2 | Assume(n1,e1,b1), Assume(n2,e2,b2) -> let c = Node.compare n1 n2 in if c <> 0 then c else - let c = Exp.compare e1 e2 in + let c = ExpStructEq.compare e1 e2 in if c <> 0 then c else Pervasives.compare b1 b2 | EnterScope(n1,vs1),EnterScope(n2,vs2) -> @@ -119,7 +118,7 @@ module Edge = struct if c <> 0 then c else let c = Kernel_function.compare kf1 kf2 in if c <> 0 then c else - let c = Extlib.list_compare Exp.compare exp1 exp2 in + let c = Extlib.list_compare ExpStructEq.compare exp1 exp2 in if c <> 0 then c else let c = Extlib.opt_compare Lval.compare lval1 lval2 in c @@ -158,9 +157,9 @@ module Edge = struct let hash = function | Assume(n,e,b) -> Hashtbl.seeded_hash (Node.hash n) - (Hashtbl.seeded_hash (Hashtbl.hash b) (Exp.hash e)) + (Hashtbl.seeded_hash (Hashtbl.hash b) (ExpStructEq.hash e)) | Assign(n,loc,typ,exp) -> - (Hashtbl.seeded_hash (Exp.hash exp) + (Hashtbl.seeded_hash (ExpStructEq.hash exp) (Hashtbl.seeded_hash (Typ.hash typ) (Hashtbl.seeded_hash (Node.hash n) @@ -174,7 +173,7 @@ module Edge = struct | CallDeclared(n,kf,exps,lval) -> let x = Kernel_function.hash kf in let x = Hashtbl.seeded_hash x (Extlib.opt_hash Lval.hash lval) in - let x = List.fold_left (fun acc e -> Hashtbl.seeded_hash acc (Exp.hash e)) x exps in + let x = List.fold_left (fun acc e -> Hashtbl.seeded_hash acc (ExpStructEq.hash e)) x exps in Hashtbl.seeded_hash (Node.hash n) x | Msg(n,s) -> Hashtbl.seeded_hash (Node.hash n) (Hashtbl.seeded_hash 5 s) | Loop(n,stmt,s,g) -> @@ -211,18 +210,23 @@ type state = { start : Node.t; current : loops; call_declared_function: bool; globals : Cil_types.varinfo list; main_formals : Cil_types.varinfo list; + (** kind of memoization of the edges *) + all_edges_ever_created : Graph.t ref; + all_loop_start : (Node.t * Graph.t) Stmt.Hashtbl.t; } (* Lattice structure for the abstract state above *) module Traces = struct (** impossible for normal values start must be bigger than current *) - let empty = { start = Node.of_int 0; current = Base (Node.of_int 0, Graph.empty); - call_declared_function = false; - globals = []; main_formals = []} - let top = { start = Node.of_int 0; current = Base (Node.of_int (-1), Graph.empty); - call_declared_function = false; - globals = []; main_formals = []} + let new_empty () = { start = Node.of_int 0; current = Base (Node.of_int 0, Graph.empty); + call_declared_function = false; + globals = []; main_formals = []; + all_edges_ever_created = ref Graph.empty; + all_loop_start = Stmt.Hashtbl.create 10; + } + let empty = new_empty () + let top = { (new_empty ()) with current = Base (Node.of_int (-1), Graph.empty); } let rec compare_loops l1 l2 = match l1,l2 with @@ -255,11 +259,13 @@ module Traces = struct | Base (c,g) -> Format.fprintf fmt "@[<hv>%a @[at %a@]@]" Graph.pretty g Node.pretty c - | OpenLoop(_,s,_,c,g,l) -> - Format.fprintf fmt "@[<hv>@[start %a@]@ %a @[at %a@]@]@ %a" - Node.pretty s Graph.pretty g Node.pretty c pretty_loops l - | UnrollLoop(_,l) -> - Format.fprintf fmt "@[<hv>@[unroll@]@ %a" + | OpenLoop(stmt,s,last,c,g,l) -> + Format.fprintf fmt "@[<hv 1>@[loop(%a) %a@]@ @[<hv 1>@[last:@]@ %a@]@ @[<hv 1>@[c:@]@ %a@]@ @[at %a@]@]@ %a" + Stmt.pretty_sid stmt + Node.pretty s Graph.pretty last Graph.pretty g Node.pretty c pretty_loops l + | UnrollLoop(stmt,l) -> + Format.fprintf fmt "@[<hv>@[unroll(%a)@]@ %a" + Stmt.pretty_sid stmt pretty_loops l (* Frama-C "datatype" for type [inout] *) @@ -339,6 +345,27 @@ module Traces = struct ~cache:Hptmap_sig.NoCache ~decide:merge_edge g1 g2 + let diff_graph g1 g2 = + let rec diff_list k l1 l2 = + match l1, l2 with + | [], _ -> [] + | l1, [] -> l1 + | h1 :: t1, h2 :: t2 -> + let c = Edge.compare h1 h2 in + if c = 0 + then diff_list k t1 t2 + else if c < 0 + then h1 :: diff_list k t1 l2 + else diff_list k l1 t2 + in + Graph.merge ~cache:NoCache + ~symmetric:false + ~idempotent:false + ~decide_both:(fun k l1 l2 -> match diff_list k l1 l2 with [] -> None | l -> Some l) + ~decide_left:Neutral + ~decide_right:Absorbing + g1 g2 + let get_node = function | Assign (n,_,_,_) | Assume (n,_,_) @@ -376,13 +403,6 @@ module Traces = struct aux l in aux state.current - let get_last state = - let rec aux = function - | Base (_,_) -> None - | OpenLoop(_,_,last,_,_,_) -> Some last - | UnrollLoop(_,l) -> aux l in - aux state.current - let find_succs current g = match Graph.find current g with | exception Not_found -> [] @@ -404,9 +424,9 @@ module Traces = struct if c <> 0 then false else let c = Typ.compare typ1 typ2 in if c <> 0 then false else - Exp.compare exp1 exp2 = 0 + ExpStructEq.compare exp1 exp2 = 0 | Assume(_,e1,b1), Assume(_,e2,b2) -> - let c = Exp.compare e1 e2 in + let c = ExpStructEq.compare e1 e2 in if c <> 0 then false else Pervasives.compare b1 b2 = 0 | EnterScope(_,vs1),EnterScope(_,vs2) -> @@ -418,7 +438,7 @@ module Traces = struct | CallDeclared(_,kf1,exp1,lval1), CallDeclared(_,kf2,exp2,lval2) -> let c = Kernel_function.compare kf1 kf2 in if c <> 0 then false else - let c = Extlib.list_compare Exp.compare exp1 exp2 in + let c = Extlib.list_compare ExpStructEq.compare exp1 exp2 in if c <> 0 then false else let c = Extlib.opt_compare Lval.compare lval1 lval2 in c = 0 @@ -430,13 +450,21 @@ module Traces = struct Node.equal s1 s2 && GraphShape.equal g1 g2 | _ -> false + let create_edge all_edges_ever_created current e = + let m = Graph.singleton current [e] in + let old = !all_edges_ever_created in + let new_ = join_graph old m in + (* if not (Graph.equal old new_) then *) + (* Format.printf "@[<hv>@[create_edge: %a ->@]@ %a@]@." *) + (* Node.pretty current Edge.pretty e; *) + all_edges_ever_created := new_; + m + let add_edge_aux c edge = let (current,graph) = get_current c in - let reusable = match get_last c with - | None -> None - | Some last -> - let succs = find_succs current last in - List.find_opt (same_edge edge) succs + let reusable = + let succs = find_succs current !(c.all_edges_ever_created) in + List.find_opt (same_edge edge) succs in let (n,e) = match reusable with | Some e -> @@ -449,14 +477,16 @@ module Traces = struct let e = change_next n edge in (n,e) in - let m = Graph.singleton current [e] in + let m = create_edge c.all_edges_ever_created current e in let graph = join_graph m graph in move_to n graph c let add_edge c e = if c == top then c else if c.call_declared_function then c (** forget intermediary state *) - else add_edge_aux c e + else + let c = if c == empty then new_empty () else c in + add_edge_aux c e let copy_edges s old_current_node g state = let cache = Node.Hashtbl.create 10 in @@ -474,7 +504,7 @@ module Traces = struct | next -> let (_,g) = get_current state in let e = change_next next e in - let m = Graph.singleton current_node [e] in + let m = create_edge state.all_edges_ever_created current_node e in let g = join_graph m g in move_to next g state in @@ -553,29 +583,32 @@ module Traces = struct let is_included c1 c2 = (* start is the same *) - c1.start = c2.start && - is_included_loops c1.current c2.current + let r = + c1.start = c2.start && + is_included_loops c1.current c2.current in + if not r && compare c1 c2 = 0 then + Printf.printf "bad is_included@."; + r let not_same_origin c1 c2 = c1.start != c2.start || c1.globals != c2.globals || - c1.main_formals != c2.main_formals + c1.main_formals != c2.main_formals || + c1.all_edges_ever_created != c2.all_edges_ever_created - let join_path ?last graph c1 c2 = + let join_path ~all_edges_ever_created graph c1 c2 = if epsilon_path c1 c2 graph then (c2, graph) else if epsilon_path c2 c1 graph then (c1, graph) else let m = Msg(Node.dumb,"join") in - let reusable = match last with - | None -> None - | Some last -> - let succs1 = find_succs c1 last in - let succs1 = List.filter (same_edge m) succs1 in - let succs2 = find_succs c2 last in - let find s1 = List.exists (Edge.equal s1) succs2 in - List.find_opt find succs1 + let reusable = + let succs1 = find_succs c1 !all_edges_ever_created in + let succs1 = List.filter (same_edge m) succs1 in + let succs2 = find_succs c2 !all_edges_ever_created in + let find s1 = List.exists (Edge.equal s1) succs2 in + List.find_opt find succs1 in let (n,m) = match reusable with | None -> @@ -587,19 +620,19 @@ module Traces = struct let n = get_node m in (n,m) in - let m1 = Graph.singleton c1 [m] in - let m2 = Graph.singleton c2 [m] in + let m1 = create_edge all_edges_ever_created c1 m in + let m2 = create_edge all_edges_ever_created c2 m in let g = join_graph (join_graph m1 graph) m2 in ( n, g) - let rec join_loops l1 l2 = + let rec join_loops ~all_edges_ever_created l1 l2 = match l1, l2 with | Base _, (OpenLoop _ | UnrollLoop _) | (OpenLoop _ | UnrollLoop _), Base _ -> (* not in the same number of loops *) `Top | Base (c1,g1), Base (c2,g2) -> let g = join_graph g1 g2 in - let (n,g) = join_path g c1 c2 in + let (n,g) = join_path ~all_edges_ever_created g c1 c2 in `Value( Base (n, g)) | (OpenLoop(stmt1,_,_,_,_,_) | UnrollLoop(stmt1,_)), (OpenLoop(stmt2,_,_,_,_,_) | UnrollLoop(stmt2,_)) when not (Stmt.equal stmt1 stmt2) -> @@ -607,27 +640,27 @@ module Traces = struct `Top | OpenLoop(stmt1,s1,last1,c1,g1,l1), OpenLoop(_,s2,_,_,_,l2) when not (Node.equal s1 s2) -> (* not entered in the loop at the same time, take arbitrarily one of them *) - begin match join_loops l1 l2 with + begin match join_loops ~all_edges_ever_created l1 l2 with | `Top -> `Top | `Value(l) -> `Value(OpenLoop(stmt1,s1,last1,c1,g1,l)) end | OpenLoop(stmt,s,last1,c1,g1,l1), OpenLoop(_,_,last2,c2,g2,l2) -> - begin match join_loops l1 l2 with + begin match join_loops ~all_edges_ever_created l1 l2 with | `Top -> `Top | `Value(l) -> let last = join_graph last1 last2 in let g = join_graph g1 g2 in - let (n,g) = join_path ~last g c1 c2 in + let (n,g) = join_path ~all_edges_ever_created g c1 c2 in `Value(OpenLoop(stmt,s,last,n,g,l)) end | UnrollLoop(stmt,l1), UnrollLoop(_,l2) -> - begin match join_loops l1 l2 with + begin match join_loops ~all_edges_ever_created l1 l2 with | `Top -> `Top | `Value l -> `Value (UnrollLoop(stmt,l)) end | (OpenLoop(stmt,s,last,c,g,l1), UnrollLoop(_,l2)) | (UnrollLoop(_,l2), OpenLoop(stmt,s,last,c,g,l1)) -> - begin match join_loops l1 l2 with + begin match join_loops ~all_edges_ever_created l1 l2 with | `Top -> `Top | `Value l -> `Value (OpenLoop(stmt,s,last,c,g,l)) end @@ -646,12 +679,10 @@ module Traces = struct | `Other c1, `Other c2 -> if not_same_origin c1 c2 then assert false else - match join_loops c1.current c2.current with + let all_edges_ever_created = c1.all_edges_ever_created in + match join_loops ~all_edges_ever_created c1.current c2.current with | `Top -> top - | `Value(current) -> { - start = c1.start; current; call_declared_function = c1.call_declared_function; - globals = c1.globals; main_formals = c1.main_formals; - } + | `Value(current) -> {c1 with current} let add_loop stmt state = let (n,g) = get_current state in @@ -662,29 +693,172 @@ module Traces = struct | _ -> None in let s,last = match Extlib.find_opt same_loop succs with | (s,last) -> s,(Graph.from_shape (fun _ v -> v) last) - | exception Not_found -> Node.next (), Graph.empty in + | exception Not_found -> + Stmt.Hashtbl.memo state.all_loop_start stmt (fun _ -> Node.next (),Graph.empty) + in let current = OpenLoop(stmt,s,last,s,Graph.empty,state.current) in { state with current } - let widen _ stmt' _ c2 = - Format.printf "@[widen@]@."; - if loop_mode_at_enter_loop + + let rec diff_loops l1 l2 = + match l1, l2 with + | Base _, (OpenLoop _ | UnrollLoop _) | (OpenLoop _ | UnrollLoop _), Base _ -> + (* not in the same number of loops *) + `Bottom + | Base (c1,g1), Base (_,g2) -> + let g = diff_graph g1 g2 in + `Value (Base (c1, g)) + | (OpenLoop(stmt1,_,_,_,_,_) | UnrollLoop(stmt1,_)), + (OpenLoop(stmt2,_,_,_,_,_) | UnrollLoop(stmt2,_)) when not (Stmt.equal stmt1 stmt2) -> + (* not same loop *) + `Bottom + | OpenLoop(stmt1,s1,last1,c1,g1,l1), OpenLoop(_,s2,_,_,_,l2) when not (Node.equal s1 s2) -> + (* not entered in the loop at the same time, take arbitrarily one of them *) + begin match diff_loops l1 l2 with + | `Bottom -> `Bottom + | `Value(l) -> `Value(OpenLoop(stmt1,s1,last1,c1,g1,l)) + end + | OpenLoop(stmt,s,last1,c1,g1,l1), OpenLoop(_,_,last2,_,g2,l2) -> + begin match diff_loops l1 l2 with + | `Bottom -> `Bottom + | `Value(l) -> + let last = diff_graph last1 last2 in + let g = diff_graph g1 g2 in + `Value(OpenLoop(stmt,s,last,c1,g,l)) + end + | UnrollLoop(stmt,l1), UnrollLoop(_,l2) -> + begin match diff_loops l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (UnrollLoop(stmt,l)) + end + | (OpenLoop(stmt,s,last,c,g,l1), UnrollLoop(_,l2)) -> + begin match diff_loops l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (OpenLoop(stmt,s,last,c,g,l)) + end + | (UnrollLoop(stmt,l2), OpenLoop(_,_,_,_,_,l1)) -> + begin match diff_loops l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (UnrollLoop(stmt,l)) + end + + + let widen _ stmt' c1 c2 = + if false then + begin + if compare_loops c1.current c2.current = 0 + then + Format.printf "@[<hv 2>@[widen %a: same loops, states are%s equal @]@]@." + Stmt.pretty_sid stmt' (if compare c1 c2 = 0 then "" else " not") + else + let c1' = diff_loops c1.current c2.current in + let c2' = diff_loops c2.current c1.current in + if (Bottom.compare compare_loops) c1' c2' = 0 then + Format.printf "@[<hv 2>@[widen %a diff equal:@]@ @[<hv 1>@[c1:@]@ %a@]@ @[<hv 1>@[c2:@]@ %a@]@]@." + Stmt.pretty_sid stmt' + pretty_loops c1.current + pretty_loops c2.current + + else + Format.printf "@[<hv 2>@[widen %a diff different:@]@ @[<hv 1>@[c1':@]@ %a@]@ @[<hv 1>@[c2':@]@ %a@]@]@." + Stmt.pretty_sid stmt' + (Bottom.pretty pretty_loops) c1' + (Bottom.pretty pretty_loops) c2' + end; + if false then + begin + if compare_loops c1.current c2.current = 0 + then + Format.printf "@[<hv 2>@[widen %a: same loops, states are%s equal @]@]@." + Stmt.pretty_sid stmt' (if compare c1 c2 = 0 then "" else " not") + else + Format.printf "@[<hv 2>@[widen %a@]@]@." Stmt.pretty_sid stmt' + end; + if not (Value_parameters.TracesUnrollLoop.get ()) then c2 else begin - match c2.current with - | Base _ -> assert false (** must be in a loop *) - | OpenLoop(stmt,_,_,_,_,_) -> - assert (Stmt.equal stmt' stmt); - c2 - | UnrollLoop(stmt,l) -> - assert (Stmt.equal stmt' stmt); - add_loop stmt' {c2 with current = l} - end + match c2.current with + | Base _ -> assert false (** must be in a loop *) + | OpenLoop(stmt,_,_,_,_,_) -> + assert (Stmt.equal stmt' stmt); + c2 + | UnrollLoop(stmt,l) -> + assert (Stmt.equal stmt' stmt); + add_loop stmt' {c2 with current = l} + end let narrow _c1 c2 = `Value c2 end + +module GraphDot = OCamlGraph.Graphviz.Dot(struct + module V = struct type t = {node : Node.t; loops : Node.t list} end + module E = struct + open V + type t = + | Usual of Node.t * Edge.t * Node.t list + | Head of Node.t * Node.t list * Node.t * Node.t list + let src = function + | Usual (src,_,loops) -> {node=src;loops} + | Head (src,loops,_,_) -> {node=src;loops} + let dst = function + | Usual (_,edge,loops) -> {node=Traces.get_node edge;loops} + | Head (_,_,s,loops) -> {node=s;loops} + end + open V + open E + type t = Graph.t + let iter_vertex f g = + let rec iter_edge k (l: Node.t list) = function + | Loop(_,_,_,g) -> iter_vertex (k::l) g + | _ -> () + and iter_vertex l g = + GraphShape.iter (fun k e -> f {node=k;loops=l}; List.iter (iter_edge k l) e) g + in + iter_vertex [] (Graph.shape g) + let iter_edges_e f g = + let rec iter_edge k l e = + f (Usual(k,e,l)); + match e with + | Loop(_,_,s,g) -> + let l' = (k::l) in + f (Head(k,l,s,l')); + iter_vertex l' g + | _ -> () + and iter_vertex l g = + GraphShape.iter (fun k e -> List.iter (iter_edge k l) e) g + in + iter_vertex [] (Graph.shape g) + + let graph_attributes _ = [] + let default_vertex_attributes : + t -> OCamlGraph.Graphviz.DotAttributes.vertex list = fun _ -> [] + let subgraph_name loops = + Format.asprintf "S%a" + (fun fmt -> List.iter (fun s -> Format.fprintf fmt "L%a" Node.pretty s)) + loops + let vertex_name v = Format.asprintf "n%a%s" Node.pretty v.node + (subgraph_name v.loops) + let vertex_attributes : + V.t -> OCamlGraph.Graphviz.DotAttributes.vertex list = + fun n -> [`Label (Format.asprintf "%a" Node.pretty n.node)] + let get_subgraph v = + match v.loops with + | [] -> None + | _::l -> Some + {OCamlGraph.Graphviz.DotAttributes.sg_name = subgraph_name v.loops; + sg_attributes = []; + sg_parent = if l = [] then None else Some (subgraph_name l); } + let default_edge_attributes : + t -> OCamlGraph.Graphviz.DotAttributes.edge list = fun _ -> [] + let edge_attributes : E.t -> OCamlGraph.Graphviz.DotAttributes.edge list = + function + | Usual(_,Loop _,_) -> [`Label (Format.asprintf "leave_loop")] + | Usual(_,e,_) -> [`Label (Format.asprintf "%a" Edge.pretty e)] + | Head _ -> [] +end) + let key = Structure.Key_Domain.create_key "traces domain" module Internal = struct @@ -814,8 +988,9 @@ module Internal = struct let enter_loop stmt state = let state = Traces.add_edge state (Msg(Node.dumb,"enter_loop")) in - let state = if loop_mode_at_enter_loop then Traces.add_loop stmt state else - { state with current = UnrollLoop(stmt,state.current) } in + let state = if not (Value_parameters.TracesUnrollLoop.get ()) + then Traces.add_loop stmt state + else { state with current = UnrollLoop(stmt,state.current) } in state let incr_loop_counter _ state = @@ -824,22 +999,38 @@ module Internal = struct | UnrollLoop(_,_) -> state | OpenLoop(stmt,s,last,_,g,l) -> let last = Traces.join_graph last g in + let last = if Value_parameters.TracesUnifyLoop.get () then + let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in + let last = Traces.join_graph last old_last in + assert (Node.equal s s'); + Stmt.Hashtbl.add state.all_loop_start stmt (s,last); + last + else last + in let current = OpenLoop(stmt,s,last,s,Graph.empty,l) in let state = { state with current } in (* Traces.add_edge state (Msg(Node.dumb,"incr_loop_counter")) *) state let leave_loop stmt' state = - match state.current with - | Base _ -> assert false (* absurd: we are in at least a loop *) - | UnrollLoop(_,l) -> { state with current = l } - | OpenLoop(stmt,s,last,old_current_node,g,current) -> - assert (Stmt.equal stmt stmt'); - let state = { state with current } in - let state = if Graph.is_empty last then state - else Traces.add_edge state (Loop(Node.dumb,stmt,s,Graph.shape last)) in - let state = Traces.copy_edges s old_current_node g state in - Traces.add_edge state (Msg(Node.dumb,"leave_loop")) + match state.current with + | Base _ -> assert false (* absurd: we are in at least a loop *) + | UnrollLoop(_,l) -> { state with current = l } + | OpenLoop(stmt,s,last,old_current_node,g,current) -> + assert (Stmt.equal stmt stmt'); + let state = { state with current } in + let last = if Value_parameters.TracesUnifyLoop.get () then + let s',old_last = Stmt.Hashtbl.find state.all_loop_start stmt in + let last = Traces.join_graph last old_last in + assert (Node.equal s s'); + Stmt.Hashtbl.add state.all_loop_start stmt (s,last); + last + else last + in + let state = if Graph.is_empty last then state + else Traces.add_edge state (Loop(Node.dumb,stmt,s,Graph.shape last)) in + let state = Traces.copy_edges s old_current_node g state in + Traces.add_edge state (Msg(Node.dumb,"leave_loop")) let enter_scope _kf vars state = Traces.add_edge state (EnterScope(Node.dumb,vars)) @@ -951,7 +1142,7 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = let is_while = match Traces.find_succs s g, Traces.find_succs n cfg with | [Assume(n1',exp1,b1)], [Assume(n2',exp2,b2)] - when Exp.equal exp1 exp2 && b1 != b2 -> + when ExpStructEq.equal exp1 exp2 && b1 != b2 -> Some (exp1, n1', b1, n2') | _ -> None in match is_while with @@ -967,12 +1158,12 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = let is_if = match l with | [] | [_] -> assert false (* absurd *) | [Assume(n1',exp1,b1) ; Assume(n2',exp2,b2)] - when Exp.equal exp1 exp2 && b1 != b2 -> + when ExpStructEq.equal exp1 exp2 && b1 != b2 -> if b1 then Some (exp1, n1', n2') else Some (exp1,n2',n1') | _ -> None in let stmt = match is_if with - | None -> Value_parameters.not_yet_implemented "Traces_domain: switch" + | None -> Value_parameters.not_yet_implemented "Traces_domain: switch at node(%a)" Node.pretty current | Some(exp,n1,n2) -> let exp = subst_in_exp var_map exp in let block1 = Cil.mkBlock (stmts_of_cfg cfg n1 var_map locals return_exp []) in @@ -1081,67 +1272,6 @@ let project_of_cfg vreturn s = (* Format.printf "@[<2>@[file3:@] %a@]@." Printer.pp_file file; *) (* ) () *) - -module GraphDot = OCamlGraph.Graphviz.Dot(struct - module V = struct type t = {node : Node.t; loops : Node.t list} end - module E = struct - open V - type t = - | Usual of Node.t * Edge.t * Node.t list - | Head of Node.t * Node.t list * Node.t * Node.t list - let src = function - | Usual (src,_,loops) -> {node=src;loops} - | Head (src,loops,_,_) -> {node=src;loops} - let dst = function - | Usual (_,edge,loops) -> {node=Traces.get_node edge;loops} - | Head (_,_,s,loops) -> {node=s;loops} - end - open V - open E - type t = Graph.t - let iter_vertex f g = - let rec iter_edge k (l: Node.t list) = function - | Loop(_,_,_,g) -> iter_vertex (k::l) g - | _ -> () - and iter_vertex l g = - GraphShape.iter (fun k e -> f {node=k;loops=l}; List.iter (iter_edge k l) e) g - in - iter_vertex [] (Graph.shape g) - let iter_edges_e f g = - let rec iter_edge k l e = - f (Usual(k,e,l)); - match e with - | Loop(_,_,s,g) -> - let l' = (k::l) in - f (Head(k,l,s,l')); - iter_vertex l' g - | _ -> () - and iter_vertex l g = - GraphShape.iter (fun k e -> List.iter (iter_edge k l) e) g - in - iter_vertex [] (Graph.shape g) - - let graph_attributes _ = [] - let default_vertex_attributes : - t -> OCamlGraph.Graphviz.DotAttributes.vertex list = fun _ -> [] - let vertex_name v = Format.asprintf "n%a%t" Node.pretty v.node - (fun fmt -> List.iter (fun s -> Format.fprintf fmt "L%a" Node.pretty s) v.loops) - let vertex_attributes : - V.t -> OCamlGraph.Graphviz.DotAttributes.vertex list = - fun n -> [`Label (Format.asprintf "%a" Node.pretty n.node)] - let get_subgraph : - V.t -> OCamlGraph.Graphviz.DotAttributes.subgraph option = fun _ -> None - let default_edge_attributes : - t -> OCamlGraph.Graphviz.DotAttributes.edge list = fun _ -> [] - let edge_attributes : E.t -> OCamlGraph.Graphviz.DotAttributes.edge list = - function - | Usual(_,Loop _,_) -> [`Label (Format.asprintf "leave_loop")] - | Usual(_,e,_) -> [`Label (Format.asprintf "%a" Edge.pretty e)] - | Head _ -> [] -end) - - - let finish_computation () = let return_stmt = Kernel_function.find_return (fst (Globals.entry_point ())) in let return_exp = match return_stmt.Cil_types.skind with @@ -1154,12 +1284,14 @@ let finish_computation () = match state with | `Bottom -> Value_parameters.failure "The trace is Bottom can't generate code" - | `Value state when state == Traces.top -> + | `Value state when state ==Traces.top -> Value_parameters.failure "The trace is TOP can't generate code" | `Value state -> - let out = open_out "traces_domain.dot" in - GraphDot.output_graph out (snd (Traces.get_current state)); - close_out out; + if false then begin + let out = open_out "traces_domain.dot" in + GraphDot.output_graph out (snd (Traces.get_current state)); + close_out out; + end; project_of_cfg return_exp state diff --git a/src/plugins/value/value_parameters.ml b/src/plugins/value/value_parameters.ml index 5b9abd266d1..9efa279d727 100644 --- a/src/plugins/value/value_parameters.ml +++ b/src/plugins/value/value_parameters.ml @@ -282,6 +282,21 @@ module TracesDomain = Domain_Parameter let default = false end) +module TracesUnrollLoop = Domain_Parameter + (struct + let option_name = "-eva-traces-unroll-loop" + let help = "Specify if the traces domain should unroll the loops." + let default = true + end) + +module TracesUnifyLoop = Domain_Parameter + (struct + let option_name = "-eva-traces-unify-loop" + let help = "Specify if all the instances of a loop should try to share theirs traces." + let default = false + end) + + let () = Parameter_customize.set_group domains module Numerors_Real_Size = Int @@ -313,6 +328,18 @@ let () = Numerors_Mode.set_possible_values ["relative"; "absolute"; "none"; "both"] let () = add_precision_dep Numerors_Mode.parameter +let () = Parameter_customize.set_group domains +module TracesStorage = + Bool + (struct + let option_name = "-eva-traces-storage" + let help = "Stores the states of the traces domain during the \ + analysis." + let default = true + end) +let () = add_precision_dep TracesStorage.parameter + + (* -------------------------------------------------------------------------- *) (* --- Performance options --- *) (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/value/value_parameters.mli b/src/plugins/value/value_parameters.mli index f02cafaf376..1015dc5e091 100644 --- a/src/plugins/value/value_parameters.mli +++ b/src/plugins/value/value_parameters.mli @@ -38,6 +38,8 @@ module SignDomain: Parameter_sig.Bool module PrinterDomain: Parameter_sig.Bool module NumerorsDomain: Parameter_sig.Bool module TracesDomain: Parameter_sig.Bool +module TracesUnrollLoop: Parameter_sig.Bool +module TracesUnifyLoop: Parameter_sig.Bool module ApronOctagon: Parameter_sig.Bool module ApronBox: Parameter_sig.Bool @@ -55,6 +57,7 @@ module SymbolicLocsStorage: Parameter_sig.Bool module GaugesStorage: Parameter_sig.Bool module ApronStorage: Parameter_sig.Bool module BitwiseOffsmStorage: Parameter_sig.Bool +module TracesStorage: Parameter_sig.Bool module AutomaticContextMaxDepth: Parameter_sig.Int diff --git a/tests/value/traces/test4.i b/tests/value/traces/test4.i index 523afaedbda..9a95eac246a 100644 --- a/tests/value/traces/test4.i +++ b/tests/value/traces/test4.i @@ -2,6 +2,8 @@ STDOPT: #"-eva-traces-domain -value-msg-key d-traces -slevel 10" +"-then-last -val -slevel 10 -print -no-eva-traces-domain" */ +/* Test of join inside a loop */ + int main(c){ int tmp = 0; for(int i = 0; i < 100; i++){ -- GitLab