From 7883e630310dec9895c65af47c7cccbfaa7bba52 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Tue, 22 Feb 2022 01:01:12 +0100 Subject: [PATCH] [Eva] multidim: add an annotation to control which bases are tracked --- src/plugins/value/domains/multidim_domain.ml | 166 ++++++++++++------- src/plugins/value/utils/eva_annotations.ml | 50 +++++- src/plugins/value/utils/eva_annotations.mli | 8 +- 3 files changed, 162 insertions(+), 62 deletions(-) diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index 2e732ece763..86d3850c471 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -305,6 +305,11 @@ struct include Base.Hptset (* The set of bases referencing the variable *) end +module TrackedBases = +struct + include Base.Hptset +end + module DomainLattice = struct (* The domain is essentially a map from bases to individual memory abstractions *) @@ -317,9 +322,31 @@ struct let top = Memory.top, References.empty end - include Hptmap.Make - (Base.Base) (V) - (Hptmap.Comp_unused) (Initial_Values) (Deps) + module BaseMap = + struct + include + Hptmap.Make + (Base.Base) (V) + (Hptmap.Comp_unused) (Initial_Values) (Deps) + + let find_or_top (state : t) (b : Base.t) = + try find b state with Not_found -> V.top + + let remove_var (state : t) (v : Cil_types.varinfo) = + remove (Base.of_varinfo v) state + + let remove_vars (state : t) (l : Cil_types.varinfo list) = + List.fold_left remove_var state l + + let remove_loc (state : t) (loc : Precise_locs.precise_location) = + let loc = Precise_locs.imprecise_location loc in + Locations.(Location_Bits.fold_bases remove loc.loc state) + end + + include Datatype.Pair_with_collections + (BaseMap) + (Datatype.Option (TrackedBases)) + (struct let module_name = "DomainLattice" end) type state = t type value = Value.t @@ -338,25 +365,19 @@ struct (* Bases handling *) - let covers_base (b : base) = + let covers_base (tracked : TrackedBases.t option) (b : base) : bool = match b with | Base.Var (vi, _) | Allocated (vi, _, _) -> - not (Cil.typeHasQualifier "volatile" vi.vtype) + not (Cil.typeHasQualifier "volatile" vi.vtype) && + Option.fold ~none:true ~some:(TrackedBases.mem b) tracked | Null -> true | CLogic_Var _ | String _ -> false - let find_or_top (state : state) (b : base) = - try find b state with Not_found -> V.top - - let remove_var (state : state) (v : Cil_types.varinfo) = - remove (Base.of_varinfo v) state + let join_tracked tracked1 tracked2 = + match tracked1, tracked2 with + | None, tracked | tracked, None -> tracked + | Some s1, Some s2 -> Some (TrackedBases.union s1 s2) - let remove_vars (state : state) (l : Cil_types.varinfo list) = - List.fold_left remove_var state l - - let remove (state : state) (loc : location) = - let loc = Precise_locs.imprecise_location loc in - Locations.(Location_Bits.fold_bases remove loc.loc state) (* Accesses *) @@ -364,9 +385,9 @@ struct type a . (memory -> offset -> a) -> (a -> a -> a) -> state -> mdlocation -> a or_bottom = - fun map reduce state loc -> + fun map reduce (state,_tracked) loc -> let f base off acc = - let v = map (fst (find_or_top state base)) off in + let v = map (fst (BaseMap.find_or_top state base)) off in Bottom.join reduce (`Value v) acc in Location.fold f loc `Bottom @@ -421,11 +442,11 @@ struct let oracle = mk_oracle state in read (Memory.extract ~oracle) (Memory.smash ~oracle) state src - let add_references state vi refs' = + let add_references (base_map,tracked) vi refs' = let base = Base.of_varinfo vi in - let memory, refs = find_or_top state base in + let memory, refs = BaseMap.find_or_top base_map base in let refs'' = References.union refs (References.of_list refs') in - add base (memory, refs'') state + BaseMap.add base (memory, refs'') base_map, tracked let add_references_l state l refs = List.fold_left (fun state vi -> add_references state vi refs) state l @@ -433,11 +454,11 @@ struct let write' (update : memory -> offset -> memory or_bottom) (state : state) (loc : mdlocation) : state or_bottom = let f base off state' = - if covers_base base then - state' >>- fun state -> - let memory, refs = find_or_top state base in + state' >>- fun (base_map,tracked) -> + if covers_base tracked base then + let memory, refs = BaseMap.find_or_top base_map base in update memory off >>-: fun memory' -> - add base (memory', refs) state + BaseMap.add base (memory', refs) base_map, tracked else state' in @@ -475,26 +496,29 @@ struct (* Lattice *) - let top = empty + let top = BaseMap.empty, None let is_included = + let open BaseMap in let cache = cache_name "is_included" in let decide_fst _b _v1 = true (* v2 is top *) in let decide_snd _b _v2 = false (* v1 is top, v2 is not *) in let decide_both _ (m1,_r1) (m2,_r2) = Memory.is_included m1 m2 in let decide_fast s t = if s == t then PTrue else PUnknown in - binary_predicate cache UniversalPredicate - ~decide_fast ~decide_fst ~decide_snd ~decide_both + let is_included = binary_predicate cache UniversalPredicate + ~decide_fast ~decide_fst ~decide_snd ~decide_both + in + fun (m1,_) (m2,_) -> is_included m1 m2 let narrow = + let open BaseMap in let cache = cache_name "narrow" in - let decide _ v1 v2 = - Memory.narrow v1 v2 - in + let decide _ v1 v2 = Memory.narrow v1 v2 in let narrow = join ~cache ~symmetric:false ~idempotent:true ~decide in - fun a b -> `Value (narrow a b) + fun (m1,t1) (m2,t2) -> `Value (narrow m1 m2, join_tracked t1 t2) - let join s1 s2 = + let join (m1,t1 as s1) (m2,t2 as s2) = + let open BaseMap in let oracle = function | Abstract_memory.Left -> mk_oracle s1 | Right -> mk_oracle s2 @@ -505,9 +529,12 @@ struct and r = References.union r1 r2 in if Memory.(is_top m) then None else Some (m,r) in - inter ~cache ~symmetric:false ~idempotent:true ~decide s1 s2 + inter ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, + join_tracked t1 t2 - let widen kf stmt s1 s2 = + + let widen kf stmt (m1,t1 as s1) (m2,t2 as s2) = + let open BaseMap in let oracle = function | Abstract_memory.Left -> mk_oracle s1 | Right -> mk_oracle s2 @@ -518,7 +545,8 @@ struct and r = References.union r1 r2 in if Memory.(is_top m) then None else Some (m,r) in - inter ~cache ~symmetric:false ~idempotent:true ~decide s1 s2 + inter ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, + join_tracked t1 t2 end module Domain = @@ -589,7 +617,7 @@ struct let update valuation state = assume_valuation valuation state - let update_array_segmentation_bounds vi expr state = + let update_array_segmentation_bounds vi expr (base_map,tracked as state) = (* TODO: more general transfer function *) let incr = Option.bind expr (fun expr -> match expr.Cil_types.enode with @@ -608,23 +636,26 @@ struct assignement of the bound to allow removing of eventual empty slice before the bound leaves the segmentation. *) let oracle = mk_oracle state in - let references = snd (find_or_top state (Base.of_varinfo vi)) in - let update_ref base state = + let references = snd (BaseMap.find_or_top base_map (Base.of_varinfo vi)) in + let update_ref base base_map = let update (memory, refs) = Memory.incr_bound ~oracle vi incr memory, refs in - replace (Option.map update) base state + BaseMap.replace (Option.map update) base base_map in - let state = References.fold update_ref references state in + let base_map = References.fold update_ref references base_map in (* If increment is None, every reference to vi should have been removed by Memory.incr_bound *) - if Option.is_none incr then - replace - (Option.map (fun (memory, _refs) -> memory, References.empty)) - (Base.of_varinfo vi) - state - else - state + let base_map = + if Option.is_none incr then + BaseMap.replace + (Option.map (fun (memory, _refs) -> memory, References.empty)) + (Base.of_varinfo vi) + base_map + else + base_map + in + base_map, tracked let update_array_segmentation lval expr state = match lval with @@ -708,11 +739,12 @@ struct (fun state vi -> update_array_segmentation_bounds vi None state) state vars in - remove_vars state vars + let (base_map,tracked) = state in + BaseMap.remove_vars base_map vars, tracked - let logic_assign assign location state = + let logic_assign assign location (base_map,tracked as state) = match assign with - | None -> remove state location + | None -> BaseMap.remove_loc base_map location, tracked | Some ((Frees _ | Allocates _), _) -> state | Some (Assigns (_dest, sources), _pre_state) -> match sources with @@ -720,7 +752,7 @@ struct let dst = Location.of_precise_loc location in erase state dst Abstract_memory.Bit.numerical | _ -> - remove state location + BaseMap.remove_loc base_map location, tracked let reduce_by_papp env li _labels args positive state = try @@ -750,7 +782,8 @@ struct reduce predicate truth state let interpret_acsl_extension extension _env state = - if extension.ext_name = "array_partition" then + match extension.ext_name with + | "array_partition" -> let annotation = Eva_annotations.read_array_segmentation extension in let vi,offset,bounds = annotation in (* Update the segmentation *) @@ -768,7 +801,16 @@ struct in let references = List.fold_left add [] bounds in add_references_l state references (Location.bases loc) - else + | "eva_domain_scope" -> + let domain,vars = Eva_annotations.read_domain_scope extension in + if domain = "multidim" then + let (base_map,tracked) = state in + let set = Option.value ~default:TrackedBases.empty tracked + and bases = List.map Base.of_varinfo vars in + base_map, Some (List.fold_right TrackedBases.add bases set) + else + state + | _ -> state let empty () = top @@ -788,18 +830,22 @@ struct let relate _kf _bases _state = Base.SetLattice.empty - let filter _kf _kind bases state = - DomainLattice.filter (fun elt -> Base.Hptset.mem elt bases) state + let filter _kf _kind bases (base_map,tracked) = + BaseMap.filter (fun elt -> Base.Hptset.mem elt bases) base_map, + tracked (* TODO: intersection with bases *) - let reuse _kf bases ~current_input ~previous_output = + let reuse _kf bases = + let open BaseMap in let cache = Hptmap_sig.NoCache in let decide_both _key _v1 v2 = Some v2 in let decide_left key v1 = if Base.Hptset.mem key bases then None else Some v1 in - merge ~cache ~symmetric:false ~idempotent:true - ~decide_both ~decide_left:(Traversing decide_left) ~decide_right:Neutral - current_input previous_output + let reuse = merge ~cache ~symmetric:false ~idempotent:true + ~decide_both ~decide_left:(Traversing decide_left) ~decide_right:Neutral + in + fun ~current_input:(m1,t1) ~previous_output:(m2,t2) -> + reuse m1 m2, join_tracked t1 t2 end include Domain diff --git a/src/plugins/value/utils/eva_annotations.ml b/src/plugins/value/utils/eva_annotations.ml index 2c0ab30aefb..9080f611f27 100644 --- a/src/plugins/value/utils/eva_annotations.ml +++ b/src/plugins/value/utils/eva_annotations.ml @@ -396,6 +396,54 @@ module ArraySegmentation = Register (struct type array_segmentation = ArraySegmentation.t -let get_array_segmentation = ArraySegmentation.get let add_array_segmentation = ArraySegmentation.add let read_array_segmentation ext = ArraySegmentation.import ext.ext_kind + + +module DomainScope = Register (struct + type t = string * Cil_types.varinfo list + let name = "eva_domain_scope" + let is_loop_annot = false + + let parse ~typing_context:context = + let parse_domain = function + | {lexpr_node = PLvar v} -> v + | _ -> raise Parse_error + and parse_var = function + | {lexpr_node = PLvar v} -> + begin match context.Logic_typing.find_var v with + | {lv_origin=Some vi} -> vi + | _ -> raise Parse_error + end + | _ -> raise Parse_error + in + function + | domain :: vars -> + parse_domain domain, List.map parse_var vars + | _ -> raise Parse_error + + let import = function + | Ext_terms ({term_node=TConst (LStr domain)} :: vars) -> + let import_var = function + | {term_node=TLval (TVar {lv_origin=Some vi}, TNoOffset)} -> vi + | _ -> assert false + in + domain, List.map import_var vars + | _ -> assert false + + let export (domain, vars) = + let export_var vi = + Logic_const.tvar (Cil.cvar_to_lvar vi) + in + Ext_terms (Logic_const.tstring domain :: List.map export_var vars) + + let print fmt (domain, vars) = + Format.fprintf fmt "%s, %a" + domain + (Pretty_utils.pp_list ~sep:",@ " Cil_printer.pp_varinfo) vars + end) + + +type domain_scope = DomainScope.t +let add_domain_scope = DomainScope.add +let read_domain_scope ext = DomainScope.import ext.ext_kind diff --git a/src/plugins/value/utils/eva_annotations.mli b/src/plugins/value/utils/eva_annotations.mli index 98d914d689a..f5c46349d56 100644 --- a/src/plugins/value/utils/eva_annotations.mli +++ b/src/plugins/value/utils/eva_annotations.mli @@ -62,12 +62,15 @@ type allocation_kind = By_stack | Fresh | Fresh_weak | Imprecise type array_segmentation = Cil_types.varinfo * Cil_types.offset * Cil_types.exp list +type domain_scope = + string (* domain *) * + Cil_types.varinfo list (* variables that must be tracked by the domain *) + val get_slevel_annot : Cil_types.stmt -> slevel_annotation option val get_unroll_annot : Cil_types.stmt -> unroll_annotation list val get_flow_annot : Cil_types.stmt -> flow_annotation list val get_subdivision_annot : Cil_types.stmt -> int list val get_allocation: Cil_types.stmt -> allocation_kind -val get_array_segmentation : Cil_types.stmt -> array_segmentation list val add_slevel_annot : emitter:Emitter.t -> Cil_types.stmt -> slevel_annotation -> unit @@ -79,6 +82,9 @@ val add_subdivision_annot : emitter:Emitter.t -> Cil_types.stmt -> int -> unit val add_array_segmentation : emitter:Emitter.t -> Cil_types.stmt -> array_segmentation -> unit +val add_domain_scope : emitter:Emitter.t -> + Cil_types.stmt -> domain_scope -> unit [@@@ api_end] val read_array_segmentation : Cil_types.acsl_extension -> array_segmentation +val read_domain_scope : Cil_types.acsl_extension -> domain_scope -- GitLab