(* * Copyright (c) 2009 -2013 Monoidics ltd. * Copyright (c) 2013 - Facebook. * All rights reserved. *) (** Abstraction for Arrays *) module L = Logging module F = Format open Utils type sigma = Sil.hpred list (** Matcher for the sigma part specialized to strexps *) module StrexpMatch : sig (** path through a strexp *) type path (** convert a path into a list of expressions *) val path_to_exps : path -> Sil.exp list (** create a path from a root and a list of offsets *) val path_from_exp_offsets : Sil.exp -> Sil.offset list -> path (** path to the root, size, elements and type of a new_array *) type strexp_data = path * Sil.strexp * Sil.typ (** sigma with info about a current array *) type t (** Find a strexp at the given path. Can raise [Not_found] *) val find_path : sigma -> path -> t (** Find a strexp with the given property. *) val find : sigma -> (sigma -> strexp_data -> bool) -> t list (** Get the array *) val get_data : t -> strexp_data (** Get the partition of the sigma: the unmatched part of the sigma and the matched hpred *) val get_sigma_partition : t -> sigma * Sil.hpred (** Replace the strexp at a given position by a new strexp *) val replace_strexp : bool -> t -> Sil.strexp -> sigma (** Replace the strexp and the unmatched part of the sigma by the givn inputs *) val replace_strexp_sigma : bool -> t -> Sil.strexp -> sigma -> sigma (** Replace the index in the array at a given position with the new index *) val replace_index : bool -> t -> Sil.exp -> Sil.exp -> sigma end = struct (** syntactic offset *) type syn_offset = Field of Ident.fieldname * Sil.typ | Index of Sil.exp (** path through an Estruct *) type path = Sil.exp * (syn_offset list) (** Find a strexp and a type at the given syntactic offset list *) let rec get_strexp_at_syn_offsets se t syn_offs = match se, t, syn_offs with | _, _, [] -> (se, t) | Sil.Estruct (fsel, _), Sil.Tstruct (ftal, sftal, _, _, _, _, _), Field (fld, _) :: syn_offs' -> let se' = snd (list_find (fun (f', se') -> Sil.fld_equal f' fld) fsel) in let t' = (fun (x,y,z) -> y) (list_find (fun (f', t', a') -> Sil.fld_equal f' fld) ftal) in get_strexp_at_syn_offsets se' t' syn_offs' | Sil.Earray (size, esel, _), Sil.Tarray(t', _), Index ind :: syn_offs' -> let se' = snd (list_find (fun (i', se') -> Sil.exp_equal i' ind) esel) in get_strexp_at_syn_offsets se' t' syn_offs' | _ -> L.d_strln "Failure of get_strexp_at_syn_offsets"; L.d_str "se: "; Sil.d_sexp se; L.d_ln (); L.d_str "t: "; Sil.d_typ_full t; L.d_ln (); assert false (** Replace a strexp at the given syntactic offset list *) let rec replace_strexp_at_syn_offsets se t syn_offs update = match se, t, syn_offs with | _, _, [] -> update se t | Sil.Estruct (fsel, inst), Sil.Tstruct (ftal, sftal, _, _, _, _, _), Field (fld, _) :: syn_offs' -> let se' = snd (list_find (fun (f', _) -> Sil.fld_equal f' fld) fsel) in let t' = (fun (x,y,z) -> y) (list_find (fun (f', _, _) -> Sil.fld_equal f' fld) ftal) in let se_mod = replace_strexp_at_syn_offsets se' t' syn_offs' update in let fsel' = list_map (fun (f'', se'') -> if Sil.fld_equal f'' fld then (fld, se_mod) else (f'', se'')) fsel in Sil.Estruct (fsel', inst) | Sil.Earray (size, esel, inst), Sil.Tarray (t', _), Index idx :: syn_offs' -> let se' = snd (list_find (fun (i', _) -> Sil.exp_equal i' idx) esel) in let se_mod = replace_strexp_at_syn_offsets se' t' syn_offs' update in let esel' = list_map (fun ese -> if Sil.exp_equal (fst ese) idx then (idx, se_mod) else ese) esel in Sil.Earray (size, esel', inst) | _ -> assert false (** convert a path into an expression *) let path_to_exps (root, syn_offs_in) = let rec convert acc = function | [] -> acc | Field (f, t) :: syn_offs' -> let acc' = list_map (fun e -> Sil.Lfield (e, f, t)) acc in convert acc' syn_offs' | Index idx :: syn_offs' -> let acc' = list_map (fun e -> Sil.Lindex (e, idx)) acc in convert acc' syn_offs' in begin convert [root] syn_offs_in end (** create a path from a root and a list of offsets *) let path_from_exp_offsets root offs = let offset_to_syn_offset = function | Sil.Off_fld (fld, typ) -> Field (fld, typ) | Sil.Off_index idx -> Index idx in let syn_offs = list_map offset_to_syn_offset offs in (root, syn_offs) (** path to the root, size, elements and type of a new_array *) type strexp_data = path * Sil.strexp * Sil.typ (** Store hpred using physical equality, and offset list for an array *) type t = sigma * Sil.hpred * (syn_offset list) (** Find an array at the given path. Can raise [Not_found] *) let find_path sigma (root, syn_offs) : t = let filter = function | Sil.Hpointsto (e, _, _) -> Sil.exp_equal root e | _ -> false in let hpred = list_find filter sigma in (sigma, hpred, syn_offs) (** Find a sub strexp with the given property. Can raise [Not_found] *) let find (sigma : sigma) (pred : sigma -> strexp_data -> bool) : t list = let found = ref [] in let rec find_offset_sexp sigma_other hpred root offs se typ = let offs' = list_rev offs in let path = (root, offs') in if pred sigma_other (path, se, typ) then found := (sigma, hpred, offs') :: !found else begin match se, typ with | Sil.Estruct (fsel, _), Sil.Tstruct (ftal, sftal, _, _, _, _, _) -> find_offset_fsel sigma_other hpred root offs fsel ftal typ | Sil.Earray (size, esel, _), Sil.Tarray (t, _) -> find_offset_esel sigma_other hpred root offs esel t | _ -> () end and find_offset_fsel sigma_other hpred root offs fsel ftal typ = match fsel with | [] -> () | (f, se) :: fsel' -> begin try let t = (fun (x,y,z) -> y) (list_find (fun (f', t, a) -> Sil.fld_equal f' f) ftal) in find_offset_sexp sigma_other hpred root ((Field (f, typ)) :: offs) se t with Not_found -> L.d_strln ("Can't find field " ^ (Ident.fieldname_to_string f) ^ " in StrexpMatch.find") end; find_offset_fsel sigma_other hpred root offs fsel' ftal typ and find_offset_esel sigma_other hpred root offs esel t = match esel with | [] -> () | (ind, se) :: esel' -> begin find_offset_sexp sigma_other hpred root ((Index ind):: offs) se t; find_offset_esel sigma_other hpred root offs esel' t end in let rec iterate sigma_seen = function | [] -> () | hpred :: sigma_rest -> begin match hpred with | Sil.Hpointsto (root, se, te) -> let sigma_other = sigma_seen @ sigma_rest in find_offset_sexp sigma_other hpred root [] se (Sil.texp_to_typ None te) | _ -> () end; iterate (hpred:: sigma_seen) sigma_rest in begin iterate [] sigma; !found end (** Get the matched strexp *) let get_data ((_ , hpred, syn_offs) : t) = match hpred with | Sil.Hpointsto (root, se, te) -> let t = Sil.texp_to_typ None te in let se', t' = get_strexp_at_syn_offsets se t syn_offs in let path' = (root, syn_offs) in (path', se', t') | _ -> assert false (** Get the partition of the sigma: the unmatched part of the sigma and the matched hpred *) let get_sigma_partition (sigma, hpred, _) = let sigma_unmatched = list_filter (fun hpred' -> not (hpred' == hpred)) sigma in (sigma_unmatched, hpred) (** Replace the current hpred *) let replace_hpred ((sigma, hpred, syn_offs) : t) hpred' = list_map (fun hpred'' -> if hpred''== hpred then hpred' else hpred'') sigma (** Replace the strexp at the given offset in the given hpred *) let hpred_replace_strexp footprint_part hpred syn_offs update = let update se' t' = let se_in = update se' t' in match se', se_in with | Sil.Earray (size, esel, inst1), Sil.Earray (_, esel_in, inst2) -> let orig_indices = list_map fst esel in let index_is_not_new idx = list_exists (Sil.exp_equal idx) orig_indices in let process_index idx = if index_is_not_new idx then idx else (Sil.array_clean_new_index footprint_part idx) in let esel_in' = list_map (fun (idx, se) -> process_index idx, se) esel_in in Sil.Earray (size, esel_in', inst2) | _, _ -> se_in in begin match hpred with | Sil.Hpointsto (root, se, te) -> let t = Sil.texp_to_typ None te in let se' = replace_strexp_at_syn_offsets se t syn_offs update in Sil.Hpointsto (root, se', te) | _ -> assert false end (** Replace the strexp at a given position by a new strexp *) let replace_strexp footprint_part ((sigma, hpred, syn_offs) : t) se_in = let update se' t' = se_in in let hpred' = hpred_replace_strexp footprint_part hpred syn_offs update in replace_hpred (sigma, hpred, syn_offs) hpred' (** Replace the strexp and the unmatched part of the sigma by the given inputs *) let replace_strexp_sigma footprint_part ((_, hpred, syn_offs) : t) se_in sigma_in = let new_sigma = hpred :: sigma_in in let sigma' = replace_strexp footprint_part (new_sigma, hpred, syn_offs) se_in in list_sort Sil.hpred_compare sigma' (** Replace the index in the array at a given position with the new index *) let replace_index footprint_part ((sigma, hpred, syn_offs) : t) (index: Sil.exp) (index': Sil.exp) = let update se' t' = match se' with | Sil.Earray (size, esel, inst) -> let esel' = list_map (fun (e', se') -> if Sil.exp_equal e' index then (index', se') else (e', se')) esel in Sil.Earray (size, esel', inst) | _ -> assert false in let hpred' = hpred_replace_strexp footprint_part hpred syn_offs update in replace_hpred (sigma, hpred, syn_offs) hpred' end (** This function renames expressions in [p]. The renaming is, roughly speaking, to replace [path.i] by [path.i'] for all (i, i') in [map]. *) let prop_replace_path_index (p: Prop.exposed Prop.t) (path: StrexpMatch.path) (map : (Sil.exp * Sil.exp) list) : Prop.exposed Prop.t = let elist_path = StrexpMatch.path_to_exps path in let expmap_list = list_fold_left (fun acc_outer e_path -> list_fold_left (fun acc_inner (old_index, new_index) -> let old_e_path_index = Prop.exp_normalize_prop p (Sil.Lindex(e_path, old_index)) in let new_e_path_index = Prop.exp_normalize_prop p (Sil.Lindex(e_path, new_index)) in (old_e_path_index, new_e_path_index) :: acc_inner ) acc_outer map ) [] elist_path in let expmap_fun e' = try let _, fresh_e = list_find (fun (e, _) -> Sil.exp_equal e e') expmap_list in fresh_e with Not_found -> e' in Prop.prop_expmap expmap_fun p (** This function uses [update] and transforms the two sigma parts of [p], the sigma of the current SH of [p] and that of the footprint of [p]. *) let prop_update_sigma_and_fp_sigma (p : Prop.normal Prop.t) (update : bool -> sigma -> sigma * bool) : Prop.normal Prop.t * bool = let sigma', changed = update false (Prop.get_sigma p) in let ep1 = Prop.replace_sigma sigma' p in let ep2, changed2 = if !Config.footprint then let sigma_fp', changed' = update true (Prop.get_sigma_footprint ep1) in (Prop.replace_sigma_footprint sigma_fp' ep1, changed') else (ep1, false) in (Prop.normalize ep2, changed || changed2) (** This function uses [update] and transforms the sigma of the current SH of [p] or that of the footprint of [p], depending on [footprint_part]. *) let prop_update_sigma_or_fp_sigma (footprint_part : bool) (p : Prop.normal Prop.t) (update : bool -> sigma -> sigma * bool) : Prop.normal Prop.t * bool = let ep1, changed1 = if footprint_part then (Prop.expose p, false) else let sigma', changed = update false (Prop.get_sigma p) in (Prop.replace_sigma sigma' p, changed) in let ep2, changed2 = if not footprint_part then (ep1, false) else begin (if not !Config.footprint then assert false); (* always run in the footprint mode *) let sigma_fp', changed = update true (Prop.get_sigma_footprint ep1) in (Prop.replace_sigma_footprint sigma_fp' ep1, changed) end in (Prop.normalize ep2, changed1 || changed2) (** Remember whether array abstraction was performed (to be reset before calling Abs.abstract) *) let array_abstraction_performed = ref false (** This function abstracts strexps. The parameter [can_abstract] spots strexps where the abstraction might be applicable, and the parameter [do_abstract] does the abstraction to those spotted strexps. *) let generic_strexp_abstract (abstraction_name : string) (p_in : Prop.normal Prop.t) (_can_abstract : sigma -> StrexpMatch.strexp_data -> bool) (do_abstract : bool -> Prop.normal Prop.t -> StrexpMatch.strexp_data -> Prop.normal Prop.t * bool) : Prop.normal Prop.t = let can_abstract s data = let r = _can_abstract s data in if r then array_abstraction_performed := true; r in let find_strexp_to_abstract p0 = let find sigma = StrexpMatch.find sigma can_abstract in let matchings_cur = find (Prop.get_sigma p0) in let matchings_fp = find (Prop.get_sigma_footprint p0) in matchings_cur, matchings_fp in let match_select_next (matchings_cur, matchings_fp) = match matchings_cur, matchings_fp with | [], [] -> raise Not_found | matched :: cur', fp' -> matched, false, (cur', fp') | [], matched :: fp' -> matched, true, ([], fp') in let rec match_abstract p0 matchings_cur_fp = try let matched, footprint_part, matchings_cur_fp' = match_select_next matchings_cur_fp in let n = list_length (snd matchings_cur_fp') + 1 in if !Config.trace_absarray then (L.d_strln ("Num of fp candidates " ^ (string_of_int n))); let strexp_data = StrexpMatch.get_data matched in let p1, changed = do_abstract footprint_part p0 strexp_data in if changed then (p1, true) else match_abstract p0 matchings_cur_fp' with | Not_found -> (p0, false) in let rec find_then_abstract bound p0 = if bound = 0 then p0 else begin if !Config.trace_absarray then (L.d_strln ("Applying " ^ abstraction_name ^ " to"); Prop.d_prop p0; L.d_ln (); L.d_ln ()); let matchings_cur_fp = find_strexp_to_abstract p0 in let p1, changed = match_abstract p0 matchings_cur_fp in if changed then find_then_abstract (bound - 1) p1 else p0 end in let matchings_cur, matchings_fp = find_strexp_to_abstract p_in in let num_matches = (list_length matchings_cur) + (list_length matchings_fp) in begin find_then_abstract num_matches p_in end (** Return [true] if there's a pointer to the index *) let index_is_pointed_to (p: Prop.normal Prop.t) (path: StrexpMatch.path) (index: Sil.exp) : bool = let indices = let index_plus_one = Sil.BinOp(Sil.PlusA, index, Sil.exp_one) in [index; index_plus_one] in let add_index_to_paths = let elist_path = StrexpMatch.path_to_exps path in let add_index i e = Prop.exp_normalize_prop p (Sil.Lindex(e, i)) in fun i -> list_map (add_index i) elist_path in let pointers = list_flatten (list_map add_index_to_paths indices) in let filter = function | Sil.Hpointsto (_, Sil.Eexp (e, inst), _) -> list_exists (Sil.exp_equal e) pointers | _ -> false in list_exists filter (Prop.get_sigma p) (** Given [p] containing an array at [path], blur [index] in it *) let blur_array_index (footprint_part: bool) (p: Prop.normal Prop.t) (path: StrexpMatch.path) (index: Sil.exp) : Prop.normal Prop.t = try let fresh_index = Sil.Var (Ident.create_fresh (if !Config.footprint then Ident.kfootprint else Ident.kprimed)) in let p2 = try if !Config.footprint then begin let sigma_fp = Prop.get_sigma_footprint p in let matched_fp = StrexpMatch.find_path sigma_fp path in let sigma_fp' = StrexpMatch.replace_index true matched_fp index fresh_index in Prop.replace_sigma_footprint sigma_fp' p end else Prop.expose p with Not_found -> Prop.expose p in let p3 = let matched = StrexpMatch.find_path (Prop.get_sigma p) path in let sigma' = StrexpMatch.replace_index false matched index fresh_index in Prop.replace_sigma sigma' p2 in let p4 = let index_next = Sil.BinOp(Sil.PlusA, index, Sil.exp_one) in let fresh_index_next = Sil.BinOp (Sil.PlusA, fresh_index, Sil.exp_one) in let map = [(index, fresh_index); (index_next, fresh_index_next)] in prop_replace_path_index p3 path map in Prop.normalize p4 with Not_found -> p (** Given [p] containing an array at [root], blur [indices] in it *) let blur_array_indices (footprint_part : bool) (p: Prop.normal Prop.t) (root: StrexpMatch.path) (indices: Sil.exp list) : Prop.normal Prop.t * bool = let f prop index = blur_array_index footprint_part prop root index in (list_fold_left f p indices, list_length indices > 0) (** Given [p] containing an array at [root], only keep [indices] in it *) let keep_only_indices (footprint_part : bool) (p: Prop.normal Prop.t) (path: StrexpMatch.path) (indices: Sil.exp list) : Prop.normal Prop.t * bool = let prune_sigma footprint_part sigma = try let matched = StrexpMatch.find_path sigma path in let (_, se, _) = StrexpMatch.get_data matched in match se with | Sil.Earray (size, esel, inst) -> let esel', esel_leftover' = list_partition (fun (e, _) -> list_exists (Sil.exp_equal e) indices) esel in if esel_leftover' == [] then (sigma, false) else begin let se' = Sil.Earray (size, esel', inst) in let sigma' = StrexpMatch.replace_strexp footprint_part matched se' in (sigma', true) end | _ -> (sigma, false) with Not_found -> (sigma, false) in prop_update_sigma_and_fp_sigma p prune_sigma (** If the type is array, check whether we should do abstraction *) let array_typ_can_abstract = function | Sil.Tarray (Sil.Tptr (Sil.Tfun _, _), _) -> false (* don't abstract arrays of pointers *) | _ -> true (** This function checks whether we can apply an abstraction to a strexp *) let strexp_can_abstract sigma_rest ((_, se, typ) : StrexpMatch.strexp_data) : bool = let can_abstract_se = match se with | Sil.Earray (size, esel, _) -> let len = list_length esel in len > 1 | _ -> false in can_abstract_se && array_typ_can_abstract typ (** This function abstracts a strexp *) let strexp_do_abstract footprint_part p ((path, se_in, typ_in) : StrexpMatch.strexp_data) : Prop.normal Prop.t * bool = if !Config.trace_absarray && footprint_part then (L.d_str "strexp_do_abstract (footprint)"; L.d_ln ()); if !Config.trace_absarray && not footprint_part then (L.d_str "strexp_do_abstract (nonfootprint)"; L.d_ln ()); let prune_and_blur d_keys keep blur path keep_keys blur_keys = let p2, changed2 = if !Config.trace_absarray then (L.d_str "keep "; d_keys keep_keys; L.d_ln ()); keep p path keep_keys in let p3, changed3 = if blur_keys == [] then (p2, false) else begin if !Config.trace_absarray then (L.d_str "blur "; d_keys blur_keys; L.d_ln ()); blur p2 path blur_keys end in if !Config.trace_absarray then (L.d_strln "Returns"; Prop.d_prop p3; L.d_ln (); L.d_ln ()); (p3, changed2 || changed3) in let prune_and_blur_indices = prune_and_blur Sil.d_exp_list (keep_only_indices footprint_part) (blur_array_indices footprint_part) in let partition_abstract should_keep abstract ksel default_keys = let keep_ksel, remove_ksel = list_partition should_keep ksel in let keep_keys, remove_keys, keys = list_map fst keep_ksel, list_map fst remove_ksel, list_map fst ksel in let keep_keys' = if keep_keys == [] then default_keys else keep_keys in abstract keep_keys' keep_keys' in let do_array_footprint esel = (* array case footprint: keep only the last index, and blur it *) let should_keep (i0, _) = index_is_pointed_to p path i0 in let abstract = prune_and_blur_indices path in let default_indices = match list_map fst esel with | [] -> [] | indices -> [list_hd (list_rev indices)] (* keep last key at least *) in partition_abstract should_keep abstract esel default_indices in let do_footprint () = match se_in with | Sil.Earray (_, esel, _) -> do_array_footprint esel | _ -> assert false in let filter_abstract d_keys should_keep abstract ksel default_keys = let keep_ksel = list_filter should_keep ksel in let keep_keys = list_map fst keep_ksel in let keep_keys' = if keep_keys == [] then default_keys else keep_keys in if !Config.trace_absarray then (L.d_str "keep "; d_keys keep_keys'; L.d_ln ()); abstract keep_keys' [] in let do_array_reexecution esel = (* array case re-execution: remove and blur constant and primed indices *) let is_pointed index = index_is_pointed_to p path index in let should_keep (index, _) = match index with | Sil.Const _ -> is_pointed index | Sil.Var id -> Ident.is_normal id || is_pointed index | _ -> false in let abstract = prune_and_blur_indices path in filter_abstract Sil.d_exp_list should_keep abstract esel [] in let do_reexecution () = match se_in with | Sil.Earray (_, esel, _) -> do_array_reexecution esel | _ -> assert false in if !Config.footprint then do_footprint () else do_reexecution () let strexp_abstract (p : Prop.normal Prop.t) : Prop.normal Prop.t = generic_strexp_abstract "strexp_abstract" p strexp_can_abstract strexp_do_abstract let report_error prop = L.d_strln "Check after array abstraction: FAIL"; Prop.d_prop prop; L.d_ln (); assert false let check_footprint_pure prop = let fav_pure = Sil.fav_new () in Prop.pi_fav_add fav_pure (Prop.get_pure prop @ Prop.get_pi_footprint prop); let fav_sigma = Sil.fav_new () in Prop.sigma_fav_add fav_sigma (Prop.get_sigma prop @ Prop.get_sigma_footprint prop); Sil.fav_filter_ident fav_pure Ident.is_footprint; Sil.fav_filter_ident fav_sigma Ident.is_footprint; if not (Sil.fav_subset_ident fav_pure fav_sigma) then (L.d_strln "footprint vars in pure and not in sigma"; report_error prop) (** Check performed after the array abstraction to see whether it was successful. Raise assert false in case of failure *) let check_after_array_abstraction prop = let check_index root offs (ind, _) = if !Config.footprint then let path = StrexpMatch.path_from_exp_offsets root offs in index_is_pointed_to prop path ind else not (Sil.fav_exists (Sil.exp_fav ind) Ident.is_primed) in let rec check_se root offs typ = function | Sil.Eexp _ -> () | Sil.Earray (_, esel, _) -> (* check that no more than 2 elements are in the array *) let typ_elem = Sil.array_typ_elem (Some Sil.Tvoid) typ in if list_length esel > 2 && array_typ_can_abstract typ then if list_for_all (check_index root offs) esel then () else report_error prop else list_iter (fun (ind, se) -> check_se root (offs @ [Sil.Off_index ind]) typ_elem se) esel | Sil.Estruct (fsel, _) -> list_iter (fun (f, se) -> let typ_f = Sil.struct_typ_fld (Some Sil.Tvoid) f typ in check_se root (offs @ [Sil.Off_fld (f, typ)]) typ_f se) fsel in let check_hpred = function | Sil.Hpointsto (root, se, texp) -> let typ = Sil.texp_to_typ (Some Sil.Tvoid) texp in check_se root [] typ se | Sil.Hlseg _ | Sil.Hdllseg _ -> () in let check_sigma sigma = list_iter check_hpred sigma in (* check_footprint_pure prop; *) check_sigma (Prop.get_sigma prop); check_sigma (Prop.get_sigma_footprint prop) (** Apply array abstraction and check the result *) let abstract_array_check p = let p_res = strexp_abstract p in check_after_array_abstraction p_res; p_res (** remove redundant elements in an array *) let remove_redundant_elements prop = Prop.d_prop prop; L.d_ln (); let occurs_at_most_once : Ident.t -> bool = (* the variable occurs at most once in the footprint or current part *) let fav_curr = Sil.fav_new () in let fav_foot = Sil.fav_new () in Sil.fav_duplicates := true; Sil.sub_fav_add fav_curr (Prop.get_sub prop); Prop.pi_fav_add fav_curr (Prop.get_pi prop); Prop.sigma_fav_add fav_curr (Prop.get_sigma prop); Prop.pi_fav_add fav_foot (Prop.get_pi_footprint prop); Prop.sigma_fav_add fav_foot (Prop.get_sigma_footprint prop); let favl_curr = Sil.fav_to_list fav_curr in let favl_foot = Sil.fav_to_list fav_foot in Sil.fav_duplicates := false; (* L.d_str "favl_curr "; list_iter (fun id -> Sil.d_exp (Sil.Var id)) favl_curr; L.d_ln(); L.d_str "favl_foot "; list_iter (fun id -> Sil.d_exp (Sil.Var id)) favl_foot; L.d_ln(); *) let num_occur l id = list_length (list_filter (fun id' -> Ident.equal id id') l) in let at_most_once v = num_occur favl_curr v <= 1 && num_occur favl_foot v <= 1 in at_most_once in let modified = ref false in let filter_redundant_e_se fp_part (e, se) = let remove () = L.d_strln "kill_redundant: removing "; Sil.d_exp e; L.d_str " "; Sil.d_sexp se; L.d_ln(); array_abstraction_performed := true; modified := true; false in match e, se with | Sil.Const (Sil.Cint i), Sil.Eexp (Sil.Var id, _) when (not fp_part || Sil.Int.iszero i) && Ident.is_normal id = false && occurs_at_most_once id -> remove () (* unknown value can be removed in re-execution mode or if the index is zero *) | Sil.Var id, Sil.Eexp _ when Ident.is_normal id = false && occurs_at_most_once id -> remove () (* index unknown can be removed *) | _ -> true in let remove_redundant_se fp_part = function | Sil.Earray (size, esel, inst) -> let esel' = list_filter (filter_redundant_e_se fp_part) esel in Sil.Earray (size, esel', inst) | se -> se in let remove_redundant_hpred fp_part = function | Sil.Hpointsto (e, se, te) -> let se' = remove_redundant_se fp_part se in Sil.Hpointsto (e, se', te) | hpred -> hpred in let remove_redundant_sigma fp_part sigma = list_map (remove_redundant_hpred fp_part) sigma in let sigma' = remove_redundant_sigma false (Prop.get_sigma prop) in let foot_sigma' = remove_redundant_sigma true (Prop.get_sigma_footprint prop) in if !modified then let prop' = Prop.replace_sigma sigma' (Prop.replace_sigma_footprint foot_sigma' prop) in Prop.normalize prop' else prop