You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2946 lines
115 KiB
2946 lines
115 KiB
(*
|
|
* Copyright (c) 2009 - 2013 Monoidics ltd.
|
|
* Copyright (c) 2013 - present Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*)
|
|
|
|
(** Functions for Propositions (i.e., Symbolic Heaps) *)
|
|
|
|
module L = Logging
|
|
module F = Format
|
|
open Utils
|
|
|
|
(** type to describe different strategies for initializing fields of a structure. [No_init] does not
|
|
initialize any fields of the struct. [Fld_init] initializes the fields of the struct with fresh
|
|
variables (C) or default values (Java). *)
|
|
type struct_init_mode =
|
|
| No_init
|
|
| Fld_init
|
|
|
|
let cil_exp_compare (e1: Sil.exp) (e2: Sil.exp) = Pervasives.compare e1 e2
|
|
|
|
let unSome = function
|
|
| Some x -> x
|
|
| _ -> assert false
|
|
|
|
type normal = Normal (** kind for normal props, i.e. normalized *)
|
|
type exposed = Exposed (** kind for exposed props *)
|
|
|
|
(** A proposition. The following invariants are mantained. [sub] is of
|
|
the form id1 = e1 ... idn = en where: the id's are distinct and do not
|
|
occur in the e's nor in [pi] or [sigma]; the id's are in sorted
|
|
order; the id's are not existentials; if idn = yn (for yn not
|
|
existential) then idn < yn in the order on ident's. [pi] is sorted
|
|
and normalized, and does not contain x = e. [sigma] is sorted and
|
|
normalized. *)
|
|
type 'a t =
|
|
{
|
|
sigma: Sil.hpred list; (** spatial part *)
|
|
sub: Sil.subst; (** substitution *)
|
|
pi: Sil.atom list; (** pure part *)
|
|
foot_sigma : Sil.hpred list; (** abduced spatial part *)
|
|
foot_pi: Sil.atom list; (** abduced pure part *)
|
|
}
|
|
|
|
exception Cannot_star of ml_location
|
|
|
|
(** Pure proposition. *)
|
|
type pure_prop = Sil.subst * Sil.atom list
|
|
|
|
(** {2 Basic Functions for Propositions} *)
|
|
|
|
(** {1 Functions for Comparison} *)
|
|
|
|
(** Comparison between lists of equalities and disequalities. Lexicographical order. *)
|
|
let rec pi_compare pi1 pi2 =
|
|
if pi1 == pi2 then 0
|
|
else match (pi1, pi2) with
|
|
| ([],[]) -> 0
|
|
| ([], _:: _) -> - 1
|
|
| (_:: _,[]) -> 1
|
|
| (a1:: pi1', a2:: pi2') ->
|
|
let n = Sil.atom_compare a1 a2 in
|
|
if n <> 0 then n
|
|
else pi_compare pi1' pi2'
|
|
|
|
let pi_equal pi1 pi2 =
|
|
pi_compare pi1 pi2 = 0
|
|
|
|
(** Comparsion between lists of heap predicates. Lexicographical order. *)
|
|
let rec sigma_compare sigma1 sigma2 =
|
|
if sigma1 == sigma2 then 0
|
|
else match (sigma1, sigma2) with
|
|
| ([],[]) -> 0
|
|
| ([], _:: _) -> - 1
|
|
| (_:: _,[]) -> 1
|
|
| (h1:: sigma1', h2:: sigma2') ->
|
|
let n = Sil.hpred_compare h1 h2 in
|
|
if n <> 0 then n
|
|
else sigma_compare sigma1' sigma2'
|
|
|
|
let sigma_equal sigma1 sigma2 =
|
|
sigma_compare sigma1 sigma2 = 0
|
|
|
|
(** Comparison between propositions. Lexicographical order. *)
|
|
let prop_compare p1 p2 =
|
|
sigma_compare p1.sigma p2.sigma
|
|
|> next Sil.sub_compare p1.sub p2.sub
|
|
|> next pi_compare p1.pi p2.pi
|
|
|> next sigma_compare p1.foot_sigma p2.foot_sigma
|
|
|> next pi_compare p1.foot_pi p2.foot_pi
|
|
|
|
(** Check the equality of two propositions *)
|
|
let prop_equal p1 p2 =
|
|
prop_compare p1 p2 = 0
|
|
|
|
(** {1 Functions for Pretty Printing} *)
|
|
|
|
(** Pretty print a footprint. *)
|
|
let pp_footprint _pe f fp =
|
|
let pe = { _pe with pe_cmap_norm = _pe.pe_cmap_foot } in
|
|
let pp_pi f () =
|
|
if fp.foot_pi != [] then
|
|
F.fprintf f "%a ;@\n" (pp_semicolon_seq_oneline pe (Sil.pp_atom pe)) fp.foot_pi in
|
|
if fp.foot_pi != [] || fp.foot_sigma != [] then
|
|
F.fprintf f "@\n[footprint@\n @[%a%a@] ]" pp_pi () (pp_semicolon_seq pe (Sil.pp_hpred pe)) fp.foot_sigma
|
|
|
|
let pp_lseg_kind f = function
|
|
| Sil.Lseg_NE -> F.fprintf f "ne"
|
|
| Sil.Lseg_PE -> F.fprintf f ""
|
|
|
|
let pp_texp_simple pe = match pe.pe_opt with
|
|
| PP_SIM_DEFAULT -> Sil.pp_texp pe
|
|
| PP_SIM_WITH_TYP -> Sil.pp_texp_full pe
|
|
|
|
(** Pretty print a pointsto representing a stack variable as an equality *)
|
|
let pp_hpred_stackvar pe0 env f hpred =
|
|
let pe, changed = Sil.color_pre_wrapper pe0 f hpred in
|
|
begin match hpred with
|
|
| Sil.Hpointsto (Sil.Lvar pvar, se, te) ->
|
|
let pe' = match se with
|
|
| Sil.Eexp (Sil.Var id, inst) when not (Sil.pvar_is_global pvar) ->
|
|
{ pe with pe_obj_sub = None } (* dont use obj sub on the var defining it *)
|
|
| _ -> pe in
|
|
(match pe'.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "%a = %a:%a" (Sil.pp_pvar_value pe') pvar (Sil.pp_sexp pe') se (pp_texp_simple pe') te
|
|
| PP_LATEX ->
|
|
F.fprintf f "%a{=}%a" (Sil.pp_pvar_value pe') pvar (Sil.pp_sexp pe') se)
|
|
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> assert false (* should not happen *)
|
|
end;
|
|
Sil.color_post_wrapper changed pe0 f
|
|
|
|
(** Pretty print a substitution. *)
|
|
let pp_sub pe f sub =
|
|
let pi_sub = IList.map (fun (id, e) -> Sil.Aeq(Sil.Var id, e)) (Sil.sub_to_list sub) in
|
|
(pp_semicolon_seq_oneline pe (Sil.pp_atom pe)) f pi_sub
|
|
|
|
(** Dump a substitution. *)
|
|
let d_sub (sub: Sil.subst) = L.add_print_action (L.PTsub, Obj.repr sub)
|
|
|
|
let pp_sub_entry pe0 f entry =
|
|
let pe, changed = Sil.color_pre_wrapper pe0 f entry in
|
|
let (x, e) = entry in
|
|
begin
|
|
match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "%a = %a" (Ident.pp pe) x (Sil.pp_exp pe) e
|
|
| PP_LATEX ->
|
|
F.fprintf f "%a{=}%a" (Ident.pp pe) x (Sil.pp_exp pe) e
|
|
end;
|
|
Sil.color_post_wrapper changed pe0 f
|
|
|
|
(** Pretty print a substitution as a list of (ident,exp) pairs *)
|
|
let pp_subl pe =
|
|
if !Config.smt_output then pp_semicolon_seq pe (pp_sub_entry pe)
|
|
else pp_semicolon_seq_oneline pe (pp_sub_entry pe)
|
|
|
|
(** Pretty print a pi. *)
|
|
let pp_pi pe =
|
|
if !Config.smt_output then pp_semicolon_seq pe (Sil.pp_atom pe)
|
|
else pp_semicolon_seq_oneline pe (Sil.pp_atom pe)
|
|
|
|
(** Dump a pi. *)
|
|
let d_pi (pi: Sil.atom list) = L.add_print_action (L.PTpi, Obj.repr pi)
|
|
|
|
(** Pretty print a sigma. *)
|
|
let pp_sigma pe =
|
|
pp_semicolon_seq pe (Sil.pp_hpred pe)
|
|
|
|
(** Split sigma into stack and nonstack parts.
|
|
The boolean indicates whether the stack should only include local variales. *)
|
|
let sigma_get_stack_nonstack only_local_vars sigma =
|
|
let hpred_is_stack_var = function
|
|
| Sil.Hpointsto (Sil.Lvar pvar, _, _) -> not only_local_vars || Sil.pvar_is_local pvar
|
|
| _ -> false in
|
|
IList.partition hpred_is_stack_var sigma
|
|
|
|
(** Pretty print a sigma in simple mode. *)
|
|
let pp_sigma_simple pe env fmt sigma =
|
|
let sigma_stack, sigma_nonstack = sigma_get_stack_nonstack false sigma in
|
|
let pp_stack fmt _sg =
|
|
let sg = IList.sort Sil.hpred_compare _sg in
|
|
if sg != [] then Format.fprintf fmt "%a" (pp_semicolon_seq pe (pp_hpred_stackvar pe env)) sg in
|
|
let pp_nl fmt doit = if doit then
|
|
(match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML -> Format.fprintf fmt " ;@\n"
|
|
| PP_LATEX -> Format.fprintf fmt " ; \\\\@\n") in
|
|
let pp_nonstack fmt = pp_semicolon_seq pe (Sil.pp_hpred_env pe (Some env)) fmt in
|
|
if sigma_stack != [] || sigma_nonstack != [] then
|
|
Format.fprintf fmt "%a%a%a" pp_stack sigma_stack pp_nl (sigma_stack != [] && sigma_nonstack != []) pp_nonstack sigma_nonstack
|
|
|
|
(** Dump a sigma. *)
|
|
let d_sigma (sigma: Sil.hpred list) = L.add_print_action (L.PTsigma, Obj.repr sigma)
|
|
|
|
(** Dump a pi and a sigma *)
|
|
let d_pi_sigma pi sigma =
|
|
let d_separator () = if pi != [] && sigma != [] then L.d_strln " *" in
|
|
d_pi pi; d_separator (); d_sigma sigma
|
|
|
|
(** Return the sub part of [prop]. *)
|
|
let get_sub (p: 'a t) : Sil.subst = p.sub
|
|
|
|
(** Return the pi part of [prop]. *)
|
|
let get_pi (p: 'a t) : Sil.atom list = p.pi
|
|
|
|
let pi_of_subst sub =
|
|
IList.map (fun (id1, e2) -> Sil.Aeq (Sil.Var id1, e2)) (Sil.sub_to_list sub)
|
|
|
|
(** Return the pure part of [prop]. *)
|
|
let get_pure (p: 'a t) : Sil.atom list =
|
|
pi_of_subst p.sub @ p.pi
|
|
|
|
(** Print existential quantification *)
|
|
let pp_evars pe f evars =
|
|
if evars != []
|
|
then match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "exists [%a]. " (pp_comma_seq (Ident.pp pe)) evars
|
|
| PP_LATEX ->
|
|
F.fprintf f "\\exists %a. " (pp_comma_seq (Ident.pp pe)) evars
|
|
|
|
(** Print an hpara in simple mode *)
|
|
let pp_hpara_simple _pe env n f pred =
|
|
let pe = pe_reset_obj_sub _pe in (* no free vars: disable object substitution *)
|
|
match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "P%d = %a%a" n (pp_evars pe) pred.Sil.evars (pp_semicolon_seq pe (Sil.pp_hpred_env pe (Some env))) pred.Sil.body
|
|
| PP_LATEX ->
|
|
F.fprintf f "P_{%d} = %a%a\\\\" n (pp_evars pe) pred.Sil.evars (pp_semicolon_seq pe (Sil.pp_hpred_env pe (Some env))) pred.Sil.body
|
|
|
|
(** Print an hpara_dll in simple mode *)
|
|
let pp_hpara_dll_simple _pe env n f pred =
|
|
let pe = pe_reset_obj_sub _pe in (* no free vars: disable object substitution *)
|
|
match pe.pe_kind with
|
|
| PP_TEXT | PP_HTML ->
|
|
F.fprintf f "P%d = %a%a" n (pp_evars pe) pred.Sil.evars_dll (pp_semicolon_seq pe (Sil.pp_hpred_env pe (Some env))) pred.Sil.body_dll
|
|
| PP_LATEX ->
|
|
F.fprintf f "P_{%d} = %a%a" n (pp_evars pe) pred.Sil.evars_dll (pp_semicolon_seq pe (Sil.pp_hpred_env pe (Some env))) pred.Sil.body_dll
|
|
|
|
(** Create an environment mapping (ident) expressions to the program variables containing them *)
|
|
let create_pvar_env (sigma: Sil.hpred list) : (Sil.exp -> Sil.exp) =
|
|
let env = ref [] in
|
|
let filter = function
|
|
| Sil.Hpointsto (Sil.Lvar pvar, Sil.Eexp (Sil.Var v, inst), _) ->
|
|
if not (Sil.pvar_is_global pvar) then env := (Sil.Var v, Sil.Lvar pvar) :: !env
|
|
| _ -> () in
|
|
IList.iter filter sigma;
|
|
let find e =
|
|
try
|
|
snd (IList.find (fun (e1, e2) -> Sil.exp_equal e1 e) !env)
|
|
with Not_found -> e in
|
|
find
|
|
|
|
(** Update the object substitution given the stack variables in the prop *)
|
|
let prop_update_obj_sub pe prop =
|
|
if !Config.pp_simple
|
|
then pe_set_obj_sub pe (create_pvar_env prop.sigma)
|
|
else pe
|
|
|
|
(** Pretty print a footprint in simple mode. *)
|
|
let pp_footprint_simple _pe env f fp =
|
|
let pe = { _pe with pe_cmap_norm = _pe.pe_cmap_foot } in
|
|
let pp_pure f pi =
|
|
if pi != [] then
|
|
F.fprintf f "%a *@\n" (pp_pi pe) pi in
|
|
if fp.foot_pi != [] || fp.foot_sigma != [] then
|
|
F.fprintf f "@\n[footprint@\n @[%a%a@] ]"
|
|
pp_pure fp.foot_pi
|
|
(pp_sigma_simple pe env) fp.foot_sigma
|
|
|
|
(** Create a predicate environment for a prop *)
|
|
let prop_pred_env prop =
|
|
let env = Sil.Predicates.empty_env () in
|
|
IList.iter (Sil.Predicates.process_hpred env) prop.sigma;
|
|
IList.iter (Sil.Predicates.process_hpred env) prop.foot_sigma;
|
|
env
|
|
|
|
(** Pretty print a proposition. *)
|
|
let pp_prop pe0 f prop =
|
|
let pe = prop_update_obj_sub pe0 prop in
|
|
let latex = pe.pe_kind == PP_LATEX in
|
|
let do_print f () =
|
|
let subl = Sil.sub_to_list (get_sub prop) in (* since prop diff is based on physical equality, we need to extract the sub verbatim *)
|
|
let pi = get_pi prop in
|
|
let pp_pure f () =
|
|
if subl != [] then F.fprintf f "%a ;@\n" (pp_subl pe) subl;
|
|
if pi != [] then F.fprintf f "%a ;@\n" (pp_pi pe) pi in
|
|
if !Config.pp_simple || latex then
|
|
begin
|
|
let env = prop_pred_env prop in
|
|
let iter_f n hpara = F.fprintf f "@,@[<h>%a@]" (pp_hpara_simple pe env n) hpara in
|
|
let iter_f_dll n hpara_dll = F.fprintf f "@,@[<h>%a@]" (pp_hpara_dll_simple pe env n) hpara_dll in
|
|
let pp_predicates fmt () =
|
|
if Sil.Predicates.is_empty env
|
|
then ()
|
|
else if latex then
|
|
begin
|
|
F.fprintf f "@\n\\\\\\textsf{where }";
|
|
Sil.Predicates.iter env iter_f iter_f_dll
|
|
end
|
|
else
|
|
begin
|
|
F.fprintf f "@,where";
|
|
Sil.Predicates.iter env iter_f iter_f_dll
|
|
end in
|
|
F.fprintf f "%a%a%a%a"
|
|
pp_pure () (pp_sigma_simple pe env) prop.sigma
|
|
(pp_footprint_simple pe env) prop pp_predicates ()
|
|
end
|
|
else
|
|
F.fprintf f "%a%a%a" pp_pure () (pp_sigma pe) prop.sigma (pp_footprint pe) prop in
|
|
if !Config.forcing_delayed_prints then (** print in html mode *)
|
|
F.fprintf f "%a%a%a" Io_infer.Html.pp_start_color Blue do_print () Io_infer.Html.pp_end_color ()
|
|
else
|
|
do_print f () (** print in text mode *)
|
|
|
|
let pp_prop_with_typ pe f p = pp_prop { pe with pe_opt = PP_SIM_WITH_TYP } f p
|
|
|
|
(** Dump a proposition. *)
|
|
let d_prop (prop: 'a t) = L.add_print_action (L.PTprop, Obj.repr prop)
|
|
|
|
(** Dump a proposition. *)
|
|
let d_prop_with_typ (prop: 'a t) = L.add_print_action (L.PTprop_with_typ, Obj.repr prop)
|
|
|
|
(** Print a list of propositions, prepending each one with the given string *)
|
|
let pp_proplist_with_typ pe f plist =
|
|
let rec pp_seq_newline f = function
|
|
| [] -> ()
|
|
| [x] -> F.fprintf f "@[%a@]" (pp_prop_with_typ pe) x
|
|
| x:: l -> F.fprintf f "@[%a@]@\n(||)@\n%a" (pp_prop_with_typ pe) x pp_seq_newline l in
|
|
F.fprintf f "@[<v>%a@]" pp_seq_newline plist
|
|
|
|
(** dump a proplist *)
|
|
let d_proplist_with_typ (pl: 'a t list) =
|
|
L.add_print_action (L.PTprop_list_with_typ, Obj.repr pl)
|
|
|
|
(** {1 Functions for computing free non-program variables} *)
|
|
|
|
let pi_fav_add fav pi =
|
|
IList.iter (Sil.atom_fav_add fav) pi
|
|
|
|
let pi_fav =
|
|
Sil.fav_imperative_to_functional pi_fav_add
|
|
|
|
let sigma_fav_add fav sigma =
|
|
IList.iter (Sil.hpred_fav_add fav) sigma
|
|
|
|
let sigma_fav =
|
|
Sil.fav_imperative_to_functional sigma_fav_add
|
|
|
|
let prop_footprint_fav_add fav prop =
|
|
sigma_fav_add fav prop.foot_sigma;
|
|
pi_fav_add fav prop.foot_pi
|
|
|
|
(** Find fav of the footprint part of the prop *)
|
|
let prop_footprint_fav prop =
|
|
Sil.fav_imperative_to_functional prop_footprint_fav_add prop
|
|
|
|
let prop_fav_add fav prop =
|
|
sigma_fav_add fav prop.sigma;
|
|
sigma_fav_add fav prop.foot_sigma;
|
|
Sil.sub_fav_add fav prop.sub;
|
|
pi_fav_add fav prop.pi;
|
|
pi_fav_add fav prop.foot_pi
|
|
|
|
let prop_fav p =
|
|
Sil.fav_imperative_to_functional prop_fav_add p
|
|
|
|
(** free vars of the prop, excluding the pure part *)
|
|
let prop_fav_nonpure_add fav prop =
|
|
sigma_fav_add fav prop.sigma;
|
|
sigma_fav_add fav prop.foot_sigma
|
|
|
|
(** free vars, except pi and sub, of current and footprint parts *)
|
|
let prop_fav_nonpure =
|
|
Sil.fav_imperative_to_functional prop_fav_nonpure_add
|
|
|
|
let hpred_fav_in_pvars_add fav = function
|
|
| Sil.Hpointsto (Sil.Lvar _, sexp, _) -> Sil.strexp_fav_add fav sexp
|
|
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> ()
|
|
|
|
let sigma_fav_in_pvars_add fav sigma =
|
|
IList.iter (hpred_fav_in_pvars_add fav) sigma
|
|
|
|
let sigma_fpv sigma =
|
|
IList.flatten (IList.map Sil.hpred_fpv sigma)
|
|
|
|
let pi_fpv pi =
|
|
IList.flatten (IList.map Sil.atom_fpv pi)
|
|
|
|
let prop_fpv prop =
|
|
(Sil.sub_fpv prop.sub) @
|
|
(pi_fpv prop.pi) @
|
|
(pi_fpv prop.foot_pi) @
|
|
(sigma_fpv prop.foot_sigma) @
|
|
(sigma_fpv prop.sigma)
|
|
|
|
(** {1 Functions for computing free or bound non-program variables} *)
|
|
|
|
let pi_av_add fav pi =
|
|
IList.iter (Sil.atom_av_add fav) pi
|
|
|
|
let sigma_av_add fav sigma =
|
|
IList.iter (Sil.hpred_av_add fav) sigma
|
|
|
|
let prop_av_add fav prop =
|
|
Sil.sub_av_add fav prop.sub;
|
|
pi_av_add fav prop.pi;
|
|
sigma_av_add fav prop.sigma;
|
|
pi_av_add fav prop.foot_pi;
|
|
sigma_av_add fav prop.foot_sigma
|
|
|
|
let prop_av =
|
|
Sil.fav_imperative_to_functional prop_av_add
|
|
|
|
(** {2 Functions for Subsitition} *)
|
|
|
|
let pi_sub (subst: Sil.subst) pi =
|
|
let f = Sil.atom_sub subst in
|
|
IList.map f pi
|
|
|
|
let sigma_sub subst sigma =
|
|
let f = Sil.hpred_sub subst in
|
|
IList.map f sigma
|
|
|
|
(** {2 Functions for normalization} *)
|
|
|
|
(** This function assumes that if (x,Sil.Var(y)) in sub, then compare x y = 1 *)
|
|
let sub_normalize sub =
|
|
let f (id, e) = (not (Ident.is_primed id)) && (not (Sil.ident_in_exp id e)) in
|
|
let sub' = Sil.sub_filter_pair f sub in
|
|
if Sil.sub_equal sub sub' then sub else sub'
|
|
|
|
let (--) = Sil.Int.sub
|
|
let (++) = Sil.Int.add
|
|
|
|
let iszero_int_float = function
|
|
| Sil.Cint i -> Sil.Int.iszero i
|
|
| Sil.Cfloat 0.0 -> true
|
|
| _ -> false
|
|
|
|
let isone_int_float = function
|
|
| Sil.Cint i -> Sil.Int.isone i
|
|
| Sil.Cfloat 1.0 -> true
|
|
| _ -> false
|
|
|
|
let isminusone_int_float = function
|
|
| Sil.Cint i -> Sil.Int.isminusone i
|
|
| Sil.Cfloat (-1.0) -> true
|
|
| _ -> false
|
|
|
|
let sym_eval abs e =
|
|
let rec eval e =
|
|
(* L.d_str " ["; Sil.d_exp e; L.d_str"] "; *)
|
|
match e with
|
|
| Sil.Var _ ->
|
|
e
|
|
| Sil.Const (Sil.Ctuple el) ->
|
|
Sil.Const (Sil.Ctuple (IList.map eval el))
|
|
| Sil.Const _ ->
|
|
e
|
|
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, e), _)
|
|
when Sil.ikind_is_char ik && !Config.curr_language <> Config.Java ->
|
|
eval e
|
|
| Sil.Sizeof _ ->
|
|
e
|
|
| Sil.Cast (_, e1) ->
|
|
eval e1
|
|
| Sil.UnOp (Sil.LNot, e1, topt) ->
|
|
begin
|
|
match eval e1 with
|
|
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
Sil.exp_one
|
|
| Sil.Const (Sil.Cint _) ->
|
|
Sil.exp_zero
|
|
| Sil.UnOp(Sil.LNot, e1', _) ->
|
|
e1'
|
|
| e1' ->
|
|
if abs then Sil.exp_get_undefined false else Sil.UnOp(Sil.LNot, e1', topt)
|
|
end
|
|
| Sil.UnOp (Sil.Neg, e1, topt) ->
|
|
begin
|
|
match eval e1 with
|
|
| Sil.UnOp (Sil.Neg, e2', _) ->
|
|
e2'
|
|
| Sil.Const (Sil.Cint i) ->
|
|
Sil.exp_int (Sil.Int.neg i)
|
|
| Sil.Const (Sil.Cfloat v) ->
|
|
Sil.exp_float (-. v)
|
|
| Sil.Var id ->
|
|
Sil.UnOp (Sil.Neg, Sil.Var id, topt)
|
|
| e1' ->
|
|
if abs then Sil.exp_get_undefined false else Sil.UnOp (Sil.Neg, e1', topt)
|
|
end
|
|
| Sil.UnOp (Sil.BNot, e1, topt) ->
|
|
begin
|
|
match eval e1 with
|
|
| Sil.UnOp(Sil.BNot, e2', _) ->
|
|
e2'
|
|
| Sil.Const (Sil.Cint i) ->
|
|
Sil.exp_int (Sil.Int.lognot i)
|
|
| e1' ->
|
|
if abs then Sil.exp_get_undefined false else Sil.UnOp (Sil.BNot, e1', topt)
|
|
end
|
|
| Sil.BinOp (Sil.Le, e1, e2) ->
|
|
begin
|
|
match eval e1, eval e2 with
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_bool (Sil.Int.leq n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_bool (v <= w)
|
|
| Sil.BinOp (Sil.PlusA, e3, Sil.Const (Sil.Cint n)), Sil.Const (Sil.Cint m) ->
|
|
Sil.BinOp (Sil.Le, e3, Sil.exp_int (m -- n))
|
|
| e1', e2' ->
|
|
Sil.exp_le e1' e2'
|
|
end
|
|
| Sil.BinOp (Sil.Lt, e1, e2) ->
|
|
begin
|
|
match eval e1, eval e2 with
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_bool (Sil.Int.lt n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_bool (v < w)
|
|
| Sil.Const (Sil.Cint n), Sil.BinOp (Sil.MinusA, f1, f2) ->
|
|
Sil.BinOp(Sil.Le, Sil.BinOp (Sil.MinusA, f2, f1), Sil.exp_int (Sil.Int.minus_one -- n))
|
|
| Sil.BinOp(Sil.MinusA, f1 , f2), Sil.Const(Sil.Cint n) ->
|
|
Sil.exp_le (Sil.BinOp(Sil.MinusA, f1 , f2)) (Sil.exp_int (n -- Sil.Int.one))
|
|
| Sil.BinOp (Sil.PlusA, e3, Sil.Const (Sil.Cint n)), Sil.Const (Sil.Cint m) ->
|
|
Sil.BinOp (Sil.Lt, e3, Sil.exp_int (m -- n))
|
|
| e1', e2' ->
|
|
Sil.exp_lt e1' e2'
|
|
end
|
|
| Sil.BinOp (Sil.Ge, e1, e2) ->
|
|
eval (Sil.exp_le e2 e1)
|
|
| Sil.BinOp (Sil.Gt, e1, e2) ->
|
|
eval (Sil.exp_lt e2 e1)
|
|
| Sil.BinOp (Sil.Eq, e1, e2) ->
|
|
begin
|
|
match eval e1, eval e2 with
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_bool (Sil.Int.eq n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_bool (v = w)
|
|
| e1', e2' ->
|
|
Sil.exp_eq e1' e2'
|
|
end
|
|
| Sil.BinOp (Sil.Ne, e1, e2) ->
|
|
begin
|
|
match eval e1, eval e2 with
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_bool (Sil.Int.neq n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_bool (v <> w)
|
|
| e1', e2' ->
|
|
Sil.exp_ne e1' e2'
|
|
end
|
|
| Sil.BinOp (Sil.LAnd, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin match e1', e2' with
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint _), _ ->
|
|
e2'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
e2'
|
|
| _, Sil.Const (Sil.Cint _) ->
|
|
e1'
|
|
| _ ->
|
|
Sil.BinOp (Sil.LAnd, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.LOr, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin
|
|
match e1', e2' with
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e2'
|
|
| Sil.Const (Sil.Cint _), _ ->
|
|
e1'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
e1'
|
|
| _, Sil.Const (Sil.Cint _) ->
|
|
e2'
|
|
| _ ->
|
|
Sil.BinOp (Sil.LOr, e1', e2')
|
|
end
|
|
| Sil.BinOp(Sil.PlusPI, Sil.Lindex (ep, e1), e2) -> (* array access with pointer arithmetic *)
|
|
let e' = Sil.BinOp (Sil.PlusA, e1, e2) in
|
|
eval (Sil.Lindex (ep, e'))
|
|
| Sil.BinOp (Sil.PlusPI, (Sil.BinOp (Sil.PlusPI, e11, e12)), e2) -> (* take care of pattern ((ptr + off1) + off2) *)
|
|
(* progress: convert inner +I to +A *)
|
|
let e2' = Sil.BinOp (Sil.PlusA, e12, e2) in
|
|
eval (Sil.BinOp (Sil.PlusPI, e11, e2'))
|
|
| Sil.BinOp (Sil.PlusA, (Sil.Sizeof (Sil.Tstruct (ftal, sftal, csu, name_opt, supers, def_mthds, iann), st) as e1), e2) -> (* pattern for extensible structs
|
|
given a struct declatead as struct s { ... t arr[n] ... }, allocation pattern malloc(sizeof(struct s) + k * siezof(t))
|
|
turn it into struct s { ... t arr[n + k] ... } *)
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
(match IList.rev ftal, e2' with
|
|
(fname, Sil.Tarray(typ, size), _):: ltfa, Sil.BinOp(Sil.Mult, num_elem, Sil.Sizeof (texp, st)) when ftal != [] && Sil.typ_equal typ texp ->
|
|
let size' = Sil.BinOp(Sil.PlusA, size, num_elem) in
|
|
let ltfa' = (fname, Sil.Tarray(typ, size'), Sil.item_annotation_empty) :: ltfa in
|
|
Sil.Sizeof(Sil.Tstruct (IList.rev ltfa', sftal, csu, name_opt, supers, def_mthds, iann), st)
|
|
| _ -> Sil.BinOp(Sil.PlusA, e1', e2'))
|
|
| Sil.BinOp (Sil.PlusA as oplus, e1, e2)
|
|
| Sil.BinOp (Sil.PlusPI as oplus, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
let isPlusA = oplus = Sil.PlusA in
|
|
let ominus = if isPlusA then Sil.MinusA else Sil.MinusPI in
|
|
let (+++) x y = match y with
|
|
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
|
|
| _ -> Sil.BinOp (oplus, x, y) in
|
|
let (---) x y = match y with
|
|
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
|
|
| _ -> Sil.BinOp (ominus, x, y) in
|
|
begin
|
|
match e1', e2' with
|
|
| Sil.Const c, _ when iszero_int_float c ->
|
|
e2'
|
|
| _, Sil.Const c when iszero_int_float c ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_int (n ++ m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_float (v +. w)
|
|
| Sil.UnOp(Sil.Neg, f1, _), f2
|
|
| f2, Sil.UnOp(Sil.Neg, f1, _) ->
|
|
Sil.BinOp (ominus, f2, f1)
|
|
| Sil.BinOp (Sil.PlusA, e, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2)
|
|
| Sil.BinOp (Sil.PlusPI, e, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2)
|
|
| Sil.Const (Sil.Cint n2), Sil.BinOp (Sil.PlusA, e, Sil.Const (Sil.Cint n1))
|
|
| Sil.Const (Sil.Cint n2), Sil.BinOp (Sil.PlusPI, e, Sil.Const (Sil.Cint n1)) ->
|
|
e +++ (Sil.exp_int (n1 ++ n2))
|
|
| Sil.BinOp (Sil.MinusA, Sil.Const (Sil.Cint n1), e), Sil.Const (Sil.Cint n2)
|
|
| Sil.Const (Sil.Cint n2), Sil.BinOp (Sil.MinusA, Sil.Const (Sil.Cint n1), e) ->
|
|
Sil.exp_int (n1 ++ n2) --- e
|
|
| Sil.BinOp (Sil.MinusA, e1, e2), e3 -> (* (e1-e2)+e3 --> e1 + (e3-e2) *)
|
|
(* progress: brings + to the outside *)
|
|
eval (e1 +++ (e3 --- e2))
|
|
| _, Sil.Const _ ->
|
|
e1' +++ e2'
|
|
| Sil.Const _, _ ->
|
|
if isPlusA then e2' +++ e1' else e1' +++ e2'
|
|
| Sil.Var _, Sil.Var _ ->
|
|
e1' +++ e2'
|
|
| _ ->
|
|
if abs && isPlusA then Sil.exp_get_undefined false else
|
|
if abs && not isPlusA then e1' +++ (Sil.exp_get_undefined false)
|
|
else e1' +++ e2'
|
|
end
|
|
| Sil.BinOp (Sil.MinusA as ominus, e1, e2)
|
|
| Sil.BinOp (Sil.MinusPI as ominus, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
let isMinusA = ominus = Sil.MinusA in
|
|
let oplus = if isMinusA then Sil.PlusA else Sil.PlusPI in
|
|
let (+++) x y = Sil.BinOp (oplus, x, y) in
|
|
let (---) x y = Sil.BinOp (ominus, x, y) in
|
|
if Sil.exp_equal e1' e2' then Sil.exp_zero
|
|
else begin
|
|
match e1', e2' with
|
|
| Sil.Const c, _ when iszero_int_float c ->
|
|
eval (Sil.UnOp(Sil.Neg, e2', None))
|
|
| _, Sil.Const c when iszero_int_float c ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_int (n -- m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_float (v -. w)
|
|
| _, Sil.UnOp (Sil.Neg, f2, _) ->
|
|
eval (e1 +++ f2)
|
|
| _ , Sil.Const(Sil.Cint n) ->
|
|
eval (e1' +++ (Sil.exp_int (Sil.Int.neg n)))
|
|
| Sil.Const _, _ ->
|
|
e1' --- e2'
|
|
| Sil.Var _, Sil.Var _ ->
|
|
e1' --- e2'
|
|
| _, _ ->
|
|
if abs then Sil.exp_get_undefined false else e1' --- e2'
|
|
end
|
|
| Sil.BinOp (Sil.MinusPP, e1, e2) ->
|
|
if abs then Sil.exp_get_undefined false
|
|
else Sil.BinOp (Sil.MinusPP, eval e1, eval e2)
|
|
| Sil.BinOp (Sil.Mult, esize, Sil.Sizeof (t, st)) | Sil.BinOp(Sil.Mult, Sil.Sizeof (t, st), esize) ->
|
|
begin
|
|
match eval esize, eval (Sil.Sizeof (t, st)) with
|
|
| Sil.Const (Sil.Cint i), e' when Sil.Int.isone i -> e'
|
|
| esize', e' -> Sil.BinOp(Sil.Mult, esize', e')
|
|
end
|
|
| Sil.BinOp (Sil.Mult, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin
|
|
match e1', e2' with
|
|
| Sil.Const c, _ when iszero_int_float c ->
|
|
Sil.exp_zero
|
|
| Sil.Const c, _ when isone_int_float c ->
|
|
e2'
|
|
| Sil.Const c, _ when isminusone_int_float c ->
|
|
eval (Sil.UnOp (Sil.Neg, e2', None))
|
|
| _, Sil.Const c when iszero_int_float c ->
|
|
Sil.exp_zero
|
|
| _, Sil.Const c when isone_int_float c ->
|
|
e1'
|
|
| _, Sil.Const c when isminusone_int_float c ->
|
|
eval (Sil.UnOp (Sil.Neg, e1', None))
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_int (Sil.Int.mul n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_float (v *. w)
|
|
| Sil.Var v, Sil.Var w ->
|
|
Sil.BinOp(Sil.Mult, e1', e2')
|
|
| _, _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp(Sil.Mult, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.Div, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin
|
|
match e1', e2' with
|
|
| _, Sil.Const c when iszero_int_float c ->
|
|
Sil.exp_get_undefined false
|
|
| Sil.Const c, _ when iszero_int_float c ->
|
|
e1'
|
|
| _, Sil.Const c when isone_int_float c ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_int (Sil.Int.div n m)
|
|
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
|
|
Sil.exp_float (v /.w)
|
|
| Sil.Sizeof(Sil.Tarray(typ, size), _), Sil.Sizeof(_typ, _) (* pattern: sizeof(arr) / sizeof(arr[0]) = size of arr *)
|
|
when Sil.typ_equal _typ typ ->
|
|
size
|
|
| _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.Div, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.Mod, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin
|
|
match e1', e2' with
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
Sil.exp_get_undefined false
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e1'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.isone i ->
|
|
Sil.exp_zero
|
|
| Sil.Const (Sil.Cint n), Sil.Const (Sil.Cint m) ->
|
|
Sil.exp_int (Sil.Int.rem n m)
|
|
| _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.Mod, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.Shiftlt, e1, e2) ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.Shiftlt, eval e1, eval e2)
|
|
| Sil.BinOp (Sil.Shiftrt, e1, e2) ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.Shiftrt, eval e1, eval e2)
|
|
| Sil.BinOp (Sil.BAnd, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin match e1', e2' with
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e1'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
e2'
|
|
| Sil.Const (Sil.Cint i1), Sil.Const(Sil.Cint i2) ->
|
|
Sil.exp_int (Sil.Int.logand i1 i2)
|
|
| _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.BAnd, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.BOr, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin match e1', e2' with
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e2'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint i1), Sil.Const(Sil.Cint i2) ->
|
|
Sil.exp_int (Sil.Int.logor i1 i2)
|
|
| _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.BOr, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.BXor, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin match e1', e2' with
|
|
| Sil.Const (Sil.Cint i), _ when Sil.Int.iszero i ->
|
|
e2'
|
|
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
e1'
|
|
| Sil.Const (Sil.Cint i1), Sil.Const(Sil.Cint i2) ->
|
|
Sil.exp_int (Sil.Int.logxor i1 i2)
|
|
| _ ->
|
|
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.BXor, e1', e2')
|
|
end
|
|
| Sil.BinOp (Sil.PtrFld, e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
begin
|
|
match e2' with
|
|
| Sil.Const (Sil.Cptr_to_fld (fn, typ)) ->
|
|
eval (Sil.Lfield(e1', fn, typ))
|
|
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i ->
|
|
Sil.exp_zero (* cause a NULL dereference *)
|
|
| _ -> Sil.BinOp (Sil.PtrFld, e1', e2')
|
|
end
|
|
| Sil.Lvar _ ->
|
|
e
|
|
| Sil.Lfield (e1, fld, typ) ->
|
|
let e1' = eval e1 in
|
|
Sil.Lfield (e1', fld, typ)
|
|
| Sil.Lindex(Sil.Lvar pv, e2) when false (* removed: it interferes with re-arrangement and error messages *) -> (* &x[n] --> &x + n *)
|
|
eval (Sil.BinOp (Sil.PlusPI, Sil.Lvar pv, e2))
|
|
| Sil.Lindex (Sil.BinOp(Sil.PlusPI, ep, e1), e2) -> (* array access with pointer arithmetic *)
|
|
let e' = Sil.BinOp (Sil.PlusA, e1, e2) in
|
|
eval (Sil.Lindex (ep, e'))
|
|
| Sil.Lindex (e1, e2) ->
|
|
let e1' = eval e1 in
|
|
let e2' = eval e2 in
|
|
Sil.Lindex(e1', e2') in
|
|
let e' = eval e in
|
|
(* L.d_str "sym_eval "; Sil.d_exp e; L.d_str" --> "; Sil.d_exp e'; L.d_ln (); *)
|
|
e'
|
|
|
|
let exp_normalize sub exp =
|
|
let exp' = Sil.exp_sub sub exp in
|
|
if !Config.abs_val >= 1 then sym_eval true exp'
|
|
else sym_eval false exp'
|
|
|
|
let rec texp_normalize sub exp = match exp with
|
|
| Sil.Sizeof (typ, st) -> Sil.Sizeof (typ_normalize sub typ, st)
|
|
| _ -> exp_normalize sub exp
|
|
|
|
and typ_normalize sub typ = match typ with
|
|
| Sil.Tvar _
|
|
| Sil.Tint _
|
|
| Sil.Tfloat _
|
|
| Sil.Tvoid
|
|
| Sil.Tfun _ ->
|
|
typ
|
|
| Sil.Tptr (t', pk) ->
|
|
Sil.Tptr (typ_normalize sub t', pk)
|
|
| Sil.Tstruct (ftal, sftal, csu, nameo, supers, def_mthds, iann) ->
|
|
let fld_norm = IList.map (fun (f, t, a) -> (f, typ_normalize sub t, a)) in
|
|
Sil.Tstruct (fld_norm ftal, fld_norm sftal, csu, nameo, supers, def_mthds, iann)
|
|
| Sil.Tarray (t, e) ->
|
|
Sil.Tarray (typ_normalize sub t, exp_normalize sub e)
|
|
| Sil.Tenum econsts ->
|
|
typ
|
|
|
|
let run_with_abs_val_eq_zero f =
|
|
let abs_val_old = !Config.abs_val in
|
|
Config.abs_val := 0;
|
|
let res = f () in
|
|
Config.abs_val := abs_val_old;
|
|
res
|
|
|
|
let exp_normalize_noabs sub exp =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> exp_normalize sub exp)
|
|
|
|
(** Return [true] if the atom is an inequality *)
|
|
let atom_is_inequality = function
|
|
| Sil.Aeq (Sil.BinOp (Sil.Le, _, _), Sil.Const (Sil.Cint i)) when Sil.Int.isone i -> true
|
|
| Sil.Aeq (Sil.BinOp (Sil.Lt, _, _), Sil.Const (Sil.Cint i)) when Sil.Int.isone i -> true
|
|
| _ -> false
|
|
|
|
(** If the atom is [e<=n] return [e,n] *)
|
|
let atom_exp_le_const = function
|
|
| Sil.Aeq(Sil.BinOp (Sil.Le, e1, Sil.Const (Sil.Cint n)), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
Some (e1, n)
|
|
| _ -> None
|
|
|
|
(** If the atom is [n<e] return [n,e] *)
|
|
let atom_const_lt_exp = function
|
|
| Sil.Aeq(Sil.BinOp (Sil.Lt, Sil.Const (Sil.Cint n), e1), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
Some (n, e1)
|
|
| _ -> None
|
|
|
|
(** Turn an inequality expression into an atom *)
|
|
let mk_inequality e =
|
|
match e with
|
|
| Sil.BinOp (Sil.Le, base, Sil.Const (Sil.Cint n)) ->
|
|
(* base <= n case *)
|
|
let nbase = exp_normalize_noabs Sil.sub_empty base in
|
|
(match nbase with
|
|
| Sil.BinOp(Sil.PlusA, base', Sil.Const (Sil.Cint n')) ->
|
|
let new_offset = Sil.exp_int (n -- n') in
|
|
let new_e = Sil.BinOp (Sil.Le, base', new_offset) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.PlusA, Sil.Const (Sil.Cint n'), base') ->
|
|
let new_offset = Sil.exp_int (n -- n') in
|
|
let new_e = Sil.BinOp (Sil.Le, base', new_offset) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.MinusA, base', Sil.Const (Sil.Cint n')) ->
|
|
let new_offset = Sil.exp_int (n ++ n') in
|
|
let new_e = Sil.BinOp (Sil.Le, base', new_offset) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.MinusA, Sil.Const (Sil.Cint n'), base') ->
|
|
let new_offset = Sil.exp_int (n' -- n -- Sil.Int.one) in
|
|
let new_e = Sil.BinOp (Sil.Lt, new_offset, base') in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.UnOp(Sil.Neg, new_base, _) ->
|
|
(* In this case, base = -new_base. Construct -n-1 < new_base. *)
|
|
let new_offset = Sil.exp_int (Sil.Int.zero -- n -- Sil.Int.one) in
|
|
let new_e = Sil.BinOp (Sil.Lt, new_offset, new_base) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| _ -> Sil.Aeq (e, Sil.exp_one))
|
|
| Sil.BinOp (Sil.Lt, Sil.Const (Sil.Cint n), base) ->
|
|
(* n < base case *)
|
|
let nbase = exp_normalize_noabs Sil.sub_empty base in
|
|
(match nbase with
|
|
| Sil.BinOp(Sil.PlusA, base', Sil.Const (Sil.Cint n')) ->
|
|
let new_offset = Sil.exp_int (n -- n') in
|
|
let new_e = Sil.BinOp (Sil.Lt, new_offset, base') in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.PlusA, Sil.Const (Sil.Cint n'), base') ->
|
|
let new_offset = Sil.exp_int (n -- n') in
|
|
let new_e = Sil.BinOp (Sil.Lt, new_offset, base') in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.MinusA, base', Sil.Const (Sil.Cint n')) ->
|
|
let new_offset = Sil.exp_int (n ++ n') in
|
|
let new_e = Sil.BinOp (Sil.Lt, new_offset, base') in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.BinOp(Sil.MinusA, Sil.Const (Sil.Cint n'), base') ->
|
|
let new_offset = Sil.exp_int (n' -- n -- Sil.Int.one) in
|
|
let new_e = Sil.BinOp (Sil.Le, base', new_offset) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| Sil.UnOp(Sil.Neg, new_base, _) ->
|
|
(* In this case, base = -new_base. Construct new_base <= -n-1 *)
|
|
let new_offset = Sil.exp_int (Sil.Int.zero -- n -- Sil.Int.one) in
|
|
let new_e = Sil.BinOp (Sil.Le, new_base, new_offset) in
|
|
Sil.Aeq (new_e, Sil.exp_one)
|
|
| _ -> Sil.Aeq (e, Sil.exp_one))
|
|
| _ -> Sil.Aeq (e, Sil.exp_one)
|
|
|
|
(** Normalize an inequality *)
|
|
let inequality_normalize a =
|
|
(** turn an expression into a triple (pos,neg,off) of positive and negative occurrences, and integer offset *)
|
|
(** representing inequality [sum(pos) - sum(neg) + off <= 0] *)
|
|
let rec exp_to_posnegoff e = match e with
|
|
| Sil.Const (Sil.Cint n) -> [],[], n
|
|
| Sil.BinOp(Sil.PlusA, e1, e2) | Sil.BinOp(Sil.PlusPI, e1, e2) ->
|
|
let pos1, neg1, n1 = exp_to_posnegoff e1 in
|
|
let pos2, neg2, n2 = exp_to_posnegoff e2 in
|
|
(pos1@pos2, neg1@neg2, n1 ++ n2)
|
|
| Sil.BinOp(Sil.MinusA, e1, e2) | Sil.BinOp(Sil.MinusPI, e1, e2) | Sil.BinOp(Sil.MinusPP, e1, e2) ->
|
|
let pos1, neg1, n1 = exp_to_posnegoff e1 in
|
|
let pos2, neg2, n2 = exp_to_posnegoff e2 in
|
|
(pos1@neg2, neg1@pos2, n1 -- n2)
|
|
| Sil.UnOp(Sil.Neg, e1, _) ->
|
|
let pos1, neg1, n1 = exp_to_posnegoff e1 in
|
|
(neg1, pos1, Sil.Int.zero -- n1)
|
|
| _ -> [e],[], Sil.Int.zero in
|
|
(** sort and filter out expressions appearing in both the positive and negative part *)
|
|
let normalize_posnegoff (pos, neg, off) =
|
|
let pos' = IList.sort Sil.exp_compare pos in
|
|
let neg' = IList.sort Sil.exp_compare neg in
|
|
let rec combine pacc nacc = function
|
|
| x:: ps, y:: ng ->
|
|
(match Sil.exp_compare x y with
|
|
| n when n < 0 -> combine (x:: pacc) nacc (ps, y :: ng)
|
|
| 0 -> combine pacc nacc (ps, ng)
|
|
| _ -> combine pacc (y:: nacc) (x :: ps, ng))
|
|
| ps, ng -> (IList.rev pacc) @ ps, (IList.rev nacc) @ ng in
|
|
let pos'', neg'' = combine [] [] (pos', neg') in
|
|
(pos'', neg'', off) in
|
|
(** turn a non-empty list of expressions into a sum expression *)
|
|
let rec exp_list_to_sum = function
|
|
| [] -> assert false
|
|
| [e] -> e
|
|
| e:: el -> Sil.BinOp(Sil.PlusA, e, exp_list_to_sum el) in
|
|
let norm_from_exp e =
|
|
match normalize_posnegoff (exp_to_posnegoff e) with
|
|
| [],[], n -> Sil.BinOp(Sil.Le, Sil.exp_int n, Sil.exp_zero)
|
|
| [], neg, n -> Sil.BinOp(Sil.Lt, Sil.exp_int (n -- Sil.Int.one), exp_list_to_sum neg)
|
|
| pos, [], n -> Sil.BinOp(Sil.Le, exp_list_to_sum pos, Sil.exp_int (Sil.Int.zero -- n))
|
|
| pos, neg, n ->
|
|
let lhs_e = Sil.BinOp(Sil.MinusA, exp_list_to_sum pos, exp_list_to_sum neg) in
|
|
Sil.BinOp(Sil.Le, lhs_e, Sil.exp_int (Sil.Int.zero -- n)) in
|
|
let ineq = match a with
|
|
| Sil.Aeq (ineq, Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
ineq
|
|
| _ -> assert false in
|
|
match ineq with
|
|
| Sil.BinOp(Sil.Le, e1, e2) ->
|
|
let e = Sil.BinOp(Sil.MinusA, e1, e2) in
|
|
mk_inequality (norm_from_exp e)
|
|
| Sil.BinOp(Sil.Lt, e1, e2) ->
|
|
let e = Sil.BinOp(Sil.MinusA, Sil.BinOp(Sil.MinusA, e1, e2), Sil.exp_minus_one) in
|
|
mk_inequality (norm_from_exp e)
|
|
| _ -> a
|
|
|
|
let exp_reorder e1 e2 = if Sil.exp_compare e1 e2 <= 0 then (e1, e2) else (e2, e1)
|
|
|
|
(** Normalize an atom.
|
|
We keep the convention that inequalities with constants
|
|
are only of the form [e <= n] and [n < e]. *)
|
|
let atom_normalize sub a0 =
|
|
let a = Sil.atom_sub sub a0 in
|
|
let rec normalize_eq eq = match eq with
|
|
| Sil.BinOp(Sil.PlusA, e1, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2) (* e1+n1==n2 ---> e1==n2-n1 *)
|
|
| Sil.BinOp(Sil.PlusPI, e1, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2) ->
|
|
(e1, Sil.exp_int (n2 -- n1))
|
|
| Sil.BinOp(Sil.MinusA, e1, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2) (* e1-n1==n2 ---> e1==n1+n2 *)
|
|
| Sil.BinOp(Sil.MinusPI, e1, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint n2) ->
|
|
(e1, Sil.exp_int (n1 ++ n2))
|
|
| Sil.BinOp(Sil.MinusA, Sil.Const (Sil.Cint n1), e1), Sil.Const (Sil.Cint n2) -> (* n1-e1 == n2 -> e1==n1-n2 *)
|
|
(e1, Sil.exp_int (n1 -- n2))
|
|
| Sil.Lfield (e1', fld1, typ1), Sil.Lfield (e2', fld2, typ2) ->
|
|
if Sil.fld_equal fld1 fld2
|
|
then normalize_eq (e1', e2')
|
|
else eq
|
|
| Sil.Lindex (e1', idx1), Sil.Lindex (e2', idx2) ->
|
|
if Sil.exp_equal idx1 idx2 then normalize_eq (e1', e2')
|
|
else if Sil.exp_equal e1' e2' then normalize_eq (idx1, idx2)
|
|
else eq
|
|
| _ -> eq in
|
|
let handle_unary_negation e1 e2 =
|
|
match e1, e2 with
|
|
| Sil.UnOp (Sil.LNot, e1', _), Sil.Const (Sil.Cint i)
|
|
| Sil.Const (Sil.Cint i), Sil.UnOp (Sil.LNot, e1', _) when Sil.Int.iszero i ->
|
|
(e1', Sil.exp_zero, true)
|
|
| _ -> (e1, e2, false) in
|
|
let handle_boolean_operation from_equality e1 e2 =
|
|
let ne1 = exp_normalize sub e1 in
|
|
let ne2 = exp_normalize sub e2 in
|
|
let ne1', ne2', op_negated = handle_unary_negation ne1 ne2 in
|
|
let (e1', e2') = normalize_eq (ne1', ne2') in
|
|
let (e1'', e2'') = exp_reorder e1' e2' in
|
|
let use_equality =
|
|
if op_negated then not from_equality else from_equality in
|
|
if use_equality then
|
|
Sil.Aeq (e1'', e2'')
|
|
else
|
|
Sil.Aneq (e1'', e2'') in
|
|
let a' = match a with
|
|
| Sil.Aeq (e1, e2) ->
|
|
handle_boolean_operation true e1 e2
|
|
| Sil.Aneq (e1, e2) ->
|
|
handle_boolean_operation false e1 e2 in
|
|
if atom_is_inequality a' then inequality_normalize a' else a'
|
|
|
|
(** Negate an atom *)
|
|
let atom_negate = function
|
|
| Sil.Aeq (Sil.BinOp (Sil.Le, e1, e2), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
mk_inequality (Sil.exp_lt e2 e1)
|
|
| Sil.Aeq (Sil.BinOp (Sil.Lt, e1, e2), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
mk_inequality (Sil.exp_le e2 e1)
|
|
| Sil.Aeq (e1, e2) -> Sil.Aneq (e1, e2)
|
|
| Sil.Aneq (e1, e2) -> Sil.Aeq (e1, e2)
|
|
|
|
let rec remove_duplicates_from_sorted special_equal = function
|
|
| [] -> []
|
|
| [x] -> [x]
|
|
| x:: y:: l ->
|
|
if (special_equal x y)
|
|
then remove_duplicates_from_sorted special_equal (y:: l)
|
|
else x:: (remove_duplicates_from_sorted special_equal (y:: l))
|
|
|
|
let rec strexp_normalize sub se =
|
|
match se with
|
|
| Sil.Eexp (e, inst) ->
|
|
Sil.Eexp (exp_normalize sub e, inst)
|
|
| Sil.Estruct (fld_cnts, inst) ->
|
|
begin
|
|
match fld_cnts with
|
|
| [] -> se
|
|
| _ ->
|
|
let fld_cnts' =
|
|
IList.map (fun (fld, cnt) ->
|
|
fld, strexp_normalize sub cnt) fld_cnts in
|
|
let fld_cnts'' = IList.sort Sil.fld_strexp_compare fld_cnts' in
|
|
Sil.Estruct (fld_cnts'', inst)
|
|
end
|
|
| Sil.Earray (size, idx_cnts, inst) ->
|
|
begin
|
|
let size' = exp_normalize_noabs sub size in
|
|
match idx_cnts with
|
|
| [] ->
|
|
if Sil.exp_equal size size' then se else Sil.Earray (size', idx_cnts, inst)
|
|
| _ ->
|
|
let idx_cnts' =
|
|
IList.map (fun (idx, cnt) ->
|
|
let idx' = exp_normalize sub idx in
|
|
idx', strexp_normalize sub cnt) idx_cnts in
|
|
let idx_cnts'' =
|
|
IList.sort Sil.exp_strexp_compare idx_cnts' in
|
|
Sil.Earray (size', idx_cnts'', inst)
|
|
end
|
|
|
|
(** create a strexp of the given type, populating the structures if [expand_structs] is true *)
|
|
let rec create_strexp_of_type tenvo struct_init_mode typ inst =
|
|
let init_value () =
|
|
let create_fresh_var () =
|
|
let fresh_id =
|
|
(Ident.create_fresh (if !Config.footprint then Ident.kfootprint else Ident.kprimed)) in
|
|
Sil.Var fresh_id in
|
|
if !Config.curr_language = Config.Java && inst = Sil.Ialloc
|
|
then
|
|
match typ with
|
|
| Sil.Tfloat _ -> Sil.Const (Sil.Cfloat 0.0)
|
|
| _ -> Sil.exp_zero
|
|
else
|
|
create_fresh_var () in
|
|
match typ with
|
|
| Sil.Tint _ | Sil.Tfloat _ | Sil.Tvoid | Sil.Tfun _ | Sil.Tptr _ | Sil.Tenum _ ->
|
|
Sil.Eexp (init_value (), inst)
|
|
| Sil.Tstruct (ftal, sftal, _, _, _, _, _) ->
|
|
begin
|
|
match struct_init_mode with
|
|
| No_init -> Sil.Estruct ([], inst)
|
|
| Fld_init ->
|
|
let f (fld, t, a) =
|
|
if Sil.is_objc_ref_counter_field (fld, t, a) then
|
|
(fld, Sil.Eexp (Sil.exp_one, inst))
|
|
else
|
|
(fld, create_strexp_of_type tenvo struct_init_mode t inst) in
|
|
Sil.Estruct (IList.map f ftal, inst)
|
|
end
|
|
| Sil.Tarray (_, size) ->
|
|
Sil.Earray (size, [], inst)
|
|
| Sil.Tvar name ->
|
|
L.out "@[<2>ANALYSIS BUG@\n";
|
|
L.out "type %a should be expanded to " (Sil.pp_typ_full pe_text) typ;
|
|
begin
|
|
match tenvo with
|
|
| None -> L.out "nothing@\n@."
|
|
| Some tenv ->
|
|
begin
|
|
match Sil.tenv_lookup tenv name with
|
|
| None -> L.out "nothing@\n@."
|
|
| Some typ' -> L.out "%a@\n@." (Sil.pp_typ_full pe_text) typ'
|
|
end;
|
|
end;
|
|
assert false
|
|
|
|
(** Sil.Construct a pointsto. *)
|
|
let mk_ptsto lexp sexp te =
|
|
let nsexp = strexp_normalize Sil.sub_empty sexp in
|
|
Sil.Hpointsto(lexp, nsexp, te)
|
|
|
|
(** Construct a points-to predicate for an expression using either the provided expression [name] as
|
|
base for fresh identifiers. If [expand_structs] is true, initialize the fields of structs with fresh variables. *)
|
|
let mk_ptsto_exp tenvo struct_init_mode (exp, te, expo) inst : Sil.hpred =
|
|
let default_strexp () = match te with
|
|
| Sil.Sizeof (typ, st) ->
|
|
create_strexp_of_type tenvo struct_init_mode typ inst
|
|
| Sil.Var id ->
|
|
Sil.Estruct ([], inst)
|
|
| te ->
|
|
L.err "trying to create ptsto with type: %a@\n@." (Sil.pp_texp_full pe_text) te;
|
|
assert false in
|
|
let strexp = match expo with
|
|
| Some e -> Sil.Eexp (e, inst)
|
|
| None -> default_strexp () in
|
|
mk_ptsto exp strexp te
|
|
|
|
let replace_array_contents hpred esel = match hpred with
|
|
| Sil.Hpointsto (root, Sil.Earray (size, [], inst), te) ->
|
|
Sil.Hpointsto (root, Sil.Earray (size, esel, inst), te)
|
|
| _ -> assert false
|
|
|
|
let rec hpred_normalize sub hpred =
|
|
let replace_hpred hpred' =
|
|
L.d_strln "found array with sizeof(..) size";
|
|
L.d_str "converting original hpred: "; Sil.d_hpred hpred; L.d_ln ();
|
|
L.d_str "into the following: "; Sil.d_hpred hpred'; L.d_ln ();
|
|
hpred' in
|
|
match hpred with
|
|
| Sil.Hpointsto (root, cnt, te) ->
|
|
let normalized_root = exp_normalize sub root in
|
|
let normalized_cnt = strexp_normalize sub cnt in
|
|
let normalized_te = texp_normalize sub te in
|
|
begin match normalized_cnt, normalized_te with
|
|
| Sil.Earray (Sil.Sizeof (t, st1), [], inst), Sil.Sizeof (Sil.Tarray _, st2) ->
|
|
(* check for an empty array whose size expression is (Sizeof type), and turn the array into a strexp of the given type *)
|
|
let hpred' = mk_ptsto_exp None Fld_init (root, Sil.Sizeof (t, st1), None) inst in
|
|
replace_hpred hpred'
|
|
| Sil.Earray (Sil.BinOp(Sil.Mult, Sil.Sizeof (t, st1), x), esel, inst), Sil.Sizeof (Sil.Tarray _, st2)
|
|
| Sil.Earray (Sil.BinOp(Sil.Mult, x, Sil.Sizeof (t, st1)), esel, inst), Sil.Sizeof (Sil.Tarray _, st2) ->
|
|
(* check for an array whose size expression is n * (Sizeof type), and turn the array into a strexp of the given type *)
|
|
let hpred' = mk_ptsto_exp None Fld_init (root, Sil.Sizeof (Sil.Tarray(t, x), st1), None) inst in
|
|
replace_hpred (replace_array_contents hpred' esel)
|
|
| _ -> Sil.Hpointsto (normalized_root, normalized_cnt, normalized_te)
|
|
end
|
|
| Sil.Hlseg (k, para, e1, e2, elist) ->
|
|
let normalized_e1 = exp_normalize sub e1 in
|
|
let normalized_e2 = exp_normalize sub e2 in
|
|
let normalized_elist = IList.map (exp_normalize sub) elist in
|
|
let normalized_para = hpara_normalize sub para in
|
|
Sil.Hlseg (k, normalized_para, normalized_e1, normalized_e2, normalized_elist)
|
|
| Sil.Hdllseg (k, para, e1, e2, e3, e4, elist) ->
|
|
let norm_e1 = exp_normalize sub e1 in
|
|
let norm_e2 = exp_normalize sub e2 in
|
|
let norm_e3 = exp_normalize sub e3 in
|
|
let norm_e4 = exp_normalize sub e4 in
|
|
let norm_elist = IList.map (exp_normalize sub) elist in
|
|
let norm_para = hpara_dll_normalize sub para in
|
|
Sil.Hdllseg (k, norm_para, norm_e1, norm_e2, norm_e3, norm_e4, norm_elist)
|
|
|
|
and hpara_normalize sub para =
|
|
let normalized_body = IList.map (hpred_normalize Sil.sub_empty) (para.Sil.body) in
|
|
let sorted_body = IList.sort Sil.hpred_compare normalized_body in
|
|
{ para with Sil.body = sorted_body }
|
|
|
|
and hpara_dll_normalize sub para =
|
|
let normalized_body = IList.map (hpred_normalize Sil.sub_empty) (para.Sil.body_dll) in
|
|
let sorted_body = IList.sort Sil.hpred_compare normalized_body in
|
|
{ para with Sil.body_dll = sorted_body }
|
|
|
|
let pi_tighten_ineq pi =
|
|
let ineq_list, nonineq_list = IList.partition atom_is_inequality pi in
|
|
let diseq_list =
|
|
let get_disequality_info acc = function
|
|
| Sil.Aneq(Sil.Const (Sil.Cint n), e) | Sil.Aneq(e, Sil.Const (Sil.Cint n)) -> (e, n):: acc
|
|
| _ -> acc in
|
|
IList.fold_left get_disequality_info [] nonineq_list in
|
|
let is_neq e n =
|
|
IList.exists (fun (e', n') -> Sil.exp_equal e e' && Sil.Int.eq n n') diseq_list in
|
|
let le_list_tightened =
|
|
let get_le_inequality_info acc a =
|
|
match atom_exp_le_const a with
|
|
| Some (e, n) -> (e, n):: acc
|
|
| _ -> acc in
|
|
let rec le_tighten le_list_done = function
|
|
| [] -> IList.rev le_list_done
|
|
| (e, n):: le_list_todo -> (* e <= n *)
|
|
if is_neq e n then le_tighten le_list_done ((e, n -- Sil.Int.one):: le_list_todo)
|
|
else le_tighten ((e, n):: le_list_done) (le_list_todo) in
|
|
let le_list = IList.rev (IList.fold_left get_le_inequality_info [] ineq_list) in
|
|
le_tighten [] le_list in
|
|
let lt_list_tightened =
|
|
let get_lt_inequality_info acc a =
|
|
match atom_const_lt_exp a with
|
|
| Some (n, e) -> (n, e):: acc
|
|
| _ -> acc in
|
|
let rec lt_tighten lt_list_done = function
|
|
| [] -> IList.rev lt_list_done
|
|
| (n, e):: lt_list_todo -> (* n < e *)
|
|
let n_plus_one = n ++ Sil.Int.one in
|
|
if is_neq e n_plus_one then lt_tighten lt_list_done ((n ++ Sil.Int.one, e):: lt_list_todo)
|
|
else lt_tighten ((n, e):: lt_list_done) (lt_list_todo) in
|
|
let lt_list = IList.rev (IList.fold_left get_lt_inequality_info [] ineq_list) in
|
|
lt_tighten [] lt_list in
|
|
let ineq_list' =
|
|
let le_ineq_list =
|
|
IList.map
|
|
(fun (e, n) -> mk_inequality (Sil.BinOp(Sil.Le, e, Sil.exp_int n)))
|
|
le_list_tightened in
|
|
let lt_ineq_list =
|
|
IList.map
|
|
(fun (n, e) -> mk_inequality (Sil.BinOp(Sil.Lt, Sil.exp_int n, e)))
|
|
lt_list_tightened in
|
|
le_ineq_list @ lt_ineq_list in
|
|
let nonineq_list' =
|
|
IList.filter
|
|
(function
|
|
| Sil.Aneq(Sil.Const (Sil.Cint n), e) | Sil.Aneq(e, Sil.Const (Sil.Cint n)) ->
|
|
(not (IList.exists (fun (e', n') -> Sil.exp_equal e e' && Sil.Int.lt n' n) le_list_tightened)) &&
|
|
(not (IList.exists (fun (n', e') -> Sil.exp_equal e e' && Sil.Int.leq n n') lt_list_tightened))
|
|
| _ -> true)
|
|
nonineq_list in
|
|
(ineq_list', nonineq_list')
|
|
|
|
(** remove duplicate atoms and redundant inequalities from a sorted pi *)
|
|
let rec pi_sorted_remove_redundant = function
|
|
| (Sil.Aeq(Sil.BinOp (Sil.Le, e1, Sil.Const (Sil.Cint n1)), Sil.Const (Sil.Cint i1)) as a1) ::
|
|
Sil.Aeq(Sil.BinOp (Sil.Le, e2, Sil.Const (Sil.Cint n2)), Sil.Const (Sil.Cint i2)) :: rest
|
|
when Sil.Int.isone i1 && Sil.Int.isone i2 && Sil.exp_equal e1 e2 && Sil.Int.lt n1 n2 -> (* second inequality redundant *)
|
|
pi_sorted_remove_redundant (a1 :: rest)
|
|
| Sil.Aeq(Sil.BinOp (Sil.Lt, Sil.Const (Sil.Cint n1), e1), Sil.Const (Sil.Cint i1)) ::
|
|
(Sil.Aeq(Sil.BinOp (Sil.Lt, Sil.Const (Sil.Cint n2), e2), Sil.Const (Sil.Cint i2)) as a2) :: rest
|
|
when Sil.Int.isone i1 && Sil.Int.isone i2 && Sil.exp_equal e1 e2 && Sil.Int.lt n1 n2 -> (* first inequality redundant *)
|
|
pi_sorted_remove_redundant (a2 :: rest)
|
|
| a1:: a2:: rest ->
|
|
if Sil.atom_equal a1 a2 then pi_sorted_remove_redundant (a2 :: rest)
|
|
else a1 :: pi_sorted_remove_redundant (a2 :: rest)
|
|
| [a] -> [a]
|
|
| [] -> []
|
|
|
|
(** find the unsigned expressions in sigma (immediately inside a pointsto, for now) *)
|
|
let sigma_get_unsigned_exps sigma =
|
|
let uexps = ref [] in
|
|
let do_hpred = function
|
|
| Sil.Hpointsto(_, Sil.Eexp(e, _), Sil.Sizeof (Sil.Tint ik, _)) when Sil.ikind_is_unsigned ik ->
|
|
uexps := e :: !uexps
|
|
| _ -> () in
|
|
IList.iter do_hpred sigma;
|
|
!uexps
|
|
|
|
(** Normalization of pi.
|
|
The normalization filters out obviously - true disequalities, such as e <> e + 1. *)
|
|
let pi_normalize sub sigma pi0 =
|
|
let pi = IList.map (atom_normalize sub) pi0 in
|
|
let ineq_list, nonineq_list = pi_tighten_ineq pi in
|
|
let syntactically_different = function
|
|
| Sil.BinOp(op1, e1, Sil.Const(c1)), Sil.BinOp(op2, e2, Sil.Const(c2))
|
|
when Sil.exp_equal e1 e2 ->
|
|
Sil.binop_equal op1 op2 && Sil.binop_injective op1 && not (Sil.const_equal c1 c2)
|
|
| e1, Sil.BinOp(op2, e2, Sil.Const(c2))
|
|
when Sil.exp_equal e1 e2 ->
|
|
Sil.binop_injective op2 && Sil.binop_is_zero_runit op2 && not (Sil.const_equal (Sil.Cint Sil.Int.zero) c2)
|
|
| Sil.BinOp(op1, e1, Sil.Const(c1)), e2
|
|
when Sil.exp_equal e1 e2 ->
|
|
Sil.binop_injective op1 && Sil.binop_is_zero_runit op1 && not (Sil.const_equal (Sil.Cint Sil.Int.zero) c1)
|
|
| _ -> false in
|
|
let filter_useful_atom =
|
|
let unsigned_exps = lazy (sigma_get_unsigned_exps sigma) in
|
|
function
|
|
| Sil.Aneq ((Sil.Var _) as e, Sil.Const (Sil.Cint n)) when Sil.Int.isnegative n ->
|
|
not (IList.exists (Sil.exp_equal e) (Lazy.force unsigned_exps))
|
|
| Sil.Aneq(e1, e2) ->
|
|
not (syntactically_different (e1, e2))
|
|
| Sil.Aeq(Sil.Const c1, Sil.Const c2) ->
|
|
not (Sil.const_equal c1 c2)
|
|
| a -> true in
|
|
let pi' = IList.stable_sort Sil.atom_compare ((IList.filter filter_useful_atom nonineq_list) @ ineq_list) in
|
|
let pi'' = pi_sorted_remove_redundant pi' in
|
|
if pi_equal pi0 pi'' then pi0 else pi''
|
|
|
|
let sigma_normalize sub sigma =
|
|
let sigma' =
|
|
IList.stable_sort Sil.hpred_compare (IList.map (hpred_normalize sub) sigma) in
|
|
if sigma_equal sigma sigma' then sigma else sigma'
|
|
|
|
(** normalize the footprint part, and rename any primed vars in the footprint with fresh footprint vars *)
|
|
let footprint_normalize prop =
|
|
let nsigma = sigma_normalize Sil.sub_empty prop.foot_sigma in
|
|
let npi = pi_normalize Sil.sub_empty nsigma prop.foot_pi in
|
|
let fp_vars =
|
|
let fav = pi_fav npi in
|
|
sigma_fav_add fav nsigma;
|
|
fav in
|
|
(* TODO (t4893479): make this check less angelic *)
|
|
if Sil.fav_exists fp_vars Ident.is_normal && not !Config.angelic_execution then
|
|
begin
|
|
L.d_strln "footprint part contains normal variables";
|
|
d_pi npi; L.d_ln ();
|
|
d_sigma nsigma; L.d_ln ();
|
|
assert false
|
|
end;
|
|
Sil.fav_filter_ident fp_vars Ident.is_primed; (* only keep primed vars *)
|
|
let npi', nsigma' =
|
|
if Sil.fav_is_empty fp_vars then npi, nsigma
|
|
else (* replace primed vars by fresh footprint vars *)
|
|
let ids_primed = Sil.fav_to_list fp_vars in
|
|
let ids_footprint =
|
|
IList.map (fun id -> (id, Ident.create_fresh Ident.kfootprint)) ids_primed in
|
|
let ren_sub = Sil.sub_of_list (IList.map (fun (id1, id2) -> (id1, Sil.Var id2)) ids_footprint) in
|
|
let nsigma' = sigma_normalize Sil.sub_empty (sigma_sub ren_sub nsigma) in
|
|
let npi' = pi_normalize Sil.sub_empty nsigma' (pi_sub ren_sub npi) in
|
|
(npi', nsigma') in
|
|
{ prop with foot_pi = npi'; foot_sigma = nsigma' }
|
|
|
|
let exp_normalize_prop prop exp =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> exp_normalize prop.sub exp)
|
|
|
|
let lexp_normalize_prop p lexp =
|
|
let root = Sil.root_of_lexp lexp in
|
|
let offsets = Sil.exp_get_offsets lexp in
|
|
let nroot = exp_normalize_prop p root in
|
|
let noffsets =
|
|
IList.map (fun n -> match n with
|
|
| Sil.Off_fld _ -> n
|
|
| Sil.Off_index e -> Sil.Off_index (exp_normalize_prop p e)
|
|
) offsets in
|
|
Sil.exp_add_offsets nroot noffsets
|
|
|
|
(** Collapse consecutive indices that should be added. For instance,
|
|
this function reduces x[1][1] to x[2]. The [typ] argument is used
|
|
to ensure the soundness of this collapsing. *)
|
|
let exp_collapse_consecutive_indices_prop p typ exp =
|
|
let typ_is_base = function
|
|
| Sil.Tint _ | Sil.Tfloat _ | Sil.Tstruct _ | Sil.Tvoid | Sil.Tfun _ -> true
|
|
| _ -> false in
|
|
let typ_is_one_step_from_base =
|
|
match typ with
|
|
| Sil.Tptr (t, _) | Sil.Tarray (t, _) -> typ_is_base t
|
|
| _ -> false in
|
|
let rec exp_remove e0 =
|
|
match e0 with
|
|
| Sil.Lindex(Sil.Lindex(base, e1), e2) ->
|
|
let e0' = Sil.Lindex(base, Sil.BinOp(Sil.PlusA, e1, e2)) in
|
|
exp_remove e0'
|
|
| _ -> e0 in
|
|
begin
|
|
if typ_is_one_step_from_base then exp_remove exp else exp
|
|
end
|
|
|
|
let atom_normalize_prop prop atom =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> atom_normalize prop.sub atom)
|
|
|
|
let strexp_normalize_prop prop strexp =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> strexp_normalize prop.sub strexp)
|
|
|
|
let hpred_normalize_prop prop hpred =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> hpred_normalize prop.sub hpred)
|
|
|
|
let sigma_normalize_prop prop sigma =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> sigma_normalize prop.sub sigma)
|
|
|
|
let pi_normalize_prop prop pi =
|
|
run_with_abs_val_eq_zero
|
|
(fun () -> pi_normalize prop.sub prop.sigma pi)
|
|
|
|
(** {2 Compaction} *)
|
|
(** Return a compact representation of the prop *)
|
|
let prop_compact sh prop =
|
|
let sigma' = IList.map (Sil.hpred_compact sh) prop.sigma in
|
|
{ prop with sigma = sigma'}
|
|
|
|
(** {2 Function for replacing occurrences of expressions.} *)
|
|
|
|
let replace_pi pi eprop =
|
|
{ eprop with pi = pi }
|
|
|
|
let replace_sigma sigma eprop =
|
|
{ eprop with sigma = sigma }
|
|
|
|
exception No_Footprint
|
|
|
|
let unSome_footprint = function
|
|
| None -> raise No_Footprint
|
|
| Some fp -> fp
|
|
|
|
let replace_sigma_footprint sigma (prop : 'a t) : exposed t =
|
|
{ prop with foot_sigma = sigma }
|
|
|
|
let replace_pi_footprint pi (prop : 'a t) : exposed t =
|
|
{ prop with foot_pi = pi }
|
|
|
|
let sigma_replace_exp epairs sigma =
|
|
let sigma' = IList.map (Sil.hpred_replace_exp epairs) sigma in
|
|
sigma_normalize Sil.sub_empty sigma'
|
|
|
|
let sigma_map prop f =
|
|
let sigma' = IList.map f prop.sigma in
|
|
{ prop with sigma = sigma' }
|
|
|
|
(** {2 Query about Proposition} *)
|
|
|
|
(** Check if the sigma part of the proposition is emp *)
|
|
let prop_is_emp p = match p.sigma with
|
|
| [] -> true
|
|
| _ -> false
|
|
|
|
(** {2 Functions for changing and generating propositions} *)
|
|
|
|
(** Replace the sub part of [prop]. *)
|
|
let prop_replace_sub sub p =
|
|
let nsub = sub_normalize sub in
|
|
{ p with sub = nsub }
|
|
|
|
(** Sil.Construct a disequality. *)
|
|
let mk_neq e1 e2 =
|
|
run_with_abs_val_eq_zero
|
|
(fun () ->
|
|
let ne1 = exp_normalize Sil.sub_empty e1 in
|
|
let ne2 = exp_normalize Sil.sub_empty e2 in
|
|
atom_normalize Sil.sub_empty (Sil.Aneq (ne1, ne2)))
|
|
|
|
(** Sil.Construct an equality. *)
|
|
let mk_eq e1 e2 =
|
|
run_with_abs_val_eq_zero
|
|
(fun () ->
|
|
let ne1 = exp_normalize Sil.sub_empty e1 in
|
|
let ne2 = exp_normalize Sil.sub_empty e2 in
|
|
atom_normalize Sil.sub_empty (Sil.Aeq (ne1, ne2)))
|
|
|
|
let unstructured_type = function
|
|
| Sil.Tstruct _ | Sil.Tarray _ -> false
|
|
| _ -> true
|
|
|
|
(** Construct a points-to predicate for a single program variable.
|
|
If [expand_structs] is true, initialize the fields of structs with fresh variables. *)
|
|
let mk_ptsto_lvar tenv expand_structs inst ((pvar: Sil.pvar), texp, expo) : Sil.hpred =
|
|
mk_ptsto_exp tenv expand_structs (Sil.Lvar pvar, texp, expo) inst
|
|
|
|
(** Sil.Construct a lseg predicate *)
|
|
let mk_lseg k para e_start e_end es_shared =
|
|
let npara = hpara_normalize Sil.sub_empty para in
|
|
Sil.Hlseg (k, npara, e_start, e_end, es_shared)
|
|
|
|
(** Sil.Construct a dllseg predicate *)
|
|
let mk_dllseg k para exp_iF exp_oB exp_oF exp_iB exps_shared =
|
|
let npara = hpara_dll_normalize Sil.sub_empty para in
|
|
Sil.Hdllseg (k, npara, exp_iF, exp_oB , exp_oF, exp_iB, exps_shared)
|
|
|
|
(** Sil.Construct a hpara *)
|
|
let mk_hpara root next svars evars body =
|
|
let para = { Sil.root = root; Sil.next = next; Sil.svars = svars; Sil.evars = evars; Sil.body = body } in
|
|
hpara_normalize Sil.sub_empty para
|
|
|
|
(** Sil.Construct a dll_hpara *)
|
|
let mk_dll_hpara iF oB oF svars evars body =
|
|
let para = { Sil.cell = iF; Sil.blink = oB; Sil.flink = oF; Sil.svars_dll = svars; Sil.evars_dll = evars; Sil.body_dll = body } in
|
|
hpara_dll_normalize Sil.sub_empty para
|
|
|
|
(** Proposition [true /\ emp]. *)
|
|
let prop_emp : normal t =
|
|
{
|
|
sub = Sil.sub_empty;
|
|
pi = [];
|
|
sigma = [];
|
|
foot_pi = [];
|
|
foot_sigma = [];
|
|
}
|
|
|
|
(** Conjoin a heap predicate by separating conjunction. *)
|
|
let prop_hpred_star (p : 'a t) (h : Sil.hpred) : exposed t =
|
|
let sigma' = h:: p.sigma in
|
|
{ p with sigma = sigma'}
|
|
|
|
let prop_sigma_star (p : 'a t) (sigma : Sil.hpred list) : exposed t =
|
|
let sigma' = sigma @ p.sigma in
|
|
{ p with sigma = sigma' }
|
|
|
|
(** return the set of subexpressions of [strexp] *)
|
|
let strexp_get_exps strexp =
|
|
let rec strexp_get_exps_rec exps = function
|
|
| Sil.Eexp (Sil.Const (Sil.Cexn e), _) -> Sil.ExpSet.add e exps
|
|
| Sil.Eexp (e, _) -> Sil.ExpSet.add e exps
|
|
| Sil.Estruct (flds, _) ->
|
|
IList.fold_left (fun exps (_, strexp) -> strexp_get_exps_rec exps strexp) exps flds
|
|
| Sil.Earray (_, elems, _) ->
|
|
IList.fold_left (fun exps (_, strexp) -> strexp_get_exps_rec exps strexp) exps elems in
|
|
strexp_get_exps_rec Sil.ExpSet.empty strexp
|
|
|
|
(** get the set of expressions on the righthand side of [hpred] *)
|
|
let hpred_get_targets = function
|
|
| Sil.Hpointsto (_, rhs, _) -> strexp_get_exps rhs
|
|
| Sil.Hlseg (_, _, _, e, el) ->
|
|
IList.fold_left (fun exps e -> Sil.ExpSet.add e exps) Sil.ExpSet.empty (e :: el)
|
|
| Sil.Hdllseg (_, _, _, oB, oF, iB, el) ->
|
|
(* only one direction supported for now *)
|
|
IList.fold_left (fun exps e -> Sil.ExpSet.add e exps) Sil.ExpSet.empty (oB :: oF :: iB :: el)
|
|
|
|
(** return the set of hpred's and exp's in [sigma] that are reachable from an expression in
|
|
[exps] *)
|
|
let compute_reachable_hpreds sigma exps =
|
|
let rec compute_reachable_hpreds_rec sigma (reach, exps) =
|
|
let add_hpred_if_reachable (reach, exps) = function
|
|
| Sil.Hpointsto (lhs, _, _) as hpred when Sil.ExpSet.mem lhs exps->
|
|
let reach' = Sil.HpredSet.add hpred reach in
|
|
let reach_exps = hpred_get_targets hpred in
|
|
(reach', Sil.ExpSet.union exps reach_exps)
|
|
| _ -> reach, exps in
|
|
let reach', exps' = IList.fold_left add_hpred_if_reachable (reach, exps) sigma in
|
|
if (Sil.HpredSet.cardinal reach) = (Sil.HpredSet.cardinal reach') then (reach, exps)
|
|
else compute_reachable_hpreds_rec sigma (reach', exps') in
|
|
compute_reachable_hpreds_rec sigma (Sil.HpredSet.empty, exps)
|
|
|
|
(** produce a (fieldname, typ) from one of the [src_exps] to [snk_exp] using [reachable_hpreds] *)
|
|
let rec get_fld_typ_path src_exps snk_exp reachable_hpreds =
|
|
let strexp_matches target_exp = function
|
|
| (_, Sil.Eexp (e, _)) -> Sil.exp_equal target_exp e
|
|
| _ -> false in
|
|
let (snk_exp, path) =
|
|
Sil.HpredSet.fold
|
|
(fun hpred (snk_exp, path) -> match hpred with
|
|
| Sil.Hpointsto (lhs, Sil.Estruct (flds, inst), Sil.Sizeof (typ, _)) ->
|
|
(match
|
|
IList.fold_left
|
|
(fun acc fld -> if strexp_matches snk_exp fld then Some fld else acc)
|
|
None
|
|
flds with
|
|
| Some (fld, _) -> (lhs, (Some fld, typ) :: path)
|
|
| None -> (snk_exp, path))
|
|
| Sil.Hpointsto (lhs, Sil.Earray (_, elems, _), Sil.Sizeof (typ, _)) ->
|
|
if IList.exists (fun pair -> strexp_matches snk_exp pair) elems
|
|
then
|
|
(* None means "no field name" ~=~ nameless array index *)
|
|
(lhs, (None, typ) :: path)
|
|
else (snk_exp, path)
|
|
| _ -> (snk_exp, path))
|
|
reachable_hpreds
|
|
(snk_exp, []) in
|
|
if Sil.ExpSet.mem snk_exp src_exps then path
|
|
else get_fld_typ_path src_exps snk_exp reachable_hpreds
|
|
|
|
|
|
(** filter [pi] by removing the pure atoms that do not contain an expression in [exps] *)
|
|
let compute_reachable_atoms pi exps =
|
|
let rec exp_contains = function
|
|
| exp when Sil.ExpSet.mem exp exps -> true
|
|
| Sil.UnOp (_, e, _) | Sil.Cast (_, e) | Sil.Lfield (e, _, _) -> exp_contains e
|
|
| Sil.BinOp (_, e0, e1) | Sil.Lindex (e0, e1) -> exp_contains e0 || exp_contains e1
|
|
| _ -> false in
|
|
IList.filter
|
|
(function
|
|
| Sil.Aeq (lhs, rhs) | Sil.Aneq (lhs, rhs) -> exp_contains lhs || exp_contains rhs)
|
|
pi
|
|
|
|
(** Eliminates all empty lsegs from sigma, and collect equalities
|
|
The empty lsegs include
|
|
(a) "lseg_pe para 0 e elist",
|
|
(b) "dllseg_pe para iF oB oF iB elist" with iF = 0 or iB = 0,
|
|
(c) "lseg_pe para e1 e2 elist" and the rest of sigma contains the "cell" e1,
|
|
(d) "dllseg_pe para iF oB oF iB elist" and the rest of sigma contains
|
|
cell iF or iB. *)
|
|
let sigma_remove_emptylseg sigma =
|
|
let alloc_set =
|
|
let rec f_alloc set = function
|
|
| [] ->
|
|
set
|
|
| Sil.Hpointsto (e, _, _) :: sigma' | Sil.Hlseg (Sil.Lseg_NE, _, e, _, _) :: sigma' ->
|
|
f_alloc (Sil.ExpSet.add e set) sigma'
|
|
| Sil.Hdllseg (Sil.Lseg_NE, _, iF, _, _, iB, _) :: sigma' ->
|
|
f_alloc (Sil.ExpSet.add iF (Sil.ExpSet.add iB set)) sigma'
|
|
| _ :: sigma' ->
|
|
f_alloc set sigma' in
|
|
f_alloc Sil.ExpSet.empty sigma
|
|
in
|
|
let rec f eqs_zero sigma_passed = function
|
|
| [] ->
|
|
(IList.rev eqs_zero, IList.rev sigma_passed)
|
|
| Sil.Hpointsto _ as hpred :: sigma' ->
|
|
f eqs_zero (hpred :: sigma_passed) sigma'
|
|
| Sil.Hlseg (Sil.Lseg_PE, _, e1, e2, _) :: sigma'
|
|
when (Sil.exp_equal e1 Sil.exp_zero) || (Sil.ExpSet.mem e1 alloc_set) ->
|
|
f (Sil.Aeq(e1, e2) :: eqs_zero) sigma_passed sigma'
|
|
| Sil.Hlseg _ as hpred :: sigma' ->
|
|
f eqs_zero (hpred :: sigma_passed) sigma'
|
|
| Sil.Hdllseg (Sil.Lseg_PE, _, iF, oB, oF, iB, _) :: sigma'
|
|
when (Sil.exp_equal iF Sil.exp_zero) || (Sil.ExpSet.mem iF alloc_set)
|
|
|| (Sil.exp_equal iB Sil.exp_zero) || (Sil.ExpSet.mem iB alloc_set) ->
|
|
f (Sil.Aeq(iF, oF):: Sil.Aeq(iB, oB):: eqs_zero) sigma_passed sigma'
|
|
| Sil.Hdllseg _ as hpred :: sigma' ->
|
|
f eqs_zero (hpred :: sigma_passed) sigma'
|
|
in
|
|
f [] [] sigma
|
|
|
|
let sigma_intro_nonemptylseg e1 e2 sigma =
|
|
let rec f sigma_passed = function
|
|
| [] ->
|
|
IList.rev sigma_passed
|
|
| Sil.Hpointsto _ as hpred :: sigma' ->
|
|
f (hpred :: sigma_passed) sigma'
|
|
| Sil.Hlseg (Sil.Lseg_PE, para, f1, f2, shared) :: sigma'
|
|
when (Sil.exp_equal e1 f1 && Sil.exp_equal e2 f2)
|
|
|| (Sil.exp_equal e2 f1 && Sil.exp_equal e1 f2) ->
|
|
f (Sil.Hlseg (Sil.Lseg_NE, para, f1, f2, shared) :: sigma_passed) sigma'
|
|
| Sil.Hlseg _ as hpred :: sigma' ->
|
|
f (hpred :: sigma_passed) sigma'
|
|
| Sil.Hdllseg (Sil.Lseg_PE, para, iF, oB, oF, iB, shared) :: sigma'
|
|
when (Sil.exp_equal e1 iF && Sil.exp_equal e2 oF)
|
|
|| (Sil.exp_equal e2 iF && Sil.exp_equal e1 oF)
|
|
|| (Sil.exp_equal e1 iB && Sil.exp_equal e2 oB)
|
|
|| (Sil.exp_equal e2 iB && Sil.exp_equal e1 oB) ->
|
|
f (Sil.Hdllseg (Sil.Lseg_NE, para, iF, oB, oF, iB, shared) :: sigma_passed) sigma'
|
|
| Sil.Hdllseg _ as hpred :: sigma' ->
|
|
f (hpred :: sigma_passed) sigma'
|
|
in
|
|
f [] sigma
|
|
|
|
let normalize_and_strengthen_atom (p : normal t) (a : Sil.atom) : Sil.atom =
|
|
let a' = atom_normalize p.sub a in
|
|
match a' with
|
|
| Sil.Aeq (Sil.BinOp (Sil.Le, Sil.Var id, Sil.Const (Sil.Cint n)), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
let lower = Sil.exp_int (n -- Sil.Int.one) in
|
|
let a_lower = Sil.Aeq (Sil.BinOp (Sil.Lt, lower, Sil.Var id), Sil.exp_one) in
|
|
if not (IList.mem Sil.atom_equal a_lower p.pi) then a'
|
|
else Sil.Aeq (Sil.Var id, Sil.exp_int n)
|
|
| Sil.Aeq (Sil.BinOp (Sil.Lt, Sil.Const (Sil.Cint n), Sil.Var id), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
let upper = Sil.exp_int (n ++ Sil.Int.one) in
|
|
let a_upper = Sil.Aeq (Sil.BinOp (Sil.Le, Sil.Var id, upper), Sil.exp_one) in
|
|
if not (IList.mem Sil.atom_equal a_upper p.pi) then a'
|
|
else Sil.Aeq (Sil.Var id, upper)
|
|
| Sil.Aeq (Sil.BinOp (Sil.Ne, e1, e2), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
|
|
Sil.Aneq (e1, e2)
|
|
| _ -> a'
|
|
|
|
(** Conjoin a pure atomic predicate by normal conjunction. *)
|
|
let rec prop_atom_and ?(footprint=false) (p : normal t) a : normal t =
|
|
let a' = normalize_and_strengthen_atom p a in
|
|
if IList.mem Sil.atom_equal a' p.pi then p
|
|
else begin
|
|
let p' =
|
|
match a' with
|
|
| Sil.Aeq (Sil.Var i, e) when Sil.ident_in_exp i e -> p
|
|
| Sil.Aeq (Sil.Var i, e) ->
|
|
let sub_list = [(i, e)] in
|
|
let mysub = Sil.sub_of_list sub_list in
|
|
let p_sub = Sil.sub_filter (fun i' -> not (Ident.equal i i')) p.sub in
|
|
let sub' = Sil.sub_join mysub (Sil.sub_range_map (Sil.exp_sub mysub) p_sub) in
|
|
let (nsub', npi', nsigma') =
|
|
let nsigma' = sigma_normalize sub' p.sigma in
|
|
(sub_normalize sub', pi_normalize sub' nsigma' p.pi, nsigma') in
|
|
let (eqs_zero, nsigma'') = sigma_remove_emptylseg nsigma' in
|
|
let p' = { p with sub = nsub'; pi = npi'; sigma = nsigma''} in
|
|
IList.fold_left (prop_atom_and ~footprint) p' eqs_zero
|
|
| Sil.Aeq (e1, e2) when (Sil.exp_compare e1 e2 = 0) ->
|
|
p
|
|
| Sil.Aneq (e1, e2) ->
|
|
let sigma' = sigma_intro_nonemptylseg e1 e2 p.sigma in
|
|
let pi' = pi_normalize p.sub sigma' (a':: p.pi) in
|
|
{ p with pi = pi'; sigma = sigma'}
|
|
| _ ->
|
|
let pi' = pi_normalize p.sub p.sigma (a':: p.pi) in
|
|
{ p with pi = pi'} in
|
|
if not footprint then p'
|
|
else begin
|
|
let fav_a' = Sil.atom_fav a' in
|
|
let fav_nofootprint_a' =
|
|
Sil.fav_copy_filter_ident fav_a' (fun id -> not (Ident.is_footprint id)) in
|
|
let predicate_warning =
|
|
not (Sil.fav_is_empty fav_nofootprint_a') in
|
|
let p'' =
|
|
if predicate_warning then footprint_normalize p'
|
|
else
|
|
match a' with
|
|
| Sil.Aeq (Sil.Var i, e) when not (Sil.ident_in_exp i e) ->
|
|
let mysub = Sil.sub_of_list [(i, e)] in
|
|
let foot_sigma' = sigma_normalize mysub p'.foot_sigma in
|
|
let foot_pi' = a' :: pi_normalize mysub foot_sigma' p'.foot_pi in
|
|
footprint_normalize { p' with foot_pi = foot_pi'; foot_sigma = foot_sigma' }
|
|
| _ ->
|
|
footprint_normalize { p' with foot_pi = a' :: p'.foot_pi } in
|
|
if predicate_warning then (L.d_warning "dropping non-footprint "; Sil.d_atom a'; L.d_ln ());
|
|
p''
|
|
end
|
|
end
|
|
|
|
(** Conjoin [exp1]=[exp2] with a symbolic heap [prop]. *)
|
|
let conjoin_eq ?(footprint = false) exp1 exp2 prop =
|
|
prop_atom_and ~footprint prop (Sil.Aeq(exp1, exp2))
|
|
|
|
(** Conjoin [exp1!=exp2] with a symbolic heap [prop]. *)
|
|
let conjoin_neq ?(footprint = false) exp1 exp2 prop =
|
|
prop_atom_and ~footprint prop (Sil.Aneq (exp1, exp2))
|
|
|
|
(** Return the spatial part *)
|
|
let get_sigma (p: 'a t) : Sil.hpred list = p.sigma
|
|
|
|
(** Return the pure part of the footprint *)
|
|
let get_pi_footprint p =
|
|
p.foot_pi
|
|
|
|
(** Return the spatial part of the footprint *)
|
|
let get_sigma_footprint p =
|
|
p.foot_sigma
|
|
|
|
(** Reset every inst in the prop using the given map *)
|
|
let prop_reset_inst inst_map prop =
|
|
let sigma' = IList.map (Sil.hpred_instmap inst_map) (get_sigma prop) in
|
|
let sigma_fp' = IList.map (Sil.hpred_instmap inst_map) (get_sigma_footprint prop) in
|
|
replace_sigma_footprint sigma_fp' (replace_sigma sigma' prop)
|
|
|
|
(** {2 Attributes} *)
|
|
|
|
(** Return the exp and attribute marked in the atom if any, and return None otherwise *)
|
|
let atom_get_exp_attribute = function
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att), e)
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att)) -> Some (e, att)
|
|
| _ -> None
|
|
|
|
(** Check whether an atom is used to mark an attribute *)
|
|
let atom_is_attribute a =
|
|
atom_get_exp_attribute a <> None
|
|
|
|
(** Get the attribute associated to the expression, if any *)
|
|
let get_exp_attributes prop exp =
|
|
let nexp = exp_normalize_prop prop exp in
|
|
let atom_get_attr attributes atom =
|
|
match atom with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att), e) when Sil.exp_equal e nexp -> att:: attributes
|
|
| _ -> attributes in
|
|
IList.fold_left atom_get_attr [] prop.pi
|
|
|
|
let attributes_in_same_category attr1 attr2 =
|
|
let cat1 = Sil.attribute_to_category attr1 in
|
|
let cat2 = Sil.attribute_to_category attr2 in
|
|
Sil.attribute_category_equal cat1 cat2
|
|
|
|
let get_attribute prop exp category =
|
|
let atts = get_exp_attributes prop exp in
|
|
try Some (IList.find
|
|
(fun att ->
|
|
Sil.attribute_category_equal
|
|
(Sil.attribute_to_category att) category)
|
|
atts)
|
|
with Not_found -> None
|
|
|
|
let get_undef_attribute prop exp =
|
|
get_attribute prop exp Sil.ACundef
|
|
|
|
let get_resource_attribute prop exp =
|
|
get_attribute prop exp Sil.ACresource
|
|
|
|
let get_taint_attribute prop exp =
|
|
get_attribute prop exp Sil.ACtaint
|
|
|
|
let get_autorelease_attribute prop exp =
|
|
get_attribute prop exp Sil.ACautorelease
|
|
|
|
let get_objc_null_attribute prop exp =
|
|
get_attribute prop exp Sil.ACobjc_null
|
|
|
|
let get_div0_attribute prop exp =
|
|
get_attribute prop exp Sil.ACdiv0
|
|
|
|
let has_dangling_uninit_attribute prop exp =
|
|
let la = get_exp_attributes prop exp in
|
|
IList.exists (fun a -> Sil.attribute_equal a (Sil.Adangling (Sil.DAuninit))) la
|
|
|
|
(** Get all the attributes of the prop *)
|
|
let get_all_attributes prop =
|
|
let res = ref [] in
|
|
let do_atom a = match atom_get_exp_attribute a with
|
|
| Some (e, att) -> res := (e, att) :: !res
|
|
| None -> () in
|
|
IList.iter do_atom prop.pi;
|
|
IList.rev !res
|
|
|
|
(** Set an attribute associated to the expression *)
|
|
let set_exp_attribute prop exp att =
|
|
let exp_att = Sil.Const (Sil.Cattribute att) in
|
|
conjoin_neq exp exp_att prop
|
|
|
|
(** Replace an attribute associated to the expression *)
|
|
let add_or_replace_exp_attribute_check_changed check_attribute_change prop exp att =
|
|
let nexp = exp_normalize_prop prop exp in
|
|
let found = ref false in
|
|
let atom_map a = match a with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att_old))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att_old), e) ->
|
|
if Sil.exp_equal nexp e && (attributes_in_same_category att_old att) then
|
|
begin
|
|
found := true;
|
|
check_attribute_change att_old att;
|
|
let e1, e2 = exp_reorder e (Sil.Const (Sil.Cattribute att)) in
|
|
Sil.Aneq (e1, e2)
|
|
end
|
|
else a
|
|
| _ -> a in
|
|
let pi' = IList.map atom_map (get_pi prop) in
|
|
if !found then replace_pi pi' prop
|
|
else set_exp_attribute prop nexp att
|
|
|
|
let add_or_replace_exp_attribute prop exp att =
|
|
(* wrapper for the most common case: do nothing *)
|
|
let check_attr_changed = (fun _ _ -> ()) in
|
|
add_or_replace_exp_attribute_check_changed check_attr_changed prop exp att
|
|
|
|
(** mark Sil.Var's or Sil.Lvar's as undefined *)
|
|
let mark_vars_as_undefined prop vars_to_mark callee_pname loc path_pos =
|
|
let att_undef = Sil.Aundef (callee_pname, loc, path_pos) in
|
|
let mark_var_as_undefined exp prop =
|
|
match exp with
|
|
| Sil.Var _ | Sil.Lvar _ -> add_or_replace_exp_attribute prop exp att_undef
|
|
| _ -> prop in
|
|
IList.fold_left (fun prop id -> mark_var_as_undefined id prop) prop vars_to_mark
|
|
|
|
(** Remove an attribute from all the atoms in the heap *)
|
|
let remove_attribute att prop =
|
|
let atom_remove atom pi = match atom with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att_old))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att_old), e) ->
|
|
if Sil.attribute_equal att_old att then
|
|
pi
|
|
else atom:: pi
|
|
| _ -> atom:: pi in
|
|
let pi' = IList.fold_right atom_remove (get_pi prop) [] in
|
|
replace_pi pi' prop
|
|
|
|
let remove_attribute_from_exp att prop exp =
|
|
let nexp = exp_normalize_prop prop exp in
|
|
let atom_remove atom pi = match atom with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att_old))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att_old), e) ->
|
|
if Sil.attribute_equal att_old att && Sil.exp_equal nexp e then
|
|
pi
|
|
else atom:: pi
|
|
| _ -> atom:: pi in
|
|
let pi' = IList.fold_right atom_remove (get_pi prop) [] in
|
|
replace_pi pi' prop
|
|
|
|
(* Replace an attribute OBJC_NULL($n1) with OBJC_NULL(var) when var = $n1, and also sets $n1 = 0 *)
|
|
let replace_objc_null prop lhs_exp rhs_exp =
|
|
match get_objc_null_attribute prop rhs_exp, rhs_exp with
|
|
| Some att, Sil.Var var ->
|
|
let prop = remove_attribute_from_exp att prop rhs_exp in
|
|
let prop = conjoin_eq rhs_exp Sil.exp_zero prop in
|
|
add_or_replace_exp_attribute prop lhs_exp att
|
|
| _ -> prop
|
|
|
|
(** Get all the attributes of the prop *)
|
|
let get_atoms_with_attribute att prop =
|
|
let atom_remove atom autoreleased_atoms = match atom with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att_old))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att_old), e) ->
|
|
if Sil.attribute_equal att_old att then
|
|
e:: autoreleased_atoms
|
|
else autoreleased_atoms
|
|
| _ -> autoreleased_atoms in
|
|
IList.fold_right atom_remove (get_pi prop) []
|
|
|
|
(** Apply f to every resource attribute in the prop *)
|
|
let attribute_map_resource prop f =
|
|
let pi = get_pi prop in
|
|
let attribute_map e = function
|
|
| Sil.Aresource ra ->
|
|
Sil.Aresource (f e ra)
|
|
| att -> att in
|
|
let atom_map a = match a with
|
|
| Sil.Aneq (e, Sil.Const (Sil.Cattribute att))
|
|
| Sil.Aneq (Sil.Const (Sil.Cattribute att), e) ->
|
|
let att' = attribute_map e att in
|
|
let e1, e2 = exp_reorder e (Sil.Const (Sil.Cattribute att')) in
|
|
Sil.Aneq (e1, e2)
|
|
| _ -> a in
|
|
let pi' = IList.map atom_map pi in
|
|
replace_pi pi' prop
|
|
|
|
(** type for arithmetic problems *)
|
|
type arith_problem =
|
|
| Div0 of Sil.exp (* division by zero *)
|
|
| UminusUnsigned of Sil.exp * Sil.typ (* unary minus of unsigned type applied to the given expression *)
|
|
|
|
(** Look for an arithmetic problem in [exp] *)
|
|
let find_arithmetic_problem proc_node_session prop exp =
|
|
let exps_divided = ref [] in
|
|
let uminus_unsigned = ref [] in
|
|
let res = ref prop in
|
|
let check_zero e =
|
|
match exp_normalize_prop prop e with
|
|
| Sil.Const c when iszero_int_float c -> true
|
|
| _ ->
|
|
res := add_or_replace_exp_attribute !res e (Sil.Adiv0 proc_node_session);
|
|
false in
|
|
let rec walk = function
|
|
| Sil.Var _ -> ()
|
|
| Sil.UnOp (Sil.Neg, e, Some ((Sil.Tint (Sil.IUChar | Sil.IUInt | Sil.IUShort | Sil.IULong | Sil.IULongLong) as typ))) ->
|
|
uminus_unsigned := (e, typ) :: !uminus_unsigned
|
|
| Sil.UnOp(_, e, _) -> walk e
|
|
| Sil.BinOp(op, e1, e2) ->
|
|
if op = Sil.Div || op = Sil.Mod then exps_divided := e2 :: !exps_divided;
|
|
walk e1; walk e2
|
|
| Sil.Const _ -> ()
|
|
| Sil.Cast (_, e) -> walk e
|
|
| Sil.Lvar _ -> ()
|
|
| Sil.Lfield (e, _, _) -> walk e
|
|
| Sil.Lindex (e1, e2) -> walk e1; walk e2
|
|
| Sil.Sizeof _ -> () in
|
|
walk exp;
|
|
try Some (Div0 (IList.find check_zero !exps_divided)), !res
|
|
with Not_found ->
|
|
(match !uminus_unsigned with
|
|
| (e, t):: _ -> Some (UminusUnsigned (e, t)), !res
|
|
| _ -> None, !res)
|
|
|
|
(** Deallocate the stack variables in [pvars], and replace them by normal variables.
|
|
Return the list of stack variables whose address was still present after deallocation. *)
|
|
let deallocate_stack_vars p pvars =
|
|
let filter = function
|
|
| Sil.Hpointsto (Sil.Lvar v, _, _) ->
|
|
IList.exists (Sil.pvar_equal v) pvars
|
|
| _ -> false in
|
|
let sigma_stack, sigma_other = IList.partition filter p.sigma in
|
|
let fresh_address_vars = ref [] in (* fresh vars substituted for the address of stack vars *)
|
|
let stack_vars_address_in_post = ref [] in (* stack vars whose address is still present *)
|
|
let exp_replace = IList.map (function
|
|
| Sil.Hpointsto (Sil.Lvar v, _, _) ->
|
|
let freshv = Ident.create_fresh Ident.kprimed in
|
|
fresh_address_vars := (v, freshv) :: !fresh_address_vars;
|
|
(Sil.Lvar v, Sil.Var freshv)
|
|
| _ -> assert false) sigma_stack in
|
|
let pi1 = IList.map (fun (id, e) -> Sil.Aeq (Sil.Var id, e)) (Sil.sub_to_list p.sub) in
|
|
let pi = IList.map (Sil.atom_replace_exp exp_replace) (p.pi @ pi1) in
|
|
let p' = { p with sub = Sil.sub_empty; pi = []; sigma = sigma_replace_exp exp_replace sigma_other } in
|
|
let p'' =
|
|
let res = ref p' in
|
|
let p'_fav = prop_fav p' in
|
|
let do_var (v, freshv) =
|
|
if Sil.fav_mem p'_fav freshv then (* the address of a de-allocated stack var in in the post *)
|
|
begin
|
|
stack_vars_address_in_post := v :: !stack_vars_address_in_post;
|
|
res :=
|
|
add_or_replace_exp_attribute !res (Sil.Var freshv) (Sil.Adangling Sil.DAaddr_stack_var)
|
|
end in
|
|
IList.iter do_var !fresh_address_vars;
|
|
!res in
|
|
!stack_vars_address_in_post, IList.fold_left prop_atom_and p'' pi
|
|
|
|
|
|
(** {1 Functions for transforming footprints into propositions.} *)
|
|
|
|
(** The ones used for abstraction add/remove local stacks in order to
|
|
stop the firing of some abstraction rules. The other usual
|
|
transforation functions do not use this hack. *)
|
|
|
|
(** Extract the footprint and return it as a prop *)
|
|
let extract_footprint p =
|
|
{ prop_emp with pi = p.foot_pi; sigma = p.foot_sigma }
|
|
|
|
(** Extract the (footprint,current) pair *)
|
|
let extract_spec p =
|
|
let pre = extract_footprint p in
|
|
let post = { p with foot_pi = []; foot_sigma = [] } in
|
|
(pre, post)
|
|
|
|
(** [prop_set_fooprint p p_foot] sets proposition [p_foot] as footprint of [p]. *)
|
|
let prop_set_footprint p p_foot =
|
|
let pi = (IList.map (fun (i, e) -> Sil.Aeq(Sil.Var i, e)) (Sil.sub_to_list p_foot.sub)) @ p_foot.pi in
|
|
{ p with foot_pi = pi; foot_sigma = p_foot.sigma }
|
|
|
|
(** {2 Functions for renaming primed variables by "canonical names"} *)
|
|
|
|
module ExpStack : sig
|
|
val init : Sil.exp list -> unit
|
|
val final : unit -> unit
|
|
val is_empty : unit -> bool
|
|
val push : Sil.exp -> unit
|
|
val pop : unit -> Sil.exp
|
|
end = struct
|
|
let stack = Stack.create ()
|
|
let init es =
|
|
Stack.clear stack;
|
|
IList.iter (fun e -> Stack.push e stack) (IList.rev es)
|
|
let final () = Stack.clear stack
|
|
let is_empty () = Stack.is_empty stack
|
|
let push e = Stack.push e stack
|
|
let pop () = Stack.pop stack
|
|
end
|
|
|
|
let sigma_get_start_lexps_sort sigma =
|
|
let exp_compare_neg e1 e2 = - (Sil.exp_compare e1 e2) in
|
|
let filter e = Sil.fav_for_all (Sil.exp_fav e) Ident.is_normal in
|
|
let lexps = Sil.hpred_list_get_lexps filter sigma in
|
|
IList.sort exp_compare_neg lexps
|
|
|
|
let sigma_dfs_sort sigma =
|
|
|
|
let init () =
|
|
let start_lexps = sigma_get_start_lexps_sort sigma in
|
|
ExpStack.init start_lexps in
|
|
|
|
let final () = ExpStack.final () in
|
|
|
|
let rec handle_strexp = function
|
|
| Sil.Eexp (e, inst) -> ExpStack.push e
|
|
| Sil.Estruct (fld_se_list, inst) ->
|
|
IList.iter (fun (_, se) -> handle_strexp se) fld_se_list
|
|
| Sil.Earray (_, idx_se_list, inst) ->
|
|
IList.iter (fun (_, se) -> handle_strexp se) idx_se_list in
|
|
|
|
let rec handle_e visited seen e = function
|
|
| [] -> (visited, IList.rev seen)
|
|
| hpred :: cur ->
|
|
begin
|
|
match hpred with
|
|
| Sil.Hpointsto (e', se, _) when Sil.exp_equal e e' ->
|
|
handle_strexp se;
|
|
(hpred:: visited, IList.rev_append cur seen)
|
|
| Sil.Hlseg (_, _, root, next, shared) when Sil.exp_equal e root ->
|
|
IList.iter ExpStack.push (next:: shared);
|
|
(hpred:: visited, IList.rev_append cur seen)
|
|
| Sil.Hdllseg (_, _, iF, oB, oF, iB, shared)
|
|
when Sil.exp_equal e iF || Sil.exp_equal e iB ->
|
|
IList.iter ExpStack.push (oB:: oF:: shared);
|
|
(hpred:: visited, IList.rev_append cur seen)
|
|
| _ ->
|
|
handle_e visited (hpred:: seen) e cur
|
|
end in
|
|
|
|
let rec handle_sigma visited = function
|
|
| [] -> IList.rev visited
|
|
| cur ->
|
|
if ExpStack.is_empty () then
|
|
let cur' = sigma_normalize Sil.sub_empty cur in
|
|
IList.rev_append cur' visited
|
|
else
|
|
let e = ExpStack.pop () in
|
|
let (visited', cur') = handle_e visited [] e cur in
|
|
handle_sigma visited' cur' in
|
|
|
|
init ();
|
|
let sigma' = handle_sigma [] sigma in
|
|
final ();
|
|
sigma'
|
|
|
|
let prop_dfs_sort p =
|
|
let sigma = get_sigma p in
|
|
let sigma' = sigma_dfs_sort sigma in
|
|
let sigma_fp = get_sigma_footprint p in
|
|
let sigma_fp' = sigma_dfs_sort sigma_fp in
|
|
let p' = { p with sigma = sigma'; foot_sigma = sigma_fp'} in
|
|
(* L.err "@[<2>P SORTED:@\n%a@\n@." pp_prop p'; *)
|
|
p'
|
|
|
|
let prop_fav_add_dfs fav prop =
|
|
prop_fav_add fav (prop_dfs_sort prop)
|
|
|
|
let rec strexp_get_array_indices acc = function
|
|
| Sil.Eexp _ -> acc
|
|
| Sil.Estruct (fsel, inst) ->
|
|
let se_list = IList.map snd fsel in
|
|
IList.fold_left strexp_get_array_indices acc se_list
|
|
| Sil.Earray (size, isel, _) ->
|
|
let acc_new = IList.fold_left (fun acc' (idx, _) -> idx:: acc') acc isel in
|
|
let se_list = IList.map snd isel in
|
|
IList.fold_left strexp_get_array_indices acc_new se_list
|
|
|
|
let hpred_get_array_indices acc = function
|
|
| Sil.Hpointsto (_, se, _) -> strexp_get_array_indices acc se
|
|
| Sil.Hlseg _ | Sil.Hdllseg _ -> acc
|
|
|
|
let sigma_get_array_indices sigma =
|
|
let indices = IList.fold_left hpred_get_array_indices [] sigma in
|
|
IList.rev indices
|
|
|
|
let compute_reindexing fav_add get_id_offset list =
|
|
let rec select list_passed list_seen = function
|
|
| [] -> list_passed
|
|
| x :: list_rest ->
|
|
let id_offset_opt = get_id_offset x in
|
|
let list_passed_new = match id_offset_opt with
|
|
| None -> list_passed
|
|
| Some (id, _) ->
|
|
let fav = Sil.fav_new () in
|
|
IList.iter (fav_add fav) list_seen;
|
|
IList.iter (fav_add fav) list_passed;
|
|
if (Sil.fav_exists fav (Ident.equal id))
|
|
then list_passed
|
|
else (x:: list_passed) in
|
|
let list_seen_new = x:: list_seen in
|
|
select list_passed_new list_seen_new list_rest in
|
|
let list_passed = select [] [] list in
|
|
let transform x =
|
|
let id, offset = match get_id_offset x with None -> assert false | Some io -> io in
|
|
let base_new = Sil.Var (Ident.create_fresh Ident.kprimed) in
|
|
let offset_new = Sil.exp_int (Sil.Int.neg offset) in
|
|
let exp_new = Sil.BinOp(Sil.PlusA, base_new, offset_new) in
|
|
(id, exp_new) in
|
|
let reindexing = IList.map transform list_passed in
|
|
Sil.sub_of_list reindexing
|
|
|
|
let compute_reindexing_from_indices indices =
|
|
let get_id_offset = function
|
|
| Sil.BinOp (Sil.PlusA, Sil.Var id, Sil.Const(Sil.Cint offset)) ->
|
|
if Ident.is_primed id then Some (id, offset) else None
|
|
| _ -> None in
|
|
let fav_add = Sil.exp_fav_add in
|
|
compute_reindexing fav_add get_id_offset indices
|
|
|
|
let apply_reindexing subst prop =
|
|
let nsigma = sigma_normalize subst prop.sigma in
|
|
let npi = pi_normalize subst nsigma prop.pi in
|
|
let nsub, atoms =
|
|
let dom_subst = IList.map fst (Sil.sub_to_list subst) in
|
|
let in_dom_subst id = IList.exists (Ident.equal id) dom_subst in
|
|
let sub' = Sil.sub_filter (fun id -> not (in_dom_subst id)) prop.sub in
|
|
let contains_substituted_id e = Sil.fav_exists (Sil.exp_fav e) in_dom_subst in
|
|
let sub_eqs, sub_keep = Sil.sub_range_partition contains_substituted_id sub' in
|
|
let eqs = Sil.sub_to_list sub_eqs in
|
|
let atoms = IList.map (fun (id, e) -> Sil.Aeq (Sil.Var id, exp_normalize subst e)) eqs in
|
|
(sub_keep, atoms) in
|
|
let p' = { prop with sub = nsub; pi = npi; sigma = nsigma } in
|
|
IList.fold_left prop_atom_and p' atoms
|
|
|
|
let prop_rename_array_indices prop =
|
|
if !Config.footprint then prop
|
|
else begin
|
|
let indices = sigma_get_array_indices prop.sigma in
|
|
let not_same_base_lt_offsets e1 e2 =
|
|
match e1, e2 with
|
|
| Sil.BinOp(Sil.PlusA, e1', Sil.Const (Sil.Cint n1')),
|
|
Sil.BinOp(Sil.PlusA, e2', Sil.Const (Sil.Cint n2')) ->
|
|
not (Sil.exp_equal e1' e2' && Sil.Int.lt n1' n2')
|
|
| _ -> true in
|
|
let rec select_minimal_indices indices_seen = function
|
|
| [] -> IList.rev indices_seen
|
|
| index:: indices_rest ->
|
|
let indices_seen' = IList.filter (not_same_base_lt_offsets index) indices_seen in
|
|
let indices_seen_new = index:: indices_seen' in
|
|
let indices_rest_new = IList.filter (not_same_base_lt_offsets index) indices_rest in
|
|
select_minimal_indices indices_seen_new indices_rest_new in
|
|
let minimal_indices = select_minimal_indices [] indices in
|
|
let subst = compute_reindexing_from_indices minimal_indices in
|
|
apply_reindexing subst prop
|
|
end
|
|
|
|
let rec pp_ren pe f = function
|
|
| [] -> ()
|
|
| [(i, x)] -> F.fprintf f "%a->%a" (Ident.pp pe) i (Ident.pp pe) x
|
|
| (i, x):: ren -> F.fprintf f "%a->%a, %a" (Ident.pp pe) i (Ident.pp pe) x (pp_ren pe) ren
|
|
|
|
let compute_renaming fav =
|
|
let ids = Sil.fav_to_list fav in
|
|
let ids_primed, ids_nonprimed = IList.partition Ident.is_primed ids in
|
|
let ids_footprint = IList.filter Ident.is_footprint ids_nonprimed in
|
|
|
|
let id_base_primed = Ident.create Ident.kprimed 0 in
|
|
let id_base_footprint = Ident.create Ident.kfootprint 0 in
|
|
|
|
let rec f id_base index ren_subst = function
|
|
| [] -> ren_subst
|
|
| id:: ids ->
|
|
let new_id = Ident.set_stamp id_base index in
|
|
if Ident.equal id new_id then
|
|
f id_base (index + 1) ren_subst ids
|
|
else
|
|
f id_base (index + 1) ((id, new_id):: ren_subst) ids in
|
|
|
|
let ren_primed = f id_base_primed 0 [] ids_primed in
|
|
let ren_footprint = f id_base_footprint 0 [] ids_footprint in
|
|
|
|
ren_primed @ ren_footprint
|
|
|
|
let rec idlist_assoc id = function
|
|
| [] -> raise Not_found
|
|
| (i, x):: l -> if Ident.equal i id then x else idlist_assoc id l
|
|
|
|
let ident_captured_ren ren id =
|
|
try (idlist_assoc id ren)
|
|
with Not_found -> id
|
|
(* If not defined in ren, id should be mapped to itself *)
|
|
|
|
let rec exp_captured_ren ren = function
|
|
| Sil.Var id -> Sil.Var (ident_captured_ren ren id)
|
|
| Sil.Const (Sil.Cexn e) -> Sil.Const (Sil.Cexn (exp_captured_ren ren e))
|
|
| Sil.Const _ as e -> e
|
|
| Sil.Sizeof (t, st) -> Sil.Sizeof (typ_captured_ren ren t, st)
|
|
| Sil.Cast (t, e) -> Sil.Cast (t, exp_captured_ren ren e)
|
|
| Sil.UnOp (op, e, topt) ->
|
|
let topt' = match topt with
|
|
| Some t -> Some (typ_captured_ren ren t)
|
|
| None -> None in
|
|
Sil.UnOp (op, exp_captured_ren ren e, topt')
|
|
| Sil.BinOp (op, e1, e2) ->
|
|
let e1' = exp_captured_ren ren e1 in
|
|
let e2' = exp_captured_ren ren e2 in
|
|
Sil.BinOp (op, e1', e2')
|
|
| Sil.Lvar id -> Sil.Lvar id
|
|
| Sil.Lfield (e, fld, typ) -> Sil.Lfield (exp_captured_ren ren e, fld, typ_captured_ren ren typ)
|
|
| Sil.Lindex (e1, e2) ->
|
|
let e1' = exp_captured_ren ren e1 in
|
|
let e2' = exp_captured_ren ren e2 in
|
|
Sil.Lindex(e1', e2')
|
|
|
|
(* Apply a renaming function to a type *)
|
|
and typ_captured_ren ren typ = match typ with
|
|
| Sil.Tvar _
|
|
| Sil.Tint _
|
|
| Sil.Tfloat _
|
|
| Sil.Tvoid
|
|
| Sil.Tstruct _
|
|
| Sil.Tfun _ ->
|
|
typ
|
|
| Sil.Tptr (t', pk) ->
|
|
Sil.Tptr (typ_captured_ren ren t', pk)
|
|
| Sil.Tarray (t, e) ->
|
|
Sil.Tarray (typ_captured_ren ren t, exp_captured_ren ren e)
|
|
| Sil.Tenum econsts ->
|
|
typ
|
|
|
|
let atom_captured_ren ren = function
|
|
| Sil.Aeq (e1, e2) ->
|
|
Sil.Aeq (exp_captured_ren ren e1, exp_captured_ren ren e2)
|
|
| Sil.Aneq (e1, e2) ->
|
|
Sil.Aneq (exp_captured_ren ren e1, exp_captured_ren ren e2)
|
|
|
|
let rec strexp_captured_ren ren = function
|
|
| Sil.Eexp (e, inst) ->
|
|
Sil.Eexp (exp_captured_ren ren e, inst)
|
|
| Sil.Estruct (fld_se_list, inst) ->
|
|
let f (fld, se) = (fld, strexp_captured_ren ren se) in
|
|
Sil.Estruct (IList.map f fld_se_list, inst)
|
|
| Sil.Earray (size, idx_se_list, inst) ->
|
|
let f (idx, se) =
|
|
let idx' = exp_captured_ren ren idx in
|
|
(idx', strexp_captured_ren ren se) in
|
|
let size' = exp_captured_ren ren size in
|
|
Sil.Earray (size', IList.map f idx_se_list, inst)
|
|
|
|
and hpred_captured_ren ren = function
|
|
| Sil.Hpointsto (base, se, te) ->
|
|
let base' = exp_captured_ren ren base in
|
|
let se' = strexp_captured_ren ren se in
|
|
let te' = exp_captured_ren ren te in
|
|
Sil.Hpointsto (base', se', te')
|
|
| Sil.Hlseg (k, para, e1, e2, elist) ->
|
|
let para' = hpara_ren para in
|
|
let e1' = exp_captured_ren ren e1 in
|
|
let e2' = exp_captured_ren ren e2 in
|
|
let elist' = IList.map (exp_captured_ren ren) elist in
|
|
Sil.Hlseg (k, para', e1', e2', elist')
|
|
| Sil.Hdllseg (k, para, e1, e2, e3, e4, elist) ->
|
|
let para' = hpara_dll_ren para in
|
|
let e1' = exp_captured_ren ren e1 in
|
|
let e2' = exp_captured_ren ren e2 in
|
|
let e3' = exp_captured_ren ren e3 in
|
|
let e4' = exp_captured_ren ren e4 in
|
|
let elist' = IList.map (exp_captured_ren ren) elist in
|
|
Sil.Hdllseg (k, para', e1', e2', e3', e4', elist')
|
|
|
|
and hpara_ren para =
|
|
let av = Sil.hpara_shallow_av para in
|
|
let ren = compute_renaming av in
|
|
let root' = ident_captured_ren ren para.Sil.root in
|
|
let next' = ident_captured_ren ren para.Sil.next in
|
|
let svars' = IList.map (ident_captured_ren ren) para.Sil.svars in
|
|
let evars' = IList.map (ident_captured_ren ren) para.Sil.evars in
|
|
let body' = IList.map (hpred_captured_ren ren) para.Sil.body in
|
|
{ Sil.root = root'; Sil.next = next'; Sil.svars = svars'; Sil.evars = evars'; Sil.body = body'}
|
|
|
|
and hpara_dll_ren para =
|
|
let av = Sil.hpara_dll_shallow_av para in
|
|
let ren = compute_renaming av in
|
|
let iF = ident_captured_ren ren para.Sil.cell in
|
|
let oF = ident_captured_ren ren para.Sil.flink in
|
|
let oB = ident_captured_ren ren para.Sil.blink in
|
|
let svars' = IList.map (ident_captured_ren ren) para.Sil.svars_dll in
|
|
let evars' = IList.map (ident_captured_ren ren) para.Sil.evars_dll in
|
|
let body' = IList.map (hpred_captured_ren ren) para.Sil.body_dll in
|
|
{ Sil.cell = iF; Sil.flink = oF; Sil.blink = oB; Sil.svars_dll = svars'; Sil.evars_dll = evars'; Sil.body_dll = body'}
|
|
|
|
let pi_captured_ren ren pi =
|
|
IList.map (atom_captured_ren ren) pi
|
|
|
|
let sigma_captured_ren ren sigma =
|
|
IList.map (hpred_captured_ren ren) sigma
|
|
|
|
let sub_captured_ren ren sub =
|
|
Sil.sub_map (ident_captured_ren ren) (exp_captured_ren ren) sub
|
|
|
|
(** Canonicalize the names of primed variables and footprint vars. *)
|
|
let prop_rename_primed_footprint_vars p =
|
|
let p = prop_rename_array_indices p in
|
|
let bound_vars =
|
|
let filter id = Ident.is_footprint id || Ident.is_primed id in
|
|
let p_dfs = prop_dfs_sort p in
|
|
let fvars_in_p = prop_fav p_dfs in
|
|
Sil.fav_filter_ident fvars_in_p filter;
|
|
fvars_in_p in
|
|
let ren = compute_renaming bound_vars in
|
|
let sub' = sub_captured_ren ren p.sub in
|
|
let pi' = pi_captured_ren ren p.pi in
|
|
let sigma' = sigma_captured_ren ren p.sigma in
|
|
let foot_pi' = pi_captured_ren ren p.foot_pi in
|
|
let foot_sigma' = sigma_captured_ren ren p.foot_sigma in
|
|
|
|
let sub_for_normalize = Sil.sub_empty in
|
|
(* It is fine to use the empty substituion during normalization
|
|
because the renaming maintains that a substitution is normalized *)
|
|
let nsub' = sub_normalize sub' in
|
|
let nsigma' = sigma_normalize sub_for_normalize sigma' in
|
|
let npi' = pi_normalize sub_for_normalize nsigma' pi' in
|
|
let p' = footprint_normalize {
|
|
sub = nsub';
|
|
pi = npi';
|
|
sigma = nsigma';
|
|
foot_pi = foot_pi';
|
|
foot_sigma = foot_sigma';
|
|
} in
|
|
p'
|
|
|
|
(** {2 Functions for changing and generating propositions} *)
|
|
|
|
let mem_idlist i l =
|
|
IList.exists (fun id -> Ident.equal i id) l
|
|
|
|
let id_exp_compare (id1, e1) (id2, e2) =
|
|
let n = Sil.exp_compare e1 e2 in
|
|
if n <> 0 then n
|
|
else Ident.compare id1 id2
|
|
|
|
let expose (p : normal t) : exposed t = Obj.magic p
|
|
|
|
(** normalize a prop *)
|
|
let normalize (eprop : 'a t) : normal t =
|
|
let p0 = { prop_emp with sigma = sigma_normalize Sil.sub_empty eprop.sigma } in
|
|
let nprop = IList.fold_left prop_atom_and p0 (get_pure eprop) in
|
|
footprint_normalize { nprop with foot_pi = eprop.foot_pi; foot_sigma = eprop.foot_sigma }
|
|
|
|
(** Apply subsitution to prop. *)
|
|
let prop_sub subst (prop: 'a t) : exposed t =
|
|
let pi = pi_sub subst (prop.pi @ pi_of_subst prop.sub) in
|
|
let sigma = sigma_sub subst prop.sigma in
|
|
let foot_pi = pi_sub subst prop.foot_pi in
|
|
let foot_sigma = sigma_sub subst prop.foot_sigma in
|
|
{ prop_emp with pi; sigma; foot_pi; foot_sigma; }
|
|
|
|
(** Apply renaming substitution to a proposition. *)
|
|
let prop_ren_sub (ren_sub: Sil.subst) (prop: normal t) : normal t =
|
|
normalize (prop_sub ren_sub prop)
|
|
|
|
(** Existentially quantify the [fav] in [prop].
|
|
[fav] should not contain any primed variables. *)
|
|
let exist_quantify fav prop =
|
|
let ids = Sil.fav_to_list fav in
|
|
if IList.exists Ident.is_primed ids then assert false; (* sanity check *)
|
|
if ids == [] then prop else
|
|
let gen_fresh_id_sub id = (id, Sil.Var (Ident.create_fresh Ident.kprimed)) in
|
|
let ren_sub = Sil.sub_of_list (IList.map gen_fresh_id_sub ids) in
|
|
let prop' =
|
|
(* throw away x=E if x becomes _x *)
|
|
let sub = Sil.sub_filter (fun i -> not (mem_idlist i ids)) prop.sub in
|
|
if Sil.sub_equal sub prop.sub then prop
|
|
else { prop with sub = sub } in
|
|
(*
|
|
L.out "@[<2>.... Existential Quantification ....\n";
|
|
L.out "SUB:%a\n" pp_sub prop'.sub;
|
|
L.out "PI:%a\n" pp_pi prop'.pi;
|
|
L.out "PROP:%a\n@." pp_prop prop';
|
|
*)
|
|
prop_ren_sub ren_sub prop'
|
|
|
|
(** Apply the substitution [fe] to all the expressions in the prop. *)
|
|
let prop_expmap (fe: Sil.exp -> Sil.exp) prop =
|
|
let f (e, sil_opt) = (fe e, sil_opt) in
|
|
let pi = IList.map (Sil.atom_expmap fe) prop.pi in
|
|
let sigma = IList.map (Sil.hpred_expmap f) prop.sigma in
|
|
let foot_pi = IList.map (Sil.atom_expmap fe) prop.foot_pi in
|
|
let foot_sigma = IList.map (Sil.hpred_expmap f) prop.foot_sigma in
|
|
{ prop with pi; sigma; foot_pi; foot_sigma; }
|
|
|
|
(** convert identifiers in fav to kind [k] *)
|
|
let vars_make_unprimed fav prop =
|
|
let ids = Sil.fav_to_list fav in
|
|
let ren_sub = Sil.sub_of_list (IList.map (fun i -> (i, Sil.Var (Ident.create_fresh Ident.knormal))) ids) in
|
|
prop_ren_sub ren_sub prop
|
|
|
|
(** convert the normal vars to primed vars. *)
|
|
let prop_normal_vars_to_primed_vars p =
|
|
let fav = prop_fav p in
|
|
Sil.fav_filter_ident fav Ident.is_normal;
|
|
exist_quantify fav p
|
|
|
|
(** Rename all primed variables fresh *)
|
|
let prop_rename_primed_fresh (p : normal t) : normal t =
|
|
let ids_primed =
|
|
let fav = prop_fav p in
|
|
let ids = Sil.fav_to_list fav in
|
|
IList.filter Ident.is_primed ids in
|
|
let ren_sub =
|
|
let f i = (i, Sil.Var (Ident.create_fresh Ident.kprimed)) in
|
|
Sil.sub_of_list (IList.map f ids_primed) in
|
|
prop_ren_sub ren_sub p
|
|
|
|
(** convert the primed vars to normal vars. *)
|
|
let prop_primed_vars_to_normal_vars (p : normal t) : normal t =
|
|
let fav = prop_fav p in
|
|
Sil.fav_filter_ident fav Ident.is_primed;
|
|
vars_make_unprimed fav p
|
|
|
|
let from_pi pi = { prop_emp with pi = pi }
|
|
|
|
let from_sigma sigma = { prop_emp with sigma = sigma }
|
|
|
|
let replace_sub sub eprop =
|
|
{ eprop with sub = sub }
|
|
|
|
(** Rename free variables in a prop replacing them with existentially quantified vars *)
|
|
let prop_rename_fav_with_existentials (p : normal t) : normal t =
|
|
let fav = Sil.fav_new () in
|
|
prop_fav_add fav p;
|
|
let ids = Sil.fav_to_list fav in
|
|
let ids' = IList.map (fun i -> (i, Ident.create_fresh Ident.kprimed)) ids in
|
|
let ren_sub = Sil.sub_of_list (IList.map (fun (i, i') -> (i, Sil.Var i')) ids') in
|
|
let p' = prop_sub ren_sub p in
|
|
(*L.d_strln "Prop after renaming:"; d_prop p'; L.d_strln "";*)
|
|
normalize p'
|
|
|
|
(** {2 Prop iterators} *)
|
|
|
|
(** Iterator state over sigma. *)
|
|
type 'a prop_iter =
|
|
{ pit_sub : Sil.subst; (** substitution for equalities *)
|
|
pit_pi : Sil.atom list; (** pure part *)
|
|
pit_newpi : (bool * Sil.atom) list; (** newly added atoms. *)
|
|
(** The first records !Config.footprint. *)
|
|
pit_old : Sil.hpred list; (** sigma already visited *)
|
|
pit_curr : Sil.hpred; (** current element *)
|
|
pit_state : 'a; (** state of current element *)
|
|
pit_new : Sil.hpred list; (** sigma not yet visited *)
|
|
pit_foot_pi : Sil.atom list; (** pure part of the footprint *)
|
|
pit_foot_sigma : Sil.hpred list; (** sigma part of the footprint *)
|
|
}
|
|
|
|
let prop_iter_create prop =
|
|
match prop.sigma with
|
|
| hpred:: sigma' -> Some
|
|
{ pit_sub = prop.sub;
|
|
pit_pi = prop.pi;
|
|
pit_newpi = [];
|
|
pit_old = [];
|
|
pit_curr = hpred;
|
|
pit_state = ();
|
|
pit_new = sigma';
|
|
pit_foot_pi = prop.foot_pi;
|
|
pit_foot_sigma = prop.foot_sigma }
|
|
| _ -> None
|
|
|
|
(** Return the prop associated to the iterator. *)
|
|
let prop_iter_to_prop iter =
|
|
let sigma = IList.rev_append iter.pit_old (iter.pit_curr:: iter.pit_new) in
|
|
let prop =
|
|
normalize
|
|
{ sub = iter.pit_sub; pi = iter.pit_pi; sigma = sigma; foot_pi = iter.pit_foot_pi; foot_sigma = iter.pit_foot_sigma } in
|
|
IList.fold_left
|
|
(fun p (footprint, atom) -> prop_atom_and ~footprint: footprint p atom)
|
|
prop iter.pit_newpi
|
|
|
|
(** Add an atom to the pi part of prop iter. The
|
|
first parameter records whether it is done
|
|
during footprint or during re - execution. *)
|
|
let prop_iter_add_atom footprint iter atom =
|
|
{ iter with pit_newpi = (footprint, atom):: iter.pit_newpi }
|
|
|
|
(** Remove the current element of the iterator, and return the prop
|
|
associated to the resulting iterator *)
|
|
let prop_iter_remove_curr_then_to_prop iter =
|
|
let sigma = IList.rev_append iter.pit_old iter.pit_new in
|
|
let normalized_sigma = sigma_normalize iter.pit_sub sigma in
|
|
{ sub = iter.pit_sub;
|
|
pi = iter.pit_pi;
|
|
sigma = normalized_sigma;
|
|
foot_pi = iter.pit_foot_pi;
|
|
foot_sigma = iter.pit_foot_sigma }
|
|
|
|
(** Return the current hpred and state. *)
|
|
let prop_iter_current iter =
|
|
let curr = hpred_normalize iter.pit_sub iter.pit_curr in
|
|
let prop = { prop_emp with sigma = [curr] } in
|
|
let prop' =
|
|
IList.fold_left
|
|
(fun p (footprint, atom) -> prop_atom_and ~footprint: footprint p atom)
|
|
prop iter.pit_newpi in
|
|
match prop'.sigma with
|
|
| [curr'] -> (curr', iter.pit_state)
|
|
| _ -> assert false
|
|
|
|
(** Update the current element of the iterator. *)
|
|
let prop_iter_update_current iter hpred =
|
|
{ iter with pit_curr = hpred }
|
|
|
|
(** Update the current element of the iterator by a nonempty list of elements. *)
|
|
let prop_iter_update_current_by_list iter = function
|
|
| [] -> assert false (* the list should be nonempty *)
|
|
| hpred:: hpred_list ->
|
|
let pit_new' = hpred_list@iter.pit_new in
|
|
{ iter with pit_curr = hpred; pit_state = (); pit_new = pit_new'}
|
|
|
|
let prop_iter_next iter =
|
|
match iter.pit_new with
|
|
| [] -> None
|
|
| hpred':: new' -> Some
|
|
{ iter with
|
|
pit_old = iter.pit_curr:: iter.pit_old;
|
|
pit_curr = hpred';
|
|
pit_state = ();
|
|
pit_new = new'}
|
|
|
|
let prop_iter_remove_curr_then_next iter =
|
|
match iter.pit_new with
|
|
| [] -> None
|
|
| hpred':: new' -> Some
|
|
{ iter with
|
|
pit_old = iter.pit_old;
|
|
pit_curr = hpred';
|
|
pit_state = ();
|
|
pit_new = new'}
|
|
|
|
(** Insert before the current element of the iterator. *)
|
|
let prop_iter_prev_then_insert iter hpred =
|
|
{ iter with
|
|
pit_new = iter.pit_curr:: iter.pit_new;
|
|
pit_curr = hpred }
|
|
|
|
(** Scan sigma to find an [hpred] satisfying the filter function. *)
|
|
let rec prop_iter_find iter filter =
|
|
match filter iter.pit_curr with
|
|
| Some st -> Some { iter with pit_state = st }
|
|
| None ->
|
|
(match prop_iter_next iter with
|
|
| None -> None
|
|
| Some iter' -> prop_iter_find iter' filter)
|
|
|
|
(** Set the state of the iterator *)
|
|
let prop_iter_set_state iter state =
|
|
{ iter with pit_state = state }
|
|
|
|
let prop_iter_make_id_primed id iter =
|
|
let pid = Ident.create_fresh Ident.kprimed in
|
|
let sub_id = Sil.sub_of_list [(id, Sil.Var pid)] in
|
|
|
|
let normalize (id, e) =
|
|
let eq' = Sil.Aeq(Sil.exp_sub sub_id (Sil.Var id), Sil.exp_sub sub_id e) in
|
|
atom_normalize Sil.sub_empty eq' in
|
|
|
|
let rec split pairs_unpid pairs_pid = function
|
|
| [] -> (IList.rev pairs_unpid, IList.rev pairs_pid)
|
|
| eq:: eqs_cur ->
|
|
begin
|
|
match eq with
|
|
| Sil.Aeq (Sil.Var id1, e1) when Sil.ident_in_exp id1 e1 ->
|
|
L.out "@[<2>#### ERROR: an assumption of the analyzer broken ####@\n";
|
|
L.out "Broken Assumption: id notin e for all (id,e) in sub@\n";
|
|
L.out "(id,e) : (%a,%a)@\n" (Ident.pp pe_text) id1 (Sil.pp_exp pe_text) e1;
|
|
L.out "PROP : %a@\n@." (pp_prop pe_text) (prop_iter_to_prop iter);
|
|
assert false
|
|
| Sil.Aeq (Sil.Var id1, e1) when Ident.equal pid id1 ->
|
|
split pairs_unpid ((id1, e1):: pairs_pid) eqs_cur
|
|
| Sil.Aeq (Sil.Var id1, e1) ->
|
|
split ((id1, e1):: pairs_unpid) pairs_pid eqs_cur
|
|
| _ ->
|
|
assert false
|
|
end in
|
|
|
|
let rec get_eqs acc = function
|
|
| [] | [_] ->
|
|
IList.rev acc
|
|
| (_, e1) :: (((_, e2) :: pairs') as pairs) ->
|
|
get_eqs (Sil.Aeq(e1, e2):: acc) pairs in
|
|
|
|
let sub_new, sub_use, eqs_add =
|
|
let eqs = IList.map normalize (Sil.sub_to_list iter.pit_sub) in
|
|
let pairs_unpid, pairs_pid = split [] [] eqs in
|
|
match pairs_pid with
|
|
| [] ->
|
|
let sub_unpid = Sil.sub_of_list pairs_unpid in
|
|
let pairs = (id, Sil.Var pid) :: pairs_unpid in
|
|
sub_unpid, Sil.sub_of_list pairs, []
|
|
| (id1, e1):: _ ->
|
|
let sub_id1 = Sil.sub_of_list [(id1, e1)] in
|
|
let pairs_unpid' =
|
|
IList.map (fun (id', e') -> (id', Sil.exp_sub sub_id1 e')) pairs_unpid in
|
|
let sub_unpid = Sil.sub_of_list pairs_unpid' in
|
|
let pairs = (id, e1) :: pairs_unpid' in
|
|
sub_unpid, Sil.sub_of_list pairs, get_eqs [] pairs_pid in
|
|
let nsub_new = sub_normalize sub_new in
|
|
|
|
{ iter with
|
|
pit_sub = nsub_new;
|
|
pit_pi = pi_sub sub_use (iter.pit_pi @ eqs_add);
|
|
pit_old = sigma_sub sub_use iter.pit_old;
|
|
pit_curr = Sil.hpred_sub sub_use iter.pit_curr;
|
|
pit_new = sigma_sub sub_use iter.pit_new }
|
|
|
|
let prop_iter_footprint_fav_add fav iter =
|
|
sigma_fav_add fav iter.pit_foot_sigma;
|
|
pi_fav_add fav iter.pit_foot_pi
|
|
|
|
(** Find fav of the footprint part of the iterator *)
|
|
let prop_iter_footprint_fav iter =
|
|
Sil.fav_imperative_to_functional prop_iter_footprint_fav_add iter
|
|
|
|
let prop_iter_fav_add fav iter =
|
|
Sil.sub_fav_add fav iter.pit_sub;
|
|
pi_fav_add fav iter.pit_pi;
|
|
pi_fav_add fav (IList.map snd iter.pit_newpi);
|
|
sigma_fav_add fav iter.pit_old;
|
|
sigma_fav_add fav iter.pit_new;
|
|
Sil.hpred_fav_add fav iter.pit_curr;
|
|
prop_iter_footprint_fav_add fav iter
|
|
|
|
(** Find fav of the iterator *)
|
|
let prop_iter_fav iter =
|
|
Sil.fav_imperative_to_functional prop_iter_fav_add iter
|
|
|
|
(** Free vars of the iterator except the current hpred (and footprint). *)
|
|
let prop_iter_noncurr_fav_add fav iter =
|
|
sigma_fav_add fav iter.pit_old;
|
|
sigma_fav_add fav iter.pit_new;
|
|
Sil.sub_fav_add fav iter.pit_sub;
|
|
pi_fav_add fav iter.pit_pi
|
|
|
|
(** Extract the sigma part of the footprint *)
|
|
let prop_iter_get_footprint_sigma iter =
|
|
iter.pit_foot_sigma
|
|
|
|
(** Replace the sigma part of the footprint *)
|
|
let prop_iter_replace_footprint_sigma iter sigma =
|
|
{ iter with pit_foot_sigma = sigma }
|
|
|
|
let prop_iter_noncurr_fav iter =
|
|
Sil.fav_imperative_to_functional prop_iter_noncurr_fav_add iter
|
|
|
|
let rec strexp_gc_fields (fav: Sil.fav) se =
|
|
match se with
|
|
| Sil.Eexp _ ->
|
|
Some se
|
|
| Sil.Estruct (fsel, inst) ->
|
|
let fselo = IList.map (fun (f, se) -> (f, strexp_gc_fields fav se)) fsel in
|
|
let fsel' =
|
|
let fselo' = IList.filter (function | (_, Some _) -> true | _ -> false) fselo in
|
|
IList.map (function (f, seo) -> (f, unSome seo)) fselo' in
|
|
if Sil.fld_strexp_list_compare fsel fsel' = 0 then Some se
|
|
else Some (Sil.Estruct (fsel', inst))
|
|
| Sil.Earray _ ->
|
|
Some se
|
|
|
|
let hpred_gc_fields (fav: Sil.fav) hpred = match hpred with
|
|
| Sil.Hpointsto (e, se, te) ->
|
|
Sil.exp_fav_add fav e;
|
|
Sil.exp_fav_add fav te;
|
|
(match strexp_gc_fields fav se with
|
|
| None -> hpred
|
|
| Some se' ->
|
|
if Sil.strexp_compare se se' = 0 then hpred
|
|
else Sil.Hpointsto (e, se', te))
|
|
| Sil.Hlseg _ | Sil.Hdllseg _ ->
|
|
hpred
|
|
|
|
let rec prop_iter_map f iter =
|
|
let hpred_curr = f iter in
|
|
let iter' = { iter with pit_curr = hpred_curr } in
|
|
match prop_iter_next iter' with
|
|
| None -> iter'
|
|
| Some iter'' -> prop_iter_map f iter''
|
|
|
|
(** Collect garbage fields. *)
|
|
let prop_iter_gc_fields iter =
|
|
let f iter' =
|
|
let fav = prop_iter_noncurr_fav iter' in
|
|
hpred_gc_fields fav iter'.pit_curr in
|
|
prop_iter_map f iter
|
|
|
|
let prop_case_split prop =
|
|
let pi_sigma_list = Sil.sigma_to_sigma_ne prop.sigma in
|
|
let f props_acc (pi, sigma) =
|
|
let sigma' = sigma_normalize_prop prop sigma in
|
|
let prop' = { prop with sigma = sigma' } in
|
|
(IList.fold_left prop_atom_and prop' pi):: props_acc in
|
|
IList.fold_left f [] pi_sigma_list
|
|
|
|
(** Raise an exception if the prop is not normalized *)
|
|
let check_prop_normalized prop =
|
|
let sigma' = sigma_normalize_prop prop prop.sigma in
|
|
if sigma_equal prop.sigma sigma' == false then assert false
|
|
|
|
let prop_expand prop =
|
|
(*
|
|
let _ = check_prop_normalized prop in
|
|
*)
|
|
prop_case_split prop
|
|
|
|
let mk_nondet il1 il2 loc =
|
|
Sil.Stackop (Sil.Push, loc) :: (* save initial state *)
|
|
il1 @ (* compute result1 *)
|
|
[Sil.Stackop (Sil.Swap, loc)] @ (* save result1 and restore initial state *)
|
|
il2 @ (* compute result2 *)
|
|
[Sil.Stackop (Sil.Pop, loc)] (* combine result1 and result2 *)
|
|
|
|
(** translate a logical and/or operation taking care of the non-strict semantics for side effects *)
|
|
let trans_land_lor op ((idl1, stml1), e1) ((idl2, stml2), e2) loc =
|
|
let no_side_effects stml =
|
|
stml = [] in
|
|
if no_side_effects stml2 then
|
|
((idl1@idl2, stml1@stml2), Sil.BinOp(op, e1, e2))
|
|
else
|
|
begin
|
|
let id = Ident.create_fresh Ident.knormal in
|
|
let prune_instr1, prune_res1, prune_instr2, prune_res2 =
|
|
let cond1, cond2, res = match op with
|
|
| Sil.LAnd -> e1, Sil.UnOp(Sil.LNot, e1, None), Sil.Int.zero
|
|
| Sil.LOr -> Sil.UnOp(Sil.LNot, e1, None), e1, Sil.Int.one
|
|
| _ -> assert false in
|
|
let cond_res1 = Sil.BinOp(Sil.Eq, Sil.Var id, e2) in
|
|
let cond_res2 = Sil.BinOp(Sil.Eq, Sil.Var id, Sil.exp_int res) in
|
|
let mk_prune cond = Sil.Prune (cond, loc, true, Sil.Ik_land_lor) (* don't report always true/false *) in
|
|
mk_prune cond1, mk_prune cond_res1, mk_prune cond2, mk_prune cond_res2 in
|
|
let instrs2 = mk_nondet (prune_instr1 :: stml2 @ [prune_res1]) ([prune_instr2; prune_res2]) loc in
|
|
((id:: idl1@idl2, stml1@instrs2), Sil.Var id)
|
|
end
|
|
|
|
(** Input of this mehtod is an exp in a prop. Output is a formal variable or path from a
|
|
formal variable that is equal to the expression, or the OBJC_NULL attribute of the expression. *)
|
|
let find_equal_formal_path e prop =
|
|
let rec find_in_sigma e seen_hpreds =
|
|
IList.fold_right (
|
|
fun hpred res ->
|
|
if IList.mem Sil.hpred_equal hpred seen_hpreds then None
|
|
else
|
|
let seen_hpreds = hpred :: seen_hpreds in
|
|
match res with
|
|
| Some _ -> res
|
|
| None ->
|
|
match hpred with
|
|
| Sil.Hpointsto (Sil.Lvar pvar1, Sil.Eexp (exp2, Sil.Iformal(_, _) ), _)
|
|
when Sil.exp_equal exp2 e && (Sil.pvar_is_local pvar1 || Sil.pvar_is_seed pvar1) ->
|
|
Some (Sil.Lvar pvar1)
|
|
| Sil.Hpointsto (exp1, Sil.Estruct (fields, _), _) ->
|
|
IList.fold_right (fun (field, strexp) res ->
|
|
match res with
|
|
| Some _ -> res
|
|
| None ->
|
|
match strexp with
|
|
| Sil.Eexp (exp2, _) when Sil.exp_equal exp2 e ->
|
|
(match find_in_sigma exp1 seen_hpreds with
|
|
| Some exp' -> Some (Sil.Lfield (exp', field, Sil.Tvoid))
|
|
| None -> None)
|
|
| _ -> None) fields None
|
|
| _ -> None) (get_sigma prop) None in
|
|
match find_in_sigma e [] with
|
|
| Some res -> Some res
|
|
| None -> match get_objc_null_attribute prop e with
|
|
| Some (Sil.Aobjc_null exp) -> Some exp
|
|
| _ -> None
|
|
|
|
(** translate an if-then-else expression *)
|
|
let trans_if_then_else ((idl1, stml1), e1) ((idl2, stml2), e2) ((idl3, stml3), e3) loc =
|
|
match sym_eval false e1 with
|
|
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> (idl1@idl3, stml1@stml3), e3
|
|
| Sil.Const (Sil.Cint _) -> (idl1@idl2, stml1@stml2), e2
|
|
| _ ->
|
|
let e1not = Sil.UnOp(Sil.LNot, e1, None) in
|
|
let id = Ident.create_fresh Ident.knormal in
|
|
let mk_prune_res e =
|
|
let mk_prune cond = Sil.Prune (cond, loc, true, Sil.Ik_land_lor) (* don't report always true/false *) in
|
|
mk_prune (Sil.BinOp(Sil.Eq, Sil.Var id, e)) in
|
|
let prune1 = Sil.Prune (e1, loc, true, Sil.Ik_bexp) in
|
|
let prune1not = Sil.Prune (e1not, loc, false, Sil.Ik_bexp) in
|
|
let stml' = mk_nondet (prune1 :: stml2 @ [mk_prune_res e2]) (prune1not :: stml3 @ [mk_prune_res e3]) loc in
|
|
(id:: idl1@idl2@idl3, stml1@stml'), Sil.Var id
|
|
|
|
(*** START of module Metrics ***)
|
|
module Metrics : sig
|
|
val prop_size : 'a t -> int
|
|
val prop_chain_size : 'a t -> int
|
|
end = struct
|
|
let ptsto_weight = 1
|
|
and lseg_weight = 3
|
|
and pi_weight = 1
|
|
|
|
let rec hpara_size hpara = sigma_size hpara.Sil.body
|
|
|
|
and hpara_dll_size hpara_dll = sigma_size hpara_dll.Sil.body_dll
|
|
|
|
and hpred_size = function
|
|
| Sil.Hpointsto _ -> ptsto_weight
|
|
| Sil.Hlseg (_, hpara, _, _, _) -> lseg_weight * hpara_size hpara
|
|
| Sil.Hdllseg (_, hpara_dll, _, _, _, _, _) -> lseg_weight * hpara_dll_size hpara_dll
|
|
|
|
and sigma_size sigma =
|
|
let size = ref 0 in
|
|
IList.iter (fun hpred -> size := hpred_size hpred + !size) sigma; !size
|
|
|
|
let pi_size pi = pi_weight * IList.length pi
|
|
|
|
|
|
(** Approximate the size of the longest chain by counting the max
|
|
number of |-> with the same type and whose lhs is primed or
|
|
footprint *)
|
|
let sigma_chain_size sigma =
|
|
let tbl = ref Sil.ExpMap.empty in
|
|
let add t =
|
|
try
|
|
let count = Sil.ExpMap.find t !tbl in
|
|
tbl := Sil.ExpMap.add t (count + 1) !tbl
|
|
with
|
|
| Not_found ->
|
|
tbl := Sil.ExpMap.add t 1 !tbl in
|
|
let process_hpred = function
|
|
| Sil.Hpointsto (e, _, te) ->
|
|
(match e with
|
|
| Sil.Var id when Ident.is_primed id || Ident.is_footprint id -> add te
|
|
| _ -> ())
|
|
| Sil.Hlseg _ | Sil.Hdllseg _ -> () in
|
|
IList.iter process_hpred sigma;
|
|
let size = ref 0 in
|
|
Sil.ExpMap.iter (fun t n -> size := max n !size) !tbl;
|
|
!size
|
|
|
|
(** Compute a size value for the prop, which indicates its
|
|
complexity *)
|
|
let prop_size p =
|
|
let size_current = sigma_size p.sigma in
|
|
let size_footprint = sigma_size p.foot_sigma in
|
|
max size_current size_footprint
|
|
|
|
(** Approximate the size of the longest chain by counting the max
|
|
number of |-> with the same type and whose lhs is primed or
|
|
footprint *)
|
|
let prop_chain_size p =
|
|
let fp_size = pi_size p.foot_pi + sigma_size p.foot_sigma in
|
|
pi_size p.pi + sigma_size p.sigma + fp_size
|
|
end
|
|
(*** END of module Metrics ***)
|
|
|
|
module CategorizePreconditions = struct
|
|
type pre_category =
|
|
| NoPres (* no preconditions *)
|
|
| Empty (* the preconditions impose no restrictions *)
|
|
| OnlyAllocation (* the preconditions only demand that some pointers are allocated *)
|
|
| DataConstraints (* the preconditions impose constraints on the values of variables and/or memory *)
|
|
|
|
(** categorize a list of preconditions *)
|
|
let categorize preconditions =
|
|
let lhs_is_lvar = function
|
|
| Sil.Lvar _ -> true
|
|
| _ -> false in
|
|
let lhs_is_var_lvar = function
|
|
| Sil.Var _ -> true
|
|
| Sil.Lvar _ -> true
|
|
| _ -> false in
|
|
let rhs_is_var = function
|
|
| Sil.Eexp (Sil.Var _, _) -> true
|
|
| _ -> false in
|
|
let rec rhs_only_vars = function
|
|
| Sil.Eexp (Sil.Var _, _) -> true
|
|
| Sil.Estruct (fsel, _) ->
|
|
IList.for_all (fun (_, se) -> rhs_only_vars se) fsel
|
|
| Sil.Earray _ -> true
|
|
| _ -> false in
|
|
let hpred_is_var = function (* stack variable with no constraints *)
|
|
| Sil.Hpointsto (e, se, _) ->
|
|
lhs_is_lvar e && rhs_is_var se
|
|
| _ -> false in
|
|
let hpred_only_allocation = function (* only constraint is allocation *)
|
|
| Sil.Hpointsto (e, se, _) ->
|
|
lhs_is_var_lvar e && rhs_only_vars se
|
|
| _ -> false in
|
|
let check_pre hpred_filter pre =
|
|
let check_pi pi =
|
|
pi = [] in
|
|
let check_sigma sigma =
|
|
IList.for_all hpred_filter sigma in
|
|
check_pi (get_pi pre) && check_sigma (get_sigma pre) in
|
|
let pres_no_constraints = IList.filter (check_pre hpred_is_var) preconditions in
|
|
let pres_only_allocation = IList.filter (check_pre hpred_only_allocation) preconditions in
|
|
match preconditions, pres_no_constraints, pres_only_allocation with
|
|
| [], _, _ ->
|
|
NoPres
|
|
| _:: _, _:: _, _ ->
|
|
Empty
|
|
| _:: _, [], _:: _ ->
|
|
OnlyAllocation
|
|
| _:: _, [], [] ->
|
|
DataConstraints
|
|
end
|