diff --git a/Makefile b/Makefile index 6c41594b0d70f77357bfb63613de66c2b368fe52..dd8b0234365cedef0c3d91c5f996c5e09fd151ae 100644 --- a/Makefile +++ b/Makefile @@ -589,6 +589,7 @@ KERNEL_CMO=\ src/kernel_services/abstract_interp/fval.cmo \ src/kernel_services/abstract_interp/int_interval.cmo \ src/kernel_services/abstract_interp/int_set.cmo \ + src/kernel_services/abstract_interp/int_val.cmo \ src/kernel_services/abstract_interp/ival.cmo \ src/kernel_services/abstract_interp/base.cmo \ src/kernel_services/abstract_interp/origin.cmo \ diff --git a/headers/header_spec.txt b/headers/header_spec.txt index a84301e7133d466ac737ba78c734c51423cff4fb..de8a57f7360a21f507ac6134c31a9fbc80d7c8fa 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -430,6 +430,8 @@ src/kernel_services/abstract_interp/int_interval.ml: CEA_LGPL src/kernel_services/abstract_interp/int_interval.mli: CEA_LGPL src/kernel_services/abstract_interp/int_set.ml: CEA_LGPL src/kernel_services/abstract_interp/int_set.mli: CEA_LGPL +src/kernel_services/abstract_interp/int_val.ml: CEA_LGPL +src/kernel_services/abstract_interp/int_val.mli: CEA_LGPL src/kernel_services/abstract_interp/ival.ml: CEA_LGPL src/kernel_services/abstract_interp/ival.mli: CEA_LGPL src/kernel_services/abstract_interp/lattice_messages.ml: CEA_LGPL diff --git a/src/kernel_services/abstract_interp/int_val.ml b/src/kernel_services/abstract_interp/int_val.ml new file mode 100644 index 0000000000000000000000000000000000000000..edc8f60b415da06336f2beaa693fe77adabfbbf0 --- /dev/null +++ b/src/kernel_services/abstract_interp/int_val.ml @@ -0,0 +1,1061 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2019 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Abstract_interp + +(* Make sure all this is synchronized with the default value of -ilevel *) +let small_cardinal = ref 8 +let small_cardinal_Int = ref (Int.of_int !small_cardinal) +let small_cardinal_log = ref 3 + +let set_small_cardinal i = + assert (2 <= i && i <= 1024); + let rec log j p = + if i <= p then j + else log (j+1) (2*p) + in + small_cardinal := i; + small_cardinal_Int := Int.of_int i; + small_cardinal_log := log 1 2; + (* TODO: share this code with Int_set *) + Int_set.set_small_cardinal i + +let get_small_cardinal () = !small_cardinal + +let emitter = Lattice_messages.register "Int_val" +let log_imprecision s = Lattice_messages.emit_imprecision emitter s + + +module Widen_Hints = Datatype.Integer.Set +type size_widen_hint = Integer.t +type generic_widen_hint = Widen_Hints.t +type widen_hint = size_widen_hint * generic_widen_hint + +(* --------------------------------- Datatype ------------------------------- *) + +type int_val = + | Set of Int_set.t + | Itv of Int_interval.t + +let bottom = Set Int_set.bottom +let top = Itv Int_interval.top + +let hash = function + | Set s -> Int_set.hash s + | Itv i -> Int_interval.hash i + +let compare e1 e2 = + match e1, e2 with + | Set s1, Set s2 -> Int_set.compare s1 s2 + | Itv i1, Itv i2 -> Int_interval.compare i1 i2 + | _, Set _ -> 1 + | Set _, _ -> -1 + +let equal e1 e2 = compare e1 e2 = 0 + +let pretty fmt = function + | Itv i -> Int_interval.pretty fmt i + | Set s -> Int_set.pretty fmt s + +let rehash = function + | Set s -> Set (Int_set.rehash s) + | x -> x + +include Datatype.Make_with_collections + (struct + type t = int_val + let name = "int_val" + open Structural_descr + let structural_descr = + t_sum + [| [| Int_set.packed_descr |]; + [| Int_interval.packed_descr |] |] + let reprs = [ top ; bottom ] + let equal = equal + let compare = compare + let hash = hash + let pretty = pretty + let rehash = rehash + let internal_pretty_code = Datatype.pp_fail + let mem_project = Datatype.never_any_project + let copy = Datatype.undefined + let varname = Datatype.undefined + end) + +(* ------------------------------- Constructors ---------------------------- *) + +let zero = Set Int_set.zero +let one = Set Int_set.one +let minus_one = Set Int_set.minus_one +let zero_or_one = Set Int_set.zero_or_one + +let positive_integers = Itv (Int_interval.inject_range (Some Int.zero) None) +let negative_integers = Itv (Int_interval.inject_range None (Some Int.zero)) + + +let fail min max r modu = + let bound fmt = function + | None -> Format.fprintf fmt "--" + | Some x -> Int.pretty fmt x + in + Kernel.fatal "Int_val: broken Itv, min=%a max=%a r=%a modu=%a" + bound min bound max Int.pretty r Int.pretty modu + +let is_safe_modulo r modu = + (Int.ge r Int.zero ) && (Int.ge modu Int.one) && (Int.lt r modu) + +let is_safe_bound bound r modu = match bound with + | None -> true + | Some m -> Int.equal (Int.e_rem m modu) r + +(* Sanity check for Itv's arguments *) +let check ~min ~max ~rem ~modu = + if not (is_safe_modulo rem modu + && is_safe_bound min rem modu + && is_safe_bound max rem modu) + then fail min max rem modu + +let inject_singleton e = Set (Int_set.inject_singleton e) + +let make ~min ~max ~rem ~modu = + match min, max with + | Some mn, Some mx -> + if Int.gt mx mn then + let l = Int.succ (Int.e_div (Int.sub mx mn) modu) in + if Int.le l !small_cardinal_Int + then + let l = Int.to_int l in + let s = Array.make l Int.zero in + let v = ref mn in + let i = ref 0 in + while (!i < l) + do + s.(!i) <- !v; + v := Int.add modu !v; + incr i + done; + assert (Int.equal !v (Int.add modu mx)); + Set (Int_set.inject_array s l) + else Itv (Int_interval.make ~min ~max ~rem ~modu) + else if Int.equal mx mn + then inject_singleton mn + else bottom + | _ -> Itv (Int_interval.make ~min ~max ~rem ~modu) + +let check_make ~min ~max ~rem ~modu = + check ~min ~max ~rem ~modu; + make ~min ~max ~rem ~modu + +let inject_interval ~min ~max ~rem:r ~modu = + assert (is_safe_modulo r modu); + let fix_bound fix bound = match bound with + | None -> None + | Some b -> Some (if Int.equal b (Int.e_rem r modu) then b else fix b) + in + let min = fix_bound (fun min -> Int.round_up_to_r ~min ~r ~modu) min + and max = fix_bound (fun max -> Int.round_down_to_r ~max ~r ~modu) max in + make ~min ~max ~rem:r ~modu + +let inject_range min max = check_make ~min ~max ~rem:Int.zero ~modu:Int.one + +(* ------------------------- Sets and Intervals ---------------------------- *) + +(* TODO: comments *) + +let inject_set_or_bottom = function + | `Bottom -> bottom + | `Value s -> Set s + +let inject_itv_or_bottom = function + | `Bottom -> bottom + | `Value i -> + match Int_interval.cardinal i with + | None -> Itv i + | Some card -> + if Int.le card !small_cardinal_Int + then + let min, max, rem, modu = Int_interval.min_max_rem_modu i in + make ~min ~max ~rem ~modu + else Itv i + +let inject_pre_itv ~min ~max ~modu = + let rem = Int.e_rem min modu in + Itv (Int_interval.make ~min:(Some min) ~max:(Some max) ~rem ~modu) + +let inject_set_or_top = function + | `Set s -> Set s + | `Top (min, max, modu) -> inject_pre_itv ~min ~max ~modu + +(* TODO: more comment *) + +let make_top_from_set s = + let min = Int_set.min s in + let modu = + Int_set.fold + (fun acc x -> + if Int.equal x min + then acc + else Int.pgcd (Int.sub x min) acc) + Int.zero + s + in + let rem = Int.e_rem min modu in + let max = Some (Int_set.max s) in + let min = Some min in + min, max, rem, modu + +let make_itv_from_set s = + let min, max, rem, modu = make_top_from_set s in + Int_interval.make ~min ~max ~rem ~modu + +let make_itv = function + | Itv i -> i + | Set s -> make_itv_from_set s + +let make_range = function + | Itv i -> i + | Set s -> + let min, max = Int_set.min s, Int_set.max s in + Int_interval.inject_range (Some min) (Some max) + +(* -------------------------------- Utils ----------------------------------- *) + +let min_le_elt min elt = + match min with + | None -> true + | Some m -> Int.le m elt + +let max_ge_elt max elt = + match max with + | None -> true + | Some m -> Int.ge m elt + +(* TODO *) +let is_bottom x = equal x bottom +let is_zero x = equal x zero +let is_one = equal one + +let contains_zero = function + | Itv i -> Int_interval.mem Int.zero i + | Set s -> Int_set.mem Int.zero s >= 0 + +let contains_non_zero = function + | Itv _ -> true (* at least two values *) + | Set _ as s -> not (is_zero s || is_bottom s) + +let fold_int f v acc = + match v with + | Itv i -> Int_interval.fold_int f i acc + | Set s -> Int_set.fold (fun acc x -> f x acc) acc s + +let fold_enum f v acc = fold_int (fun x acc -> f (inject_singleton x) acc) v acc + +(* -------------------------------- Accessors ------------------------------- *) + +let min_int = function + | Itv i -> fst (Int_interval.min_and_max i) + | Set s -> + if Int_set.cardinal s = 0 + then raise Error_Bottom + else Some (Int_set.min s) + +let max_int = function + | Itv i -> snd (Int_interval.min_and_max i) + | Set s -> + if Int_set.cardinal s = 0 + then raise Error_Bottom + else Some (Int_set.max s) + +let min_and_max = function + | Set s -> + let l = Int_set.cardinal s in + if l = 0 + then raise Error_Bottom + else Some (Int_set.min s), Some (Int_set.max s) + | Itv i -> Int_interval.min_and_max i + +let min_max_rem_modu = function + | Set s -> + assert (Int_set.cardinal s >= 2); + make_top_from_set s + | Itv i -> Int_interval.min_max_rem_modu i + +exception Not_Singleton_Int +let project_int = function + | Set s -> + if Int_set.cardinal s = 1 then Int_set.min s else raise Not_Singleton_Int + | Itv _ -> raise Not_Singleton_Int + +let is_small_set = function + | Set _ -> true + | Itv _ -> false + +let project_small_set = function + | Set a -> Some (Int_set.to_list a) + | Itv _ -> None + +(* ----------------------------- Cardinality -------------------------------- *) + +let is_singleton = function + | Itv _ -> false + | Set s -> Int_set.cardinal s = 1 + +let cardinal_zero_or_one = function + | Set s -> Int_set.cardinal s <= 1 + | Itv _ -> false + +let cardinal = function + | Set s -> Some (Int.of_int (Int_set.cardinal s)) + | Itv i -> Int_interval.cardinal i + +let cardinal_estimate ~size = function + | Set s -> Int.of_int (Int_set.cardinal s) + | Itv i -> Extlib.opt_conv (Int.two_power size) (Int_interval.cardinal i) + +let cardinal_less_than v n = + let c = + match v with + | Set s -> Int.of_int (Int_set.cardinal s) + | Itv i -> Extlib.the ~exn:Not_less_than (Int_interval.cardinal i) + in + if Int.le c (Int.of_int n) + then Int.to_int c (* This is smaller than the original [n] *) + else raise Not_less_than + +let cardinal_is_less_than v n = + match cardinal v with + | None -> false + | Some c -> Int.le c (Int.of_int n) + +let diff_if_one value rem = + match rem with + | Set s when Int_set.cardinal s = 1 -> + begin + let v = Int_set.min s in + match value with + | Set s -> inject_set_or_bottom (Int_set.remove s v) + | Itv i -> + let min, max, rem, modu = Int_interval.min_max_rem_modu i in + match min, max with + | Some mn, _ when Int.equal v mn -> + check_make ~min:(Some (Int.add mn modu)) ~max ~rem ~modu + | _, Some mx when Int.equal v mx -> + check_make ~min ~max:(Some (Int.sub mx modu)) ~rem ~modu + | Some mn, Some mx when + Int.equal (Int.sub mx mn) (Int.mul modu !small_cardinal_Int) + && Int_interval.mem v i -> + let r = ref mn in + let array = + Array.init !small_cardinal + (fun _ -> + let c = !r in + let corrected_c = if Int.equal c v then Int.add c modu else c in + r := Int.add corrected_c modu; + corrected_c) + in + Set (Int_set.inject_array array !small_cardinal) + | _, _ -> value + end + | _ -> value + +let diff value rem = + log_imprecision "Ival.diff"; + diff_if_one value rem + +(* ------------------------------- Lattice ---------------------------------- *) + +let is_included t1 t2 = + (t1 == t2) || + match t1, t2 with + | Set s, _ when Int_set.cardinal s = 0 -> true + | Itv i1, Itv i2 -> Int_interval.is_included i1 i2 + | Itv _, Set _ -> false (* Itv _ represents more elements + than can be represented by Set _ *) + | Set s, Itv i -> + let min, max, rem, modu = Int_interval.min_max_rem_modu i in + (* Inclusion of bounds is needed for the entire inclusion *) + min_le_elt min (Int_set.min s) && max_ge_elt max (Int_set.max s) + && (Int.equal Int.one modu (* Top side contains all integers, we're done *) + || Int_set.for_all (fun x -> Int.equal (Int.e_rem x modu) rem) s) + | Set s1, Set s2 -> Int_set.is_included s1 s2 + +let join v1 v2 = + if v1 == v2 then v1 else + match v1,v2 with + | Itv i1, Itv i2 -> Itv (Int_interval.join i1 i2) + | Set i1, Set i2 -> inject_set_or_top (Int_set.join i1 i2) + | Set s, (Itv i as t) + | (Itv i as t), Set s -> + let min, max, r, modu = Int_interval.min_max_rem_modu i in + let l = Int_set.cardinal s in + if l = 0 then t + else + let f modu elt = Int.pgcd modu (Int.abs (Int.sub r elt)) in + let modu = Int_set.fold f modu s in + let rem = Int.e_rem r modu in + let min = match min with + None -> None + | Some m -> Some (Int.min m (Int_set.min s)) + in + let max = match max with + None -> None + | Some m -> Some (Int.max m (Int_set.max s)) + in + check ~min ~max ~rem ~modu; + Itv (Int_interval.make ~min ~max ~rem ~modu) + +let link v1 v2 = + match v1, v2 with + | Set s1, Set s2 -> inject_set_or_top (Int_set.link s1 s2) + | Itv i1, Itv i2 -> Itv (Int_interval.link i1 i2) + | Itv i, Set s | Set s, Itv i -> + let min, max, rem, modu = Int_interval.min_max_rem_modu i in + let move_bound add = function + | None -> None + | Some bound -> + let cur = ref bound in + Int_set.iter (fun e -> if Int.equal e (add !cur modu) then cur := e) s; + Some !cur + in + let min = move_bound Int.sub min + and max = move_bound Int.add max in + check_make ~min ~max ~rem ~modu + +let meet v1 v2 = + if v1 == v2 then v1 else + match v1, v2 with + | Itv i1, Itv i2 -> inject_itv_or_bottom (Int_interval.meet i1 i2) + | Set s1 , Set s2 -> inject_set_or_bottom (Int_set.meet s1 s2) + | Set s, Itv itv + | Itv itv, Set s -> + inject_set_or_bottom (Int_set.filter (fun i -> Int_interval.mem i itv) s) + +let narrow v1 v2 = + match v1, v2 with + | Set s1, Set s2 -> inject_set_or_bottom (Int_set.narrow s1 s2) + | (Itv _| Set _), (Itv _ | Set _) -> meet v1 v2 (* meet is exact *) + +let widen widen_hints t1 t2 = + if equal t1 t2 || cardinal_zero_or_one t1 then t2 + else + match t2 with + | Itv _ | Set _ -> + let i1 = make_itv t1 + and i2 = make_itv t2 in + inject_itv_or_bottom (`Value (Int_interval.widen widen_hints i1 i2)) + +let intersects v1 v2 = + v1 == v2 || + match v1, v2 with + | Itv _, Itv _ -> not (is_bottom (meet v1 v2)) (* YYY: slightly inefficient *) + | Set s1 , Set s2 -> Int_set.intersects s1 s2 + | Set s, Itv itv | Itv itv, Set s -> + Int_set.exists (fun i -> Int_interval.mem i itv) s + +(* -------------------------------- Arithmetics ----------------------------- *) + +let add_singleton i = function + | Set s -> Set (Int_set.add_singleton i s) + | Itv itv -> Itv (Int_interval.add_singleton_int i itv) + +let add v1 v2 = + match v1, v2 with + | Set s1, Set s2 -> inject_set_or_top (Int_set.add s1 s2) + | Itv i1, Itv i2 -> Itv (Int_interval.add i1 i2) + | Set s, Itv i | Itv i, Set s -> + let l = Int_set.cardinal s in + if l = 0 + then bottom + else if l = 1 + then Itv (Int_interval.add_singleton_int (Int_set.min s) i) + else Itv (Int_interval.add i (make_itv_from_set s)) + +let add_under v1 v2 = + match v1, v2 with + | Set s1, Set s2 -> inject_set_or_top (Int_set.add_under s1 s2) + | Itv i1, Itv i2 -> inject_itv_or_bottom (Int_interval.add_under i1 i2) + | Set s, Itv i | Itv i, Set s -> + let l = Int_set.cardinal s in + if l = 0 + then bottom + else + begin + if l <> 1 + then log_imprecision "Ival.add_int_under"; + (* This is precise if [s] has only one element. Otherwise, this is not worse + than another computation. *) + Itv (Int_interval.add_singleton_int (Int_set.min s) i) + end + +let neg = function + | Set s -> Set (Int_set.neg s) + | Itv i -> Itv (Int_interval.neg i) + +let sub v1 v2 = add v1 (neg v2) +let sub_under v1 v2 = add_under v1 (neg v2) + + +let scale f v = + if Int.is_zero f + then zero + else + match v with + | Set s -> Set (Int_set.scale f s) + | Itv i-> Itv (Int_interval.scale f i) + +let scale_div_common ~pos f v scale_interval = + assert (not (Int.is_zero f)); + match v with + | Set s -> inject_set_or_bottom (Int_set.scale_div ~pos f s) + | Itv i -> inject_itv_or_bottom (scale_interval ~pos f i) + +let scale_div ~pos f v = + let scale_div ~pos f v = `Value (Int_interval.scale_div ~pos f v) in + scale_div_common ~pos f v scale_div + +(* TODO: a more precise result could be obtained by transforming + Itv(min,max,r,m) into Itv(min,max,r/f,m/gcd(m,f)). But this is + more complex to implement when pos or f is negative. *) +let scale_div_under ~pos f v = + scale_div_common ~pos f v Int_interval.scale_div_under + +let mul v1 v2 = + if is_one v1 then v2 + else if is_zero v2 || is_zero v1 then zero + else if is_one v2 then v1 + else + match v1, v2 with + | Set s1, Set s2 -> inject_set_or_top (Int_set.mul s1 s2) + | Itv i1, Itv i2 -> Itv (Int_interval.mul i1 i2) + | Set s, Itv i | Itv i, Set s -> + let l = Int_set.cardinal s in + if l = 0 + then bottom + else if l = 1 + then Itv (Int_interval.scale (Int_set.min s) i) + else Itv (Int_interval.mul i (make_itv_from_set s)) + +let div x y = + match y with + | Set sy -> + Int_set.fold + (fun acc elt -> + if Int.is_zero elt then acc else join acc (scale_div ~pos:false elt x)) + bottom sy + | Itv iy -> inject_itv_or_bottom (Int_interval.div (make_range x) iy) + +(* [scale_rem ~pos:false f v] is an over-approximation of the set of + elements [x mod f] for [x] in [v]. + [scale_rem ~pos:true f v] is an over-approximation of the set of + elements [x pos_rem f] for [x] in [v]. *) +let scale_rem ~pos f v = + if Int.is_zero f then bottom + else + match v with + | Set s -> inject_set_or_top (Int_set.scale_rem ~pos f s) + | Itv i -> inject_itv_or_bottom (`Value (Int_interval.scale_rem ~pos f i)) + +let c_rem x y = + match y with + | Itv iy -> + if is_bottom x then bottom + else inject_itv_or_bottom (Int_interval.c_rem (make_range x) iy) + | Set yy -> + match x with + | Set xx -> inject_set_or_top (Int_set.c_rem xx yy) + | Itv _ -> + let f acc y = + join (scale_rem ~pos:false y x) acc + in + Int_set.fold f bottom yy + +(** Computes [x (op) ({y >= 0} * 2^n)], as an auxiliary function for + [shift_left] and [shift_right]. [op] and [scale] must verify + [scale a b == op (inject_singleton a) b] *) +let shift_aux scale op (x: t) (y: t) = + let y = narrow (inject_range (Some Int.zero) None) y in + try + match y with + | Set s -> + Int_set.fold (fun acc n -> join acc (scale (Int.two_power n) x)) bottom s + | Itv _ -> + let min = Extlib.opt_map Int.two_power (min_int y) in + let max = Extlib.opt_map Int.two_power (max_int y) in + let modu = match min with None -> Int.one | Some m -> m in + let factor = check_make ~min ~max ~rem:Int.zero ~modu in + op x factor + with Z.Overflow -> + Lattice_messages.emit_imprecision emitter "Ival.shift_aux"; + (* We only preserve the sign of the result *) + if is_included x positive_integers then positive_integers + else + if is_included x negative_integers then negative_integers + else top + +let shift_right x y = shift_aux (scale_div ~pos:true) div x y +let shift_left x y = shift_aux scale mul x y + +(* ----------------------------------- Misc --------------------------------- *) + +let create_all_values ~signed ~size = + let min, max = + if signed then + let b = Int.two_power_of_int (size - 1) in + Int.neg b, Int.pred b + else + let b = Int.two_power_of_int size in + Int.zero, Int.pred b + in + let min = Some min + and max = Some max in + if size <= !small_cardinal_log + then make ~min ~max ~rem:Int.zero ~modu:Int.one + else Itv (Int_interval.inject_range min max) + +let cast_int_to_int ~size ~signed value = + if equal top value + then create_all_values ~size:(Int.to_int size) ~signed + else + let result = + match value with + | Itv i -> + inject_itv_or_bottom (`Value (Int_interval.cast ~size ~signed i)) + | Set s -> + let all = + create_all_values ~size:(Int.to_int size) ~signed + in + if is_included value all + then value + else + let rem_f value = Int.cast ~size ~signed ~value in + Set (Int_set.map rem_f s) + in + (* If sharing is no longer preserved, please change Cvalue.V.cast *) + if equal result value then value else result + +let all_values ~size = function + | Itv i -> + begin + let min, max, _, modu = Int_interval.min_max_rem_modu i in + match min, max with + | None, _ | _, None -> Int.is_one modu + | Some mn, Some mx -> + Int.is_one modu && + Int.le + (Int.two_power size) + (Int.length mn mx) + end + | Set s as v -> + let siz = Int.to_int size in + Int_set.cardinal s >= 1 lsl siz && + equal + (cast_int_to_int ~size ~signed:false v) + (create_all_values ~size:siz ~signed:false) + +let subdivide = function + | Set s -> let s1, s2 = Int_set.subdivide s in Set s1, Set s2 + | Itv i -> + let i1, i2 = Int_interval.subdivide i in + (* Redo make in case an interval should be converted into a set. *) + let t1 = + let min, max, rem, modu = Int_interval.min_max_rem_modu i1 in + make ~min ~max ~rem ~modu + and t2 = + let min, max, rem, modu = Int_interval.min_max_rem_modu i2 in + make ~min ~max ~rem ~modu + in + t1, t2 + +let extract_bits ~start ~stop = function + | Set s -> Set (Int_set.map (Int.extract_bits ~start ~stop) s) + | Itv _ as d -> + try + let dived = scale_div ~pos:true (Int.two_power start) d in + scale_rem ~pos:true (Int.two_power (Int.length start stop)) dived + with Z.Overflow -> + Lattice_messages.emit_imprecision emitter "Ival.extract_bits"; + top + +let overlaps ~partial ~size t1 t2 = + let diff = sub t1 t2 in + match diff with + | Set array -> + not (Int_set.for_all + (fun i -> Int.ge (Int.abs i) size || (partial && Int.is_zero i)) + array) + | Itv i -> + let min, max = Int_interval.min_and_max i in + let pred_size = Int.pred size in + min_le_elt min pred_size && max_ge_elt max (Int.neg pred_size) + +let make = check_make + +(* ------------------------------------------------------------------------ *) +(* --- Bitwise operators --- *) +(* ------------------------------------------------------------------------ *) + +(* --- Bit lattice --- *) + +type bit_value = On | Off | Both + +module Bit = +struct + type t = bit_value + + let to_string = function + | Off -> "0" + | On -> "1" + | Both -> "T" + + let _pretty (fmt : Format.formatter) (b :t) = + Format.pp_print_string fmt (to_string b) + + let union (b1 : t) (b2 : t) : t = + if b1 = b2 then b1 else Both + + let not : t -> t = function + | On -> Off + | Off -> On + | Both -> Both +end + + +(* --- Bit operators --- *) + +module type BitOperator = +sig + (* Printable version of the operator *) + val representation : string + (* forward is given here as the lifted function of some bit operator op + where op + 1. is assumed to be commutative (backward functions do not assume the + position of the arguments) + 2. must ensure 0 op 0 = 0 as otherwise applying op on a sign bit may + produce a negative result from two positive operands; but we don't + want to produce a negative result when the operation is unsigned which + we don't know unless one of the operands is negative; + 3. is not constant, otherwise nothing of all of this makes sense. + forward is defined as + forward b1 b2 = { x1 op x2 | x1 \in b1, x2 \in b2 } *) + val forward : bit_value -> bit_value -> bit_value + (* backward_off b = { x | \exist y \in b . x op y = y op x = 1 } *) + val backward_off : bit_value -> bit_value + (* backward_on b = { x | \exist y \in b . x op y = y op x = 0 } *) + val backward_on : bit_value -> bit_value +end + +module And : BitOperator = +struct + let representation = "&" + + let forward v1 v2 = + match v1 with + | Off -> Off + | On -> v2 + | Both -> if v2 = Off then Off else Both + + let backward_off = function + | (Off | Both) -> Both + | On -> Off + + let backward_on = function + | Off -> assert false + | (On | Both) -> On +end + +module Or : BitOperator = +struct + let representation = "|" + + let forward v1 v2 = + match v1 with + | On -> On + | Off -> v2 + | Both -> if v2 = On then On else Both + + let backward_off = function + | On -> assert false + | (Off | Both) -> Off + + let backward_on = function + | (On | Both) -> Both + | Off -> On +end + +module Xor : BitOperator = +struct + let representation = "^" + + let forward v1 v2 = + match v1 with + | Both -> Both + | Off -> v2 + | On -> Bit.not v2 + + let backward_on v = Bit.not v + + let backward_off v = v +end + + +(* --- Bit extraction and mutation --- *) + +let significant_bits (v : t) : int option = + match min_and_max v with + | None, _ | _, None -> None + | Some l, Some u -> Some (max (Z.numbits l) (Z.numbits u)) + +let extract_sign (v : t) : bit_value = + match min_and_max v with + | _, Some u when Int.(lt u zero) -> On + | Some l, _ when Int.(ge l zero) -> Off + | _, _ -> Both + +let extract_bit (i : int) (v : t) : bit_value = + let bit_value x = if Z.testbit x i then On else Off in + match v with + | Set s -> Int_set.map_reduce bit_value Bit.union s + | Itv itv -> + match Int_interval.min_and_max itv with + | None, _ | _, None -> Both + | Some l, Some u -> + (* It does not take modulo into account *) + if Int.(ge (sub u l) (two_power_of_int i)) (* u - l >= mask *) + then Both + else Bit.union (bit_value l) (bit_value u) + +let reduce_sign (v : t) (b : bit_value) : t = + match b with + | Both -> v + | On -> + begin match v with + | Set s -> inject_set_or_bottom (Int_set.filter Int.(gt zero) s) + | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_sign itv true) + end + | Off -> + begin match v with + | Set s -> inject_set_or_bottom (Int_set.filter Int.(le zero) s) + | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_sign itv false) + end + +let reduce_bit (i : int) (v : t) (b : bit_value) : t = + let bit_value x = if Z.testbit x i then On else Off in + if b = Both + then v + else match v with + | Set s -> inject_set_or_bottom (Int_set.filter (fun x -> bit_value x = b) s) + | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_bit i itv (b = On)) + +type bit = Sign | Bit of int + +let extract_bit = function + | Sign -> extract_sign + | Bit i -> extract_bit i + +let set_bit_on ~size bit = + let mask = match bit with + | Sign -> Int.(neg (two_power_of_int size)) + | Bit i -> Int.(two_power_of_int i) + in + fun v -> Int.logor mask v + +let reduce_bit = function + | Sign -> reduce_sign + | Bit i -> reduce_bit i + +(* --- Bitwise binary operators --- *) + +module BitwiseOperator (Op : BitOperator) = +struct + + let backward (b : bit_value) = function + | On -> Op.backward_on b + | Off -> Op.backward_off b + | Both -> assert false + + (** Bit masks are composed of an array of significant bit values where index 0 + represents the lowest bit, and a single bit_value to represent the + possible leading bits. *) + type bit_mask = bit_value array * bit_value + + (* Converts an integer [x] into a bit array of size [n]. *) + let int_to_bit_array n (x : Int.t) = + let make i = if Z.testbit x i then On else Off in + Array.init n make + + (* Computes a bit_mask for the lowest bits of an ival, using the modulo + information for non singleton values. *) + let low_bit_mask : t -> bit_mask = function + | Set s when Int_set.cardinal s = 0 -> raise Error_Bottom + | Set s when Int_set.cardinal s = 1 -> (* singleton : build a full mask *) + let x = Int_set.min s in + let n = Z.numbits x in + int_to_bit_array n x, if Int.(ge x zero) then Off else On + | v -> + let _,_,r,modu = min_max_rem_modu v in (* requires cardinal > 1 *) + (* Find how much [modu] can be divided by two. *) + let n = Z.trailing_zeros modu in + int_to_bit_array n r, Both + + (* Computes a remainder and modulo for the result of [v1 op v2]. *) + let compute_modulo v1 v2 = + let b1, s1 = low_bit_mask v1 + and b2, s2 = low_bit_mask v2 in + let size = max (Array.length b1) (Array.length b2) in + (* Sets the [i] nth bits of [rem] until an uncertainty appears. *) + let rec step i rem = + let b1 = try b1.(i) with _ -> s1 + and b2 = try b2.(i) with _ -> s2 in + let b = Op.forward b1 b2 in + if i >= size || b = Both + then rem, Int.two_power_of_int i + else + (* [rem] starts at 0, so we only need to turn on the 1 bits. *) + let rem = if b = On then set_bit_on ~size (Bit i) rem else rem in + step (i+1) rem + in + step 0 Int.zero + + (* The number of bits on which the result should be significant *) + let result_size (v1 : t) (v2 : t) : int option = + let n1 = significant_bits v1 and n2 = significant_bits v2 in + let n1_greater = + match n1, n2 with + | None, _ -> true + | _, None -> false + | Some n1, Some n2 -> n1 >= n2 + in + (* whether n1 or n2 is greater, look if the sign bit oped with anything is + not constant. If it is constant, then the highest bits are irrelevant. *) + if n1_greater + then if Op.forward Both (extract_sign v2) = Both then n1 else n2 + else if Op.forward (extract_sign v1) Both = Both then n2 else n1 + + module O = FCSet.Make(Integer) + + exception Do_not_fit_small_sets + + (* Try to build a small set. + It is basically enumerating the possible results, by choosing the possible + bits from left to right. This function aborts if it ever exceeds the small + set size. The algorithm is probably not complete, as it is not always + possible to reduce the operands leading to a result (without an + exponential cost) meaning that sometimes small sets can be obtained but + the algorithm will fail to find them. *) + let compute_small_set ~size (v1 : t) (v2 : t) (r : Int.t) (modu : Int.t) = + let set_bit i acc (r, v1, v2) = + let b1 = extract_bit i v1 + and b2 = extract_bit i v2 in + match Op.forward b1 b2 with + | On -> (set_bit_on ~size i r, v1, v2) :: acc + | Off -> (r, v1, v2) :: acc + | Both -> + let v1_off = reduce_bit i v1 (Op.backward_off b2) + and v2_off = reduce_bit i v2 (Op.backward_off b1) in + let v1_on = reduce_bit i v1 (Op.backward_on b2) + and v2_on = reduce_bit i v2 (Op.backward_on b1) in + (set_bit_on ~size i r, v1_on, v2_on) :: (r, v1_off, v2_off) :: acc + in + let acc = ref (set_bit Sign [] (r, v1, v2)) in + for i = size - 1 downto Z.numbits modu - 1 do + acc := List.fold_left (set_bit (Bit i)) [] !acc; + if List.length !acc > !small_cardinal then raise Do_not_fit_small_sets + done; + let o = List.fold_left (fun o (r,_,_) -> O.add r o) O.empty !acc in + let cardinal = O.cardinal o in + if cardinal = 0 then bottom else + let a = Array.make cardinal Int.zero in + let i = ref 0 in + O.iter (fun e -> a.(!i) <- e; incr i) o; + Set (Int_set.inject_array a cardinal) + + (* If lower is true (resp. false), compute the lower (resp. upper) bound of + the result interval when applying the bitwise operator to [v1] and [v2]. + [size] is the number of bits of the result. + This function should be exact when the operands are small sets or tops + with modulo 1. Otherwise, it is an overapproximation of the bound. *) + let compute_bound ~size v1 v2 lower = + (* Sets the [i]-nth bit of the currently computed bound [r] of [v1 op v2]. + If possible, reduces [v1] and [v2] accordingly. *) + let set_bit i (r, v1, v2) = + let b1 = extract_bit i v1 + and b2 = extract_bit i v2 in + let b, v1, v2 = + match Op.forward b1 b2 with + | On | Off as b -> b, v1, v2 (* Constant bit, no reduction. *) + | Both -> + (* Choose the best bit for the searched bound, and reduces [v1] and + [v2] accordingly. *) + let b = match i with + | Sign -> if lower then On else Off + | Bit _ -> if lower then Off else On + in + let v1 = reduce_bit i v1 (backward b2 b) + and v2 = reduce_bit i v2 (backward b1 b) in + b, v1, v2 + in + (* Only sets 1 bit, as [r] is 0 at the beginning. *) + let r = if b = On then set_bit_on ~size i r else r in + r, v1, v2 + in + (* The result is 0 at the beginning, and [set_bit] turns on the 1 bits. *) + let r = ref (Int.zero, v1, v2) in + (* Sets the sign bit, and then the bits from size to 0. *) + r := set_bit Sign !r; + for i = (size - 1) downto 0 do + r := set_bit (Bit i) !r; + done; + let bound, _v1, _v2 = !r in + bound + + let bitwise_forward (v1 : t) (v2 : t) : t = + try + let r, modu = compute_modulo v1 v2 in + match result_size v1 v2 with + | None -> + (* We could do better here, as one of the bound may be finite. However, + this case should occur rarely or not at all. *) + inject_interval None None r modu + | Some size -> + try compute_small_set ~size v1 v2 r modu + with Do_not_fit_small_sets -> + let min = compute_bound ~size v1 v2 true + and max = compute_bound ~size v1 v2 false in + inject_interval (Some min) (Some max) r modu + with Error_Bottom -> bottom +end + +let bitwise_or = let module M = BitwiseOperator (Or) in M.bitwise_forward +let bitwise_and = let module M = BitwiseOperator (And) in M.bitwise_forward +let bitwise_xor = let module M = BitwiseOperator (Xor) in M.bitwise_forward + + +(* --- Bitwise not --- *) + +let bitwise_signed_not v = + match v with + | Itv _ -> add (neg v) minus_one (* [-v - 1] *) + | Set s -> Set (Int_set.bitwise_signed_not s) + +let bitwise_unsigned_not ~size v = + let size = Int.of_int size in + cast_int_to_int ~size ~signed:false (bitwise_signed_not v) diff --git a/src/kernel_services/abstract_interp/int_val.mli b/src/kernel_services/abstract_interp/int_val.mli new file mode 100644 index 0000000000000000000000000000000000000000..2d60dabf7a992b6a6ccc961cf12f723dace5c21c --- /dev/null +++ b/src/kernel_services/abstract_interp/int_val.mli @@ -0,0 +1,123 @@ +(**************************************************************************) +(* *) +(* This file is part of Frama-C. *) +(* *) +(* Copyright (C) 2007-2019 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + + +type t + +module Widen_Hints = Datatype.Integer.Set +type size_widen_hint = Integer.t +type generic_widen_hint = Widen_Hints.t + +include Datatype.S_with_collections with type t := t +include Lattice_type.Full_AI_Lattice_with_cardinality + with type t := t + and type widen_hint = size_widen_hint * generic_widen_hint + + +val zero : t +val one : t +val minus_one : t +val zero_or_one : t + +val positive_integers : t +val negative_integers : t + +val inject_singleton : Integer.t -> t +val inject_range : Integer.t option -> Integer.t option -> t +val inject_interval: + min: Integer.t option -> max: Integer.t option -> + rem: Integer.t -> modu: Integer.t -> + t +val make: + min: Integer.t option -> max: Integer.t option -> + rem: Integer.t -> modu: Integer.t -> + t + +val create_all_values: signed:bool -> size:int -> t + +val min_int : t -> Integer.t option +val max_int : t -> Integer.t option +val min_max_rem_modu : + t -> Integer.t option * Integer.t option * Integer.t * Integer.t +val min_and_max : + t -> Integer.t option * Integer.t option + +exception Not_Singleton_Int +val project_int : t -> Integer.t +val is_small_set: t -> bool +val project_small_set: t -> Integer.t list option + + +val is_singleton: t -> bool +val cardinal_zero_or_one : t -> bool +val cardinal: t -> Integer.t option +val cardinal_estimate: size:Integer.t -> t -> Integer.t +val cardinal_less_than : t -> int -> int +val cardinal_is_less_than: t -> int -> bool + +val is_bottom: t -> bool + +val is_zero: t -> bool +val contains_zero : t -> bool +val contains_non_zero : t -> bool + +val add : t -> t -> t +val add_under : t -> t -> t +val add_singleton: Integer.t -> t -> t + +val neg : t -> t +val sub : t -> t -> t +val sub_under: t -> t -> t + +val scale : Integer.t -> t -> t +val scale_div : pos:bool -> Integer.t -> t -> t +val scale_div_under : pos:bool -> Integer.t -> t -> t +val scale_rem : pos:bool -> Integer.t -> t -> t + +val mul : t -> t -> t +val div : t -> t -> t +val c_rem : t -> t -> t +val shift_left: t -> t -> t +val shift_right: t -> t -> t + +val bitwise_and : t -> t -> t +val bitwise_or : t -> t -> t +val bitwise_xor : t -> t -> t +val bitwise_signed_not: t -> t +val bitwise_unsigned_not: size:int -> t -> t + +val cast_int_to_int : size:Integer.t -> signed:bool -> t -> t + +val subdivide: t -> t * t + +val extract_bits: start:Integer.t -> stop:Integer.t -> t -> t + +val all_values: size:Integer.t -> t -> bool + +val overlaps: partial:bool -> size:Integer.t -> t -> t -> bool + +val fold_int : (Integer.t -> 'a -> 'a) -> t -> 'a -> 'a + +val get_small_cardinal: unit -> int +val set_small_cardinal: int -> unit + +val rehash: t -> t diff --git a/src/kernel_services/abstract_interp/ival.ml b/src/kernel_services/abstract_interp/ival.ml index cd48a43913991a52c006d3ef1f6e999bfeac1262..3f7a55813d20716d781c0487fedae00c50eece7a 100644 --- a/src/kernel_services/abstract_interp/ival.ml +++ b/src/kernel_services/abstract_interp/ival.ml @@ -22,34 +22,15 @@ open Abstract_interp -(* Make sure all this is synchronized with the default value of -ilevel *) -let small_cardinal = ref 8 -let small_cardinal_Int = ref (Int.of_int !small_cardinal) -let small_cardinal_log = ref 3 - -let set_small_cardinal i = - assert (2 <= i && i <= 1024); - let rec log j p = - if i <= p then j - else log (j+1) (2*p) - in - small_cardinal := i; - small_cardinal_Int := Int.of_int i; - small_cardinal_log := log 1 2; - (* TODO: share this code with Int_set *) - Int_set.set_small_cardinal i - -let get_small_cardinal () = !small_cardinal +let set_small_cardinal i = Int_val.set_small_cardinal i +let get_small_cardinal = Int_val.get_small_cardinal let emitter = Lattice_messages.register "Ival" let log_imprecision s = Lattice_messages.emit_imprecision emitter s -module O = FCSet.Make(Integer) - type t = - | Set of Int_set.t + | Int of Int_val.t | Float of Fval.t - | Itv of Int_interval.t (* Binary abstract operations do not model precisely float/integer operations. It is the responsibility of the callers to have two operands of the same implicit type. The only exception is for [singleton_zero], which is the @@ -61,90 +42,51 @@ type size_widen_hint = Integer.t type numerical_widen_hint = Widen_Hints.t * Fc_float.Widen_Hints.t type widen_hint = size_widen_hint * numerical_widen_hint -let bottom = Set Int_set.bottom -let top = Itv Int_interval.top +let bottom = Int Int_val.bottom +let top = Int Int_val.top let hash = function - | Set s -> Int_set.hash s - | Itv i -> Int_interval.hash i + | Int i -> Int_val.hash i | Float f -> 3 + 17 * Fval.hash f let compare e1 e2 = if e1==e2 then 0 else match e1, e2 with - | Set s1, Set s2 -> Int_set.compare s1 s2 - | Itv i1, Itv i2 -> Int_interval.compare i1 i2 + | Int i1, Int i2 -> Int_val.compare i1 i2 | Float f1, Float f2 -> Fval.compare f1 f2 - | _, Set _ -> 1 - | Set _, _ -> -1 - | _, Itv _ -> 1 - | Itv _, _ -> -1 + | _, Int _ -> 1 + | Int _, _ -> -1 let equal e1 e2 = compare e1 e2 = 0 let pretty fmt = function - | Itv i -> Int_interval.pretty fmt i + | Int i -> Int_val.pretty fmt i | Float f -> Fval.pretty fmt f - | Set s -> Int_set.pretty fmt s - -let min_le_elt min elt = - match min with - | None -> true - | Some m -> Int.le m elt - -let max_ge_elt max elt = - match max with - | None -> true - | Some m -> Int.ge m elt - - -let fail min max r modu = - let bound fmt = function - | None -> Format.fprintf fmt "--" - | Some x -> Int.pretty fmt x - in - Kernel.fatal "Ival: broken Itv, min=%a max=%a r=%a modu=%a" - bound min bound max Int.pretty r Int.pretty modu - -let is_safe_modulo r modu = - (Int.ge r Int.zero ) && (Int.ge modu Int.one) && (Int.lt r modu) - -let is_safe_bound bound r modu = match bound with - | None -> true - | Some m -> Int.equal (Int.e_rem m modu) r - -(* Sanity check for Itv's arguments *) -let check min max r modu = - if not (is_safe_modulo r modu - && is_safe_bound min r modu - && is_safe_bound max r modu) - then fail min max r modu let cardinal_zero_or_one = function - | Itv _ -> false - | Set s -> Int_set.cardinal s <= 1 + | Int i -> Int_val.cardinal_zero_or_one i | Float f -> Fval.is_singleton f -let is_singleton_int v = match v with - | Float _ | Itv _ -> false - | Set s -> Int_set.cardinal s = 1 +let is_singleton_int = function + | Float _ -> false + | Int i -> Int_val.is_singleton i (* TODO *) let is_bottom x = equal x bottom -let zero = Set Int_set.zero -let one = Set Int_set.one -let minus_one = Set Int_set.minus_one -let zero_or_one = Set Int_set.zero_or_one +let zero = Int Int_val.zero +let one = Int Int_val.one +let minus_one = Int Int_val.minus_one +let zero_or_one = Int Int_val.zero_or_one let float_zeros = Float Fval.zeros -let positive_integers = Itv (Int_interval.inject_range (Some Int.zero) None) -let negative_integers = Itv (Int_interval.inject_range None (Some Int.zero)) +let positive_integers = Int Int_val.positive_integers +let negative_integers = Int Int_val.negative_integers -let is_zero x = x == zero +let is_zero x = equal x zero -let inject_singleton e = Set (Int_set.inject_singleton e) +let inject_singleton e = Int (Int_val.inject_singleton e) let inject_float f = if Fval.(equal plus_zero f) @@ -169,52 +111,46 @@ let project_float v = else match v with | Float f -> f - | Itv _ | Set _ -> assert false (* by hypothesis that it is a float *) + | Int _ -> assert false (* by hypothesis that it is a float *) let is_float = function | Float _ -> true - | Itv _ -> false - | Set _ as i -> equal zero i || equal bottom i + | Int _ as i -> equal zero i || equal bottom i let is_int = function - | Itv _ | Set _ -> true + | Int _ -> true | Float _ -> false let contains_zero = function - | Itv i -> Int_interval.mem Int.zero i - | Set s -> Int_set.mem Int.zero s >= 0 + | Int i -> Int_val.contains_zero i | Float f -> Fval.contains_a_zero f let contains_non_zero = function - | Itv _ -> true (* at least two values *) - | Set _ as s -> not (is_zero s || is_bottom s) + | Int i -> Int_val.contains_non_zero i | Float f -> Fval.contains_non_zero f -exception Not_Singleton_Int +exception Not_Singleton_Int = Int_val.Not_Singleton_Int let project_int = function - | Set s -> - if Int_set.cardinal s = 1 then Int_set.min s else raise Not_Singleton_Int - | _ -> raise Not_Singleton_Int + | Int i -> Int_val.project_int i + | Float _ -> raise Not_Singleton_Int let is_small_set = function - | Set _ -> true - | _ -> false + | Int i -> Int_val.is_small_set i + | Float _ -> false let project_small_set = function - | Set a -> Some (Int_set.to_list a) - | _ -> None + | Int i -> Int_val.project_small_set i + | Float _ -> None let cardinal = function - | Itv i -> Int_interval.cardinal i - | Set s -> Some (Int.of_int (Int_set.cardinal s)) + | Int i -> Int_val.cardinal i | Float f -> if Fval.is_singleton f then Some Int.one else None let cardinal_estimate v ~size = match v with - | Set s -> Int.of_int (Int_set.cardinal s) - | Itv i -> Extlib.opt_conv (Int.two_power size) (Int_interval.cardinal i) + | Int i -> Int_val.cardinal_estimate ~size i | Float f -> if Fval.is_singleton f then Int.one @@ -231,94 +167,20 @@ let cardinal_estimate v ~size = List.fold_left count Int.zero bits_list let cardinal_less_than v n = - let c = - match v with - | Itv i -> Extlib.the ~exn:Not_less_than (Int_interval.cardinal i) - | Set s -> Int.of_int (Int_set.cardinal s) - | Float f -> - if Fval.is_singleton f then Int.one else raise Not_less_than - in - if Int.le c (Int.of_int n) - then Int.to_int c (* This is smaller than the original [n] *) - else raise Not_less_than + match v with + | Int i -> Int_val.cardinal_less_than i n + | Float f -> + if Fval.is_singleton f then 1 else raise Not_less_than let cardinal_is_less_than v n = match cardinal v with | None -> false | Some c -> Int.le c (Int.of_int n) -let make ~min ~max ~rem ~modu = - match min, max with - | Some mn, Some mx -> - if Int.gt mx mn then - let l = Int.succ (Int.e_div (Int.sub mx mn) modu) in - if Int.le l !small_cardinal_Int - then - let l = Int.to_int l in - let s = Array.make l Int.zero in - let v = ref mn in - let i = ref 0 in - while (!i < l) - do - s.(!i) <- !v; - v := Int.add modu !v; - incr i - done; - assert (Int.equal !v (Int.add modu mx)); - Set (Int_set.inject_array s l) - else Itv (Int_interval.make ~min ~max ~rem ~modu) - else if Int.equal mx mn - then inject_singleton mn - else bottom - | _ -> Itv (Int_interval.make ~min ~max ~rem ~modu) - -let inject_top min max rem modu = - check min max rem modu; - make ~min ~max ~rem ~modu - -let inject_interval ~min ~max ~rem:r ~modu = - assert (is_safe_modulo r modu); - let fix_bound fix bound = match bound with - | None -> None - | Some b -> Some (if Int.equal b (Int.e_rem r modu) then b else fix b) - in - let min = fix_bound (fun min -> Int.round_up_to_r ~min ~r ~modu) min - and max = fix_bound (fun max -> Int.round_down_to_r ~max ~r ~modu) max in - make ~min ~max ~rem:r ~modu - - -let inject_set_or_bottom = function - | `Bottom -> bottom - | `Value s -> Set s - -let inject_itv_or_bottom = function - | `Bottom -> bottom - | `Value i -> - match Int_interval.cardinal i with - | None -> Itv i - | Some card -> - if Int.le card !small_cardinal_Int - then - let min, max, rem, modu = Int_interval.min_max_rem_modu i in - make ~min ~max ~rem ~modu - else Itv i - +let inject_top min max rem modu = Int (Int_val.make ~min ~max ~rem ~modu) -let subdiv_int v = - match v with - | Float _ -> raise Can_not_subdiv - | Set s -> let s1, s2 = Int_set.subdivide s in Set s1, Set s2 - | Itv i -> - let i1, i2 = Int_interval.subdivide i in - (* Redo make in case an interval should be converted into a set. *) - let t1 = - let min, max, rem, modu = Int_interval.min_max_rem_modu i1 in - make ~min ~max ~rem ~modu - and t2 = - let min, max, rem, modu = Int_interval.min_max_rem_modu i2 in - make ~min ~max ~rem ~modu - in - t1, t2 +let inject_interval ~min ~max ~rem ~modu = + Int (Int_val.inject_interval ~min ~max ~rem ~modu) let subdivide ~size = function | Float fval -> @@ -329,74 +191,25 @@ let subdivide ~size = function in let f1, f2 = Fval.subdiv_float_interval fkind fval in inject_float f1, inject_float f2 - | ival -> subdiv_int ival + | Int i -> let i1, i2 = Int_val.subdivide i in Int i1, Int i2 let inject_range min max = inject_top min max Int.zero Int.one let top_float = Float Fval.top let top_single_precision_float = Float Fval.top -let make_top_from_set s = - let min = Int_set.min s in - let modu = - Int_set.fold - (fun acc x -> - if Int.equal x min - then acc - else Int.pgcd (Int.sub x min) acc) - Int.zero - s - in - let rem = Int.e_rem min modu in - let max = Some (Int_set.max s) in - let min = Some min in - min, max, rem, modu - -let make_itv_from_set s = - let min, max, rem, modu = make_top_from_set s in - Int_interval.make ~min ~max ~rem ~modu - -let make_itv = function - | Itv i -> i - | Set s -> make_itv_from_set s - | Float _ -> Int_interval.top - -let make_range = function - | Itv i -> i - | Set s -> - let min, max = Int_set.min s, Int_set.max s in - Int_interval.inject_range (Some min) (Some max) - | Float _ -> Int_interval.top - -let inject_pre_itv ~min ~max ~modu = - let rem = Int.e_rem min modu in - Itv (Int_interval.make ~min:(Some min) ~max:(Some max) ~rem ~modu) - -let inject_set_or_top = function - | `Set s -> Set s - | `Top (min, max, modu) -> inject_pre_itv ~min ~max ~modu - -let min_max_r_mod t = - match t with - | Set s -> - assert (Int_set.cardinal s >= 2); - make_top_from_set s - | Itv i -> Int_interval.min_max_rem_modu i + +let min_max_r_mod = function + | Int i -> Int_val.min_max_rem_modu i | Float _ -> None, None, Int.zero, Int.one -let min_and_max t = - match t with - | Set s -> - let l = Int_set.cardinal s in - if l = 0 - then raise Error_Bottom - else Some (Int_set.min s), Some (Int_set.max s) - | Itv i -> Int_interval.min_and_max i +let min_and_max = function + | Int i -> Int_val.min_and_max i | Float _ -> None, None let min_and_max_float t = match t with - | Set _ when is_zero t -> Some (Fval.F.plus_zero, Fval.F.plus_zero), false + | Int _ when is_zero t -> Some (Fval.F.plus_zero, Fval.F.plus_zero), false | Float f -> Fval.min_and_max f | _ -> assert false @@ -440,27 +253,25 @@ let widen (bitsize,(wh,fh)) t1 t2 = else Float_sig.Single in Float (Fval.widen fh prec f1 f2) - | Itv _ | Set _ -> - let i1 = make_itv t1 - and i2 = make_itv t2 in - inject_itv_or_bottom (`Value (Int_interval.widen (bitsize, wh) i1 i2)) + | Int i2 -> + let i1 = match t1 with + | Int i1 -> i1 + | Float _ -> Int_val.top + in + Int (Int_val.widen (bitsize,wh) i1 i2) let meet v1 v2 = if v1 == v2 then v1 else let result = - match v1,v2 with - | Itv i1, Itv i2 -> inject_itv_or_bottom (Int_interval.meet i1 i2) - | Set s1 , Set s2 -> inject_set_or_bottom (Int_set.meet s1 s2) - | Set s, Itv itv - | Itv itv, Set s -> - inject_set_or_bottom (Int_set.filter (fun i -> Int_interval.mem i itv) s) + match v1, v2 with + | Int i1, Int i2 -> Int (Int_val.meet i1 i2) | Float(f1), Float(f2) -> begin match Fval.meet f1 f2 with | `Value f -> inject_float f | `Bottom -> bottom end - | (Float f as ff), (Itv _ | Set _ as o) - | (Itv _ | Set _ as o), (Float f as ff) -> + | (Float f as ff), (Int _ as o) + | (Int _ as o), (Float f as ff) -> if equal o top then ff else if contains_zero o && Fval.contains_plus_zero f then zero else bottom @@ -472,10 +283,7 @@ let meet v1 v2 = let intersects v1 v2 = v1 == v2 || match v1, v2 with - | Itv _, Itv _ -> not (is_bottom (meet v1 v2)) (* YYY: slightly inefficient *) - | Set s1 , Set s2 -> Int_set.intersects s1 s2 - | Set s, Itv itv | Itv itv, Set s -> - Int_set.exists (fun i -> Int_interval.mem i itv) s + | Int i1, Int i2 -> Int_val.intersects i1 i2 | Float f1, Float f2 -> begin match Fval.forward_comp Comp.Eq f1 f2 with | Comp.False -> false @@ -486,63 +294,28 @@ let intersects v1 v2 = let narrow v1 v2 = match v1, v2 with - | Set s1, Set s2 -> inject_set_or_bottom (Int_set.narrow s1 s2) - | Float _, Float _ | (Itv _| Set _), (Itv _ | Set _) -> - meet v1 v2 (* meet is exact *) - | v, (Itv _ as t) when equal t top -> v - | (Itv _ as t), v when equal t top -> v - | Float f, (Set _ as s) | (Set _ as s), Float f when is_zero s -> begin + | Float _, Float _ -> meet v1 v2 (* meet is exact *) + | v, (Int _ as t) when equal t top -> v + | (Int _ as t), v when equal t top -> v + | Int i1, Int i2 -> Int (Int_val.narrow i1 i2) + | Float f, (Int _ as s) | (Int _ as s), Float f when is_zero s -> begin match Fval.narrow f Fval.zeros with | `Value f -> inject_float f | `Bottom -> bottom end - | Float _, (Set _ | Itv _) | (Set _ | Itv _), Float _ -> + | Float _, Int _ | Int _, Float _ -> (* ill-typed case. It is better to keep the operation symmetric *) top let link v1 v2 = match v1, v2 with - | Set s1, Set s2 -> inject_set_or_top (Int_set.link s1 s2) - | Itv i1, Itv i2 -> Itv (Int_interval.link i1 i2) - | Itv i, Set s | Set s, Itv i -> - let min, max, rem, modu = Int_interval.min_max_rem_modu i in - let move_bound add = function - | None -> None - | Some bound -> - let cur = ref bound in - Int_set.iter (fun e -> if Int.equal e (add !cur modu) then cur := e) s; - Some !cur - in - let min = move_bound Int.sub min - and max = move_bound Int.add max in - inject_top min max rem modu + | Int i1, Int i2 -> Int (Int_val.link i1 i2) | _ -> bottom - let join v1 v2 = let result = if v1 == v2 then v1 else match v1,v2 with - | Itv i1, Itv i2 -> Itv (Int_interval.join i1 i2) - | Set i1, Set i2 -> inject_set_or_top (Int_set.join i1 i2) - | Set s, (Itv i as t) - | (Itv i as t), Set s -> - let min, max, r, modu = Int_interval.min_max_rem_modu i in - let l = Int_set.cardinal s in - if l = 0 then t - else - let f modu elt = Int.pgcd modu (Int.abs (Int.sub r elt)) in - let modu = Int_set.fold f modu s in - let rem = Int.e_rem r modu in - let min = match min with - None -> None - | Some m -> Some (Int.min m (Int_set.min s)) - in - let max = match max with - None -> None - | Some m -> Some (Int.max m (Int_set.max s)) - in - check min max rem modu; - Itv (Int_interval.make ~min ~max ~rem ~modu) + | Int i1, Int i2 -> Int (Int_val.join i1 i2) | Float(f1), Float(f2) -> inject_float (Fval.join f1 f2) | Float (f) as ff, other | other, (Float (f) as ff) -> @@ -594,98 +367,49 @@ let complement_int_under ~size ~signed i = let fold_int f v acc = match v with | Float _ -> raise Error_Top - | Itv i -> Int_interval.fold_int f i acc - | Set s -> Int_set.fold (fun acc x -> f x acc) acc s + | Int i -> Int_val.fold_int f i acc let fold_enum f v acc = match v with | Float fl when Fval.is_singleton fl -> f v acc | Float _ -> raise Error_Top - | Set _ | Itv _ -> fold_int (fun x acc -> f (inject_singleton x) acc) v acc + | Int _ -> fold_int (fun x acc -> f (inject_singleton x) acc) v acc let is_included t1 t2 = (t1 == t2) || - match t1,t2 with - | Set s, _ when Int_set.cardinal s = 0 -> true - | Itv i1, Itv i2 -> Int_interval.is_included i1 i2 - | Itv _, Set _ -> false (* Itv _ represents more elements - than can be represented by Set _ *) - | Set s, Itv i -> - let min, max, rem, modu = Int_interval.min_max_rem_modu i in - (* Inclusion of bounds is needed for the entire inclusion *) - min_le_elt min (Int_set.min s) && max_ge_elt max (Int_set.max s) - && (Int.equal Int.one modu (* Top side contains all integers, we're done *) - || Int_set.for_all (fun x -> Int.equal (Int.e_rem x modu) rem) s) - | Set s1, Set s2 -> Int_set.is_included s1 s2 + match t1, t2 with + | Int i1, Int i2 -> Int_val.is_included i1 i2 | Float f1, Float f2 -> Fval.is_included f1 f2 | Float _, _ -> equal t2 top - | Set _, Float f -> is_zero t1 && Fval.contains_plus_zero f - | Itv _, Float _ -> false + | Int i, Float f -> Int_val.is_zero i && Fval.contains_plus_zero f -let add_singleton_int i v = match v with +let add_singleton_int i = function | Float _ -> assert false - | Set s -> Set (Int_set.add_singleton i s) - | Itv itv -> Itv (Int_interval.add_singleton_int i itv) + | Int itv -> Int (Int_val.add_singleton i itv) let add_int v1 v2 = - match v1,v2 with + match v1, v2 with | Float _, _ | _, Float _ -> assert false - | Set s1, Set s2 -> inject_set_or_top (Int_set.add s1 s2) - | Itv i1, Itv i2 -> Itv (Int_interval.add i1 i2) - | Set s, Itv i | Itv i, Set s -> - let l = Int_set.cardinal s in - if l = 0 - then bottom - else if l = 1 - then (* only one element *) - Itv (Int_interval.add_singleton_int (Int_set.min s) i) - else - Itv (Int_interval.add i (make_itv_from_set s)) + | Int i1, Int i2 -> Int (Int_val.add i1 i2) -let add_int_under v1 v2 = match v1,v2 with +let add_int_under v1 v2 = + match v1, v2 with | Float _, _ | _, Float _ -> assert false - | Set s1, Set s2 -> inject_set_or_top (Int_set.add_under s1 s2) - | Itv i1, Itv i2 -> inject_itv_or_bottom (Int_interval.add_under i1 i2) - | Set s, Itv i | Itv i, Set s -> - let l = Int_set.cardinal s in - if l = 0 - then bottom - else - begin - if l <> 1 - then log_imprecision "Ival.add_int_under"; - (* This is precise if [s] has only one element. Otherwise, this is not worse - than another computation. *) - Itv (Int_interval.add_singleton_int (Int_set.min s) i) - end + | Int i1, Int i2 -> Int (Int_val.add_under i1 i2) - -let neg_int v = - match v with +let neg_int = function | Float _ -> assert false - | Set s -> Set (Int_set.neg s) - | Itv i -> Itv (Int_interval.neg i) + | Int i -> Int (Int_val.neg i) let sub_int v1 v2 = add_int v1 (neg_int v2) let sub_int_under v1 v2 = add_int_under v1 (neg_int v2) -let min_int s = - match s with - | Itv i -> fst (Int_interval.min_and_max i) - | Set s -> - if Int_set.cardinal s = 0 - then raise Error_Bottom - else Some (Int_set.min s) +let min_int = function + | Int i -> fst (Int_val.min_and_max i) | Float _ -> None - -let max_int s = - match s with - | Itv i -> snd (Int_interval.min_and_max i) - | Set s -> - if Int_set.cardinal s = 0 - then raise Error_Bottom - else Some (Int_set.max s) +let max_int = function + | Int i -> snd (Int_val.min_and_max i) | Float _ -> None @@ -696,45 +420,20 @@ let scale f v = else match v with | Float _ -> top - | Itv i-> Itv (Int_interval.scale f i) - | Set s -> Set (Int_set.scale f s) + | Int i -> Int (Int_val.scale f i) +let scale_div ~pos f = function + | Int i -> Int (Int_val.scale_div ~pos f i) + | Float _ -> top +let scale_div_under ~pos f = function + | Int i -> Int (Int_val.scale_div_under ~pos f i) + | Float _ -> bottom -let scale_div_common ~pos f v scale_interval degenerate_float = - assert (not (Int.is_zero f)); - match v with - | Itv i -> inject_itv_or_bottom (scale_interval ~pos f i) - | Set s -> inject_set_or_bottom (Int_set.scale_div ~pos f s) - | Float _ -> degenerate_float - -let scale_div ~pos f v = - let scale_div ~pos f x = `Value (Int_interval.scale_div ~pos f x) in - scale_div_common ~pos f v scale_div top - -let scale_div_under ~pos f v = - (* TODO: a more precise result could be obtained by transforming - Itv(min,max,r,m) into Itv(min,max,r/f,m/gcd(m,f)). But this is - more complex to implement when pos or f is negative. *) - scale_div_common ~pos f v Int_interval.scale_div_under bottom - -let div_set x sy = - Int_set.fold - (fun acc elt -> - if Int.is_zero elt - then acc - else join acc (scale_div ~pos:false elt x)) - bottom - sy - -let div x y = - (*if (intersects y negative || intersects x negative) then ignore - (CilE.warn_once "using 'round towards zero' semantics for '/', - which only became specified in C99."); *) - match y with - | Set sy -> div_set x sy - | Itv iy -> inject_itv_or_bottom (Int_interval.div (make_range x) iy) - | Float _ -> assert false +let div v1 v2 = + match v1, v2 with + | Int i1, Int i2 -> Int (Int_val.div i1 i2) + | Float _, _ | _, Float _ -> assert false (* [scale_rem ~pos:false f v] is an over-approximation of the set of elements [x mod f] for [x] in [v]. @@ -746,65 +445,23 @@ let scale_rem ~pos f v = if Int.is_zero f then bottom else match v with - | Itv i -> inject_itv_or_bottom (`Value (Int_interval.scale_rem ~pos f i)) - | Set s -> inject_set_or_top (Int_set.scale_rem ~pos f s) + | Int i -> Int (Int_val.scale_rem ~pos f i) | Float _ -> top -let c_rem x y = - match y with - | Float _ -> top - | Itv iy -> - if is_bottom x then bottom - else inject_itv_or_bottom (Int_interval.c_rem (make_range x) iy) - | Set yy -> - match x with - | Set xx -> inject_set_or_top (Int_set.c_rem xx yy) - | Float _ -> top - | Itv _ -> - let f acc y = - join (scale_rem ~pos:false y x) acc - in - Int_set.fold f bottom yy +let c_rem v1 v2 = + match v1, v2 with + | Float _, _ | _, Float _ -> top + | Int i1, Int i2 -> Int (Int_val.c_rem i1 i2) let create_all_values ~signed ~size = - let min, max = - if signed then - let b = Int.two_power_of_int (size - 1) in - Int.neg b, Int.pred b - else - let b = Int.two_power_of_int size in - Int.zero, Int.pred b - in - let min = Some min - and max = Some max in - if size <= !small_cardinal_log - then make ~min ~max ~rem:Int.zero ~modu:Int.one - else Itv (Int_interval.inject_range min max) + Int (Int_val.create_all_values ~signed ~size) let big_int_64 = Int.of_int 64 let big_int_32 = Int.thirtytwo -let cast_int_to_int ~size ~signed value = - if equal top value - then create_all_values ~size:(Int.to_int size) ~signed - else - let result = - match value with - | Itv i -> - inject_itv_or_bottom (`Value (Int_interval.cast ~size ~signed i)) - | Set s -> - let all = - create_all_values ~size:(Int.to_int size) ~signed - in - if is_included value all - then value - else - let rem_f value = Int.cast ~size ~signed ~value in - Set (Int_set.map rem_f s) - | Float _ -> assert false - in - (* If sharing is no longer preserved, please change Cvalue.V.cast *) - if equal result value then value else result +let cast_int_to_int ~size ~signed = function + | Int i -> Int (Int_val.cast_int_to_int ~size ~signed i) + | Float _ -> assert false let reinterpret_float_as_int ~signed ~size f = let reinterpret_list l = @@ -828,7 +485,7 @@ let reinterpret_float_as_int ~signed ~size f = let reinterpret_as_int ~size ~signed i = match i with - | Set _ | Itv _ -> + | Int _ -> (* On integers, cast and reinterpretation are the same operation *) cast_int_to_int ~signed ~size i | Float f -> reinterpret_float_as_int ~signed ~size f @@ -841,59 +498,29 @@ let cast_float_to_float fkind v = | Fval.Single -> inject_float (Fval.round_to_single_precision_float f) end - | Set _ when is_zero v -> zero - | Set _ | Itv _ -> top_float + | Int _ when is_zero v -> zero + | Int _ -> top_float (* TODO rename to mul_int *) let mul v1 v2 = - (* Format.printf "mul. Args: '%a' '%a'@\n" pretty v1 pretty v2; *) - let result = - if is_one v1 then v2 - else if is_zero v2 || is_zero v1 then zero - else if is_one v2 then v1 - else - match v1,v2 with - | Float _, _ | _, Float _ -> - top - | Set s1, Set s2 -> inject_set_or_top (Int_set.mul s1 s2) - | Itv i1, Itv i2 -> Itv (Int_interval.mul i1 i2) - | Set s, Itv i | Itv i, Set s -> - let l = Int_set.cardinal s in - if l = 0 - then bottom - else if l = 1 - then Itv (Int_interval.scale (Int_set.min s) i) - else Itv (Int_interval.mul i (make_itv_from_set s)) - in - (* Format.printf "mul. result : %a@\n" pretty result;*) - result + if is_one v1 then v2 + else if is_zero v2 || is_zero v1 then zero + else if is_one v2 then v1 + else + match v1, v2 with + | Float _, _ | _, Float _ -> top + | Int i1, Int i2 -> Int (Int_val.mul i1 i2) -(** Computes [x (op) ({y >= 0} * 2^n)], as an auxiliary function for - [shift_left] and [shift_right]. [op] and [scale] must verify - [scale a b == op (inject_singleton a) b] *) -let shift_aux scale op (x: t) (y: t) = - let y = narrow (inject_range (Some Int.zero) None) y in - try - match y with - | Set s -> - Int_set.fold (fun acc n -> join acc (scale (Int.two_power n) x)) bottom s - | _ -> - let min_factor = Extlib.opt_map Int.two_power (min_int y) in - let max_factor = Extlib.opt_map Int.two_power (max_int y) in - let modu = match min_factor with None -> Int.one | Some m -> m in - let factor = inject_top min_factor max_factor Int.zero modu in - op x factor - with Z.Overflow -> - Lattice_messages.emit_imprecision emitter "Ival.shift_aux"; - (* We only preserve the sign of the result *) - if is_included x positive_integers then positive_integers - else - if is_included x negative_integers then negative_integers - else top +let shift_right v1 v2 = + match v1, v2 with + | Int i1, Int i2 -> Int (Int_val.shift_right i1 i2) + | _, _ -> top -let shift_right x y = shift_aux (scale_div ~pos:true) div x y -let shift_left x y = shift_aux scale mul x y +let shift_left v1 v2 = + match v1, v2 with + | Int i1, Int i2 -> Int (Int_val.shift_left i1 i2) + | _, _ -> top let interp_boolean ~contains_zero ~contains_non_zero = @@ -968,47 +595,20 @@ let backward_mult_int_left ~right ~result = let backward_le_int max v = match v with | Float _ -> v - | Set _ | Itv _ -> narrow v (Itv (Int_interval.inject_range None max)) + | Int _ -> narrow v (Int (Int_val.inject_range None max)) let backward_ge_int min v = match v with | Float _ -> v - | Set _ | Itv _ -> narrow v (Itv (Int_interval.inject_range min None)) + | Int _ -> narrow v (Int (Int_val.inject_range min None)) let backward_lt_int max v = backward_le_int (Extlib.opt_map Int.pred max) v let backward_gt_int min v = backward_ge_int (Extlib.opt_map Int.succ min) v let diff_if_one value rem = - match rem with - | Set s when Int_set.cardinal s = 1 -> - begin - let v = Int_set.min s in - match value with - | Set s -> inject_set_or_bottom (Int_set.remove s v) - | Float _ -> value - | Itv i -> - let min, max, rem, modu = Int_interval.min_max_rem_modu i in - match min, max with - | Some mn, _ when Int.equal v mn -> - inject_top (Some (Int.add mn modu)) max rem modu - | _, Some mx when Int.equal v mx -> - inject_top min (Some (Int.sub mx modu)) rem modu - | Some mn, Some mx when - Int.equal (Int.sub mx mn) (Int.mul modu !small_cardinal_Int) - && Int_interval.mem v i -> - let r = ref mn in - let array = - Array.init !small_cardinal - (fun _ -> - let c = !r in - let corrected_c = if Int.equal c v then Int.add c modu else c in - r := Int.add corrected_c modu; - corrected_c) - in - Set (Int_set.inject_array array !small_cardinal) - | _, _ -> value - end - | _ -> value + match value, rem with + | Int i1, Int i2 -> Int (Int_val.diff_if_one i1 i2) + | _, _ -> value let diff value rem = log_imprecision "Ival.diff"; @@ -1018,7 +618,7 @@ let diff value rem = let fold_int_bounds f v acc = match v with | Float _ -> f v acc - | Set _ | Itv _ -> + | Int _ -> if cardinal_zero_or_one v then f v acc else (* apply [f] to [b] and reduce [v] if [b] is finite, @@ -1069,7 +669,7 @@ let backward_comp_float_left_false op fkind f1 f2 = let rec extract_bits ~start ~stop ~size v = match v with - | Set s -> Set (Int_set.map (Int.extract_bits ~start ~stop) s) + | Int i -> Int (Int_val.extract_bits ~start ~stop i) | Float f -> let inject (b, e) = inject_range (Some b) (Some e) in let itvs = @@ -1082,14 +682,6 @@ let rec extract_bits ~start ~stop ~size v = in let bits = List.map (extract_bits ~start ~stop ~size) itvs in List.fold_left join bottom bits - | Itv _ as d -> - try - let dived = scale_div ~pos:true (Int.two_power start) d in - scale_rem ~pos:true (Int.two_power (Int.length start stop)) dived - with Z.Overflow -> - Lattice_messages.emit_imprecision emitter "Ival.extract_bits"; - top -;; let all_values ~size v = if Int.lt big_int_64 size then false @@ -1099,23 +691,7 @@ let all_values ~size v = else match v with | Float _ -> false - | Itv i -> - begin - let min, max, _, modu = Int_interval.min_max_rem_modu i in - match min, max with - | None, _ | _, None -> Int.is_one modu - | Some mn, Some mx -> - Int.is_one modu && - Int.le - (Int.two_power size) - (Int.length mn mx) - end - | Set s -> - let siz = Int.to_int size in - Int_set.cardinal s >= 1 lsl siz && - equal - (cast_int_to_int ~size ~signed:false v) - (create_all_values ~size:siz ~signed:false) + | Int i -> Int_val.all_values ~size i let compare_min_max min max = match min, max with @@ -1154,10 +730,9 @@ let forward_comp_int op i1 i2 = | Eq -> forward_eq_int i1 i2 | Ne -> inv_truth (forward_eq_int i1 i2) -let rehash x = - match x with - | Set s -> Set (Int_set.rehash s) - | _ -> x +let rehash = function + | Int i -> Int (Int_val.rehash i) + | x -> x include ( Datatype.Make_with_collections @@ -1167,13 +742,9 @@ include ( let name = Int.name ^ " lattice_mod" open Structural_descr let structural_descr = - let s_int = Descr.str Int.descr in t_sum - [| - [| pack (t_array s_int) |]; - [| Fval.packed_descr |]; - [| Int_interval.packed_descr |] - |] + [| [| Int_val.packed_descr |]; + [| Fval.packed_descr |] |] let reprs = [ top ; bottom ] let equal = equal let compare = compare @@ -1342,8 +913,8 @@ let cast_int_to_float fkind v = let reinterpret_as_float kind i = match i with | Float _ -> i - | Set _ when is_zero i || is_bottom i -> i - | Itv _ | Set _ -> + | Int _ when is_zero i || is_bottom i -> i + | Int _ -> (* Reinterpret a range of integers as a range of floats. Float are ordered this way : if [min_i], [max_i] are the bounds of the signed integer type that @@ -1411,373 +982,27 @@ let reinterpret_as_float kind i = top_float let overlaps ~partial ~size t1 t2 = - let diff = sub_int t1 t2 in - match diff with - | Set array -> - not (Int_set.for_all - (fun i -> Int.ge (Int.abs i) size || (partial && Int.is_zero i)) - array) - | Itv i -> - let min, max = Int_interval.min_and_max i in - let pred_size = Int.pred size in - min_le_elt min pred_size && max_ge_elt max (Int.neg pred_size) - | Float _ -> assert false - - - -(* ------------------------------------------------------------------------ *) -(* --- Bitwise operators --- *) -(* ------------------------------------------------------------------------ *) - -(* --- Bit lattice --- *) - -type bit_value = On | Off | Both - -module Bit = -struct - type t = bit_value - - let to_string = function - | Off -> "0" - | On -> "1" - | Both -> "T" - - let _pretty (fmt : Format.formatter) (b :t) = - Format.pp_print_string fmt (to_string b) - - let union (b1 : t) (b2 : t) : t = - if b1 = b2 then b1 else Both - - let not : t -> t = function - | On -> Off - | Off -> On - | Both -> Both -end - - -(* --- Bit operators --- *) - -module type BitOperator = -sig - (* Printable version of the operator *) - val representation : string - (* forward is given here as the lifted function of some bit operator op - where op - 1. is assumed to be commutative (backward functions do not assume the - position of the arguments) - 2. must ensure 0 op 0 = 0 as otherwise applying op on a sign bit may - produce a negative result from two positive operands; but we don't - want to produce a negative result when the operation is unsigned which - we don't know unless one of the operands is negative; - 3. is not constant, otherwise nothing of all of this makes sense. - forward is defined as - forward b1 b2 = { x1 op x2 | x1 \in b1, x2 \in b2 } *) - val forward : bit_value -> bit_value -> bit_value - (* backward_off b = { x | \exist y \in b . x op y = y op x = 1 } *) - val backward_off : bit_value -> bit_value - (* backward_on b = { x | \exist y \in b . x op y = y op x = 0 } *) - val backward_on : bit_value -> bit_value -end - -module And : BitOperator = -struct - let representation = "&" - - let forward v1 v2 = - match v1 with - | Off -> Off - | On -> v2 - | Both -> if v2 = Off then Off else Both - - let backward_off = function - | (Off | Both) -> Both - | On -> Off - - let backward_on = function - | Off -> assert false - | (On | Both) -> On -end - -module Or : BitOperator = -struct - let representation = "|" - - let forward v1 v2 = - match v1 with - | On -> On - | Off -> v2 - | Both -> if v2 = On then On else Both + match t1, t2 with + | Int i1, Int i2 -> Int_val.overlaps ~partial ~size i1 i2 + | _, _ -> assert false - let backward_off = function - | On -> assert false - | (Off | Both) -> Off - - let backward_on = function - | (On | Both) -> Both - | Off -> On -end - -module Xor : BitOperator = -struct - let representation = "^" - - let forward v1 v2 = - match v1 with - | Both -> Both - | Off -> v2 - | On -> Bit.not v2 - - let backward_on v = Bit.not v - - let backward_off v = v -end - - -(* --- Bit extraction and mutation --- *) - -let significant_bits (v : t) : int option = - match min_and_max v with - | None, _ | _, None -> None - | Some l, Some u -> Some (max (Z.numbits l) (Z.numbits u)) - -let extract_sign (v : t) : bit_value = - match min_and_max v with - | _, Some u when Int.(lt u zero) -> On - | Some l, _ when Int.(ge l zero) -> Off - | _, _ -> Both - -let extract_bit (i : int) (v : t) : bit_value = - let bit_value x = if Z.testbit x i then On else Off in - match v with - | Float _ -> Both - | Set s -> Int_set.map_reduce bit_value Bit.union s - | Itv itv -> - match Int_interval.min_and_max itv with - | None, _ | _, None -> Both - | Some l, Some u -> - (* It does not take modulo into account *) - if Int.(ge (sub u l) (two_power_of_int i)) (* u - l >= mask *) - then Both - else Bit.union (bit_value l) (bit_value u) - -let reduce_sign (v : t) (b : bit_value) : t = - match b with - | Both -> v - | On -> - begin match v with - | Float _ -> v - | Set s -> inject_set_or_bottom (Int_set.filter Int.(gt zero) s) - | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_sign itv true) - end - | Off -> - begin match v with - | Float _ -> v - | Set s -> inject_set_or_bottom (Int_set.filter Int.(le zero) s) - | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_sign itv false) - end - -let reduce_bit (i : int) (v : t) (b : bit_value) : t = - let bit_value x = if Z.testbit x i then On else Off in - if b = Both - then v - else match v with - | Float _ -> v - | Set s -> inject_set_or_bottom (Int_set.filter (fun x -> bit_value x = b) s) - | Itv itv -> inject_itv_or_bottom (Int_interval.reduce_bit i itv (b = On)) - -type bit = Sign | Bit of int - -let extract_bit = function - | Sign -> extract_sign - | Bit i -> extract_bit i - -let set_bit_on ~size bit = - let mask = match bit with - | Sign -> Int.(neg (two_power_of_int size)) - | Bit i -> Int.(two_power_of_int i) - in - fun v -> Int.logor mask v - -let reduce_bit = function - | Sign -> reduce_sign - | Bit i -> reduce_bit i - -(* --- Bitwise binary operators --- *) - -module BitwiseOperator (Op : BitOperator) = -struct - - let backward (b : bit_value) = function - | On -> Op.backward_on b - | Off -> Op.backward_off b - | Both -> assert false - - (** Bit masks are composed of an array of significant bit values where index 0 - represents the lowest bit, and a single bit_value to represent the - possible leading bits. *) - type bit_mask = bit_value array * bit_value - - (* Converts an integer [x] into a bit array of size [n]. *) - let int_to_bit_array n (x : Int.t) = - let make i = if Z.testbit x i then On else Off in - Array.init n make - - (* Computes a bit_mask for the lowest bits of an ival, using the modulo - information for non singleton values. *) - let low_bit_mask : t -> bit_mask = function - | Set s when Int_set.cardinal s = 0 -> raise Error_Bottom - | Set s when Int_set.cardinal s = 1 -> (* singleton : build a full mask *) - let x = Int_set.min s in - let n = Z.numbits x in - int_to_bit_array n x, if Int.(ge x zero) then Off else On - | v -> - let _,_,r,modu = min_max_r_mod v in (* requires cardinal > 1 *) - (* Find how much [modu] can be divided by two. *) - let n = Z.trailing_zeros modu in - int_to_bit_array n r, Both - - (* Computes a remainder and modulo for the result of [v1 op v2]. *) - let compute_modulo v1 v2 = - let b1, s1 = low_bit_mask v1 - and b2, s2 = low_bit_mask v2 in - let size = max (Array.length b1) (Array.length b2) in - (* Sets the [i] nth bits of [rem] until an uncertainty appears. *) - let rec step i rem = - let b1 = try b1.(i) with _ -> s1 - and b2 = try b2.(i) with _ -> s2 in - let b = Op.forward b1 b2 in - if i >= size || b = Both - then rem, Int.two_power_of_int i - else - (* [rem] starts at 0, so we only need to turn on the 1 bits. *) - let rem = if b = On then set_bit_on ~size (Bit i) rem else rem in - step (i+1) rem - in - step 0 Int.zero - - (* The number of bits on which the result should be significant *) - let result_size (v1 : t) (v2 : t) : int option = - let n1 = significant_bits v1 and n2 = significant_bits v2 in - let n1_greater = - match n1, n2 with - | None, _ -> true - | _, None -> false - | Some n1, Some n2 -> n1 >= n2 - in - (* whether n1 or n2 is greater, look if the sign bit oped with anything is - not constant. If it is constant, then the highest bits are irrelevant. *) - if n1_greater - then if Op.forward Both (extract_sign v2) = Both then n1 else n2 - else if Op.forward (extract_sign v1) Both = Both then n2 else n1 - - exception Do_not_fit_small_sets - - (* Try to build a small set. - It is basically enumerating the possible results, by choosing the possible - bits from left to right. This function aborts if it ever exceeds the small - set size. The algorithm is probably not complete, as it is not always - possible to reduce the operands leading to a result (without an - exponential cost) meaning that sometimes small sets can be obtained but - the algorithm will fail to find them. *) - let compute_small_set ~size (v1 : t) (v2 : t) (r : Int.t) (modu : Int.t) = - let set_bit i acc (r, v1, v2) = - let b1 = extract_bit i v1 - and b2 = extract_bit i v2 in - match Op.forward b1 b2 with - | On -> (set_bit_on ~size i r, v1, v2) :: acc - | Off -> (r, v1, v2) :: acc - | Both -> - let v1_off = reduce_bit i v1 (Op.backward_off b2) - and v2_off = reduce_bit i v2 (Op.backward_off b1) in - let v1_on = reduce_bit i v1 (Op.backward_on b2) - and v2_on = reduce_bit i v2 (Op.backward_on b1) in - (set_bit_on ~size i r, v1_on, v2_on) :: (r, v1_off, v2_off) :: acc - in - let acc = ref (set_bit Sign [] (r, v1, v2)) in - for i = size - 1 downto Z.numbits modu - 1 do - acc := List.fold_left (set_bit (Bit i)) [] !acc; - if List.length !acc > !small_cardinal then raise Do_not_fit_small_sets - done; - let o = List.fold_left (fun o (r,_,_) -> O.add r o) O.empty !acc in - let cardinal = O.cardinal o in - if cardinal = 0 then bottom else - let a = Array.make cardinal Int.zero in - let i = ref 0 in - O.iter (fun e -> a.(!i) <- e; incr i) o; - Set (Int_set.inject_array a cardinal) - - (* If lower is true (resp. false), compute the lower (resp. upper) bound of - the result interval when applying the bitwise operator to [v1] and [v2]. - [size] is the number of bits of the result. - This function should be exact when the operands are small sets or tops - with modulo 1. Otherwise, it is an overapproximation of the bound. *) - let compute_bound ~size v1 v2 lower = - (* Sets the [i]-nth bit of the currently computed bound [r] of [v1 op v2]. - If possible, reduces [v1] and [v2] accordingly. *) - let set_bit i (r, v1, v2) = - let b1 = extract_bit i v1 - and b2 = extract_bit i v2 in - let b, v1, v2 = - match Op.forward b1 b2 with - | On | Off as b -> b, v1, v2 (* Constant bit, no reduction. *) - | Both -> - (* Choose the best bit for the searched bound, and reduces [v1] and - [v2] accordingly. *) - let b = match i with - | Sign -> if lower then On else Off - | Bit _ -> if lower then Off else On - in - let v1 = reduce_bit i v1 (backward b2 b) - and v2 = reduce_bit i v2 (backward b1 b) in - b, v1, v2 - in - (* Only sets 1 bit, as [r] is 0 at the beginning. *) - let r = if b = On then set_bit_on ~size i r else r in - r, v1, v2 - in - (* The result is 0 at the beginning, and [set_bit] turns on the 1 bits. *) - let r = ref (Int.zero, v1, v2) in - (* Sets the sign bit, and then the bits from size to 0. *) - r := set_bit Sign !r; - for i = (size - 1) downto 0 do - r := set_bit (Bit i) !r; - done; - let bound, _v1, _v2 = !r in - bound - - let bitwise_forward (v1 : t) (v2 : t) : t = - try - let r, modu = compute_modulo v1 v2 in - match result_size v1 v2 with - | None -> - (* We could do better here, as one of the bound may be finite. However, - this case should occur rarely or not at all. *) - inject_interval None None r modu - | Some size -> - try compute_small_set ~size v1 v2 r modu - with Do_not_fit_small_sets -> - let min = compute_bound ~size v1 v2 true - and max = compute_bound ~size v1 v2 false in - inject_interval (Some min) (Some max) r modu - with Error_Bottom -> bottom -end - -let bitwise_or = let module M = BitwiseOperator (Or) in M.bitwise_forward -let bitwise_and = let module M = BitwiseOperator (And) in M.bitwise_forward -let bitwise_xor = let module M = BitwiseOperator (Xor) in M.bitwise_forward +let bitwise_int f_int v1 v2 = + match v1, v2 with + | Int i1, Int i2 -> Int (f_int i1 i2) + | _, _ -> top -(* --- Bitwise not --- *) +let bitwise_or = bitwise_int Int_val.bitwise_or +let bitwise_and = bitwise_int Int_val.bitwise_and +let bitwise_xor = bitwise_int Int_val.bitwise_xor -let bitwise_signed_not v = - match v with +let bitwise_signed_not = function | Float _ -> assert false - | Itv _ -> add_int (neg_int v) minus_one (* [-v - 1] *) - | Set s -> Set (Int_set.bitwise_signed_not s) + | Int i -> Int (Int_val.bitwise_signed_not i) -let bitwise_unsigned_not ~size v = - let size = Int.of_int size in - cast_int_to_int ~size ~signed:false (bitwise_signed_not v) +let bitwise_unsigned_not ~size = function + | Float _ -> assert false + | Int i -> Int (Int_val.bitwise_unsigned_not ~size i) let bitwise_not ~size ~signed v = if signed then