From c33d2de60b7e121028b726afa1c300c95bd5aef0 Mon Sep 17 00:00:00 2001 From: Bouillaguet Quentin <quentin.bouillaguet@cea.fr> Date: Tue, 12 Mar 2019 11:30:14 +0000 Subject: [PATCH] [Eva] Split edges into transition and next node - Definition used in the branch from traces to WP Cfg --- src/plugins/value/domains/traces_domain.ml | 836 ++++++++++---------- src/plugins/value/domains/traces_domain.mli | 35 +- 2 files changed, 436 insertions(+), 435 deletions(-) diff --git a/src/plugins/value/domains/traces_domain.ml b/src/plugins/value/domains/traces_domain.ml index d2547a163c9..c300d052d27 100644 --- a/src/plugins/value/domains/traces_domain.ml +++ b/src/plugins/value/domains/traces_domain.ml @@ -28,6 +28,7 @@ module OCamlGraph = Graph module Frama_c_File = File +open Cil_types open Cil_datatype [@@@ warning "-40-42"] @@ -50,162 +51,167 @@ end (** Can't use Graph.t it needs an impossible recursive module *) module GraphShape = Hptmap.Shape(Node) -type edge = - | Assign of Node.t * Cil_types.lval * Cil_types.typ * Cil_types.exp - | Assume of Node.t * Cil_types.exp * bool - | EnterScope of Node.t * Cil_types.varinfo list - | LeaveScope of Node.t * Cil_types.varinfo list +type node = Node.t + +type transition = + | Assign of lval * typ * exp + | Assume of exp * bool + | EnterScope of varinfo list + | LeaveScope of varinfo list (** For call of functions without definition *) - | CallDeclared of Node.t * Cil_types.kernel_function * Cil_types.exp list * Cil_types.lval option - | Loop of Node.t * Stmt.t * Node.t (** start *) * edge list GraphShape.t - | Msg of Node.t * string - - -(* Frama-C "datatype" for type [inout] *) -module Edge = struct - - let succ = function - | Assign (n,_,_,_) - | Assume (n,_,_) - | EnterScope (n,_) - | LeaveScope (n,_) - | CallDeclared (n,_,_,_) - | Msg (n,_) - | Loop(n,_,_,_) -> n - - - let rec pretty_edge fmt = function - | Assign(_,loc,_typ,exp) -> Format.fprintf fmt "Assign:@ %a = %a" - Lval.pretty loc ExpStructEq.pretty exp - | Assume(_,e,b) -> Format.fprintf fmt "Assume:@ %a %b" ExpStructEq.pretty e b - | EnterScope(_,vs) -> Format.fprintf fmt "EnterScope:@ %a" - (Pretty_utils.pp_list ~sep:"@ " Varinfo.pretty) vs - | LeaveScope(_,vs) -> Format.fprintf fmt "LeaveScope:@ %a" - (Pretty_utils.pp_list ~sep:"@ " Varinfo.pretty) vs - | CallDeclared(_,kf1,exp1,lval1) -> - Format.fprintf fmt "CallDeclared:@ %a%s(%a)" - (Pretty_utils.pp_opt ~pre:"" ~suf:" =@ " Lval.pretty) lval1 - (Kernel_function.get_name kf1) - (Pretty_utils.pp_list ~sep:",@ " ExpStructEq.pretty) exp1 + | CallDeclared of kernel_function * exp list * lval option + | Loop of stmt * node (** start *) * edge list GraphShape.t + | Msg of string - | Msg(_,s) -> Format.fprintf fmt "%s" s - | Loop(_,stmt,s,g) -> Format.fprintf fmt "@[Loop(%a) %a@] %a" - Stmt.pretty_sid stmt - Node.pretty s - (GraphShape.pretty pretty_list) g - and pretty fmt e = - Format.fprintf fmt "@[<hv 2>%a @[-> %a@]@]" pretty_edge e Node.pretty (succ e) - and pretty_list fmt l = Pretty_utils.pp_list ~sep:";@ " pretty fmt l +and edge = { + edge_trans : transition; + edge_dst : node; +} - include Datatype.Make_with_collections(struct - type nonrec t = edge - let name = "Value.Traces_domain.Edge.t" +module rec Edge : sig + include Datatype.S_with_collections with type t = edge + val succ : t -> node + val change_next : node -> t -> t - let reprs = [Msg(Node.of_int 0,"msg")] + val pretty_list : t list Pretty_utils.formatter +end = struct + module T = struct + type t = edge + let name = "Value.Traces_domain.Edge" - let structural_descr = Structural_descr.t_abstract + include Datatype.Serializable_undefined - let rec compare (m1:t) (m2:t) = - match m1, m2 with - | Assign (n1,loc1,typ1,exp1), Assign (n2,loc2,typ2,exp2) -> - let c = Node.compare n1 n2 in - if c <> 0 then c else - let c = Lval.compare loc1 loc2 in - if c <> 0 then c else - let c = Typ.compare typ1 typ2 in - if c <> 0 then c else - 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 = ExpStructEq.compare e1 e2 in - if c <> 0 then c else - Pervasives.compare b1 b2 - | EnterScope(n1,vs1),EnterScope(n2,vs2) -> - let c = Node.compare n1 n2 in - if c <> 0 then c else - let c = Extlib.list_compare Varinfo.compare vs1 vs2 in - c - | LeaveScope(n1,vs1), LeaveScope(n2,vs2) -> - let c = Node.compare n1 n2 in - if c <> 0 then c else - let c = Extlib.list_compare Varinfo.compare vs1 vs2 in - c - | CallDeclared(n1,kf1,exp1,lval1), CallDeclared(n2,kf2,exp2,lval2) -> - let c = Node.compare n1 n2 in - 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 ExpStructEq.compare exp1 exp2 in - if c <> 0 then c else - let c = Extlib.opt_compare Lval.compare lval1 lval2 in - c - | Msg(n1,s1), Msg(n2,s2) -> - let c = Node.compare n1 n2 in - if c <> 0 then c else - let c = String.compare s1 s2 in - c - | Loop(n1,stmt1,s1,g1), Loop(n2,stmt2,s2,g2) -> - let c = Node.compare n1 n2 in - if c <> 0 then c else - let c = Stmt.compare stmt1 stmt2 in - if c <> 0 then c else - let c = Node.compare s1 s2 in - if c <> 0 then c else - let c = GraphShape.compare (Extlib.list_compare compare) g1 g2 in - if c <> 0 then c else - 0 - | Assign _, _ -> -1 - | _ , Assign _ -> 1 - | Assume _, _ -> -1 - | _ , Assume _ -> 1 - | EnterScope _, _ -> -1 - | _ , EnterScope _ -> 1 - | LeaveScope _, _ -> -1 - | _ , LeaveScope _ -> 1 - | CallDeclared _, _ -> -1 - | _ , CallDeclared _ -> 1 - | Msg _, _ -> -1 - | _, Msg _ -> 1 + let reprs = [ { + edge_trans = Msg "msg"; + edge_dst = Node.dumb; + }] - let equal = Datatype.from_compare + let structural_descr = Structural_descr.t_abstract - let pretty = pretty - - let hash = function - | Assume(n,e,b) -> - Hashtbl.seeded_hash (Node.hash n) - (Hashtbl.seeded_hash (Hashtbl.hash b) (ExpStructEq.hash e)) - | Assign(n,loc,typ,exp) -> - (Hashtbl.seeded_hash (ExpStructEq.hash exp) - (Hashtbl.seeded_hash (Typ.hash typ) - (Hashtbl.seeded_hash - (Node.hash n) - (Hashtbl.seeded_hash 2 (Lval.hash loc))))) - | EnterScope(n,vs) -> - let x = List.fold_left (fun acc e -> Hashtbl.seeded_hash acc (Varinfo.hash e)) 3 vs in - Hashtbl.seeded_hash (Node.hash n) x - | LeaveScope(n,vs) -> - let x = List.fold_left (fun acc e -> Hashtbl.seeded_hash acc (Varinfo.hash e)) 4 vs in - Hashtbl.seeded_hash (Node.hash n) x - | 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 (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) -> - Hashtbl.seeded_hash (Stmt.hash stmt) - (Hashtbl.seeded_hash (GraphShape.hash g) (Hashtbl.seeded_hash (Node.hash n) (Node.hash s))) - - let rehash = Datatype.Serializable_undefined.rehash - let varname = Datatype.Serializable_undefined.varname - let mem_project = Datatype.Serializable_undefined.mem_project - let internal_pretty_code = Datatype.Serializable_undefined.internal_pretty_code + let compare (e1: t) (e2 : t) = + let c = Node.compare e1.edge_dst e2.edge_dst in + if c <> 0 then c else + Transition.compare e1.edge_trans e2.edge_trans - let copy c = c + let equal = Datatype.from_compare - end) + let pretty fmt e = + Format.fprintf fmt "@[<hv 2>%a @[-> %a@]@]" + Transition.pretty e.edge_trans Node.pretty e.edge_dst + + let hash e = + Hashtbl.seeded_hash (Node.hash e.edge_dst) (Transition.hash e.edge_trans) + end + + include Datatype.Make_with_collections(T) + + let pretty_list fmt l = Pretty_utils.pp_list ~sep:";@ " pretty fmt l + + let succ e = e.edge_dst + let change_next n e = { e with edge_dst = n } +end +and Transition : sig + type t = transition + + val compare : t -> t -> int + val equal : t -> t -> bool + val hash : t -> int + + val pretty : t Pretty_utils.formatter +end = struct + type t = transition + + let compare (t1: t) (t2: t) = match t1, t2 with + | Assign (loc1, typ1, exp1), Assign (loc2, typ2, exp2) -> + let c = Lval.compare loc1 loc2 in + if c <> 0 then c else + let c = Typ.compare typ1 typ2 in + if c <> 0 then c else + ExpStructEq.compare exp1 exp2 + | Assume (e1, b1), Assume (e2, b2) -> + let c = ExpStructEq.compare e1 e2 in + if c <> 0 then c else + Pervasives.compare b1 b2 + | EnterScope vs1, EnterScope vs2 -> + Extlib.list_compare Varinfo.compare vs1 vs2 + | LeaveScope vs1, LeaveScope vs2 -> + Extlib.list_compare Varinfo.compare vs1 vs2 + | CallDeclared (kf1, es1, lv1), CallDeclared (kf2, es2, lv2) -> + let c = Kernel_function.compare kf1 kf2 in + if c <> 0 then c else + let c = Extlib.list_compare ExpStructEq.compare es1 es2 in + if c <> 0 then c else + Extlib.opt_compare Lval.compare lv1 lv2 + | Msg s1, Msg s2 -> + String.compare s1 s2 + | Loop (stmt1, s1, g1), Loop (stmt2, s2, g2) -> + let c = Stmt.compare stmt1 stmt2 in + if c <> 0 then c else + let c = Node.compare s1 s2 in + if c <> 0 then c else + GraphShape.compare (Extlib.list_compare Edge.compare) g1 g2 + | Assign _, _ -> -1 + | _ , Assign _ -> 1 + | Assume _, _ -> -1 + | _ , Assume _ -> 1 + | EnterScope _, _ -> -1 + | _ , EnterScope _ -> 1 + | LeaveScope _, _ -> -1 + | _ , LeaveScope _ -> 1 + | CallDeclared _, _ -> -1 + | _ , CallDeclared _ -> 1 + | Msg _, _ -> -1 + | _, Msg _ -> 1 + + let equal t1 t2 = (compare t1 t2 = 0) + + let pretty fmt = function + | Assign (loc, _typ, exp) -> + Format.fprintf fmt "Assign:@ %a = %a" + Lval.pretty loc ExpStructEq.pretty exp + | Assume (e, b) -> + Format.fprintf fmt "Assume:@ %a %b" ExpStructEq.pretty e b + | EnterScope vs -> + Format.fprintf fmt "EnterScope:@ %a" + (Pretty_utils.pp_list ~sep:"@ " Varinfo.pretty) vs + | LeaveScope vs -> + Format.fprintf fmt "LeaveScope:@ %a" + (Pretty_utils.pp_list ~sep:"@ " Varinfo.pretty) vs + | CallDeclared(kf1, exp1, lval1) -> + Format.fprintf fmt "CallDeclared:@ %a%s(%a)" + (Pretty_utils.pp_opt ~pre:"" ~suf:" =@ " Lval.pretty) lval1 + (Kernel_function.get_name kf1) + (Pretty_utils.pp_list ~sep:",@ " ExpStructEq.pretty) exp1 + | Msg s -> Format.fprintf fmt "%s" s + | Loop(stmt, s, g) -> + Format.fprintf fmt "@[Loop(%a) %a@] %a" + Stmt.pretty_sid stmt + Node.pretty s + (GraphShape.pretty (Edge.pretty_list)) g + + let hash = function + | Assume (e, b) -> + Hashtbl.seeded_hash (Hashtbl.hash b) (ExpStructEq.hash e) + | Assign (lv, t, e) -> + Hashtbl.seeded_hash (ExpStructEq.hash e) + (Hashtbl.seeded_hash (Typ.hash t) + (Hashtbl.seeded_hash 2 (Lval.hash lv))) + | EnterScope vs -> + List.fold_left + (fun acc e -> Hashtbl.seeded_hash acc (Varinfo.hash e)) 3 vs + | LeaveScope vs -> + List.fold_left + (fun acc e -> Hashtbl.seeded_hash acc (Varinfo.hash e)) 5 vs + | CallDeclared (kf, es, lv) -> + let x = Kernel_function.hash kf in + let x = Hashtbl.seeded_hash x (Extlib.opt_hash Lval.hash lv) in + List.fold_left + (fun acc e -> Hashtbl.seeded_hash acc (ExpStructEq.hash e)) x es + | Msg s -> Hashtbl.seeded_hash 7 s + | Loop (stmt, s, g) -> + Hashtbl.seeded_hash (Stmt.hash stmt) + (Hashtbl.seeded_hash (GraphShape.hash g) + (Hashtbl.seeded_hash 11 (Node.hash s))) end module EdgeList = struct @@ -214,45 +220,163 @@ module EdgeList = struct let pretty_debug = pretty end -module Graph = - Hptmap.Make(Node)(EdgeList) - (Hptmap.Comp_unused)(struct let v = [[]] end) - (struct let l = [Ast.self] end) +module Graph = struct + include Hptmap.Make(Node)(EdgeList)(Hptmap.Comp_unused) + (struct let v = [[]] end) + (struct let l = [Ast.self] end) + + let is_included = + let cache = Hptmap_sig.NoCache in + let decide_fast = decide_fast_inclusion in + let decide_fst _n _v1 = false in + let decide_snd _n _v2 = true in + let rec decide_both k l1 l2 = + match l1, l2 with + | [], _ -> true + | _, [] -> false + | h1 :: t1, h2 :: t2 -> + let c = Edge.compare h1 h2 in + if c = 0 then decide_both k t1 t2 + else if c < 0 then false + else decide_both k l1 t2 + in + binary_predicate cache UniversalPredicate + ~decide_fast ~decide_fst ~decide_snd ~decide_both + + let join = + let cache = Hptmap_sig.NoCache in + let rec merge_edge k l1 l2 = + match l1, l2 with + | [], l2 -> l2 + | l1, [] -> l1 + | h1 :: t1, h2 :: t2 -> + let c = Edge.compare h1 h2 in + if c = 0 then h1 :: merge_edge k t1 t2 + else if c < 0 then h1 :: merge_edge k t1 l2 + else h2 :: merge_edge k l1 t2 + in + join ~cache ~symmetric:true ~idempotent:true ~decide:merge_edge + let diff = + let cache = Hptmap_sig.NoCache in + let decide_left = Neutral in + let decide_right = Absorbing in + 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 + let decide_both k l1 l2 = match diff_list k l1 l2 with [] -> None | l -> Some l in + merge ~cache ~symmetric:false ~idempotent:false + ~decide_both ~decide_left ~decide_right + + let succs g (n : Node.t) = try find n g with Not_found -> [] +end + +let is_included_graph = Graph.is_included + +let join_graph = Graph.join + +let diff_graph = Graph.diff + +let rec epsilon_path current stop g = + Node.equal current stop || + begin + Node.compare current stop <= 0 && + match Graph.find current g with + | exception Not_found -> false + | l -> + let exists = function + | { edge_dst; edge_trans = Msg _ } -> epsilon_path edge_dst stop g + | _ -> false + in + List.exists exists l + end + + + +(* A loop + .*) type loops = | Base of Node.t * Graph.t (* current last *) | OpenLoop of Cil_types.stmt * Node.t (* start node *) * Graph.t (* last iteration *) * Node.t (** current *) * Graph.t * loops | UnrollLoop of Cil_types.stmt * loops -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; - } - -let start s = s.start -let current s = s.current -let globals s = s.globals -let entry_formals s = s.main_formals - -(* Lattice structure for the abstract state above *) -module Traces = struct +module Loops = struct + let rec is_included l1 l2 = match l1, l2 with + | Base _, (OpenLoop _ | UnrollLoop _) + | (OpenLoop _ | UnrollLoop _), Base _ -> + (* not in the same number of loops *) + false + | Base (c1,_), Base (c2,g2) -> + epsilon_path c1 c2 g2 + | (OpenLoop(stmt1,_,_,_,_,_) | UnrollLoop(stmt1,_)), + (OpenLoop(stmt2,_,_,_,_,_) | UnrollLoop(stmt2,_)) when not (Stmt.equal stmt1 stmt2) -> + (* not same loop *) + false + | OpenLoop(_,s1,_,_,_,_), OpenLoop(_,s2,_,_,_,_) when not (Node.equal s1 s2) -> + (* not entered in the loop at the same time, take arbitrarily one of them *) + false + | OpenLoop(_,_,last1,c1,g1,l1), OpenLoop(_,_,last2,c2,g2,l2) -> + let g2' = Graph.join last2 g2 in + is_included l1 l2 && + Graph.is_included last1 last2 && + Graph.is_included g1 g2' && + epsilon_path c1 c2 g2' + | UnrollLoop(_,l1), UnrollLoop(_,l2) -> + is_included l1 l2 + | OpenLoop(_,_,_,_,_,_), UnrollLoop(_,_) -> + false + | UnrollLoop(_,l1), OpenLoop(_,_,_,_,_,l2) -> + is_included l1 l2 - (** impossible for normal values start must be bigger than current *) - 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 diff 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 = Graph.diff 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 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 l1 l2 with + | `Bottom -> `Bottom + | `Value(l) -> + let last = Graph.diff last1 last2 in + let g = Graph.diff g1 g2 in + `Value(OpenLoop(stmt,s,last,c1,g,l)) + end + | UnrollLoop(stmt,l1), UnrollLoop(_,l2) -> + begin match diff l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (UnrollLoop(stmt,l)) + end + | (OpenLoop(stmt,s,last,c,g,l1), UnrollLoop(_,l2)) -> + begin match diff l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (OpenLoop(stmt,s,last,c,g,l)) + end + | (UnrollLoop(stmt,l2), OpenLoop(_,_,_,_,_,l1)) -> + begin match diff l1 l2 with + | `Bottom -> `Bottom + | `Value l -> `Value (UnrollLoop(stmt,l)) + end - let rec compare_loops l1 l2 = - match l1,l2 with + let rec compare l1 l2 = match l1, l2 with | Base (c1,g1), Base (c2,g2) -> let c = Node.compare c1 c2 in if c <> 0 then c else @@ -268,28 +392,64 @@ module Traces = struct if c <> 0 then c else let c = Graph.compare g1 g2 in if c <> 0 then c else - compare_loops l1 l2 + compare l1 l2 | UnrollLoop(stmt1,l1), UnrollLoop(stmt2,l2) -> let c = Stmt.compare stmt1 stmt2 in if c <> 0 then c else - compare_loops l1 l2 + compare l1 l2 | Base _, _ -> -1 | _, Base _ -> 1 | OpenLoop _, _ -> -1 | _, OpenLoop _ -> 1 - let rec pretty_loops fmt = function + let rec pretty fmt = function | Base (c,g) -> Format.fprintf fmt "@[<hv>%a @[at %a@]@]" Graph.pretty g Node.pretty c | 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 + Node.pretty s Graph.pretty last Graph.pretty g Node.pretty c pretty l | UnrollLoop(stmt,l) -> Format.fprintf fmt "@[<hv>@[unroll(%a)@]@ %a" Stmt.pretty_sid stmt - pretty_loops l + pretty l +end + +let is_included_loops = Loops.is_included + +let diff_loops = Loops.diff + +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; + } + +let start s = s.start +let current s = s.current +let globals s = s.globals +let entry_formals s = s.main_formals + +(* Lattice structure for the abstract state above *) +module Traces = struct + + (** impossible for normal values start must be bigger than current *) + 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 compare_loops = Loops.compare + + let pretty_loops = Loops.pretty (* Frama-C "datatype" for type [inout] *) include Datatype.Make_with_collections(struct @@ -349,46 +509,6 @@ module Traces = struct if m == top then `Top else `Other m - let join_graph g1 g2 = - let rec merge_edge k l1 l2 = - match l1, l2 with - | [], l2 -> l2 - | l1, [] -> l1 - | h1 :: t1, h2 :: t2 -> - let c = Edge.compare h1 h2 in - if c = 0 - then h1 :: merge_edge k t1 t2 - else if c < 0 - then h1 :: merge_edge k t1 l2 - else h2 :: merge_edge k l1 t2 - in - Graph.join - ~symmetric:true - ~idempotent:true - ~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 move_to c g state = let rec aux = function | Base (_,_) -> Base (c,g) @@ -422,47 +542,10 @@ module Traces = struct | exception Not_found -> [] | l -> l - let change_next n = function - | Assign (_,loc,typ,exp) -> Assign(n,loc,typ,exp) - | Assume (_,a,b) -> Assume(n,a,b) - | EnterScope (_,vs) -> EnterScope(n,vs) - | LeaveScope (_,vs) -> LeaveScope(n,vs) - | CallDeclared (_,kf,exps,lval) -> CallDeclared(n,kf,exps,lval) - | Msg (_,s) -> Msg(n,s) - | Loop(_,stmt,s,g) -> Loop(n,stmt,s,g) + let change_next = Edge.change_next let same_edge edge edge' = - match edge, edge' with - | Assign (_,loc1,typ1,exp1), Assign (_,loc2,typ2,exp2) -> - let c = Lval.compare loc1 loc2 in - if c <> 0 then false else - let c = Typ.compare typ1 typ2 in - if c <> 0 then false else - ExpStructEq.compare exp1 exp2 = 0 - | Assume(_,e1,b1), Assume(_,e2,b2) -> - let c = ExpStructEq.compare e1 e2 in - if c <> 0 then false else - Pervasives.compare b1 b2 = 0 - | EnterScope(_,vs1),EnterScope(_,vs2) -> - let c = Extlib.list_compare Varinfo.compare vs1 vs2 in - c = 0 - | LeaveScope(_,vs1), LeaveScope(_,vs2) -> - let c = Extlib.list_compare Varinfo.compare vs1 vs2 in - c = 0 - | 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 ExpStructEq.compare exp1 exp2 in - if c <> 0 then false else - let c = Extlib.opt_compare Lval.compare lval1 lval2 in - c = 0 - | Msg(_,s1), Msg(_,s2) -> - let c = String.compare s1 s2 in - c = 0 - | Loop(_,stmt1,s1,g1), Loop(_,stmt2,s2,g2) -> - Stmt.equal stmt1 stmt2 && - Node.equal s1 s2 && GraphShape.equal g1 g2 - | _ -> false + Transition.equal edge.edge_trans edge'.edge_trans let create_edge all_edges_ever_created current e = let m = Graph.singleton current [e] in @@ -504,6 +587,13 @@ module Traces = struct let c = if c == empty then new_empty () else c in add_edge_aux c e + let add_edge_tmp c dest trans = + if c == top then c + else if c.call_declared_function then c (** forget intermediary state *) + else + let c = if c == empty then new_empty () else c in + add_edge_aux c { edge_dst = dest; edge_trans = trans } + let copy_edges s old_current_node g state = let cache = Node.Hashtbl.create 10 in let rec aux old_current_node state = @@ -532,71 +622,6 @@ module Traces = struct let c = Node.Hashtbl.find cache old_current_node in replace_to c state - let rec epsilon_path current stop g = - Node.equal current stop || - begin - Node.compare current stop <= 0 && - match Graph.find current g with - | exception Not_found -> false - | l -> - let exists = function - | Msg (n,_) -> epsilon_path n stop g - | _ -> false - in - List.exists exists l - end - - - let is_included_graph g1 g2 = - (* The graph is c1.graph is included into c2.graph *) - let rec decide_both k l1 l2 = - match l1, l2 with - | [], _ -> true - | _, [] -> false - | h1 :: t1, h2 :: t2 -> - let c = Edge.compare h1 h2 in - if c = 0 - then decide_both k t1 t2 - else if c < 0 - then false - else decide_both k l1 t2 - in - Graph.binary_predicate - Hptmap_sig.NoCache - Graph.UniversalPredicate - ~decide_fast:Graph.decide_fast_inclusion - ~decide_fst:(fun _ _ -> false) - ~decide_snd:(fun _ _ -> true) - ~decide_both - g1 g2 - - let rec is_included_loops l1 l2 = - match l1, l2 with - | Base _, (OpenLoop _ | UnrollLoop _) | (OpenLoop _ | UnrollLoop _), Base _ -> - (* not in the same number of loops *) - false - | Base (c1,_), Base (c2,g2) -> - epsilon_path c1 c2 g2 - | (OpenLoop(stmt1,_,_,_,_,_) | UnrollLoop(stmt1,_)), - (OpenLoop(stmt2,_,_,_,_,_) | UnrollLoop(stmt2,_)) when not (Stmt.equal stmt1 stmt2) -> - (* not same loop *) - false - | OpenLoop(_,s1,_,_,_,_), OpenLoop(_,s2,_,_,_,_) when not (Node.equal s1 s2) -> - (* not entered in the loop at the same time, take arbitrarily one of them *) - false - | OpenLoop(_,_,last1,c1,g1,l1), OpenLoop(_,_,last2,c2,g2,l2) -> - let g2' = join_graph last2 g2 in - is_included_loops l1 l2 && - is_included_graph last1 last2 && - is_included_graph g1 g2' && - epsilon_path c1 c2 g2' - | UnrollLoop(_,l1), UnrollLoop(_,l2) -> - is_included_loops l1 l2 - | OpenLoop(_,_,_,_,_,_), UnrollLoop(_,_) -> - false - | UnrollLoop(_,l1), OpenLoop(_,_,_,_,_,l2) -> - is_included_loops l1 l2 - let is_included c1 c2 = (* start is the same *) let r = @@ -618,7 +643,7 @@ module Traces = struct else if epsilon_path c2 c1 graph then (c1, graph) else - let m = Msg(Node.dumb,"join") in + let m = { edge_dst = Node.dumb; edge_trans = Msg("join") } in (* TODO *) let reusable = let succs1 = find_succs c1 !all_edges_ever_created in let succs1 = List.filter (same_edge m) succs1 in @@ -707,8 +732,8 @@ module Traces = struct | [] -> Stmt.Hashtbl.memo state.all_loop_start stmt (fun _ -> Node.next (),Graph.empty) | edge :: tl -> - match edge with - | Loop (_,stmt',s,last) when Stmt.equal stmt' stmt -> + match edge.edge_trans with + | Loop (stmt',s,last) when Stmt.equal stmt' stmt -> s, Graph.from_shape_id last | _ -> find_same_loop tl in @@ -717,49 +742,6 @@ module Traces = struct { state with current } - 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 @@ -830,8 +812,8 @@ module GraphDot = OCamlGraph.Graphviz.Dot(struct 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 + let rec iter_edge k (l: Node.t list) e = match e.edge_trans with + | 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 @@ -840,8 +822,8 @@ module GraphDot = OCamlGraph.Graphviz.Dot(struct let iter_edges_e f g = let rec iter_edge k l e = f (Usual(k,e,l)); - match e with - | Loop(_,_,s,g) -> + match e.edge_trans with + | Loop(_,s,g) -> let l' = (k::l) in f (Head(k,l,s,l')); iter_vertex (Some s) l' g @@ -877,8 +859,8 @@ module GraphDot = OCamlGraph.Graphviz.Dot(struct 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 "@[<h>%a@]" Edge.pretty_edge e)] + | Usual(_,{edge_trans = Loop _},_) -> [`Label (Format.asprintf "leave_loop")] + | Usual(_,e,_) -> [`Label (Format.asprintf "@[<h>%a@]" Transition.pretty e.edge_trans)] | Head _ -> [] | Back(_,_,_) -> [`Constraint false] end) @@ -889,21 +871,22 @@ let rec complete_graph (graph:Graph.t) = let graph, l = Extlib.fold_map (fun graph e -> let m = Graph.singleton (Traces.get_node e) [] in - let e = match e with - | Assign (_,_,_,_) - | Assume (_,_,_) - | EnterScope (_,_) - | LeaveScope (_,_) - | CallDeclared (_,_,_,_) - | Msg (_,_) -> e - | Loop (n,stmt,s,g) -> + let e = match e.edge_trans with + | Assign (_,_,_) + | Assume (_,_) + | EnterScope _ + | LeaveScope _ + | CallDeclared (_,_,_) + | Msg _ -> e + | Loop (stmt,s,g) -> + let n = e.edge_dst in let g = Graph.shape (complete_graph (Graph.from_shape_id g)) in - Loop(n,stmt,s,g) + { edge_dst = n; edge_trans = Loop(stmt,s,g) } in - Traces.join_graph graph m, e) + join_graph graph m, e) graph l in - Traces.join_graph graph (Graph.singleton k l) + join_graph graph (Graph.singleton k l) ) graph Graph.empty @@ -940,42 +923,44 @@ module Internal = struct type valuation = Valuation.t let assign _ki lv e _v _valuation state = - `Value(Traces.add_edge state (Assign(Node.dumb,lv.Eval.lval,lv.Eval.ltyp,e))) + let trans = Assign (lv.Eval.lval, lv.Eval.ltyp, e) in + `Value (Traces.add_edge_tmp state Node.dumb trans) let assume _stmt e pos _valuation state = - `Value(Traces.add_edge state (Assume(Node.dumb,e,pos))) + let trans = Assume (e, pos) in + `Value (Traces.add_edge_tmp state Node.dumb trans) let start_call _stmt call _valuation state = if Kernel_function.is_definition call.Eval.kf then let msg = Format.asprintf "start_call: %s (%b)" (Kernel_function.get_name call.Eval.kf) (Kernel_function.is_definition call.Eval.kf) in - let state = Traces.add_edge state (Msg(Node.dumb,msg)) in + let state = Traces.add_edge_tmp state Node.dumb (Msg msg) in let formals = List.map (fun arg -> arg.Eval.formal) call.Eval.arguments in - let state = Traces.add_edge state (EnterScope(Node.dumb,formals)) in + let state = Traces.add_edge_tmp state Node.dumb (EnterScope formals) in let state = List.fold_left (fun state arg -> - Traces.add_edge state (Assign(Node.dumb, - Cil.var arg.Eval.formal, - arg.Eval.formal.Cil_types.vtype, - arg.Eval.concrete))) state call.Eval.arguments in + Traces.add_edge_tmp state Node.dumb + (Assign (Cil.var arg.Eval.formal, + arg.Eval.formal.Cil_types.vtype, + arg.Eval.concrete))) state call.Eval.arguments in `Value state else (** enter the scope of the dumb result variable *) let var = call.Eval.return in let state = match var with - | Some var -> Traces.add_edge state (EnterScope(Node.dumb,[var])) + | Some var -> Traces.add_edge_tmp state Node.dumb (EnterScope [var]) | None -> state in let exps = List.map (fun arg -> arg.Eval.concrete) call.Eval.arguments in - let state = Traces.add_edge state (CallDeclared(Node.dumb, call.Eval.kf, exps, - Extlib.opt_map Cil.var var)) in - `Value {state with call_declared_function = true} + let state = Traces.add_edge_tmp state Node.dumb + (CallDeclared (call.Eval.kf, exps, Extlib.opt_map Cil.var var)) + in `Value {state with call_declared_function = true} let finalize_call _stmt call ~pre:_ ~post = if post.call_declared_function then `Value {post with call_declared_function = false} else let msg = Format.asprintf "finalize_call: %s" (Kernel_function.get_name call.Eval.kf) in - let state = Traces.add_edge post (Msg(Node.dumb,msg)) in + let state = Traces.add_edge_tmp post Node.dumb (Msg msg) in `Value state let update _valuation state = `Value state @@ -998,7 +983,7 @@ module Internal = struct let introduce_globals vars state = {state with globals = vars @ state.globals} let initialize_variable lv _ ~initialized:_ _ state = - Traces.add_edge state (Msg(Node.dumb,Format.asprintf "initialize variable: %a" Printer.pp_lval lv )) + Traces.add_edge_tmp state Node.dumb (Msg(Format.asprintf "initialize variable: %a" Printer.pp_lval lv )) let initialize_variable_using_type init_kind varinfo state = let state = match init_kind with @@ -1014,11 +999,11 @@ module Internal = struct init_kind Varinfo.pretty varinfo in - Traces.add_edge state (Msg(Node.dumb,msg)) + Traces.add_edge_tmp state Node.dumb (Msg msg) (* TODO *) let logic_assign _assign _location ~pre:_ state = - Traces.add_edge state (Msg(Node.dumb,"logic assign")) + Traces.add_edge_tmp state Node.dumb (Msg "logic assign") (* Logic *) let evaluate_predicate _ _ _ = Alarmset.Unknown @@ -1035,7 +1020,7 @@ module Internal = struct `Value (loc, value) let enter_loop stmt state = - let state = Traces.add_edge state (Msg(Node.dumb,"enter_loop")) in + let state = Traces.add_edge_tmp state Node.dumb (Msg "enter_loop") in let state = if not (Value_parameters.TracesUnrollLoop.get ()) then Traces.add_loop stmt state else { state with current = UnrollLoop(stmt,state.current) } in @@ -1046,10 +1031,10 @@ module Internal = struct | Base _ -> assert false | UnrollLoop(_,_) -> state | OpenLoop(stmt,s,last,_,g,l) -> - let last = Traces.join_graph last g in + let last = 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 + let last = join_graph last old_last in assert (Node.equal s s'); Stmt.Hashtbl.add state.all_loop_start stmt (s,last); last @@ -1069,20 +1054,22 @@ module Internal = struct 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 + let last = 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 + else Traces.add_edge_tmp state Node.dumb (Loop(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")) + Traces.add_edge_tmp state Node.dumb (Msg "leave_loop") - let enter_scope _kf vars state = Traces.add_edge state (EnterScope(Node.dumb,vars)) - let leave_scope _kf vars state = Traces.add_edge state (LeaveScope(Node.dumb,vars)) + let enter_scope _kf vars state = + Traces.add_edge_tmp state Node.dumb (EnterScope vars) + let leave_scope _kf vars state = + Traces.add_edge_tmp state Node.dumb (LeaveScope vars) let reduce_further _state _expr _value = [] (*Nothing intelligent to suggest*) @@ -1144,15 +1131,16 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = end | [] -> assert false | [a] -> begin - match a with + let n = a.edge_dst in + match a.edge_trans with - | Assign (n,lval,_typ,exp) -> + | Assign (lval,_typ,exp) -> let exp = subst_in_exp var_map exp in let lval = subst_in_lval var_map lval in let stmt = Cil.mkStmtOneInstr ~valid_sid (Cil_types.Set(lval,exp,dummy_loc)) in stmts_of_cfg cfg n var_map locals return_exp (stmt::acc) - | Assume (n,exp,b) -> + | Assume (exp,b) -> let exp = subst_in_exp var_map exp in let predicate = (Logic_utils.expr_to_predicate ~cast:true exp).Cil_types.ip_content in let predicate = if b then predicate else Logic_const.pnot predicate in @@ -1160,7 +1148,7 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = let stmt = Cil.mkStmtOneInstr ~valid_sid (Cil_types.Code_annot(code_annot,dummy_loc)) in stmts_of_cfg cfg n var_map locals return_exp (stmt::acc) - | EnterScope (n,vs) -> + | EnterScope (vs) -> (** all our variables are assigned, not defined *) let var_map = List.fold_left fresh_varinfo var_map vs in let vs = List.map (subst_in_varinfo var_map) vs in @@ -1173,21 +1161,22 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = let stmt = Cil.mkStmt ~valid_sid (Cil_types.Block(block)) in List.rev (stmt::acc) - | LeaveScope (n,_) -> stmts_of_cfg cfg n var_map locals return_exp acc + | LeaveScope _ -> stmts_of_cfg cfg n var_map locals return_exp acc - | CallDeclared (n,kf,exps,lval) -> + | CallDeclared (kf,exps,lval) -> let exps = List.map (subst_in_exp var_map) exps in let lval = Extlib.opt_map (subst_in_lval var_map) lval in let call = Cil.evar ~loc:dummy_loc (subst_in_varinfo var_map (Kernel_function.get_vi kf)) in let stmt = Cil.mkStmtOneInstr ~valid_sid (Cil_types.Call(lval,call,exps,dummy_loc)) in stmts_of_cfg cfg n var_map locals return_exp (stmt::acc) - | Msg (n,_) -> stmts_of_cfg cfg n var_map locals return_exp acc - | Loop(n,_,s,g) -> + | Msg _ -> stmts_of_cfg cfg n var_map locals return_exp acc + | Loop (_,s,g) -> let g = Graph.from_shape (fun _ v -> v) g in let is_while = match Traces.find_succs s g, Traces.find_succs n cfg with - | [Assume(n1',exp1,b1)], [Assume(n2',exp2,b2)] + | [{ edge_dst = n1'; edge_trans = Assume(exp1,b1) }], + [{ edge_dst = n2'; edge_trans = Assume(exp2,b2) }] when ExpStructEq.equal exp1 exp2 && b1 != b2 -> Some (exp1, n1', b1, n2') | _ -> None in @@ -1203,7 +1192,8 @@ let rec stmts_of_cfg cfg current var_map locals return_exp acc = | l -> let is_if = match l with | [] | [_] -> assert false (* absurd *) - | [Assume(n1',exp1,b1) ; Assume(n2',exp2,b2)] + | [{ edge_dst = n1'; edge_trans = Assume(exp1,b1) } ; + { edge_dst = n2'; edge_trans = Assume(exp2,b2) }] when ExpStructEq.equal exp1 exp2 && b1 != b2 -> if b1 then Some (exp1, n1', n2') else Some (exp1,n2',n1') | _ -> None in diff --git a/src/plugins/value/domains/traces_domain.mli b/src/plugins/value/domains/traces_domain.mli index 9801e7ae8bc..cebaa9298b6 100644 --- a/src/plugins/value/domains/traces_domain.mli +++ b/src/plugins/value/domains/traces_domain.mli @@ -21,27 +21,38 @@ (**************************************************************************) (** Traces domain *) -open Cil_datatype +open Cil_types module Node : Datatype.S module GraphShape : sig type 'value t end -type edge = - | Assign of Node.t * Cil_types.lval * Cil_types.typ * Cil_types.exp - | Assume of Node.t * Cil_types.exp * bool - | EnterScope of Node.t * Cil_types.varinfo list - | LeaveScope of Node.t * Cil_types.varinfo list +type node = Node.t + +type transition = + | Assign of lval * typ * exp + | Assume of exp * bool + | EnterScope of varinfo list + | LeaveScope of varinfo list (** For call of functions without definition *) - | CallDeclared of Node.t * Cil_types.kernel_function * Cil_types.exp list * Cil_types.lval option - | Loop of Node.t * Stmt.t * Node.t (** start *) * edge list GraphShape.t (** cfg of the loop **) - | Msg of Node.t * string + | CallDeclared of kernel_function * exp list * lval option + | Loop of stmt * node (** start *) * edge list GraphShape.t + | Msg of string + +and edge = { + edge_trans : transition; + edge_dst : node; +} module Edge : Datatype.S with type t = edge -module Graph : Hptmap_sig.S with type key = Node.t - and type v = edge list - and type 'a shape = 'a GraphShape.t +module Graph : sig + include Hptmap_sig.S with type key = Node.t + and type v = edge list + and type 'a shape = 'a GraphShape.t + + val join : t -> t -> t +end (** stack of open loops *) type loops = -- GitLab