diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml index 50ef904b996e0e165ccedc234f7c12f795673fa9..cac821fd8712c7deb031ee8db234c7b204a2eb0e 100644 --- a/src/kernel_services/abstract_interp/abstract_memory.ml +++ b/src/kernel_services/abstract_interp/abstract_memory.ml @@ -842,36 +842,48 @@ struct cmp ~oracle b1 b2 = Equal let lower_bound ~oracle b1 b2 = - let i1 = to_ival ~oracle:(oracle Left) b1 - and i2 = to_ival ~oracle:(oracle Right) b2 in - let l1 = Option.get (Ival.min_int i1) (* TODO: handle Nones *) - and l2 = Option.get (Ival.min_int i2) in - Const (Integer.min l1 l2) + if b1 == b2 then b1 else + match b1, b2 with + | Const i1, Const i2 -> + Const (Integer.min i1 i2) + | Exp (e1, i1), Exp (e2, i2) when Exp.equal e1 e2 -> + Exp (e1, Integer.min i1 i2) + | _, _ -> + let i1 = to_ival ~oracle:(oracle Left) b1 + and i2 = to_ival ~oracle:(oracle Right) b2 in + let l1 = Option.get (Ival.min_int i1) (* TODO: handle Nones *) + and l2 = Option.get (Ival.min_int i2) in + Const (Integer.min l1 l2) let upper_bound ~oracle b1 b2 = - let i1 = to_ival ~oracle:(oracle Left) b1 - and i2 = to_ival ~oracle:(oracle Right) b2 in - let u1 = Option.get (Ival.max_int i1) (* TODO: handle Nones *) - and u2 = Option.get (Ival.max_int i2) in - Const (Integer.max u1 u2) - - exception NoBound + if b1 == b2 then b1 else + match b1, b2 with + | Const i1, Const i2 -> + Const (Integer.max i1 i2) + | Exp (e1, i1), Exp (e2, i2) when Exp.equal e1 e2 -> + Exp (e1, Integer.max i1 i2) + | _, _ -> + let i1 = to_ival ~oracle:(oracle Left) b1 + and i2 = to_ival ~oracle:(oracle Right) b2 in + let u1 = Option.get (Ival.max_int i1) (* TODO: handle Nones *) + and u2 = Option.get (Ival.max_int i2) in + Const (Integer.max u1 u2) let lower_const ~oracle b = match Ival.min_int (to_ival ~oracle b) with - | Some l -> Const l - | None -> (* TODO: handle exception *) - Kernel.feedback ~current:true "cannot retrieve lower bound for %a in %a" - pretty b Ival.pretty (to_ival ~oracle b); - raise NoBound + | Some l -> Some (Const l) + | None -> + Kernel.warning ~current:true "cannot retrieve lower bound for %a" + pretty b; + None let upper_const ~oracle b = match Ival.max_int (to_ival ~oracle b) with - | Some u -> Const u + | Some u -> Some (Const u) | None -> (* TODO: handle exception *) - Kernel.feedback ~current:true "cannot retrieve upper bound for %a in %a" - pretty b Ival.pretty (to_ival ~oracle b); - raise NoBound + Kernel.warning ~current:true "cannot retrieve upper bound for %a" + pretty b; + None let _operators oracle = operators (cmp ~oracle) end @@ -902,14 +914,15 @@ struct Bound.lower_bound ~oracle b1 b2, Integer.min a1 a2 let upper_bound ~oracle (b1,a1) (b2,a2) = Bound.upper_bound ~oracle b1 b2, Integer.min a1 a2 - let lower_const ~oracle (b,_a) = - Bound.lower_const ~oracle b - let upper_const ~oracle (b,_a) = - Bound.upper_const ~oracle b + let lower_const ~oracle (b,a) = + Option.map (fun b -> (b,a)) (Bound.lower_const ~oracle b) + let upper_const ~oracle (b,a) = + Option.map (fun b -> (b,a)) (Bound.upper_const ~oracle b) let birth b = b, Integer.zero let aging (b,a) = b, Integer.succ a let age (_,a) = a + let raw_bound (b,_) = b let unify_age ~other:(_,a') (b,a) = (b, Integer.min a' a) let operators oracle : (module Operators with type t = t) = operators (cmp ~oracle) @@ -925,7 +938,7 @@ sig val hash : t -> int val equal : t -> t -> bool val compare : t -> t -> int - val hull : oracle:oracle -> t -> bound * bound + val hull : oracle:oracle -> t -> (bound * bound) option val raw : t -> Bit.t val weak_erase : Bit.t -> t -> t val is_included : t -> t -> bool @@ -937,7 +950,8 @@ sig val write : oracle:oracle -> (submemory -> submemory or_bottom) -> t -> bound -> bound -> t or_bottom val incr_bound : - oracle:oracle -> Bound.Var.t -> Integer.t option -> t -> t + oracle:oracle -> Bound.Var.t -> Integer.t option -> t -> + [ `Value of t | `Top ] val map : (submemory -> submemory) -> t -> t val add_segmentation_bounds : oracle:oracle -> bound list -> t -> t end @@ -1031,27 +1045,29 @@ struct List.for_all2 included_segments m1.segments m2.segments with Invalid_argument _ -> false (* Segmentations have different sizes *) - let hull ~oracle (m : t) : bound * bound = + let hull ~oracle (m : t) : (bound * bound) option = let rec last = function | [] -> assert false | [(_v,u)] -> u | _ :: t -> last t in - B.lower_const ~oracle m.start, - B.upper_const ~oracle (B.pred (last m.segments)) + let l = m.start and u = last m.segments in + match B.lower_const ~oracle l, B.upper_const ~oracle u with + | None, _ | _, None -> None + | Some l, Some u -> Some (B.raw_bound l, B.raw_bound (B.pred u)) let is_empty_segment ~oracle l u = let open (val (B.operators oracle)) in l >= u - let check_segments s = (* TODO: remove *) - match s with + let check m = (* temporarily checks type invariant; TODO: remove *) + match m.segments with | [] -> assert false - | s -> s + | _ :: _ -> m (* Merge the two first slices of a segmentation *) exception NothingToMerge - let merge_first ~oracle l = function + let smash_head ~oracle l = function | [] | [_] -> raise NothingToMerge | (v1,m) :: (v2,u) :: tail -> let v1' = if is_empty_segment ~oracle l m then `Bottom else `Value v1 @@ -1061,20 +1077,16 @@ struct | `Bottom -> tail | `Value v -> (v,u) :: tail + let rec smash_all ~oracle l = function + | [] -> `Bottom, l + | [(v,u)] -> `Value v, u + | t -> smash_all ~oracle l (smash_head ~oracle l t) + (* Unify two arrays m1 and m2 *) let unify ~oracle f (m1 (*Left*): t) (m2 (*Right*) : t) : t = (* Shortcuts *) - let compare side = B.cmp ~oracle:(oracle side) in - let equals side b1 b2 = - let r = compare side b1 b2 = Equal in - (* Format.printf "%a (%a) = %a (%a) ? %B@." - B.pretty b1 - Ival.pretty (B._to_ival ~oracle:(oracle side) b1) - B.pretty b2 - Ival.pretty (B._to_ival ~oracle:(oracle side) b2) - r; *) - r - in + let left = oracle Left and right = oracle Right in + let equals side b1 b2 = B.cmp ~oracle:(oracle side) b1 b2 = Equal in let smash side = Bottom.join (M.smash ~oracle:(oracle side)) in let {start=l1 ; segments=s1 ; padding=p1 } = m1 and {start=l2 ; segments=s2 ; padding=p2 } = m2 in @@ -1087,15 +1099,10 @@ struct in debug dunify "Unification start@.%a U@.%a" pretty_segments (l,s1) pretty_segments (l,s2); (* Unify the segmentation end *) - let merge_first side = merge_first ~oracle:(oracle side) in - let rec smash_end side l = function - | [] -> `Bottom, l - | [(v,u)] -> `Value v, u - | t -> smash_end side l (merge_first side l t) - in + let merge_first side = smash_head ~oracle:(oracle side) in let unify_end l s1 s2 = - let v1, u1 = smash_end Left l s1 - and v2, u2 = smash_end Right l s2 in + let v1, u1 = smash_all ~oracle:left l s1 + and v2, u2 = smash_all ~oracle:right l s2 in let u = B.upper_bound ~oracle u1 u2 in let w1 = if equals Left u u1 then v1 else smash Left (`Value (M.of_raw p1)) v1 @@ -1142,7 +1149,7 @@ struct | _ :: _, [] | [], _ :: _-> unify_end l s1 s2 @ acc | (v1,u1) :: t1, (v2,u2) :: t2 -> try - match compare Left u1 u2, compare Right u1 u2 with (* Compare bounds *) + match B.cmp ~oracle:left u1 u2, B.cmp ~oracle:right u1 u2 with (* Compare bounds *) | _, Equal -> (* u1 and u2 can be indeferently used right side -> use u1 as next bound @@ -1172,17 +1179,13 @@ struct let segments = List. rev (aux l s1 s2 []) in debug dunify "Unification result :@.%a" pretty_segments (l,segments); (* Iterate through segmentations *) - { - start = l ; - segments = check_segments segments ; - padding = Bit.join p1 p2 ; - } + check { start = l ; segments ; padding = Bit.join p1 p2 } let single padding lindex (* included *) uindex (* included *) value = - { + check { padding ; start = B.birth lindex ; - segments = check_segments [(value,B.birth (Bound.succ uindex))] ; + segments = [(value,B.birth (Bound.succ uindex))] ; } let read ~oracle map reduce m lindex uindex = @@ -1230,7 +1233,7 @@ struct | ([] | [_]) as t -> List.rev (t @ acc) | ((v,u) :: t) as s -> if B.age u = oldest_age then - aux acc l (merge_first ~oracle l s) + aux acc l (smash_head ~oracle l s) else aux ((v,u) :: acc) u t in @@ -1304,9 +1307,9 @@ struct | [] when previous_is_empty -> [], lindex | head -> head, start in - { + check { m with - segments = check_segments (List.rev_append head' tail'); + segments = List.rev_append head' tail'; start = start'; } in @@ -1330,39 +1333,54 @@ struct else aux u ((v,u) :: acc) t in - { m with segments = check_segments (aux m.start [] m.segments) } + check { m with segments = aux m.start [] m.segments } let incr_bound ~oracle vi x m = let incr = B.incr_or_constantify ~oracle vi x in - let rec aux acc = function - | [] -> acc + let rec incr_start p l = function + | [] -> `Top (* No more segments, top segmentation *) + | (v,u) :: t as s -> + match incr l with + | Some l' -> incr_end p l' (List.rev s) + | None -> (* No replacement, try to find a lower bound instead *) + match B.lower_const ~oracle l with + | None -> (* No lower bound, completely remove the segment *) + let p' = Bit.join p (M.raw v) in + incr_start p' u t + | Some l' -> + let v' = M.smash ~oracle (M.of_raw p) v in + incr_end p l' (List.rev ((v',u) :: t)) + and incr_end p l = function (* In reverse order *) + | [] -> `Top (* No more segments, top segmentation *) | (v,u) :: t -> match incr u with - | Some u -> aux ((v,u) :: acc) t + | Some u' -> incr_inner p l [] ((v,u') :: t) + | None -> (* No replacement, try to find an upper bound instead *) + match B.upper_const ~oracle u with + | None -> (* No upper bound, completely remove the segment *) + let p' = Bit.join p (M.raw v) in + incr_end p' l t + | Some u' -> + let v' = M.smash ~oracle (M.of_raw p) v in + incr_inner p l [] ((v',u') :: t) + and incr_inner p l acc (* In right order *) = function (* In reverse order *) + | [] -> assert false + | [s] -> + check { start=l ; padding=p ; segments=s :: acc } |> + remove_empty_segments |> + fun m -> `Value m + | (v1,u) :: (v2,m) :: t -> (* u is already increased *) + match incr m with + | Some m' -> + incr_inner p l ((v1,u) :: acc) ((v2,m') :: t) | None -> - match t with - | [] -> - let u = B.upper_const ~oracle u, snd u - and v = M.smash ~oracle (M.of_raw m.padding) v in - (v,u) :: acc - | (v',u') :: t -> aux acc ((M.smash ~oracle v v',u') :: t) - in - let start, segments = - match incr m.start with - | Some start -> start, m.segments - | None -> - match m.segments with - | [] -> assert false - | (v,u) :: t -> - let l = B.lower_const ~oracle m.start, snd m.start - and v = M.smash ~oracle (M.of_raw m.padding) v in - l, (v,u) :: t + let v' = M.smash ~oracle v1 v2 in + incr_inner p l acc ((v',u) :: t) in - { m with start ; segments = check_segments (List.rev (aux [] segments)) } |> - remove_empty_segments + incr_start m.padding m.start m.segments let map f m = - { m with segments = check_segments (List.map (fun (v,u) -> f v, u) m.segments) } + check { m with segments=List.map (fun (v,u) -> f v, u) m.segments } let add_segmentation_bounds ~oracle bounds m = let add_bound m b = @@ -1616,9 +1634,9 @@ struct | m -> D.of_raw ci (raw m) let unify ~oracle f = - let raw_to_array ~oracle side prototype b = - let l,u = A.hull ~oracle:(oracle side) prototype in - A.single b l u (Raw b) + let raw_to_array side prototype b = + A.hull ~oracle:(oracle side) prototype |> + Option.map (fun (l,u) -> A.single b l u (Raw b)) in let rec aux m1 m2 = debug dunify "unification between@.%a and@.%a" pretty m1 pretty m2; @@ -1633,28 +1651,26 @@ struct let array_value = A.unify ~oracle aux a1.array_value a2.array_value in Array { a1 with array_value } | Array a1, Raw b2 -> - begin try - let array_value2 = raw_to_array ~oracle Left a1.array_value b2 in + begin match raw_to_array Left a1.array_value b2 with + | None -> weak_erase b2 (Array a1) (* Should not happen unless oracle is very unprecise *) + | Some array_value2 -> let array_value = A.unify ~oracle aux a1.array_value array_value2 in debug demerging "emerging unification between@.%a and@.%a result:@.%a" A.pretty a1.array_value A.pretty array_value2 A.pretty array_value; Array { a1 with array_value } - with Bound.NoBound -> - weak_erase b2 m1 (* TODO: find a better way to handle this case *) end | Raw b1, Array a2 -> - begin try - let array_value1 = raw_to_array ~oracle Right a2.array_value b1 in + begin match raw_to_array Right a2.array_value b1 with + | None -> weak_erase b1 (Array a2) (* Should not happen unless oracle is very unprecise *) + | Some array_value1 -> let array_value = A.unify ~oracle aux array_value1 a2.array_value in debug demerging "emerging unification between@.%a and@.%a result:@.%a" A.pretty array_value1 A.pretty a2.array_value A.pretty array_value; Array { a2 with array_value } - with Bound.NoBound -> - weak_erase b1 m2 (* TODO: find a better way to handle this case *) end | Struct s1, Struct s2 when are_structs_compatible s1 s2 -> let struct_value = S.unify aux s1.struct_value s2.struct_value in @@ -1782,12 +1798,13 @@ struct | Disjunct d -> Disjunct { d with disj_value = D.map aux d.disj_value } | Union u -> Union { u with union_value = aux u.union_value } | Array a -> - let array_value = A.incr_bound ~oracle vi x a.array_value in - Array { a with array_value=A.map aux array_value } + match A.incr_bound ~oracle vi x a.array_value with + | `Top -> Raw (A.raw a.array_value) + | `Value array_value -> + (* debug true "bounds updated@.%a@.to%a" A.pretty a.array_value A.pretty array_value; *) + Array { a with array_value=A.map aux array_value } in - let r = aux m in - (* debug true "bounds updated@.%a@.to%a" pretty m pretty r; *) - r + aux m let add_segmentation_bounds ~oracle ~typ bounds = let convert_bound exp = diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index 1477bdc8095b57ea28010b5986afc1deeff8456c..4152193ac2b225df341902075a5b5279b9ac8a6d 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -320,6 +320,7 @@ struct include Datatype.Pair (Memory) (References) let pretty_debug = pretty let top = Memory.top, References.empty + let _istop (m,r) = Memory.is_top m && References.is_empty r end module BaseMap = @@ -447,7 +448,7 @@ struct let oracle = convert_oracle oracle in read (Memory.extract ~oracle) (Memory.smash ~oracle) state src - let add_references (base_map,tracked) vi refs' = + let add_references (base_map,tracked : t) vi refs' : t = let base = Base.of_varinfo vi in let memory, refs = BaseMap.find_or_top base_map base in let refs'' = References.union refs (References.of_list refs') in @@ -458,17 +459,20 @@ struct let write' (update : memory -> offset -> memory or_bottom) (state : state) (loc : mdlocation) : state or_bottom = + let deps = Location.references loc in + let deps_set = TrackedBases.of_list (List.map Base.of_varinfo deps) in let f base off state' = 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' -> - BaseMap.add base (memory', refs) base_map, tracked + BaseMap.add base (memory', refs) base_map, + Option.map (TrackedBases.union deps_set) tracked else state' in Location.fold f loc (`Value state) >>-: fun state -> - add_references_l state (Location.references loc) (Location.bases loc) + add_references_l state deps (Location.bases loc) let write update state loc = (* Result can never be bottom if update never returns bottom *) @@ -528,12 +532,12 @@ struct let join' ~oracle (m1,t1) (m2,t2) = let open BaseMap in let cache = Hptmap_sig.NoCache - and decide _ (m1,r1) (m2,r2) = - let m = Memory.join ~oracle m1 m2 - and r = References.union r1 r2 in - if Memory.(is_top m) then None else Some (m,r) + and decide _ x1 x2 = + let m1,r1 = Option.value ~default:V.top x1 + and m2,r2 = Option.value ~default:V.top x2 in + Memory.join ~oracle m1 m2, References.union r1 r2 (* TODO: Remove tops *) in - inter ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, + generic_join ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, join_tracked t1 t2 let join s1 s2 = @@ -545,12 +549,12 @@ struct let oracle = mk_bioracle s1 s2 and _,get_hints = Widen.getWidenHints kf stmt in let cache = Hptmap_sig.NoCache - and decide base (m1,r1) (m2,r2) = - let m = Memory.widen ~oracle (get_hints base) m1 m2 - and r = References.union r1 r2 in - if Memory.(is_top m) then None else Some (m,r) + and decide base x1 x2 = + let m1,r1 = Option.value ~default:V.top x1 + and m2,r2 = Option.value ~default:V.top x2 in + Memory.widen ~oracle (get_hints base) m1 m2, References.union r1 r2 in - inter ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, + generic_join ~cache ~symmetric:false ~idempotent:true ~decide m1 m2, join_tracked t1 t2 end @@ -817,7 +821,9 @@ struct 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) + let tracked = List.fold_right TrackedBases.add bases set in + let base_map = BaseMap.inter_with_shape tracked base_map in (* Only keep tracked bases in the current base map *) + base_map, Some tracked else state | _ -> diff --git a/tests/value/oracle/multidim.res.oracle b/tests/value/oracle/multidim.res.oracle index 331359373bff7134c024297ce3ee1410fa859bb2..8de3c7a61eb8925ccd4bc822f6bab27f535943ce 100644 --- a/tests/value/oracle/multidim.res.oracle +++ b/tests/value/oracle/multidim.res.oracle @@ -220,9 +220,9 @@ [eva] multidim.c:182: Frama_C_domain_show_each: t1 : # cvalue: {0} or UNINITIALIZED - # multidim: { [0 .. i - 1] = {0}, [i .. 9] = UNINITIALIZED } + # multidim: [0 .. 9] = {0} t2 : # cvalue: {0} or UNINITIALIZED - # multidim: UNINITIALIZED + # multidim: T [eva] Recording results for main9 [eva] Done for function main9 [eva] Recording results for main