(**************************************************************************) (* *) (* This file is part of WP plug-in of Frama-C. *) (* *) (* Copyright (C) 2007-2021 *) (* CEA (Commissariat a l'energie atomique et aux energies *) (* 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). *) (* *) (**************************************************************************) (* -------------------------------------------------------------------------- *) (* --- Integer Arithmetics Model --- *) (* -------------------------------------------------------------------------- *) open Qed open Qed.Logic open Lang open Lang.F module FunMap = Map.Make(Lang.Fun) (* -------------------------------------------------------------------------- *) (* --- Kernel Interface --- *) (* -------------------------------------------------------------------------- *) let is_overflow_an_error iota = if Ctypes.signed iota then Kernel.SignedOverflow.get () else Kernel.UnsignedOverflow.get () let is_downcast_an_error iota = if Ctypes.signed iota then Kernel.SignedDowncast.get () else Kernel.UnsignedDowncast.get () (* -------------------------------------------------------------------------- *) (* --- Library Cint --- *) (* -------------------------------------------------------------------------- *) let is_cint_map = ref FunMap.empty let to_cint_map = ref FunMap.empty let is_cint f = FunMap.find f !is_cint_map let to_cint f = FunMap.find f !to_cint_map let library = "cint" let make_fun_int op i = Lang.extern_f ~library ~result:Logic.Int "%s_%a" op Ctypes.pp_int i let make_pred_int op i = Lang.extern_f ~library ~result:Logic.Prop ~coloring:true "%s_%a" op Ctypes.pp_int i (* let fun_int op = Ctypes.imemo (make_fun_int op) *) (* unused for now *) (* let pred_int op = Ctypes.imemo (make_pred_int op) *) (* unused for now *) (* Signature int,int -> int over Z *) let ac = { associative = true ; commutative = true ; idempotent = false ; invertible = false ; neutral = E_none ; absorbant = E_none ; } (* Functions -> Z *) let result = Logic.Int (* -------------------------------------------------------------------------- *) (* --- Library Cbits --- *) (* -------------------------------------------------------------------------- *) let library = "cbits" let balance = Lang.Left let op_lxor = { ac with neutral = E_int 0 ; invertible = true } let op_lor = { ac with neutral = E_int 0 ; absorbant = E_int (-1); idempotent = true } let op_land = { ac with neutral = E_int (-1); absorbant = E_int 0 ; idempotent = true } let f_lnot = Lang.extern_f ~library ~result "lnot" let f_lor = Lang.extern_f ~library ~result ~category:(Operator op_lor) ~balance "lor" let f_land = Lang.extern_f ~library ~result ~category:(Operator op_land) ~balance "land" let f_lxor = Lang.extern_f ~library ~result ~category:(Operator op_lxor) ~balance "lxor" let f_lsl = Lang.extern_f ~library ~result "lsl" let f_lsr = Lang.extern_f ~library ~result "lsr" let f_bitwised = [ f_lnot ; f_lor ; f_land ; f_lxor ; f_lsl ; f_lsr ] (* [f_bit_stdlib] is related to the function [bit_test] of Frama-C StdLib *) let f_bit_stdlib = Lang.extern_p ~library ~bool:"bit_testb" ~prop:"bit_test" () (* [f_bit_positive] is actually exported in forgoting the fact the position is positive *) let f_bit_positive = Lang.extern_p ~library ~bool:"bit_testb" ~prop:"bit_test" () (* At export, some constructs such as [e & (1 << k)] are written into [f_bit_export] construct *) let f_bit_export = Lang.extern_p ~library ~bool:"bit_testb" ~prop:"bit_test" () let () = let open LogicBuiltins in add_builtin "\\bit_test_stdlib" [Z;Z] f_bit_stdlib let () = let open LogicBuiltins in add_builtin "\\bit_test" [Z;Z] f_bit_positive let f_bits = [ f_bit_stdlib ; f_bit_positive ; f_bit_export ] let bit_test e k = let r = F.e_fun ~result:Logic.Bool (if k <= 0 then f_bit_positive else f_bit_stdlib) [e ; e_int k] in assert (is_prop r) ; r (* -------------------------------------------------------------------------- *) (* --- Matching utilities for simplifications --- *) (* -------------------------------------------------------------------------- *) let is_leq a b = F.is_true (F.e_leq a b) let match_integer t = match F.repr t with | Logic.Kint c -> c | _ -> raise Not_found let match_to_cint t = match F.repr t with | Logic.Fun( conv , [a] ) -> (to_cint conv), a | _ -> raise Not_found let match_mod t = match F.repr t with | Logic.Mod (e1, e2) -> e1, e2 | _ -> raise Not_found (* integration with qed should be improved! *) let is_positive t = match F.repr t with | Logic.Kint c -> Integer.le Integer.one c | _ -> false (* integration with qed should be improved! *) let rec is_positive_or_null e = match F.repr e with | Logic.Fun( f , [e] ) when Fun.equal f f_lnot -> is_negative e | Logic.Fun( f , es ) when Fun.equal f f_land -> List.exists is_positive_or_null es | Logic.Fun( f , es ) when Fun.equal f f_lor -> List.for_all is_positive_or_null es | Logic.Fun( f , es ) when Fun.equal f f_lxor -> (match mul_xor_sign es with | Some b -> b | _ -> false) | Logic.Fun( f , es ) when Fun.equal f f_lsr || Fun.equal f f_lsl -> List.for_all is_positive_or_null es | _ -> (* try some improvement first then ask to qed *) let improved_is_positive_or_null e = match F.repr e with | Logic.Add es -> List.for_all is_positive_or_null es | Logic.Mul es -> (match mul_xor_sign es with | Some b -> b | _ -> false) | Logic.Mod(e1,e2) when is_positive e2 || is_negative e2 -> (* e2<>0 ==> ( 0<=(e1 % e2) <=> 0<=e1 ) *) is_positive_or_null e1 | _ -> false in if improved_is_positive_or_null e then true else match F.is_true (F.e_leq e_zero e) with | Logic.Yes -> true | Logic.No | Logic.Maybe -> false and is_negative e = match F.repr e with | Logic.Fun( f , [e] ) when Fun.equal f f_lnot -> is_positive_or_null e | Logic.Fun( f , es ) when Fun.equal f f_lor -> List.exists is_negative es | Logic.Fun( f , es ) when Fun.equal f f_land -> List.for_all is_negative es | Logic.Fun( f , es ) when Fun.equal f f_lxor -> (match mul_xor_sign es with | Some b -> (not b) | _ -> false) | Logic.Fun( f , [k;n] ) when Fun.equal f f_lsr || Fun.equal f f_lsl -> is_positive_or_null n && is_negative k | _ -> (* try some improvement first then ask to qed *) let improved_is_negative e = match F.repr e with | Logic.Add es -> List.for_all is_negative es | Logic.Mul es -> (match mul_xor_sign es with | Some b -> (not b) | _ -> false) | _ -> false in if improved_is_negative e then true else match F.is_true (F.e_lt e e_zero) with | Logic.Yes -> true | Logic.No | Logic.Maybe -> false and mul_xor_sign es = try Some (List.fold_left (fun acc e -> if is_positive_or_null e then acc (* as previous *) else if is_negative e then (not acc) (* opposite sign *) else raise Not_found) true es) with Not_found -> None let match_positive_or_null e = if not (is_positive_or_null e) then raise Not_found; e let match_power2, match_power2_minus1 = let highest_bit_number = let hsb p = if p land 2 = 0 then 0 else 1 in let hsb p = let n = p lsr 2 in if n = 0 then hsb p else 2 + hsb n in let hsb p = let n = p lsr 4 in if n = 0 then hsb p else 4 + hsb n in let hsb = Array.init 256 hsb in let hsb p = let n = p lsr 8 in if n = 0 then hsb.(p) else 8 + hsb.(n) in let hsb p = let n = Integer.shift_right p Integer.sixteen in Integer.of_int (if Integer.is_zero n then hsb (Integer.to_int_exn p) else 16 + hsb (Integer.to_int_exn n)) in let rec hsb_aux p = let n = Integer.shift_right p Integer.thirtytwo in if Integer.is_zero n then hsb p else Integer.add Integer.thirtytwo (hsb_aux n) in hsb_aux in let is_power2 k = (* exists n such that k == 2**n? *) (Integer.gt k Integer.zero) && (Integer.equal k (Integer.logand k (Integer.neg k))) in let rec match_power2 e = match F.repr e with | Logic.Kint z when is_power2 z -> e_zint (highest_bit_number z) | Logic.Fun( f , [n;k] ) when Fun.equal f f_lsl && is_positive_or_null k -> e_add k (match_power2 n) | _ -> raise Not_found in let match_power2_minus1 e = match F.repr e with | Logic.Kint z when is_power2 (Integer.succ z) -> e_zint (highest_bit_number (Integer.succ z)) | _ -> match_power2 (e_add e_one e) in match_power2, match_power2_minus1 let match_fun op t = match F.repr t with | Logic.Fun( f , es ) when Fun.equal f op -> es | _ -> raise Not_found let match_ufun uop t = match F.repr t with | Logic.Fun( f , e::[] ) when Fun.equal f uop -> e | _ -> raise Not_found let match_positive_or_null_integer t = match F.repr t with | Logic.Kint c when Integer.le Integer.zero c -> c | _ -> raise Not_found let match_binop_arg1 match_f = function (* for binop *) | [e1;e2] -> (match_f e1),e2 | _ -> raise Not_found let match_binop_arg2 match_f = function (* for binop *) | [e1;e2] -> e1,(match_f e2) | _ -> raise Not_found let match_list_head match_f = function | [] -> raise Not_found | e::es -> (match_f e), es let match_binop_one_arg1 binop e = match F.repr e with | Logic.Fun( f , [one; e2] ) when Fun.equal f binop && one == e_one -> e2 | _ -> raise Not_found let match_list_extraction match_f = let match_f_opt n = try Some (match_f n) with Not_found -> None in let rec aux rs = function | [] -> raise Not_found | e::es -> match match_f_opt e with | Some k -> k, e, List.rev_append rs es | None -> aux (e::rs) es in aux [] let match_integer_arg1 = match_binop_arg1 match_integer let match_positive_or_null_arg2 = match_binop_arg2 match_positive_or_null let match_positive_or_null_integer_arg2 = match_binop_arg2 match_positive_or_null_integer let match_integer_extraction = match_list_head match_integer let match_power2_extraction = match_list_extraction match_power2 let match_power2_minus1_extraction = match_list_extraction match_power2_minus1 let match_binop_one_extraction binop = match_list_extraction (match_binop_one_arg1 binop) (* -------------------------------------------------------------------------- *) (* --- Conversion Symbols --- *) (* -------------------------------------------------------------------------- *) (* rule A: to_a(to_b x) = to_b x when domain(b) is all included in domain(a) *) (* rule B: to_a(to_b x) = to_a x when range(b) is a multiple of range(a) AND a is not bool *) (* to_iota(e) where e = to_iota'(e'), only ranges for iota *) let simplify_range_comp f iota e conv e' = let iota' = to_cint conv in let size' = Ctypes.i_bits iota' in let size = Ctypes.i_bits iota in if size <= size' then e_fun f [e'] (* rule B: iota' is multiple of iota -> keep iota(e') *) else if ((Ctypes.signed iota) || not (Ctypes.signed iota')) then e (* rule A: have iota > iota' check sign to apply rule. unsigned iota -> iota' must be unsigned signed iota -> iota' can have any sign *) else raise Not_found let simplify_f_to_bounds iota e = (* min(ctypes)<=y<=max(ctypes) ==> to_ctypes(y)=y *) let lower,upper = Ctypes.bounds iota in if (F.decide (F.e_leq e (e_zint upper))) && (F.decide (F.e_leq (e_zint lower) e)) then e else raise Not_found let f_to_int = Ctypes.i_memo (fun iota -> make_fun_int "to" iota) let configure_to_int iota = let simplify_range f iota e = begin try match F.repr e with | Logic.Kint value -> let size = Integer.of_int (Ctypes.i_bits iota) in let signed = Ctypes.signed iota in F.e_zint (Integer.cast ~size ~signed ~value) | Logic.Fun( fland , es ) when Fun.equal fland f_land && not (Ctypes.signed iota) && List.exists is_positive_or_null es -> (* to_uintN(a) == a & (2^N-1) when a >= 0 *) let m = F.e_zint (snd (Ctypes.bounds iota)) in F.e_fun f_land (m :: es) | Logic.Fun( flor , es ) when (Fun.equal flor f_lor) && not (Ctypes.signed iota) -> (* to_uintN(a|b) == (to_uintN(a) | to_uintN(b)) *) F.e_fun f_lor (List.map (fun e' -> e_fun f [e']) es) | Logic.Fun( flnot , [ e ] ) when (Fun.equal flnot f_lnot) && not (Ctypes.signed iota) -> begin match F.repr e with | Logic.Fun( f' , w ) when f' == f -> e_fun f [ e_fun f_lnot w ] | _ -> raise Not_found end | Logic.Fun( conv , [e'] ) -> (* unary op *) simplify_range_comp f iota e conv e' | _ -> raise Not_found with Not_found -> simplify_f_to_bounds iota e end in let simplify_conv f iota e = if iota = Ctypes.CBool then match F.is_equal e F.e_zero with | Yes -> F.e_zero | No -> F.e_one | Maybe -> raise Not_found else simplify_range f iota e in let simplify_leq f iota x y = let lower,upper = Ctypes.bounds iota in match F.repr y with | Logic.Fun( conv , [_] ) when (Fun.equal conv f) && (F.decide (F.e_leq x (e_zint lower))) -> (* x<=min(ctypes) ==> x<=to_ctypes(y) *) e_true | _ -> begin match F.repr x with | Logic.Fun( conv , [_] ) when (Fun.equal conv f) && (F.decide (F.e_leq (e_zint upper) y)) -> (* max(ctypes)<=y ==> to_ctypes(y)<=y *) e_true | _ -> raise Not_found end in let f = f_to_int iota in F.set_builtin_1 f (simplify_conv f iota) ; F.set_builtin_leq f (simplify_leq f iota) ; to_cint_map := FunMap.add f iota !to_cint_map let simplify_p_is_bounds iota e = let bounds = Ctypes.bounds iota in (* min(ctypes)<=y<=max(ctypes) <==> is_ctypes(y) *) match F.is_true (F.e_and [F.e_leq (e_zint (fst bounds)) e; F.e_leq e (e_zint (snd bounds))]) with | Logic.Yes -> e_true | Logic.No -> e_false | _ -> raise Not_found (* is_<cint> : int -> prop *) let p_is_int = Ctypes.i_memo (fun iota -> make_pred_int "is" iota) let configure_is_int iota = let f = p_is_int iota in let simplify = function | [e] -> begin match F.repr e with | Logic.Kint k -> let vmin,vmax = Ctypes.bounds iota in F.e_bool (Z.leq vmin k && Z.leq k vmax) | Logic.Fun( flor , es ) when (Fun.equal flor f_lor) && not (Ctypes.signed iota) -> (* is_uintN(a|b) == is_uintN(a) && is_uintN(b) *) F.e_and (List.map (fun e' -> e_fun f [e']) es) | _ -> simplify_p_is_bounds iota e end | _ -> raise Not_found in F.set_builtin f simplify; is_cint_map := FunMap.add f iota !is_cint_map let convert i a = e_fun (f_to_int i) [a] (* -------------------------------------------------------------------------- *) type model = | Natural (** Integer arithmetics with no upper-bound *) | Machine (** Integer/Module wrt Kernel options on RTE *) let () = Context.register begin fun () -> Ctypes.i_iter configure_to_int; Ctypes.i_iter configure_is_int; end let model = Context.create "Cint.model" let current () = Context.get model let configure m = let orig_model = Context.push model m in (fun () -> Context.pop model orig_model) let to_integer a = a let of_integer i a = convert i a let of_real i a = convert i (Cmath.int_of_real a) let range i a = match Context.get model with | Natural -> if Ctypes.signed i then F.p_true else F.p_leq F.e_zero a | Machine -> p_call (p_is_int i) [a] let ensures warn i a = if warn i then a else e_fun (f_to_int i) [a] let downcast = ensures is_downcast_an_error let overflow = ensures is_overflow_an_error (* -------------------------------------------------------------------------- *) (* --- Arithmetics --- *) (* -------------------------------------------------------------------------- *) let binop f i x y = overflow i (f x y) let unop f i x = overflow i (f x) (* C Code Semantics *) let iopp = unop e_opp let iadd = binop e_add let isub = binop e_sub let imul = binop e_mul let idiv = binop e_div let imod = binop e_mod (* -------------------------------------------------------------------------- *) (* --- Bits --- *) (* -------------------------------------------------------------------------- *) (* smp functions raise Not_found when simplification isn't possible *) let smp1 zf = (* f(c1) ~> zf(c1) *) function | [e] -> begin match F.repr e with | Logic.Kint c1 -> e_zint (zf c1) | _ -> raise Not_found end | _ -> raise Not_found let smp2 f zf = (* f(c1,c2) ~> zf(c1,c2), f(c1,c2,...) ~> f(zf(c1,c2),...) *) function | e1::e2::others -> begin match (F.repr e1), (F.repr e2) with (* integers should be at the beginning of the list *) | Logic.Kint c1, Logic.Kint c2 -> let z12 = ref (zf c1 c2) in let rec smp2 = function (* look at the other integers *) | [] -> [] | (e::r) as l -> begin match F.repr e with | Logic.Kint c -> z12 := zf !z12 c; smp2 r | _ -> l end in let others = smp2 others in let c12 = e_zint !z12 in if others = [] || F.is_absorbant f c12 then c12 else if F.is_neutral f c12 then e_fun f others else e_fun f (c12::others) | _ -> raise Not_found end | _ -> raise Not_found let bitk_positive k e = F.e_fun ~result:Logic.Bool f_bit_positive [e;k] let smp_mk_bit_stdlib = function | [ a ; k ] when is_positive_or_null k -> (* No need to expand the logic definition of the ACSL stdlib symbol when [k] is positive (the definition must comply with that simplification). *) bitk_positive k a | [ a ; k ] -> (* TODO: expand the current logic definition of the ACSL stdlib symbol *) F.e_neq F.e_zero (F.e_fun f_land [a; (F.e_fun f_lsl [F.e_one;k])]) | _ -> raise Not_found let smp_bitk_positive = function | [ a ; k ] -> (* requires k>=0 *) begin try e_eq (match_power2 a) k with Not_found -> match F.repr a with | Logic.Kint za -> let zk = match_integer k (* simplifies constants *) in if Integer.is_zero (Integer.logand za (Integer.shift_left Integer.one zk)) then e_false else e_true | Logic.Fun( f , [e;n] ) when Fun.equal f f_lsr && is_positive_or_null n -> bitk_positive (e_add k n) e | Logic.Fun( f , [e;n] ) when Fun.equal f f_lsl && is_positive_or_null n -> begin match is_leq n k with | Logic.Yes -> bitk_positive (e_sub k n) e | Logic.No -> e_false | Logic.Maybe -> raise Not_found end | Logic.Fun( f , es ) when Fun.equal f f_land -> F.e_and (List.map (bitk_positive k) es) | Logic.Fun( f , es ) when Fun.equal f f_lor -> F.e_or (List.map (bitk_positive k) es) | Logic.Fun( f , [a;b] ) when Fun.equal f f_lxor -> F.e_neq (bitk_positive k a) (bitk_positive k b) | Logic.Fun( f , [a] ) when Fun.equal f f_lnot -> F.e_not (bitk_positive k a) | Logic.Fun( conv , [a] ) (* when is_to_c_int conv *) -> let iota = to_cint conv in let range = Ctypes.i_bits iota in let signed = Ctypes.signed iota in if signed then (* beware of sign-bit *) begin match is_leq k (e_int (range-2)) with | Logic.Yes -> bitk_positive k a | Logic.No | Logic.Maybe -> raise Not_found end else begin match is_leq (e_int range) k with | Logic.Yes -> e_false | Logic.No -> bitk_positive k a | Logic.Maybe -> raise Not_found end | _ -> raise Not_found end | _ -> raise Not_found let introduction_bit_test_positive es b = (* introduces bit_test(n,k) only when k>=0 *) let k,_,es = match_power2_extraction es in let es' = List.map (bitk_positive k) es in if b == e_zero then e_not (e_and es') else try let k' = match_power2 b in e_and ( e_eq k k' :: es' ) with Not_found -> let bs = match_fun f_land b in let k',_,bs = match_power2_extraction bs in let bs' = List.map (bitk_positive k') bs in match F.is_true (F.e_eq k k') with | Logic.Yes -> e_eq (e_and es') (e_and bs') | Logic.No -> e_and [e_not (e_and es'); e_not (e_and bs')] | Logic.Maybe -> raise Not_found let smp_land es = let introduction_bit_test_positive_from_land es = if true then raise Not_found; (* [PB] true: until alt-ergo 0.95.2 trouble *) let k,e,es = match_power2_extraction es in let t = match es with | x::[] -> x | _ -> e_fun f_land es in e_if (bitk_positive k t) e e_zero in try let r = smp2 f_land Integer.logand es in try match F.repr r with | Logic.Fun( f , es ) when Fun.equal f f_land -> introduction_bit_test_positive_from_land es | _ -> r with Not_found -> r with Not_found -> introduction_bit_test_positive_from_land es let smp_shift zf = (* f(e1,0)~>e1, c2>0==>f(c1,c2)~>zf(c1,c2), c2>0==>f(0,c2)~>0 *) function | [e1;e2] -> begin match (F.repr e1), (F.repr e2) with | _, Logic.Kint c2 when Z.equal c2 Z.zero -> e1 | Logic.Kint c1, Logic.Kint c2 when Z.leq Z.zero c2 -> (* undefined when c2 is negative *) e_zint (zf c1 c2) | Logic.Kint c1, _ when Z.equal c1 Z.zero && is_positive_or_null e2 -> (* undefined when c2 is negative *) e1 | _ -> raise Not_found end | _ -> raise Not_found let smp_lnot = function | ([e] as args) -> begin match F.repr e with | Logic.Fun( f , [e] ) when Fun.equal f f_lnot -> (* ~~e ~> e *) e | _ -> smp1 Integer.lognot args end | _ -> raise Not_found (* -------------------------------------------------------------------------- *) (* --- Comparision with L-AND / L-OR / L-NOT --- *) (* -------------------------------------------------------------------------- *) let smp_leq_improved f a b = ignore (match_fun f b) ; (* It must be an improved of [is_positive_or_null f(args)] *) (* a <= 0 && 0 <= f(args) *) if F.decide (F.e_leq a F.e_zero) && is_positive_or_null b then e_true else raise Not_found let smp_leq_with_land a b = try let es = match_fun f_land a in let a1,_ = match_list_head match_positive_or_null_integer es in if F.decide (F.e_leq (e_zint a1) b) then e_true else raise Not_found with Not_found -> (* a <= 0 && 0 <= (x&y) ==> a <= (x & y) *) smp_leq_improved f_land a b let smp_eq_with_land a b = let es = match_fun f_land a in try try let b1 = match_integer b in try (* (b1&~a2)!=0 ==> (b1==(a2&e) <=> false) *) let a2,_ = match_integer_extraction es in if Integer.is_zero (Integer.logand b1 (Integer.lognot a2)) then raise Not_found ; e_false with Not_found when b == e_minus_one -> (* -1==(a1&a2) <=> (-1==a1 && -1==a2) *) F.e_and (List.map (e_eq b) es) with Not_found -> introduction_bit_test_positive es b with Not_found -> try (* k>=0 & b1>=0 ==> (b1 & ((1 << k) -1) == b1 % (1 << k) <==> true) *) let b1,b2 = match_mod b in let k = match_power2 b2 in (* note: a positive or null k is required by match_power2, match_power2_minus1 *) let k',_,es = match_power2_minus1_extraction es in if not ((is_positive_or_null b1) && (F.decide (F.e_eq k k')) && (F.decide (F.e_eq b1 (F.e_fun f_land es)))) then raise Not_found ; F.e_true with Not_found -> (* k in {8,16,32,64} ==> (b1 & ((1 << k) -1) == to_cint_unsigned_bits(k, b1) <==> true *) let iota,b1 = match_to_cint b in if Ctypes.signed iota then raise Not_found ; let n = Ctypes.i_bits iota in if n = 1 then (* rejects [to_bool()] that is not a modulo *) raise Not_found ; let k',_,es = match_power2_minus1_extraction es in let k' = match_integer k' in let k = Integer.of_int n in if not ((Integer.equal k k') && (F.decide (F.e_eq b1 (F.e_fun f_land es)))) then raise Not_found ; F.e_true let smp_eq_with_lor a b = let b1 = match_integer b in let es = match_fun f_lor a in try (* b1==(a2|e) <==> (b1^a2)==(~a2&e) *) let a2,es = match_integer_extraction es in let k1 = Integer.logxor b1 a2 in let k2 = Integer.lognot a2 in e_eq (e_zint k1) (e_fun f_land [e_zint k2 ; e_fun f_lor es]) with Not_found when b == e_zero -> (* 0==(a1|a2) <=> (0==a1 && 0==a2) *) F.e_and (List.map (e_eq b) es) let smp_eq_with_lxor a b = (* b1==(a2^e) <==> (b1^a2)==e *) let b1 = match_integer b in let es = match_fun f_lxor a in try (* b1==(a2^e) <==> (b1^a2)==e *) let a2,es = match_integer_extraction es in let k1 = Integer.logxor b1 a2 in e_eq (e_zint k1) (e_fun f_lxor es) with Not_found when b == e_zero -> (* 0==(a1^a2) <=> (a1==a2) *) (match es with | e1::e2::[] -> e_eq e1 e2 | e1::((_::_) as e22) -> e_eq e1 (e_fun f_lxor e22) | _ -> raise Not_found) | Not_found when b == e_minus_one -> (* -1==(a1^a2) <=> (a1==~a2) *) (match es with | e1::e2::[] -> e_eq e1 (e_fun f_lnot [e2]) | e1::((_::_) as e22) -> e_eq e1 (e_fun f_lnot [e_fun f_lxor e22]) | _ -> raise Not_found) let smp_eq_with_lnot a b = let e = match_ufun f_lnot a in try (* b1==~e <==> ~b1==e *) let b1 = match_integer b in let k1 = Integer.lognot b1 in e_eq (e_zint k1) e with Not_found ->(* ~b==~e <==> b==e *) let b = match_ufun f_lnot b in e_eq e b (* -------------------------------------------------------------------------- *) (* --- Comparision with LSL / LSR --- *) (* -------------------------------------------------------------------------- *) let two_power_k k = try Integer.two_power k with Z.Overflow -> raise Not_found let two_power_k_minus1 k = try Integer.pred (Integer.two_power k) with Z.Overflow -> raise Not_found let smp_eq_with_lsl_cst a0 b0 = let b1 = match_integer b0 in let es = match_fun f_lsl a0 in try (* looks at the sd arg of a0 *) let e,a2= match_positive_or_null_integer_arg2 es in if not (Integer.is_zero (Integer.logand b1 (two_power_k_minus1 a2))) then (* a2>=0 && 0!=(b1 & ((2**a2)-1)) ==> ( (e<<a2)==b1 <==> false ) *) e_false else (* a2>=0 && 0==(b1 & ((2**a2)-1)) ==> ( (e<<a2)==b1 <==> e==(b1>>a2) ) *) e_eq e (e_zint (Integer.shift_right b1 a2)) with Not_found -> (* looks at the fistt arg of a0 *) let a1,e= match_integer_arg1 es in if is_negative e then raise Not_found ; (* [PB] can be generalized to any term for a1 *) if Integer.le Integer.zero a1 && Integer.lt b1 a1 then (* e>=0 && 0<=a1 && b1<a1 ==> ( (a1<<e)==b1 <==> false ) *) e_false else if Integer.ge Integer.zero a1 && Integer.gt b1 a1 then (* e>=0 && 0>=a1 && b1>a1 ==> ( (a1<<e)==b1 <==> false ) *) e_false else raise Not_found let smp_cmp_with_lsl cmp a0 b0 = if a0 == e_zero then let b,_ = match_fun f_lsl b0 |> match_positive_or_null_arg2 in cmp e_zero b (* q>=0 ==> ( (0 cmp(b<<q)) <==> (0 cmp b) ) *) else if b0 == e_zero then let a,_ = match_fun f_lsl a0 |> match_positive_or_null_arg2 in cmp a e_zero (* p>=0 ==> ( ((a<<p) cmp 0) <==> (a cmp 0) ) *) else let a,p = match_fun f_lsl a0 |> match_positive_or_null_arg2 in let b,q = match_fun f_lsl b0 |> match_positive_or_null_arg2 in if p == q then (* p>=0 && q>=0 && p==q ==> ( ((a<<p)cmp(b<<q)) <==> (a cmp b) ) *) cmp a b else if a == b && (cmp==e_eq || is_positive_or_null a) then (* p>=0 && q>=0 && a==b ==> ( ((a<<p)== (b<<q)) <==> (p == q) ) *) (* p>=0 && q>=0 && a==b && a>=0 ==> ( ((a<<p)cmp(b<<q)) <==> (p cmp q) ) *) cmp p q else if a == b && is_negative a then (* p>=0 && q>=0 && a==b && a<0 ==> ( ((a<<p)cmp(b<<q)) <==> (q cmp p) ) *) cmp q p else let p = match_integer p in let q = match_integer q in if Z.lt p q then (* p>=0 && q>=0 && p>q ==> ( ((a<<p)cmp(b<<q)) <==> (a cmp(b<<(q-p))) ) *) cmp a (e_fun f_lsl [b;e_zint (Z.sub q p)]) else if Z.lt q p then (* p>=0 && q>=0 && p<q ==> ( ((a<<p)cmp(b<<q)) <==> ((a<<(p-q)) cmp b) ) *) cmp (e_fun f_lsl [a;e_zint (Z.sub p q)]) b else (* p>=0 && q>=0 && p==q ==> ( ((a<<p)cmp(b<<q)) <==> (a cmp b) ) *) cmp a b let smp_eq_with_lsl a b = try smp_eq_with_lsl_cst a b with Not_found -> smp_cmp_with_lsl e_eq a b let smp_leq_with_lsl a b = try smp_cmp_with_lsl e_leq a b with Not_found -> (* a <= 0 && 0 <= (x << y) ==> a <= (x << y) *) smp_leq_improved f_lsl a b let smp_eq_with_lsr a0 b0 = try let b1 = match_integer b0 in let e,a2 = match_fun f_lsr a0 |> match_positive_or_null_integer_arg2 in (* (e>>a2) == b1 <==> (e&~((2**a2)-1)) == (b1<<a2) That rule is similar to e/A2 == b2 <==> (e/A2)*A2 == b2*A2) with A2==2**a2 So, A2>0 and (e/A2)*A2 == e&~((2**a2)-1) *) (* build (e&~((2**a2)-1)) == (b1<<a2) *) e_eq (e_zint (Integer.shift_left b1 a2)) (e_fun f_land [e_zint (Integer.lognot (two_power_k_minus1 a2));e]) with Not_found -> (* This rule takes into acount several cases. One of them is (a>>p) == (b>>(n+p)) <==> (a&~((2**p)-1)) == (b>>n)&~((2**p)-1) That rule is similar to (a/P) == (b/(N*P)) <==> (a/P)*P == ((b/N)/P)*P with P==2**p, N=2**n, q=p+n. So, (a/P)*P==a&~((2**p)-1), b/N==b>>n, ((b/N)/P)*P==(b>>n)&~((2**p)-1) *) let a,p = match_fun f_lsr a0 |> match_positive_or_null_integer_arg2 in let b,q = match_fun f_lsr b0 |> match_positive_or_null_integer_arg2 in let n = Integer.min p q in let a = if Integer.lt n p then e_fun f_lsr [a;e_zint (Z.sub p n)] else a in let b = if Integer.lt n q then e_fun f_lsr [b;e_zint (Z.sub q n)] else b in let m = F.e_zint (Integer.lognot (two_power_k_minus1 n)) in e_eq (e_fun f_land [a;m]) (e_fun f_land [b;m]) let smp_leq_with_lsr x y = try let a,p = match_fun f_lsr y |> match_positive_or_null_integer_arg2 in (* x <= (a >> p) with p >= 0 *) if x == e_zero then (* p >= 0 ==> ( 0 <= (a >> p) <==> 0 <= a )*) e_leq e_zero a else (* p >= 0 ==> ( x <= (a >> p) <==> x <= a/(2**p) ) *) let k = two_power_k p in e_leq x (e_div a (e_zint k)) with Not_found -> try let a,p = match_fun f_lsr x |> match_positive_or_null_integer_arg2 in (* (a >> p) <= y with p >= 0 *) if y == e_zero then (* p >= 0 ==> ( (a >> p) <= 0 <==> a <= 0 ) *) e_leq a e_zero else (* p >= 0 ==> ( (a >> p) <= y <==> a/(2**p) <= y ) *) let k = two_power_k p in e_leq (e_div a (e_zint k)) y with Not_found -> (* x <= y && 0 <= (a&b) ==> x <= (a >> b) *) smp_leq_improved f_lsr x y (* Rewritting at export *) let bitk_export k e = F.e_fun ~result:Logic.Bool f_bit_export [e;k] let export_eq_with_land a b = let es = match_fun f_land a in if b == e_zero then let k,_,es = match_binop_one_extraction f_lsl es in (* e1 & ... & en & (1 << k) = 0 <==> !bit_test(e1 & ... & en, k) *) e_not (bitk_export k (e_fun f_land es)) else raise Not_found (* ACSL Semantics *) type l_builtin = { f: lfun ; eq: (term -> term -> term) option ; leq: (term -> term -> term) option ; smp: term list -> term ; } let () = Context.register begin fun () -> begin let mk_builtin n f ?eq ?leq smp = n, { f ; eq; leq; smp } in (* From [smp_mk_bit_stdlib], the built-in [f_bit_stdlib] is such that there is no creation of [e_fun f_bit_stdlib args] *) let bi_lbit_stdlib = mk_builtin "f_bit_stdlib" f_bit_stdlib smp_mk_bit_stdlib in let bi_lbit = mk_builtin "f_bit" f_bit_positive smp_bitk_positive in let bi_lnot = mk_builtin "f_lnot" f_lnot ~eq:smp_eq_with_lnot smp_lnot ~leq:(smp_leq_improved f_lnot) in let bi_lxor = mk_builtin "f_lxor" f_lxor ~eq:smp_eq_with_lxor ~leq:(smp_leq_improved f_lxor) (smp2 f_lxor Integer.logxor) in let bi_lor = mk_builtin "f_lor" f_lor ~eq:smp_eq_with_lor ~leq:(smp_leq_improved f_lor) (smp2 f_lor Integer.logor) in let bi_land = mk_builtin "f_land" f_land ~eq:smp_eq_with_land ~leq:smp_leq_with_land smp_land in let bi_lsl = mk_builtin "f_lsl" f_lsl ~eq:smp_eq_with_lsl ~leq:smp_leq_with_lsl (smp_shift Integer.shift_left) in let bi_lsr = mk_builtin "f_lsr" f_lsr ~eq:smp_eq_with_lsr ~leq:smp_leq_with_lsr (smp_shift Integer.shift_right) in List.iter begin fun (_name, { f; eq; leq; smp }) -> F.set_builtin f smp ; (match eq with | None -> () | Some eq -> F.set_builtin_eq f eq); (match leq with | None -> () | Some leq -> F.set_builtin_leq f leq) end [bi_lbit_stdlib ; bi_lbit; bi_lnot; bi_lxor; bi_lor; bi_land; bi_lsl; bi_lsr]; Lang.For_export.set_builtin_eq f_land export_eq_with_land end end (* ACSL Semantics *) let l_not a = e_fun f_lnot [a] let l_xor a b = e_fun f_lxor [a;b] let l_or a b = e_fun f_lor [a;b] let l_and a b = e_fun f_land [a;b] let l_lsl a b = e_fun f_lsl [a;b] let l_lsr a b = e_fun f_lsr [a;b] (* C Code Semantics *) (* we need a (forced) conversion to properly encode the semantics of C in terms of the semantics in Z(ACSL). Typically, lnot(128) becomes (-129), which must be converted to obtain an unsigned. *) let mask_unsigned i m = if Ctypes.signed i then m else convert i m let bnot i x = mask_unsigned i (l_not x) let bxor i x y = mask_unsigned i (l_xor x y) let bor _i = l_or (* no needs of range conversion *) let band _i = l_and (* no needs of range conversion *) let blsl i x y = overflow i (l_lsl x y) (* mult. by 2^y *) let blsr _i = l_lsr (* div. by 2^y, never overflow *) (** Simplifiers *) let c_int_bounds_ival f = let (umin,umax) = Ctypes.bounds f in Ival.inject_range (Some umin) (Some umax) let max_reduce_quantifiers = 1000 module Dom = struct type t = Ival.t Tmap.t let is_top_ival = Ival.equal Ival.top let top = Tmap.empty [@@@ warning "-32"] let pretty fmt dom = Tmap.iter (fun k v -> Format.fprintf fmt "%a: %a,@ " Lang.F.pp_term k Ival.pretty v) dom let find t dom = Tmap.find t dom let get t dom = try find t dom with Not_found -> Ival.top let narrow t v dom = if Ival.is_bottom v then raise Lang.Contradiction else if is_top_ival v then dom else Tmap.change (fun _ v old -> match old with | None -> Some v | (Some old) as old' -> let v = Ival.narrow v old in if Ival.is_bottom v then raise Lang.Contradiction; if Ival.equal v old then old' else Some v) t v dom let add_with_bot t v dom = if is_top_ival v then dom else Tmap.add t v dom let add t v dom = if Ival.is_bottom v then raise Lang.Contradiction; add_with_bot t v dom let remove t dom = Tmap.remove t dom let assume_cmp = let module Local = struct type t = Integer of Ival.t | Term of Ival.t option end in fun cmp t1 t2 dom -> let encode t = match Lang.F.repr t with | Kint z -> Local.Integer (Ival.inject_singleton z) | _ -> Local.Term (try Some (Tmap.find t dom) with Not_found -> None) in let term_dom = function | Some v -> v | None -> Ival.top in match encode t1, encode t2 with | Local.Integer cst1, Local.Integer cst2 -> (* assume cmp cst1 cst2 *) if Abstract_interp.Comp.False = Ival.forward_comp_int cmp cst1 cst2 then raise Lang.Contradiction; dom | Local.Term None, Local.Term None -> dom (* nothing can be collected *) | Local.Term opt1, Local.Integer cst2 -> let v1 = term_dom opt1 in add t1 (Ival.backward_comp_int_left cmp v1 cst2) dom | Local.Integer cst1, Local.Term opt2 -> let v2 = term_dom opt2 in let cmp_sym = Abstract_interp.Comp.sym cmp in add t2 (Ival.backward_comp_int_left cmp_sym v2 cst1) dom | Local.Term opt1, Local.Term opt2 -> let v1 = term_dom opt1 in let v2 = term_dom opt2 in let cmp_sym = Abstract_interp.Comp.sym cmp in add t1 (Ival.backward_comp_int_left cmp v1 v2) (add t2 (Ival.backward_comp_int_left cmp_sym v2 v1) dom) let assume_literal t dom = match Lang.F.repr t with | Eq(a,b) -> assume_cmp Abstract_interp.Comp.Eq a b dom | Leq(a,b) -> assume_cmp Abstract_interp.Comp.Le a b dom | Lt(a,b) -> assume_cmp Abstract_interp.Comp.Lt a b dom | Fun(g,[a]) -> begin try let ubound = c_int_bounds_ival (is_cint g) (* may raise Not_found *) in narrow a ubound dom with Not_found -> dom end | Not p -> begin match Lang.F.repr p with | Fun(g,[a]) -> begin try (* just checks for a contraction *) let ubound = c_int_bounds_ival (is_cint g) (* may raise Not_found *) in let v = Tmap.find a dom (* may raise Not_found *) in if Ival.is_included v ubound then raise Lang.Contradiction; dom with Not_found -> dom end | _ -> dom end | _ -> dom end let is_cint_simplifier = let reduce_bound ~add_bonus quant v tv dom t : term = (** Returns [new_t] such that [c_bind quant (alpha,t)] equals [c_bind quant v (alpha,new_t)] under the knowledge that [(not t) ==> (var in dom)]. Note: [~add_bonus] has not effect on the correctness of the transformation. It is a parameter that can be used in order to get better results. Bonus: Add additionnal hypothesis when we could deduce better constraint on the variable *) let module Tool = struct exception Stop exception Empty exception Unknown of Integer.t type t = { when_empty: unit -> term; add_hyp: term list -> term -> term; when_true: bool ref -> unit; when_false: bool ref -> unit; when_stop: unit -> term; } end in let tools = Tool.(match quant with | Forall -> { when_empty=(fun () -> e_true); add_hyp =(fun hyps t -> e_imply hyps t); when_true=(fun bonus -> bonus := add_bonus); when_false=(fun _ -> raise Stop); when_stop=(fun () -> e_false); } | Exists ->{ when_empty= (fun () -> e_false); add_hyp =(fun hyps t -> e_and (t::hyps)); when_true=(fun _ -> raise Stop); when_false=(fun bonus -> bonus := add_bonus); when_stop=(fun () -> e_true); } | _ -> assert false) in if Vars.mem v (vars t) then try let bonus_min = ref false in let bonus_max = ref false in let dom = if Ival.cardinal_is_less_than dom max_reduce_quantifiers then (* try to reduce the domain when [var] is still in [t] *) let red reduced i () = match repr (QED.e_subst_var v (e_zint i) t) with | True -> tools.Tool.when_true reduced | False -> tools.Tool.when_false reduced | _ -> raise (Tool.Unknown i) in let min_bound = try Ival.fold_int (red bonus_min) dom (); raise Tool.Empty with Tool.Unknown i -> i in let max_bound = try Ival.fold_int_decrease (red bonus_max) dom (); raise Tool.Empty with Tool.Unknown i -> i in let red_dom = Ival.inject_range (Some min_bound) (Some max_bound) in Ival.narrow dom red_dom else dom in begin match Ival.min_and_max dom with | None, None -> t (* Cannot be reduced *) | Some min, None -> (* May be reduced to [min ...] *) if !bonus_min then tools.Tool.add_hyp [e_leq (e_zint min) tv] t else t | None, Some max -> (* May be reduced to [... max] *) if !bonus_max then tools.Tool.add_hyp [e_leq tv (e_zint max)] t else t | Some min, Some max -> if Integer.equal min max then (* Reduced to only one value: [min] *) QED.e_subst_var v (e_zint min) t else if Integer.lt min max then let h = if !bonus_min then [e_leq (e_zint min) tv] else [] in let h = if !bonus_max then (e_leq tv (e_zint max))::h else h in tools.Tool.add_hyp h t else assert false (* Abstract_interp.Error_Bottom raised *) end with | Tool.Stop -> tools.Tool.when_stop () | Tool.Empty -> tools.Tool.when_empty () | Abstract_interp.Error_Bottom -> tools.Tool.when_empty () | Abstract_interp.Error_Top -> t else (* [alpha] is no more in [t] *) if Ival.is_bottom dom then tools.Tool.when_empty () else t in let module Polarity = struct type t = Pos | Neg | Both let flip = function | Pos -> Neg | Neg -> Pos | Both -> Both let from_bool = function | false -> Neg | true -> Pos end in object (self) val mutable domain : Dom.t = Dom.top method name = "Remove redundant is_cint" method copy = {< domain = domain >} method target _ = () method fixpoint = () method assume p = Lang.iter_consequence_literals (fun p -> domain <- Dom.assume_literal p domain) (Lang.F.e_prop p) method private simplify ~is_goal p = let pool = Lang.get_pool () in let reduce op var_domain base = let dom = match Lang.F.repr base with | Kint z -> Ival.inject_singleton z | _ -> try Tmap.find base domain with Not_found -> Ival.top in var_domain := Ival.backward_comp_int_left op !var_domain dom in let rec reduce_on_neg var var_domain t = match Lang.F.repr t with | _ when not (is_prop t) -> () | Leq(a,b) when Lang.F.equal a var -> reduce Abstract_interp.Comp.Le var_domain b | Leq(b,a) when Lang.F.equal a var -> reduce Abstract_interp.Comp.Ge var_domain b | Lt(a,b) when Lang.F.equal a var -> reduce Abstract_interp.Comp.Lt var_domain b | Lt(b,a) when Lang.F.equal a var -> reduce Abstract_interp.Comp.Gt var_domain b | And l -> List.iter (reduce_on_neg var var_domain) l | Not p -> reduce_on_pos var var_domain p | _ -> () and reduce_on_pos var var_domain t = match Lang.F.repr t with | Neq _ | Leq _ | Lt _ -> reduce_on_neg var var_domain (e_not t) | Imply (l,p) -> List.iter (reduce_on_neg var var_domain) l; reduce_on_pos var var_domain p | Or l -> List.iter (reduce_on_pos var var_domain) l; | Not p -> reduce_on_neg var var_domain p | _ -> () in (* [~term_pol] gives the polarity of the term [t] from the top level. That informs about how should be considered the quantifiers of [t] *) let rec walk ~term_pol t = let walk_flip_pol t = walk ~term_pol:(Polarity.flip term_pol) t and walk_same_pol t = walk ~term_pol t and walk_both_pol t = walk ~term_pol:Polarity.Both t in match repr t with | _ when not (is_prop t) -> t | Bind((Forall|Exists),_,_) -> let ctx,t = e_open ~pool ~lambda:false t in let ctx_with_dom = List.map (fun ((quant,var) as qv) -> match tau_of_var var with | Int -> let tvar = (e_var var) in let var_domain = ref Ival.top in if quant = Forall then reduce_on_pos tvar var_domain t else reduce_on_neg tvar var_domain t; domain <- Dom.add_with_bot tvar !var_domain domain; qv, Some (tvar, var_domain) | _ -> qv, None) ctx in let t = walk_same_pol t in let f_close t = function | (quant,var), None -> e_bind quant var t | (quant,var), Some (tvar,var_domain) -> domain <- Dom.remove tvar domain; (** Bonus: Add additionnal hypothesis in forall when we could deduce better constraint on the variable *) let add_bonus = match term_pol with | Polarity.Both -> false | _ -> (term_pol=Polarity.Pos) = (quant=Forall) in let t = reduce_bound ~add_bonus quant var tvar !var_domain t in e_bind quant var t in List.fold_left f_close t ctx_with_dom | Fun(g,[a]) -> (** Here we simplifies the cints which are redoundant *) begin try let ubound = c_int_bounds_ival (is_cint g) in let dom = (Tmap.find a domain) in if Ival.is_included dom ubound then e_true else t with Not_found -> t end | Imply (l1,l2) -> e_imply (List.map walk_flip_pol l1) (walk_same_pol l2) | Not p -> e_not (walk_flip_pol p) | And _ | Or _ -> Lang.F.QED.f_map walk_same_pol t | _ -> Lang.F.QED.f_map ~pool ~forall:false ~exists:false walk_both_pol t in Lang.F.p_bool (walk ~term_pol:(Polarity.from_bool is_goal) (Lang.F.e_prop p)) method simplify_exp (e : term) = e method simplify_hyp p = self#simplify ~is_goal:false p method simplify_goal p = self#simplify ~is_goal:true p method simplify_branch p = p method infer = [] end let mask_simplifier = let update x m ctx = Tmap.insert (fun _ m old -> if Integer.lt m old then (*better*) m else old) x m ctx and rewrite ctx e = let reduce m x = match F.repr x with | Kint v -> F.e_zint (Integer.logand m v) | _ -> x and collect ctx d x = try let m = Tmap.find x ctx in match d with | None -> Some m | Some m0 -> if Integer.lt m m0 then Some m else d with Not_found -> d in match F.repr e with | Fun(f,es) when f == f_land -> begin match List.fold_left (collect ctx) None es with | None -> raise Not_found | Some m -> F.e_fun f_land (List.map (reduce m) es) end | _ -> raise Not_found in object (** Must be 2^n-1 *) val mutable magnitude : Integer.t Tmap.t = Tmap.empty method name = "Rewrite unsigned masks" method copy = {< magnitude = magnitude >} method target _ = () method infer = [] method fixpoint = () method assume p = Lang.iter_consequence_literals (fun p -> match F.repr p with | Fun(f,[x]) -> begin try let iota = is_cint f in if not (Ctypes.signed iota) then magnitude <- update x (snd (Ctypes.bounds iota)) magnitude with Not_found -> () end | _ -> ()) (F.e_prop p) method simplify_exp e = if Tmap.is_empty magnitude then e else Lang.e_subst (rewrite magnitude) e method simplify_hyp p = if Tmap.is_empty magnitude then p else Lang.p_subst (rewrite magnitude) p method simplify_branch p = if Tmap.is_empty magnitude then p else Lang.p_subst (rewrite magnitude) p method simplify_goal p = if Tmap.is_empty magnitude then p else Lang.p_subst (rewrite magnitude) p end (* -------------------------------------------------------------------------- *)