diff --git a/src/kernel_services/abstract_interp/abstract_memory.ml b/src/kernel_services/abstract_interp/abstract_memory.ml index a250acd10d3dbc8ef2708abc90c9b5cebabf49d6..35efe1542310ce184496e43c2f808a86c0a73262 100644 --- a/src/kernel_services/abstract_interp/abstract_memory.ml +++ b/src/kernel_services/abstract_interp/abstract_memory.ml @@ -30,6 +30,19 @@ open Abstract_offset type size = Integer.t + +let dunify = false +let dread = false +let dwrite = false +let demerging = false + +let dp = ref 0 +let debug b format = + incr dp; + if b then + Kernel.debug ~current:true ("#%d " ^^ format) ~level:0 !dp + else Kernel.debug format ~level:50 + (* Composition operator for compare function *) let (<?>) c (cmp,x,y) = @@ -86,6 +99,13 @@ struct let numerical = Any Bases.empty let top = Any Bases.top + let pretty fmt = function + | Uninitialized -> Format.fprintf fmt "UNINITIALIZED" + | Zero -> Format.fprintf fmt "0" + | Any (Set set) when Base.SetLattice.O.is_empty set -> + Format.fprintf fmt "[--..--]" + | Any _ -> Format.fprintf fmt "T" + let is_any = function Any _ -> true | _ -> false let hash = function @@ -162,6 +182,8 @@ type oracle = Cil_types.exp -> Ival.t type bioracle = side -> oracle type strength = Strong | Weak | Reinforce (* update strength *) +let no_oracle = fun _exp -> Ival.top + module type ProtoMemory = sig type t @@ -313,15 +335,71 @@ end (* ------------------------------------------------------------------------ *) -(* --- Arrays abstraction --- *) +(* --- Comparison operators --- *) (* ------------------------------------------------------------------------ *) -type comparison = Equal | Lower | Greater | Uncomparable + +type comparison = + | Equal + | Lower + | Greater + | LowerOrEqual + | GreaterOrEqual + | Uncomparable + +module type Operators = +sig + [@@@warning "-32"] (* unused operators... for now*) + + type t + val (<) : t -> t -> bool + val (>) : t -> t -> bool + val (<=) : t -> t -> bool + val (>=) : t -> t -> bool + val (===) : t -> t -> bool +end + +let operators : + type a . (a -> a -> comparison) -> (module Operators with type t = a) = + fun f -> + (module struct + type t = a + + let (<) b1 b2 = match f b1 b2 with + | Lower -> true + | LowerOrEqual | Equal | Greater | GreaterOrEqual | Uncomparable -> false + + let (<=) b1 b2 = match f b1 b2 with + | Lower | Equal | LowerOrEqual -> true + | Greater | GreaterOrEqual | Uncomparable -> false + + let (>) b1 b2 = match f b1 b2 with + | Greater -> true + | Equal | LowerOrEqual | Lower | GreaterOrEqual | Uncomparable -> false + + let (>=) b1 b2 = match f b1 b2 with + | Greater | GreaterOrEqual | Equal -> true + | Lower | LowerOrEqual | Uncomparable -> false + + let (===) b1 b2 = match f b1 b2 with + | Equal -> true + | GreaterOrEqual | Greater | Lower | LowerOrEqual | Uncomparable -> false + end) + + +(* ------------------------------------------------------------------------ *) +(* --- Arrays abstraction --- *) +(* ------------------------------------------------------------------------ *) module Bound = struct module Var = Cil_datatype.Varinfo - module Exp = Cil_datatype.ExpStructEq + module Exp = + struct + include Cil_datatype.ExpStructEq + let equal e1 e2 = + if e1 == e2 then true else equal e1 e2 + end type t = | Const of Integer.t @@ -364,16 +442,6 @@ struct let of_integer (i : Integer.t) : t = Const i - let succ = function - | Const i -> Const (Integer.succ i) - | Exp (e, i) -> Exp (e, Integer.succ i) - | Ptroffset _ -> raise Not_implemented - - let pred = function - | Const i -> Const (Integer.pred i) - | Exp (e, i) -> Exp (e, Integer.pred i) - | Ptroffset _ -> raise Not_implemented - exception UnsupportedBoundExpression exception NonLinear @@ -435,6 +503,22 @@ struct | Const _ -> assert false (* should not happen ? even with absolute adresses ? *) | Ptroffset _ -> assert false (* Not produced by of_exp *) + (* Stupid oracle built from an Ival oracle *) + let to_ival ~oracle = function + | Const i -> Ival.inject_singleton i + | Exp (e, i) -> Ival.add_singleton_int i (oracle e) + | Ptroffset _ -> raise Not_implemented + + let succ = function + | Const i -> Const (Integer.succ i) + | Exp (e, i) -> Exp (e, Integer.succ i) + | Ptroffset _ -> raise Not_implemented + + let pred = function + | Const i -> Const (Integer.pred i) + | Exp (e, i) -> Exp (e, Integer.pred i) + | Ptroffset _ -> raise Not_implemented + let incr vi i b = try match b with @@ -451,31 +535,34 @@ struct else Option.map (fun i -> Ptroffset (e, base, Integer.(sub j (mul l i)))) i with NonLinear -> None - (* Stupid oracle built from an Ival oracle *) - let to_ival ~oracle = function - | Const i -> Ival.inject_singleton i - | Exp (e, i) -> Ival.add_singleton_int i (oracle e) - | Ptroffset _ -> raise Not_implemented + let cmp_int i1 i2 = + let r = Integer.sub i1 i2 in + if Integer.is_zero r + then Equal + else if Integer.(lt r zero) then Lower else Greater let cmp ~oracle b1 b2 = if b1 == b2 then Equal else match b1, b2 with - | Const i1, Const i2 -> - let r = Integer.sub i1 i2 in - if Integer.is_zero r - then Equal - else if Integer.(lt r zero) then Lower else Greater + | Const i1, Const i2 -> cmp_int i1 i2 + | Exp (e1, i1), Exp (e2, i2) when Exp.equal e1 e2 -> cmp_int i1 i2 + | Ptroffset _, _ | _, Ptroffset _ -> raise Not_implemented | _, _ -> let r = Ival.sub_int (to_ival ~oracle b1) (to_ival ~oracle b2) in match Ival.min_and_max r with | Some min, Some max when Integer.is_zero min && Integer.is_zero max -> Equal - | Some l, _ when Integer.(gt l zero) -> Greater - | _, Some u when Integer.(lt u zero) -> Lower + | Some l, _ when Integer.(ge l zero) -> + if Integer.(gt l zero) then Greater else GreaterOrEqual + | _, Some u when Integer.(le u zero) -> + if Integer.(lt u zero) then Lower else LowerOrEqual | _ -> Uncomparable + let eq ?(oracle=no_oracle) b1 b2 = + 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 @@ -490,13 +577,50 @@ struct and u2 = Option.get (Ival.max_int i2) in Const (Integer.max u1 u2) - let upper_const ~oracle b = + let lower_const ~oracle b = Const (Option.get (Ival.min_int (to_ival ~oracle b))) (* TODO: handle exception *) - let lower_const ~oracle b = + let upper_const ~oracle b = Const (Option.get (Ival.min_int (to_ival ~oracle b))) (* TODO: handle exception *) + + let _operators oracle = operators (cmp ~oracle) end +module AgingBound = +struct + type age = Integer.t + type t = Bound.t * age + + let pretty fmt (b,_) = Bound.pretty fmt b + let hash (b,a) = Hashtbl.hash (Bound.hash b, Integer.hash a) + let compare (b1,a1) (b2,a2) = + Bound.compare b1 b2 <?> (Integer.compare, a1, a2) + let equal (b1,a1) (b2,a2) = + Bound.equal b1 b2 && Integer.equal a1 a2 + let of_integer i a = Bound.of_integer i, a + let _of_exp e a = Bound.of_exp e, a + let _succ (b,a) = (Bound.succ b, a) + let pred (b,a) = (Bound.pred b, a) + let incr vi i (b,a) = Bound.incr vi i b |> Option.map (fun b -> b,a) + let _to_ival ~oracle (b,_a) = Bound.to_ival ~oracle b + let cmp ~oracle (b1,_a1) (b2,_a2) = Bound.cmp ~oracle b1 b2 + let eq ?oracle (b1,_a1) (b2,_a2) = Bound.eq ?oracle b1 b2 + let lower_bound ~oracle (b1,a1) (b2,a2) = + Bound.lower_bound ~oracle b1 b2, Integer.max a1 a2 + let upper_bound ~oracle (b1,a1) (b2,a2) = + Bound.upper_bound ~oracle b1 b2, Integer.max 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 born b = b, Integer.zero + let age (_,a) = a + let operators oracle : (module Operators with type t = t) = + operators (cmp ~oracle) +end + + module type Segmentation = sig type bound = Bound.t @@ -514,7 +638,7 @@ sig t -> t -> t val single : bit -> bound -> bound -> submemory -> t val read : oracle:oracle -> - (submemory -> 'a) -> ('a -> 'a -> 'a) -> t -> bound -> 'a + (submemory -> 'a) -> ('a -> 'a -> 'a) -> t -> bound -> bound -> 'a val write : oracle:oracle -> (submemory -> submemory) -> t -> bound -> bound -> t val incr_bound : @@ -524,29 +648,34 @@ end module Segmentation (Config : Config) (M : ProtoMemory) = struct + module B = AgingBound + type bound = Bound.t + type age = Integer.t type submemory = M.t type t = { - start: bound; - segments: (M.t * bound) list; (* should not be empty *) - padding: bit (* padding at the left and right of the segmentation *) + start: B.t; + segments: (M.t * B.t) list; (* should not be empty *) + padding: bit; (* padding at the left and right of the segmentation *) + age: age; (* number of writes to the array during the analysis *) } let _pretty_debug fmt (l,s) : unit = - Format.fprintf fmt " {%a} " Bound.pretty l; + Format.fprintf fmt " {%a} " B.pretty l; let pp fmt (v,u) = - Format.fprintf fmt "%a {%a} " M.pretty v Bound.pretty u + Format.fprintf fmt "%a {%a} " M.pretty v B.pretty u in List.iter (pp fmt) s - let pretty_segments fmt (l,s) : unit = + let pretty_segments fmt ((l : B.t), (s : (M.t * B.t) list)) : unit = let pp fmt (l,v,u) = - let u = Bound.pred u in (* Upper bound is not included *) - if Bound.(equal l u) then - Format.fprintf fmt "[%a]%a" Bound.pretty l M.pretty v + let u = B.pred u in (* Upper bound is not included *) + (* Less strict than B.equal even with no oracles *) + if B.eq l u then + Format.fprintf fmt "[%a]%a" B.pretty l M.pretty v else - Format.fprintf fmt "[%a..%a]%a" Bound.pretty l Bound.pretty u M.pretty v + Format.fprintf fmt "[%a .. %a]%a" B.pretty l B.pretty u M.pretty v in match s with | [] -> Format.fprintf fmt "[]" (* should not happen *) @@ -563,23 +692,23 @@ struct let hash (m : t) : int = Hashtbl.hash ( - Bound.hash m.start, - List.map (fun (v,u) -> Hashtbl.hash (M.hash v, Bound.hash u)) m.segments, + B.hash m.start, + List.map (fun (v,u) -> Hashtbl.hash (M.hash v, B.hash u)) m.segments, Bit.hash m.padding) let compare (m1 : t) (m2 : t) : int = let compare_segments (v1,u1) (v2,u2) = - M.compare v1 v2 <?> (Bound.compare, u1, u2) + M.compare v1 v2 <?> (B.compare, u1, u2) in - Bound.compare m1.start m2.start <?> + B.compare m1.start m2.start <?> (Transitioning.List.compare compare_segments, m1.segments, m2.segments) <?> (Bit.compare, m1.padding, m2.padding) let equal (m1 : t) (m2 : t) : bool = let equal_segments (v1,u1) (v2,u2) = - M.equal v1 v2 && Bound.equal u1 u2 + M.equal v1 v2 && B.equal u1 u2 in - Bound.equal m1.start m2.start && + B.equal m1.start m2.start && Transitioning.List.equal equal_segments m1.segments m2.segments && Bit.equal m1.padding m2.padding @@ -598,28 +727,26 @@ struct let is_included (m1 : t) (m2 : t) : bool = let included_segments (v1,u1) (v2,u2) = - M.is_included v1 v2 && - Bound.equal u1 u2 + M.is_included v1 v2 && B.eq u1 u2 in - Bound.equal m1.start m2.start && + B.eq m1.start m2.start && Bit.is_included m1.padding m2.padding && try List.for_all2 included_segments m1.segments m2.segments with Invalid_argument _ -> false (* Segmentations have different sizes *) - let hull ~oracle (m : t) : Bound.t * Bound.t = + let hull ~oracle (m : t) : bound * bound = let rec last = function | [] -> assert false | [(_v,u)] -> u | _ :: t -> last t in - Bound.lower_const ~oracle m.start, - Bound.upper_const ~oracle (last m.segments) + B.lower_const ~oracle m.start, + B.upper_const ~oracle (B.pred (last m.segments)) let is_empty_segment ~oracle l u = - match Bound.cmp ~oracle l u with - | Equal | Greater -> true - | Lower | Uncomparable -> false + let open (val (B.operators oracle)) in + l >= u let check_segments s = (* TODO: remove *) match s with @@ -641,21 +768,21 @@ struct (* Unify two arrays m1 and m2 *) let unify ~oracle f (m1 (*Left*): t) (m2 (*Right*) : t) : t = (* Shortcuts *) - let compare side = Bound.cmp ~oracle:(oracle side) in + let compare side = B.cmp ~oracle:(oracle side) in let equals side b1 b2 = compare side b1 b2 = Equal in - let join side = - let oracle = fun _ -> oracle side in - Bottom.join (M.join ~oracle) - 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 (* Unify the segmentation start *) - let l = Bound.lower_bound ~oracle l1 l2 in + let l = B.lower_bound ~oracle l1 l2 in + debug dunify "%a LUB %a = %a" B.pretty l1 B.pretty l2 B.pretty l; + let ll = l in let s1 = if equals Left l l1 then s1 else (M.of_raw p1, l1) :: s1 and s2 = if equals Right l l2 then s2 else (M.of_raw p2, l2) :: s2 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 @@ -666,11 +793,11 @@ struct let unify_end l s1 s2 = let v1, u1 = smash_end Left l s1 and v2, u2 = smash_end Right l s2 in - let u = Bound.upper_bound ~oracle u1 u2 in + let u = B.upper_bound ~oracle u1 u2 in let w1 = - if equals Left u u1 then v1 else join Left (`Value (M.of_raw p1)) v1 + if equals Left u u1 then v1 else smash Left (`Value (M.of_raw p1)) v1 and w2 = - if equals Right u u2 then v2 else join Right (`Value (M.of_raw p2)) v2 + if equals Right u u2 then v2 else smash Right (`Value (M.of_raw p2)) v2 in match Bottom.join f w1 w2 with | `Bottom -> [] (* should not happen, but [] is still correct *) @@ -685,6 +812,7 @@ struct +------+-------+--- l u2 *) let rec aux l s1 s2 acc = + debug dunify "Unification progress@.%a U@.%a result:@.%a" pretty_segments (l,s1) pretty_segments (l,s2) pretty_segments (ll, List.rev acc); (* Look for emerging slices *) let left_slice_emerges = match s1 with | (v1,u1) :: t1 when equals Right l u1 -> Some (v1,u1,t1) @@ -715,54 +843,54 @@ struct (* u1 and u2 can be indeferently used left side -> use u2 as next bound *) aux u2 t1 t2 ((f v1 v2, u2) :: acc) - | Greater, (Greater | Uncomparable) -> + | (Greater | GreaterOrEqual), + (Greater | GreaterOrEqual | Uncomparable) -> (* u1 > u2 on the left side, we are sure u2 doesn't appear left -> remove u2, merge slices *) aux l s1 (merge_first Right l s2) acc - | (Lower | Uncomparable), Lower -> + | (Lower | LowerOrEqual | Uncomparable), + (Lower | LowerOrEqual) -> (* u1 < u2 on the right side, we are sure u1 doesn't appear right -> remove u1, merge slices *) aux l (merge_first Left l s1) s2 acc - | Greater, Lower (* Can't choose which bound to remove first *) - | (Lower | Uncomparable), (Greater | Uncomparable) -> + | (Greater | GreaterOrEqual), (Lower | LowerOrEqual) (* Can't choose which bound to remove first *) + | (Lower | LowerOrEqual | Uncomparable), + (Greater | GreaterOrEqual | Uncomparable) -> aux l (merge_first Left l s1) (merge_first Right l s2) acc with NothingToMerge -> (* There is nothing left to merge *) unify_end l s1 s2 @ acc in + 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 (aux l s1 s2 []) ; + segments = check_segments segments ; padding = Bit.join p1 p2 ; + age = Integer.max m1.age m2.age ; } - let single padding lindex uindex value = + let single padding lindex (* included *) uindex (* included *) value = { padding ; - start = lindex ; - segments = check_segments [(value,uindex)] ; + start = B.born lindex ; + segments = check_segments [(value,B.born (Bound.succ uindex))] ; + age = Integer.zero } - let read ~oracle map reduce m index = - let is_below = function - | Lower-> true - | Equal | Greater | Uncomparable -> false - and is_above = function - | Greater | Equal -> true - | Lower | Uncomparable -> false - and fold acc x = + let read ~oracle map reduce m lindex uindex = + let open (val (B.operators oracle)) in + let lindex = B.born lindex and uindex = B.born uindex in + let fold acc x = Bottom.join reduce acc (`Value (map x)) in - let aux (prev,acc) (v,u) = - let next = Bound.cmp ~oracle index u in - next, - if is_below prev || is_above next then acc else fold acc v + let aux (l,acc) (v,u) = + u, if l > uindex || u <= lindex then acc else fold acc v in - let first = Bound.cmp ~oracle index m.start in let acc = `Bottom in - let acc = if is_above first then acc else fold acc (M.of_raw m.padding) in - let last,acc = List.fold_left aux (first,acc) m.segments in - let acc = if is_below last then acc else fold acc (M.of_raw m.padding) in + let acc = if m.start <= lindex then acc else fold acc (M.of_raw m.padding) in + let last,acc = List.fold_left aux (m.start,acc) m.segments in + let acc = if last > uindex then acc else fold acc (M.of_raw m.padding) in match acc with | `Bottom -> assert false (* TODO: ensure that with typing *) | `Value v -> v @@ -771,14 +899,10 @@ struct 1. reinforcement without loss 2. weak update without singularization 3. update reduces the number of segments to 3 *) - let write ~oracle f m lindex uindex = (* lindex < uindex *) - let (<=) b1 b2 = match Bound.cmp ~oracle b1 b2 with - | Lower | Equal -> true - | Greater | Uncomparable -> false - and (>=) b1 b2 = match Bound.cmp ~oracle b1 b2 with - | Greater | Equal -> true - | Lower | Uncomparable -> false - in + let write ~oracle f m lindex (* included *) uindex (* excluded *) = (* lindex < uindex *) + let open (val (B.operators oracle)) in + let age = Integer.succ m.age in + let lindex = lindex, age and uindex = uindex, age in (* (start,head) : segmentation kept identical below the write indexes, head is a list in reverse order (l,v,u) : the segment (l,u) beeing overwriten with previous value v @@ -786,21 +910,23 @@ struct head = (_,l) :: _ *) let rec aux_before l s = - (* Format.printf "aux before: %a@." pretty_segments (l,s); *) + debug dwrite "aux before: %a@." pretty_segments (l,s); + debug dwrite "%a (%a) >= %a (%a) ? %B@." B.pretty lindex Ival.pretty (B._to_ival ~oracle lindex) B.pretty l Ival.pretty (B._to_ival ~oracle l) (lindex >= l); if lindex >= l then aux_below l [] l s else aux_over lindex [] lindex (M.of_raw m.padding) l s and aux_below start head l = fun t -> - (* Format.printf "aux_below: %a [%a] %a@." pretty_segments (start,head) Bound.pretty l pretty_segments (l,t); *) + debug dwrite "aux_below: %a <{%a}> %a@." pretty_segments (start,head) B.pretty l pretty_segments (l,t); match t with (* l <= lindex *) | [] -> aux_end start head l (M.of_raw m.padding) uindex [] | (v,u) :: t -> + debug dwrite "%a (%a) >= %a (%a) ? %B@." B.pretty lindex Ival.pretty (B._to_ival ~oracle lindex) B.pretty u Ival.pretty (B._to_ival ~oracle u) (lindex >= u); if lindex >= u then aux_below start ((v,u) :: head) u t else aux_over start head l v u t and aux_over start head l v u s = (* l <= lindex *) - (* Format.printf "aux_over: %a [%a,%a,%a] %a@." pretty_segments (start,head) Bound.pretty l M.pretty v Bound.pretty u pretty_segments (u,s); *) + debug dwrite "aux_over: %a <{%a} %a {%a}> %a@." pretty_segments (start,head) B.pretty l M.pretty v B.pretty u pretty_segments (u,s); if uindex <= u then aux_end start head l v u s else @@ -811,43 +937,60 @@ struct (* TODO: do not smash if the slices are covered by the write *) aux_over start head l (M.smash ~oracle v v') u' t and aux_end start head l v u tail = (* l <= lindex < uindex <= u*) - (* Format.printf "aux_end: %a [%a,%a,%a] %a@." pretty_segments (start,head) Bound.pretty l M.pretty v Bound.pretty u pretty_segments (u,tail); *) + debug dwrite "aux_end: %a <{%a} %a {%a}> %a@." pretty_segments (start,head) B.pretty l M.pretty v B.pretty u pretty_segments (u,tail); + let previous_is_empty = is_empty_segment ~oracle l lindex + and next_is_empty = is_empty_segment ~oracle uindex u in let tail' = - (if is_empty_segment ~oracle l lindex then [] else [(v,lindex)]) @ + (if previous_is_empty then [] else [(v,lindex)]) @ [(f v,uindex)] @ - (if is_empty_segment ~oracle uindex u then [] else [(v,u)]) @ + (if next_is_empty then [] else [(v,u)]) @ tail + and head',start' = match head with (* change last bound to match lindex *) + | (v',_u) :: t when previous_is_empty -> (v',lindex) :: t, start + | [] when previous_is_empty -> [], lindex + | head -> head, start in { m with - segments = check_segments (List.rev_append head tail'); - start ; + segments = check_segments (List.rev_append head' tail'); + start = start'; + age; } in aux_before m.start m.segments let incr_bound ~oracle vi x m = + let incr b = + match B.incr vi x b with + | Some b -> Some b + | None -> + try + let i = Ival.project_int (oracle (Cil.evar vi)) in + Some (B.of_integer i (B.age b)) + with Ival.Not_Singleton_Int -> + None + in let rec aux acc = function | [] -> acc | (v,u) :: t -> - match Bound.incr vi x u with + match incr u with | Some u -> aux ((v,u) :: acc) t | None -> match t with | [] -> - let u = Bound.upper_const ~oracle u + 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 ((M.smash ~oracle v v',u') :: acc) t + | (v',u') :: t -> aux acc ((M.smash ~oracle v v',u') :: t) in let start, segments = - match Bound.incr vi x m.start with + match incr m.start with | Some start -> start, m.segments | None -> match m.segments with | [] -> assert false | (v,u) :: t -> - let l = Bound.lower_const ~oracle m.start + 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 in @@ -910,16 +1053,9 @@ struct let prefix fmt = if not root then Format.fprintf fmt " = " in - let pretty_bit fmt = function - | Uninitialized -> Format.fprintf fmt "UNINITIALIZED" - | Zero -> Format.fprintf fmt "0" - | Any (Set set) when Base.SetLattice.O.is_empty set -> - Format.fprintf fmt "[--..--]" - | Any _ -> Format.fprintf fmt "T" - in function | Raw b -> - Format.fprintf fmt "%t%a" prefix pretty_bit b + Format.fprintf fmt "%t%a" prefix Bit.pretty b | Scalar { scalar_value } -> Format.fprintf fmt "%t%a" prefix V.pretty scalar_value | Struct s -> @@ -1061,6 +1197,9 @@ struct 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) + and struct_value = function + | Struct s -> s.struct_value + | m -> S.of_raw (raw m) in let rec aux m1 m2 = match m1, m2 with @@ -1076,14 +1215,22 @@ struct | Array a, Raw b -> let array_value' = raw_to_array ~oracle Left a.array_value b in let array_value = A.unify ~oracle aux a.array_value array_value' in + debug demerging "emerging unification between@.%a and@.%a result:@.%a" A.pretty a.array_value A.pretty array_value' A.pretty array_value; Array { a with array_value } | Raw b, Array a -> let array_value' = raw_to_array ~oracle Right a.array_value b in let array_value = A.unify ~oracle aux array_value' a.array_value in + debug demerging "emerging unification between@.%a and@.%a result:@.%a" A.pretty array_value' A.pretty a.array_value A.pretty array_value; Array { a with array_value } | Struct s1, Struct s2 when are_structs_compatible s1 s2 -> let struct_value = S.unify aux s1.struct_value s2.struct_value in Struct { s1 with struct_value } + | Struct s, Raw _ | Raw _, Struct s -> + (* Previously implemented as weak_erase, it is useful to continue + the recursive unification as there can be an emerging array segment + in the sub structure. *) + let struct_value = S.unify aux (struct_value m1) (struct_value m2) in + Struct { s with struct_value } | Union u1, Union u2 when are_union_compatible u1 u2 -> Union { u1 with @@ -1100,7 +1247,12 @@ struct let smash ~oracle = join ~oracle:(fun _ -> oracle) let read ~oracle (map : Cil_types.typ -> t -> 'a) (reduce : 'a -> 'a -> 'a) = + let map t v = + (* debug true "read %a" pretty v; *) + map t v + in let rec aux offset m = + debug dread "@[<hv>read at %a in %a@]" TypedOffset.pretty offset pretty m; match offset, m with | NoOffset t, m -> map t m @@ -1110,9 +1262,16 @@ struct | Field (fi, offset'), Union u when Cil_datatype.Fieldinfo.equal u.union_field fi -> aux offset' u.union_value - | Index (exp, _index, elem_type, offset'), Array a + | Index (exp, index, elem_type, offset'), Array a when are_typ_compatible a.array_cell_type elem_type -> - A.read ~oracle (aux offset') reduce a.array_value (Bound.of_exp (Option.get exp)) (* TODO: handle None *) + let lindex, uindex = match Option.map Bound.of_exp exp with + | Some b -> b, b + | None | exception Bound.UnsupportedBoundExpression -> + let l, u = Int_val.min_and_max index in + Bound.of_integer (Option.get l), + Bound.of_integer (Option.get u) (* TODO: handle None *) + in + A.read ~oracle (aux offset') reduce a.array_value lindex uindex | _, m -> (* structure mismatch *) let r = Raw (raw m) in match offset with @@ -1123,6 +1282,7 @@ struct let write ~oracle (f : weak:bool -> Cil_types.typ -> t -> t) = let rec aux ~weak offset m = + debug dwrite "@[<hv>write at %a from %a" TypedOffset.pretty offset pretty m; match offset with | NoOffset t -> f ~weak t m @@ -1148,25 +1308,25 @@ struct union_value = aux ~weak offset' old.union_value } | Index (exp, index, elem_type, offset') -> - let lindex, uindex, weak = match exp with - | None -> + let lindex, uindex, weak = match Option.map Bound.of_exp exp with + | Some b -> b, b, weak + | None | exception Bound.UnsupportedBoundExpression -> let l, u = Int_val.min_and_max index in let l = Option.get l and u = Option.get u in (* TODO: handle exceptions *) Bound.of_integer l, Bound.of_integer u, weak || Integer.equal l u - | Some e -> - let b = Bound.of_exp e in - b, b, weak in match m with | Array a when are_typ_compatible a.array_cell_type elem_type -> let array_value = A.write ~oracle (aux ~weak offset') a.array_value lindex (Bound.succ uindex) in + debug dwrite "wrote from previous@.%a@.->%a" A.pretty a.array_value A.pretty array_value; Array { a with array_value } | _ -> let b = raw m in let new_value = aux ~weak offset' (Raw b) in - let array_value = A.single b lindex (Bound.succ uindex) new_value in + let array_value = A.single b lindex uindex new_value in + debug dwrite "wrote from raw@.%a" A.pretty array_value; Array { array_cell_type = elem_type ; array_value } in aux diff --git a/src/kernel_services/abstract_interp/abstract_offset.ml b/src/kernel_services/abstract_interp/abstract_offset.ml index f33429517c1901515a2fef367b701594f17fdd16..e315b28127226f01776e802dc1a45d65600d10af 100644 --- a/src/kernel_services/abstract_interp/abstract_offset.ml +++ b/src/kernel_services/abstract_interp/abstract_offset.ml @@ -83,19 +83,22 @@ struct let array_bounds array_size = (* TODO: handle undefined sizes and not const foldable sizes *) match array_size with - | None -> None, None + | None -> None | Some size_exp -> - Some Integer.zero, - Option.map Integer.pred (Cil.constFoldToInt size_exp) + match Cil.constFoldToInt size_exp with + | None -> None + | Some size when Integer.(gt size zero) -> Some (Integer.zero, size) + | Some _ -> None let array_range array_size = - let l,u = array_bounds array_size in - Int_val.inject_range l u + Option.map + (fun (l,u) -> Int_val.inject_range (Some l) (Some u)) + (array_bounds array_size) let assert_valid_index idx size = - let range = array_range size in - if not (Int_val.is_included idx range) then - raise Abstract_interp.Error_Top + match array_range size with + | Some range when Int_val.is_included idx range -> () + | _ -> raise Abstract_interp.Error_Top let rec of_cil_offset oracle base_typ = function | Cil_types.NoOffset -> NoOffset base_typ @@ -119,7 +122,8 @@ struct let elem_size = Integer.of_int (Cil.bitsSizeOf elem_typ) in if Integer.is_zero elem_size then (* array of elements of size 0 *) if Int_val.is_zero ival then (* the whole range is valid *) - array_range array_size, ival + let range = (array_range array_size) in + Extlib.the ~exn:Abstract_interp.Error_Top range, ival else (* Non-zero offset cannot represent anything here *) raise Abstract_interp.Error_Top else @@ -148,8 +152,14 @@ struct let offset, width = Cil.fieldBitsOffset fi in let l = Integer.(of_int offset) in let u = Integer.(pred (add l (of_int width))) in - let range = Int_val.inject_range (Some l) (Some u) in - if Int_val.is_included ival range then + let matches = + if width = 0 then + Int_val.(equal ival (inject_singleton l)) + else + let range = Int_val.inject_range (Some l) (Some u) in + Int_val.is_included ival range + in + if matches then let sub_ival = Int_val.add_singleton (Integer.neg l) ival in Field (fi, of_int_val ~base_typ:fi.ftype ~typ sub_ival) else @@ -169,13 +179,14 @@ struct | TConst (Integer (v, _)) -> Int_val.inject_singleton v | Trange (l,u) -> let eval bound = function - | None -> bound - | Some { Cil_types.term_node=TConst (Integer (v, _)) } -> Some v + | None -> Extlib.the ~exn:Abstract_interp.Error_Top bound + | Some { Cil_types.term_node=TConst (Integer (v, _)) } -> v | Some _ -> raise Abstract_interp.Error_Top in - let lb, ub = array_bounds array_size in + let bounds = array_bounds array_size in + let lb = Option.map fst bounds and ub = Option.map snd bounds in let l' = eval lb l and u' = eval ub u in - Int_val.inject_range l' u' + Int_val.inject_range (Some l') (Some u') | _ -> raise Abstract_interp.Error_Top let rec of_term_offset base_typ = function diff --git a/src/plugins/value/domains/multidim_domain.ml b/src/plugins/value/domains/multidim_domain.ml index 1b4d74a046d2e74dc1c72248af313f9b12999414..deb69557b5ee59c5b34b3b8721f3ab8f0012b510 100644 --- a/src/plugins/value/domains/multidim_domain.ml +++ b/src/plugins/value/domains/multidim_domain.ml @@ -305,7 +305,11 @@ struct Bottom.non_bottom ival (* TODO: handle exception *) | _ -> Value.top in - fun exp -> Value.project_ival (oracle exp) (* TODO: handle exception *) + fun exp -> + try + Value.project_ival (oracle exp) + with Value.Not_based_on_null -> + Ival.top (* TODO: should it just not happen ? *) let extract (state : state) (src : mdlocation) : Memory.t or_bottom = let oracle = mk_oracle state in @@ -375,7 +379,7 @@ struct let r = Memory.join ~oracle v1 v2 in if Memory.(is_top r) then None else Some r in - inter ~cache ~symmetric:true ~idempotent:true ~decide s1 s2 + inter ~cache ~symmetric:false ~idempotent:true ~decide s1 s2 let widen kf stmt s1 s2 = let oracle = function @@ -430,7 +434,7 @@ struct let oracle = make_oracle valuation in try match expr.enode, record.value.v with - | Lval lv, `Value value -> + | Lval lv, `Value value when not (Value.is_topint value) -> let loc = Location.of_lval oracle lv in let update value' = `Value (Value.narrow value value') @@ -450,31 +454,37 @@ struct let update valuation state = `Value (assume_valuation valuation state) - let update_array_segmentation lval expr state = + let update_array_segmentation_bounds vi expr state = (* TODO: more general transfer function *) (* TODO: only update memory models where the lval is present in the segmentation *) - let increment expr = - match expr.Cil_types.enode with - | BinOp ((PlusA|PlusPI), { enode=Lval lval' }, exp, _typ) - when Cil_datatype.LvalStructEq.equal lval lval' -> - Cil.constFoldToInt exp - | BinOp ((PlusA|PlusPI), exp, { enode=Lval lval'}, _typ) - when Cil_datatype.LvalStructEq.equal lval lval' -> - Cil.constFoldToInt exp - | BinOp ((MinusA|MinusPI), { enode=Lval lval' }, exp, _typ) - when Cil_datatype.LvalStructEq.equal lval lval' -> - Option.map Integer.neg (Cil.constFoldToInt exp) - | _ -> None + let incr = Option.bind expr (fun expr -> + match expr.Cil_types.enode with + | BinOp ((PlusA|PlusPI), { enode=Lval (Var vi', NoOffset) }, exp, _typ) + when Cil_datatype.Varinfo.equal vi vi' -> + Cil.constFoldToInt exp + | BinOp ((PlusA|PlusPI), exp, { enode=Lval (Var vi', NoOffset)}, _typ) + when Cil_datatype.Varinfo.equal vi vi' -> + Cil.constFoldToInt exp + | BinOp ((MinusA|MinusPI), { enode=Lval (Var vi', NoOffset) }, exp, _typ) + when Cil_datatype.Varinfo.equal vi vi' -> + Option.map Integer.neg (Cil.constFoldToInt exp) + | _ -> None) in + (* Very important : oracle must be the oracle before a non-invertible + assignement of the bound to allow removing of eventual empty slice + before the bound leaves the segmentation. *) + map (Memory.incr_bound ~oracle:(mk_oracle state) vi incr) state + + let update_array_segmentation lval expr state = match lval with | Mem _, _ -> state (* Do nothing *) | Var vi, offset -> - let incr = match offset with - | NoOffset -> increment expr + let expr = match offset with + | NoOffset -> expr | _ -> None in - map (Memory.incr_bound ~oracle:(mk_oracle state) vi incr) state + update_array_segmentation_bounds vi expr state let assign_lval lval assigned_value oracle state = try @@ -493,7 +503,7 @@ struct `Value top let assign _kinstr left expr assigned_value valuation state = - let state = update_array_segmentation left.lval expr state in + let state = update_array_segmentation left.lval (Some expr) state in let state = assume_valuation valuation state in let oracle = make_oracle valuation in assign_lval left.lval assigned_value oracle state @@ -543,6 +553,11 @@ struct List.fold_left enter_one state vars let leave_scope _kf vars state = + let state = + List.fold_left + (fun state vi -> update_array_segmentation_bounds vi None state) + state vars + in remove_vars state vars let logic_assign assign location state = diff --git a/tests/value/multidim.c b/tests/value/multidim.c index 9d941f69452fa42871d135f382cf46cd4e51234d..ee8bdd889a853d6b3c0905470263d1ec1cc811a4 100644 --- a/tests/value/multidim.c +++ b/tests/value/multidim.c @@ -1,5 +1,7 @@ /* run.config* - STDOPT: +"-eva-msg-key d-multidim -eva-domains multidim -eva-plevel 1" + STDOPT: #"-main main -eva-msg-key d-multidim -eva-domains multidim -eva-plevel 1" + STDOPT: #"-main main2 -eva-msg-key d-multidim -eva-domains multidim -eva-plevel 1" + STDOPT: #"-main main3 -eva-msg-key d-multidim -eva-domains multidim -eva-plevel 1" */ #include "__fc_builtin.h" #define N 4 @@ -21,7 +23,6 @@ typedef struct { */ s z[M]; -s w[M]; /*@ assigns z[..] \from \nothing; ensures \are_finite(z[..].t1[..].f) && \are_finite(z[..].t2[..].f); @@ -37,7 +38,7 @@ void main(s x) { if (nondet) y2.t1[0].f = 4.0; - Frama_C_domain_show_each(x, y1, y2, z, w); + Frama_C_domain_show_each(x, y1, y2, z); /* The multidim domain wont infer anything except the structure from the assign: it considers x can contain anything including pointers, and thus no @@ -49,29 +50,29 @@ void main(s x) { Frama_C_domain_show_each(z); /* The multidim domain doesn't implement forward logic yet */ //@ check \are_finite(z[..].t1[..].f) && \are_finite(z[..].t2[..].f); - - for (int i = 0 ; i < M ; i ++) { - for (int j = 0 ; j < N ; j ++) { - w[i].t1[j].f = 2.0; - w[i].t1[j].i = 2; - } - } - - Frama_C_domain_show_each(w); - - t t1 = w[Frama_C_interval(0,M-1)].t1[Frama_C_interval(0,N-1)]; - - Frama_C_domain_show_each(t1); } void main2(void) { int t[10] = {0}; - Frama_C_domain_show_each_begin(t); for (int i = 0 ; i < 10 ; i++) { - Frama_C_domain_show_each_before(i, t); t[i] = 1; - Frama_C_domain_show_each_after(i, t); } Frama_C_domain_show_each(t); //@ check t[0..9] == 1; } + +void main3(void) { + for (int i = 0 ; i < M ; i ++) { + for (int j = 0 ; j < N ; j ++) { + z[i].t1[j].f = 2.0; + z[i].t1[j].i = 2; + } + } + + Frama_C_domain_show_each(z); + + int a = Frama_C_interval(0,M-1); + int b = Frama_C_interval(0,N-1); + t r = z[a].t1[b]; + Frama_C_domain_show_each(r); +} diff --git a/tests/value/oracle/multidim.res.oracle b/tests/value/oracle/multidim.0.res.oracle similarity index 58% rename from tests/value/oracle/multidim.res.oracle rename to tests/value/oracle/multidim.0.res.oracle index 9e283f9be301248921b7903f324c2d945be798bb..e088682cb917b4a7efc3edeb15a8f882df15a24d 100644 --- a/tests/value/oracle/multidim.res.oracle +++ b/tests/value/oracle/multidim.0.res.oracle @@ -1,5 +1,5 @@ [kernel] Parsing multidim.c (with preprocessing) -[kernel:typing:no-proto] multidim.c:48: Warning: +[kernel:typing:no-proto] multidim.c:49: Warning: Calling function f that is declared without prototype. Its formals will be inferred from actual arguments [eva:experimental] Warning: The multidim domain is experimental. @@ -8,9 +8,8 @@ [eva] Initial state computed [eva:initial-state] Values of globals at initialization z[0..3] ∈ {0} - w[0..3] ∈ {0} nondet ∈ [--..--] -[eva] multidim.c:40: +[eva] multidim.c:41: Frama_C_domain_show_each: x : # cvalue: .t1[0].f ∈ [-3.40282346639e+38 .. 3.40282346639e+38] .t1[0].i ∈ [--..--] @@ -45,9 +44,7 @@ # multidim: { .t1[0] = { .f = {4.} } } z : # cvalue: {0} # multidim: 0 - w : # cvalue: {0} - # multidim: 0 -[eva] multidim.c:46: +[eva] multidim.c:47: Frama_C_domain_show_each: x : # cvalue: .t1[0].f ∈ [-3.40282346639e+38 .. 3.40282346639e+38] .t1[0].i ∈ [--..--] @@ -66,99 +63,48 @@ .t2[3].f ∈ [-3.40282346639e+38 .. 3.40282346639e+38] .t2[3].i ∈ [--..--] # multidim: { - .t1[0..3] = { .f = {{ ANYTHING }} }, - .t2[0..3] = { .f = {{ ANYTHING }} } + .t1[0 .. 4] = { .f = {{ ANYTHING }} }, + .t2[0 .. 4] = { .f = {{ ANYTHING }} } } [eva] computing for function f <- main. - Called from multidim.c:48. + Called from multidim.c:49. [eva] using specification for function f [eva] Done for function f -[eva] multidim.c:49: +[eva] multidim.c:50: Frama_C_domain_show_each: z : # cvalue: [--..--] - # multidim: [0..3] = { - .t1[0..3] = { + # multidim: [0 .. 4] = { + .t1[0 .. 4] = { .f = [-3.40282346639e+38 .. 3.40282346639e+38] }, - .t2[0..3] = { + .t2[0 .. 4] = { .f = [-3.40282346639e+38 .. 3.40282346639e+38] } } -[eva:alarm] multidim.c:51: Warning: check got status unknown. -[eva] multidim.c:54: starting to merge loop iterations -[eva] multidim.c:53: starting to merge loop iterations -[kernel] multidim.c:55: - more than 1(20) locations to update in array. Approximating. -[kernel] multidim.c:56: - more than 1(20) locations to update in array. Approximating. -[kernel] multidim.c:55: - more than 1(28) locations to update in array. Approximating. -[kernel] multidim.c:56: - more than 1(28) locations to update in array. Approximating. -[eva] multidim.c:60: - Frama_C_domain_show_each: - w : # cvalue: [0].t1[0].f ∈ [0. .. 2.] - {[0]{.t1{[0].i; [1..3]}; .t2[0..3]}; [1..2]; [3].t1{[0..2]; [3].f}} ∈ - [--..--] - [3].t1[3].i ∈ {0; 2} - [3].t2[0..3] ∈ {0} - # multidim: [0..3] = { .t1[-1..3] = { .f = [0. .. 2.], .i = {0; 2} } } -[eva] computing for function Frama_C_interval <- main. - Called from multidim.c:62. -[eva] using specification for function Frama_C_interval -[eva] multidim.c:62: - function Frama_C_interval: precondition 'order' got status valid. -[eva] Done for function Frama_C_interval -[eva] computing for function Frama_C_interval <- main. - Called from multidim.c:62. -[eva] multidim.c:62: - function Frama_C_interval: precondition 'order' got status valid. -[eva] Done for function Frama_C_interval -[eva] multidim.c:64: - Frama_C_domain_show_each: - t1 : # cvalue: [--..--] - # multidim: T +[eva:alarm] multidim.c:52: Warning: check got status unknown. [eva] Recording results for main -[kernel] multidim.c:55: more than 1(28) elements to enumerate. Approximating. -[kernel] multidim.c:56: more than 1(28) elements to enumerate. Approximating. [eva] done for function main -[kernel] multidim.c:51: more than 1(28) elements to enumerate. Approximating. +[kernel] multidim.c:52: more than 1(28) elements to enumerate. Approximating. +[kernel] multidim.c:52: more than 1(28) elements to enumerate. Approximating. [eva] ====== VALUES COMPUTED ====== [eva:final-states] Values at end of function main: - Frama_C_entropy_source ∈ [--..--] z[0..3] ∈ [--..--] - w[0].t1[0].f ∈ [0. .. 2.] - {[0]{.t1{[0].i; [1..3]}; .t2[0..3]}; [1..2]; [3].t1{[0..2]; [3].f}} ∈ - [--..--] - [3].t1[3].i ∈ {0; 2} - [3].t2[0..3] ∈ {0} y1.t1[0].f ∈ {3.} {.t1{[0].i; [1..3]}; .t2[0..3]} ∈ {0} y2.t1[0].f ∈ {4.} or UNINITIALIZED {.t1{[0].i; [1..3]}; .t2[0..3]} ∈ UNINITIALIZED - t1 ∈ [--..--] [from] Computing for function main [from] Computing for function f <-main [from] Done for function f -[kernel] multidim.c:55: more than 1(28) dependencies to update. Approximating. -[kernel] multidim.c:56: more than 1(28) dependencies to update. Approximating. -[from] Computing for function Frama_C_interval <-main -[from] Done for function Frama_C_interval [from] Done for function main [from] ====== DEPENDENCIES COMPUTED ====== These dependencies hold at termination for the executions that terminate: -[from] Function Frama_C_interval: - Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) - \result FROM Frama_C_entropy_source; min; max [from] Function f: z[0..3] FROM \nothing [from] Function main: - Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) z[0..3] FROM \nothing - w{[0..2]; [3].t1[0..3]} FROM \nothing (and SELF) [from] ====== END OF DEPENDENCIES ====== [inout] Out (internal) for function main: - Frama_C_entropy_source; z[0..3]; w{[0..2]; [3].t1[0..3]}; y1; y2.t1[0].f; - i; j; t1; tmp; tmp_0 + z[0..3]; y1; y2.t1[0].f [inout] Inputs for function main: - Frama_C_entropy_source; w{[0..2]; [3].t1[0..3]}; nondet + nondet diff --git a/tests/value/oracle/multidim.1.res.oracle b/tests/value/oracle/multidim.1.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..fecc3076b5e7ce6f2bd571c2ea764aecef811440 --- /dev/null +++ b/tests/value/oracle/multidim.1.res.oracle @@ -0,0 +1,33 @@ +[kernel] Parsing multidim.c (with preprocessing) +[kernel:typing:no-proto] multidim.c:49: Warning: + Calling function f that is declared without prototype. + Its formals will be inferred from actual arguments +[eva:experimental] Warning: The multidim domain is experimental. +[eva] Analyzing a complete application starting at main2 +[eva] Computing initial state +[eva] Initial state computed +[eva:initial-state] Values of globals at initialization + z[0..3] ∈ {0} + nondet ∈ [--..--] +[eva] multidim.c:57: starting to merge loop iterations +[eva] multidim.c:60: + Frama_C_domain_show_each: + t : # cvalue: {0; 1} + # multidim: { [0 .. 9] = {1}, [10 .. 9] = {0} } +[eva:alarm] multidim.c:61: Warning: check got status unknown. +[eva] Recording results for main2 +[eva] done for function main2 +[eva] ====== VALUES COMPUTED ====== +[eva:final-states] Values at end of function main2: + t[0..9] ∈ {0; 1} +[from] Computing for function main2 +[from] Done for function main2 +[from] ====== DEPENDENCIES COMPUTED ====== + These dependencies hold at termination for the executions that terminate: +[from] Function main2: + NO EFFECTS +[from] ====== END OF DEPENDENCIES ====== +[inout] Out (internal) for function main2: + t[0..9]; i +[inout] Inputs for function main2: + \nothing diff --git a/tests/value/oracle/multidim.2.res.oracle b/tests/value/oracle/multidim.2.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..6288a0d7a9579c400915b7a974e7100ca930a0ff --- /dev/null +++ b/tests/value/oracle/multidim.2.res.oracle @@ -0,0 +1,83 @@ +[kernel] Parsing multidim.c (with preprocessing) +[kernel:typing:no-proto] multidim.c:49: Warning: + Calling function f that is declared without prototype. + Its formals will be inferred from actual arguments +[eva:experimental] Warning: The multidim domain is experimental. +[eva] Analyzing a complete application starting at main3 +[eva] Computing initial state +[eva] Initial state computed +[eva:initial-state] Values of globals at initialization + z[0..3] ∈ {0} + nondet ∈ [--..--] +[eva] multidim.c:66: starting to merge loop iterations +[eva] multidim.c:65: starting to merge loop iterations +[kernel] multidim.c:67: + more than 1(20) locations to update in array. Approximating. +[kernel] multidim.c:68: + more than 1(20) locations to update in array. Approximating. +[kernel] multidim.c:67: + more than 1(28) locations to update in array. Approximating. +[kernel] multidim.c:68: + more than 1(28) locations to update in array. Approximating. +[eva] multidim.c:72: + Frama_C_domain_show_each: + z : # cvalue: [0].t1[0].f ∈ [0. .. 2.] + {[0]{.t1{[0].i; [1..3]}; .t2[0..3]}; [1..2]; [3].t1{[0..2]; [3].f}} ∈ + [--..--] + [3].t1[3].i ∈ {0; 2} + [3].t2[0..3] ∈ {0} + # multidim: [0 .. 3] = { + .t1{ + [0 .. 3] = { .f = [0. .. 2.], .i = {0; 2} }, + [4 .. 3] = 0 + } + } +[eva] computing for function Frama_C_interval <- main3. + Called from multidim.c:74. +[eva] using specification for function Frama_C_interval +[eva] multidim.c:74: + function Frama_C_interval: precondition 'order' got status valid. +[eva] Done for function Frama_C_interval +[eva] computing for function Frama_C_interval <- main3. + Called from multidim.c:75. +[eva] multidim.c:75: + function Frama_C_interval: precondition 'order' got status valid. +[eva] Done for function Frama_C_interval +[eva] multidim.c:77: + Frama_C_domain_show_each: + r : # cvalue: [--..--] + # multidim: { .f = [0. .. 2.], .i = {0; 2} } +[eva] Recording results for main3 +[kernel] multidim.c:67: more than 1(28) elements to enumerate. Approximating. +[kernel] multidim.c:68: more than 1(28) elements to enumerate. Approximating. +[eva] done for function main3 +[eva] ====== VALUES COMPUTED ====== +[eva:final-states] Values at end of function main3: + Frama_C_entropy_source ∈ [--..--] + z[0].t1[0].f ∈ [0. .. 2.] + {[0]{.t1{[0].i; [1..3]}; .t2[0..3]}; [1..2]; [3].t1{[0..2]; [3].f}} ∈ + [--..--] + [3].t1[3].i ∈ {0; 2} + [3].t2[0..3] ∈ {0} + a ∈ {0; 1; 2; 3} + b ∈ {0; 1; 2; 3} + r ∈ [--..--] +[from] Computing for function main3 +[kernel] multidim.c:67: more than 1(28) dependencies to update. Approximating. +[kernel] multidim.c:68: more than 1(28) dependencies to update. Approximating. +[from] Computing for function Frama_C_interval <-main3 +[from] Done for function Frama_C_interval +[from] Done for function main3 +[from] ====== DEPENDENCIES COMPUTED ====== + These dependencies hold at termination for the executions that terminate: +[from] Function Frama_C_interval: + Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) + \result FROM Frama_C_entropy_source; min; max +[from] Function main3: + Frama_C_entropy_source FROM Frama_C_entropy_source (and SELF) + z{[0..2]; [3].t1[0..3]} FROM \nothing (and SELF) +[from] ====== END OF DEPENDENCIES ====== +[inout] Out (internal) for function main3: + Frama_C_entropy_source; z{[0..2]; [3].t1[0..3]}; i; j; a; b; r +[inout] Inputs for function main3: + Frama_C_entropy_source; z{[0..2]; [3].t1[0..3]}