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.
690 lines
25 KiB
690 lines
25 KiB
10 years ago
|
(*
|
||
9 years ago
|
* 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.
|
||
|
*)
|
||
10 years ago
|
|
||
8 years ago
|
open! IStd
|
||
9 years ago
|
|
||
10 years ago
|
(** Abstraction for Arrays *)
|
||
|
|
||
|
module L = Logging
|
||
|
module F = Format
|
||
|
|
||
|
type sigma = Sil.hpred list
|
||
|
|
||
|
(** Matcher for the sigma part specialized to strexps *)
|
||
|
module StrexpMatch : sig
|
||
9 years ago
|
(** path through a strexp *)
|
||
10 years ago
|
type path
|
||
|
|
||
8 years ago
|
val path_to_exps : path -> Exp.t list
|
||
7 years ago
|
(** convert a path into a list of expressions *)
|
||
10 years ago
|
|
||
8 years ago
|
val path_from_exp_offsets : Exp.t -> Sil.offset list -> path
|
||
7 years ago
|
(** create a path from a root and a list of offsets *)
|
||
10 years ago
|
|
||
9 years ago
|
(** path to the root, length, elements and type of a new_array *)
|
||
9 years ago
|
type strexp_data = path * Sil.strexp * Typ.t
|
||
10 years ago
|
|
||
|
(** sigma with info about a current array *)
|
||
|
type t
|
||
|
|
||
|
val find_path : sigma -> path -> t
|
||
7 years ago
|
(** Find a strexp at the given path. Can raise [Not_found] *)
|
||
10 years ago
|
|
||
8 years ago
|
val find : Tenv.t -> sigma -> (strexp_data -> bool) -> t list
|
||
7 years ago
|
(** Find a strexp with the given property. *)
|
||
10 years ago
|
|
||
8 years ago
|
val get_data : Tenv.t -> t -> strexp_data
|
||
7 years ago
|
(** Get the array *)
|
||
10 years ago
|
|
||
8 years ago
|
val replace_strexp : Tenv.t -> bool -> t -> Sil.strexp -> sigma
|
||
7 years ago
|
(** Replace the strexp at a given position by a new strexp *)
|
||
10 years ago
|
|
||
8 years ago
|
val replace_index : Tenv.t -> bool -> t -> Exp.t -> Exp.t -> sigma
|
||
7 years ago
|
(** Replace the index in the array at a given position with the new index *)
|
||
|
(*
|
||
9 years ago
|
(** Get the partition of the sigma: the unmatched part of the sigma and the matched hpred *)
|
||
|
val get_sigma_partition : t -> sigma * Sil.hpred
|
||
10 years ago
|
|
||
9 years ago
|
(** Replace the strexp and the unmatched part of the sigma by the givn inputs *)
|
||
|
val replace_strexp_sigma : bool -> t -> Sil.strexp -> sigma -> sigma
|
||
|
*)
|
||
10 years ago
|
end = struct
|
||
|
(** syntactic offset *)
|
||
8 years ago
|
type syn_offset = Field of Typ.Fieldname.t * Typ.t | Index of Exp.t
|
||
10 years ago
|
|
||
|
(** path through an Estruct *)
|
||
7 years ago
|
type path = Exp.t * syn_offset list
|
||
10 years ago
|
|
||
|
(** Find a strexp and a type at the given syntactic offset list *)
|
||
8 years ago
|
let rec get_strexp_at_syn_offsets tenv se (t: Typ.t) syn_offs =
|
||
|
let fail () =
|
||
7 years ago
|
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: " ;
|
||
|
Typ.d_full t ;
|
||
|
L.d_ln () ;
|
||
8 years ago
|
assert false
|
||
|
in
|
||
7 years ago
|
match (se, t.desc, syn_offs) with
|
||
7 years ago
|
| _, _, [] ->
|
||
|
(se, t)
|
||
7 years ago
|
| Sil.Estruct (fsel, _), Tstruct name, (Field (fld, _)) :: syn_offs' -> (
|
||
|
match Tenv.lookup tenv name with
|
||
7 years ago
|
| Some {fields} ->
|
||
|
let se' = snd (List.find_exn ~f:(fun (f', _) -> Typ.Fieldname.equal f' fld) fsel) in
|
||
7 years ago
|
let t' = snd3 (List.find_exn ~f:(fun (f', _, _) -> Typ.Fieldname.equal f' fld) fields) in
|
||
|
get_strexp_at_syn_offsets tenv se' t' syn_offs'
|
||
7 years ago
|
| None ->
|
||
|
fail () )
|
||
7 years ago
|
| Sil.Earray (_, esel, _), Typ.Tarray {elt= t'}, (Index ind) :: syn_offs' ->
|
||
7 years ago
|
let se' = snd (List.find_exn ~f:(fun (i', _) -> Exp.equal i' ind) esel) in
|
||
8 years ago
|
get_strexp_at_syn_offsets tenv se' t' syn_offs'
|
||
7 years ago
|
| _ ->
|
||
|
fail ()
|
||
|
|
||
10 years ago
|
|
||
|
(** Replace a strexp at the given syntactic offset list *)
|
||
8 years ago
|
let rec replace_strexp_at_syn_offsets tenv se (t: Typ.t) syn_offs update =
|
||
7 years ago
|
match (se, t.desc, syn_offs) with
|
||
7 years ago
|
| _, _, [] ->
|
||
|
update se
|
||
7 years ago
|
| Sil.Estruct (fsel, inst), Tstruct name, (Field (fld, _)) :: syn_offs' -> (
|
||
|
match Tenv.lookup tenv name with
|
||
7 years ago
|
| Some {fields} ->
|
||
|
let se' = snd (List.find_exn ~f:(fun (f', _) -> Typ.Fieldname.equal f' fld) fsel) in
|
||
7 years ago
|
let t' =
|
||
|
(fun (_, y, _) -> y)
|
||
|
(List.find_exn ~f:(fun (f', _, _) -> Typ.Fieldname.equal f' fld) fields)
|
||
|
in
|
||
|
let se_mod = replace_strexp_at_syn_offsets tenv se' t' syn_offs' update in
|
||
|
let fsel' =
|
||
|
List.map
|
||
|
~f:(fun (f'', se'') ->
|
||
7 years ago
|
if Typ.Fieldname.equal f'' fld then (fld, se_mod) else (f'', se'') )
|
||
7 years ago
|
fsel
|
||
|
in
|
||
|
Sil.Estruct (fsel', inst)
|
||
7 years ago
|
| None ->
|
||
|
assert false )
|
||
7 years ago
|
| Sil.Earray (len, esel, inst), Tarray {elt= t'}, (Index idx) :: syn_offs' ->
|
||
7 years ago
|
let se' = snd (List.find_exn ~f:(fun (i', _) -> Exp.equal i' idx) esel) in
|
||
8 years ago
|
let se_mod = replace_strexp_at_syn_offsets tenv se' t' syn_offs' update in
|
||
8 years ago
|
let esel' =
|
||
7 years ago
|
List.map ~f:(fun ese -> if Exp.equal (fst ese) idx then (idx, se_mod) else ese) esel
|
||
|
in
|
||
9 years ago
|
Sil.Earray (len, esel', inst)
|
||
7 years ago
|
| _ ->
|
||
|
assert false
|
||
|
|
||
10 years ago
|
|
||
|
(** convert a path into an expression *)
|
||
|
let path_to_exps (root, syn_offs_in) =
|
||
|
let rec convert acc = function
|
||
7 years ago
|
| [] ->
|
||
|
acc
|
||
|
| (Field (f, t)) :: syn_offs' ->
|
||
|
let acc' = List.map ~f:(fun e -> Exp.Lfield (e, f, t)) acc in
|
||
10 years ago
|
convert acc' syn_offs'
|
||
7 years ago
|
| (Index idx) :: syn_offs' ->
|
||
|
let acc' = List.map ~f:(fun e -> Exp.Lindex (e, idx)) acc in
|
||
7 years ago
|
convert acc' syn_offs'
|
||
|
in
|
||
|
convert [root] syn_offs_in
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** create a path from a root and a list of offsets *)
|
||
|
let path_from_exp_offsets root offs =
|
||
|
let offset_to_syn_offset = function
|
||
7 years ago
|
| Sil.Off_fld (fld, typ) ->
|
||
|
Field (fld, typ)
|
||
|
| Sil.Off_index idx ->
|
||
|
Index idx
|
||
7 years ago
|
in
|
||
8 years ago
|
let syn_offs = List.map ~f:offset_to_syn_offset offs in
|
||
10 years ago
|
(root, syn_offs)
|
||
|
|
||
7 years ago
|
|
||
9 years ago
|
(** path to the root, len, elements and type of a new_array *)
|
||
9 years ago
|
type strexp_data = path * Sil.strexp * Typ.t
|
||
10 years ago
|
|
||
|
(** Store hpred using physical equality, and offset list for an array *)
|
||
7 years ago
|
type t = sigma * Sil.hpred * syn_offset list
|
||
10 years ago
|
|
||
|
(** Find an array at the given path. Can raise [Not_found] *)
|
||
|
let find_path sigma (root, syn_offs) : t =
|
||
7 years ago
|
let filter = function Sil.Hpointsto (e, _, _) -> Exp.equal root e | _ -> false in
|
||
8 years ago
|
let hpred = List.find_exn ~f:filter sigma in
|
||
10 years ago
|
(sigma, hpred, syn_offs)
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Find a sub strexp with the given property. Can raise [Not_found] *)
|
||
7 years ago
|
let find tenv (sigma: sigma) (pred: strexp_data -> bool) : t list =
|
||
10 years ago
|
let found = ref [] in
|
||
8 years ago
|
let rec find_offset_sexp sigma_other hpred root offs se (typ: Typ.t) =
|
||
8 years ago
|
let offs' = List.rev offs in
|
||
10 years ago
|
let path = (root, offs') in
|
||
9 years ago
|
if pred (path, se, typ) then found := (sigma, hpred, offs') :: !found
|
||
7 years ago
|
else
|
||
|
match (se, typ.desc) with
|
||
8 years ago
|
| Sil.Estruct (fsel, _), Tstruct name -> (
|
||
7 years ago
|
match Tenv.lookup tenv name with
|
||
7 years ago
|
| Some {fields} ->
|
||
|
find_offset_fsel sigma_other hpred root offs fsel fields typ
|
||
|
| None ->
|
||
|
() )
|
||
7 years ago
|
| Sil.Earray (_, esel, _), Tarray {elt} ->
|
||
|
find_offset_esel sigma_other hpred root offs esel elt
|
||
7 years ago
|
| _ ->
|
||
|
()
|
||
7 years ago
|
and find_offset_fsel sigma_other hpred root offs fsel ftal typ =
|
||
|
match fsel with
|
||
7 years ago
|
| [] ->
|
||
|
()
|
||
|
| (f, se) :: fsel' ->
|
||
|
( match List.find ~f:(fun (f', _, _) -> Typ.Fieldname.equal f' f) ftal with
|
||
|
| Some (_, t, _) ->
|
||
|
find_offset_sexp sigma_other hpred root (Field (f, typ) :: offs) se t
|
||
|
| None ->
|
||
|
L.d_strln ("Can't find field " ^ Typ.Fieldname.to_string f ^ " in StrexpMatch.find")
|
||
7 years ago
|
) ;
|
||
10 years ago
|
find_offset_fsel sigma_other hpred root offs fsel' ftal typ
|
||
7 years ago
|
and find_offset_esel sigma_other hpred root offs esel t =
|
||
|
match esel with
|
||
7 years ago
|
| [] ->
|
||
|
()
|
||
|
| (ind, se) :: esel' ->
|
||
|
find_offset_sexp sigma_other hpred root (Index ind :: offs) se t ;
|
||
7 years ago
|
find_offset_esel sigma_other hpred root offs esel' t
|
||
|
in
|
||
10 years ago
|
let rec iterate sigma_seen = function
|
||
7 years ago
|
| [] ->
|
||
|
()
|
||
|
| hpred :: sigma_rest ->
|
||
|
( match hpred with
|
||
|
| Sil.Hpointsto (root, se, te) ->
|
||
|
let sigma_other = sigma_seen @ sigma_rest in
|
||
7 years ago
|
find_offset_sexp sigma_other hpred root [] se (Exp.texp_to_typ None te)
|
||
7 years ago
|
| _ ->
|
||
|
() ) ;
|
||
7 years ago
|
iterate (hpred :: sigma_seen) sigma_rest
|
||
|
in
|
||
|
iterate [] sigma ; !found
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Get the matched strexp *)
|
||
7 years ago
|
let get_data tenv ((_, hpred, syn_offs): t) =
|
||
|
match hpred with
|
||
7 years ago
|
| Sil.Hpointsto (root, se, te) ->
|
||
|
let t = Exp.texp_to_typ None te in
|
||
8 years ago
|
let se', t' = get_strexp_at_syn_offsets tenv se t syn_offs in
|
||
10 years ago
|
let path' = (root, syn_offs) in
|
||
|
(path', se', t')
|
||
7 years ago
|
| _ ->
|
||
|
assert false
|
||
|
|
||
10 years ago
|
|
||
|
(** Replace the current hpred *)
|
||
7 years ago
|
let replace_hpred ((sigma, hpred, _): t) hpred' =
|
||
8 years ago
|
List.map ~f:(fun hpred'' -> if phys_equal hpred'' hpred then hpred' else hpred'') sigma
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Replace the strexp at the given offset in the given hpred *)
|
||
8 years ago
|
let hpred_replace_strexp tenv footprint_part hpred syn_offs update =
|
||
9 years ago
|
let update se' =
|
||
|
let se_in = update se' in
|
||
7 years ago
|
match (se', se_in) with
|
||
7 years ago
|
| Sil.Earray (len, esel, _), Sil.Earray (_, esel_in, inst2) ->
|
||
|
let orig_indices = List.map ~f:fst esel in
|
||
8 years ago
|
let index_is_not_new idx = List.exists ~f:(Exp.equal idx) orig_indices in
|
||
10 years ago
|
let process_index idx =
|
||
7 years ago
|
if index_is_not_new idx then idx else Sil.array_clean_new_index footprint_part idx
|
||
|
in
|
||
|
let esel_in' = List.map ~f:(fun (idx, se) -> (process_index idx, se)) esel_in in
|
||
9 years ago
|
Sil.Earray (len, esel_in', inst2)
|
||
7 years ago
|
| _, _ ->
|
||
|
se_in
|
||
7 years ago
|
in
|
||
|
match hpred with
|
||
7 years ago
|
| Sil.Hpointsto (root, se, te) ->
|
||
|
let t = Exp.texp_to_typ None te in
|
||
7 years ago
|
let se' = replace_strexp_at_syn_offsets tenv se t syn_offs update in
|
||
|
Sil.Hpointsto (root, se', te)
|
||
7 years ago
|
| _ ->
|
||
|
assert false
|
||
|
|
||
10 years ago
|
|
||
|
(** Replace the strexp at a given position by a new strexp *)
|
||
7 years ago
|
let replace_strexp tenv footprint_part ((sigma, hpred, syn_offs): t) se_in =
|
||
9 years ago
|
let update _ = se_in in
|
||
8 years ago
|
let hpred' = hpred_replace_strexp tenv footprint_part hpred syn_offs update in
|
||
10 years ago
|
replace_hpred (sigma, hpred, syn_offs) hpred'
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Replace the index in the array at a given position with the new index *)
|
||
7 years ago
|
let replace_index tenv footprint_part ((sigma, hpred, syn_offs): t) (index: Exp.t)
|
||
|
(index': Exp.t) =
|
||
9 years ago
|
let update se' =
|
||
10 years ago
|
match se' with
|
||
7 years ago
|
| Sil.Earray (len, esel, inst) ->
|
||
|
let esel' =
|
||
7 years ago
|
List.map
|
||
|
~f:(fun (e', se') -> if Exp.equal e' index then (index', se') else (e', se'))
|
||
|
esel
|
||
|
in
|
||
9 years ago
|
Sil.Earray (len, esel', inst)
|
||
7 years ago
|
| _ ->
|
||
|
assert false
|
||
7 years ago
|
in
|
||
8 years ago
|
let hpred' = hpred_replace_strexp tenv footprint_part hpred syn_offs update in
|
||
10 years ago
|
replace_hpred (sigma, hpred, syn_offs) hpred'
|
||
|
end
|
||
|
|
||
|
(** This function renames expressions in [p]. The renaming is, roughly
|
||
9 years ago
|
speaking, to replace [path.i] by [path.i'] for all (i, i') in [map]. *)
|
||
7 years ago
|
let prop_replace_path_index tenv (p: Prop.exposed Prop.t) (path: StrexpMatch.path)
|
||
|
(map: (Exp.t * Exp.t) list) : Prop.exposed Prop.t =
|
||
10 years ago
|
let elist_path = StrexpMatch.path_to_exps path in
|
||
|
let expmap_list =
|
||
7 years ago
|
List.fold
|
||
|
~f:(fun acc_outer e_path ->
|
||
|
List.fold
|
||
|
~f:(fun acc_inner (old_index, new_index) ->
|
||
|
let old_e_path_index =
|
||
|
Prop.exp_normalize_prop tenv p (Exp.Lindex (e_path, old_index))
|
||
|
in
|
||
|
let new_e_path_index =
|
||
|
Prop.exp_normalize_prop tenv p (Exp.Lindex (e_path, new_index))
|
||
|
in
|
||
7 years ago
|
(old_e_path_index, new_e_path_index) :: acc_inner )
|
||
|
~init:acc_outer map )
|
||
7 years ago
|
~init:[] elist_path
|
||
|
in
|
||
10 years ago
|
let expmap_fun e' =
|
||
7 years ago
|
Option.value_map ~f:snd (List.find ~f:(fun (e, _) -> Exp.equal e e') expmap_list) ~default:e'
|
||
|
in
|
||
10 years ago
|
Prop.prop_expmap expmap_fun p
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** This function uses [update] and transforms the two sigma parts of [p],
|
||
9 years ago
|
the sigma of the current SH of [p] and that of the footprint of [p]. *)
|
||
7 years ago
|
let prop_update_sigma_and_fp_sigma tenv (p: Prop.normal Prop.t)
|
||
|
(update: bool -> sigma -> sigma * bool) : Prop.normal Prop.t * bool =
|
||
8 years ago
|
let sigma', changed = update false p.Prop.sigma in
|
||
|
let ep1 = Prop.set p ~sigma:sigma' in
|
||
10 years ago
|
let ep2, changed2 =
|
||
|
if !Config.footprint then
|
||
8 years ago
|
let sigma_fp', changed' = update true ep1.Prop.sigma_fp in
|
||
|
(Prop.set ep1 ~sigma_fp:sigma_fp', changed')
|
||
7 years ago
|
else (ep1, false)
|
||
|
in
|
||
8 years ago
|
(Prop.normalize tenv ep2, changed || changed2)
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** 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
|
||
9 years ago
|
where the abstraction might be applicable, and the parameter [do_abstract] does
|
||
|
the abstraction to those spotted strexps. *)
|
||
7 years ago
|
let generic_strexp_abstract tenv (abstraction_name: string) (p_in: Prop.normal Prop.t)
|
||
|
(can_abstract_: StrexpMatch.strexp_data -> bool)
|
||
|
(do_abstract:
|
||
7 years ago
|
bool -> Prop.normal Prop.t -> StrexpMatch.strexp_data -> Prop.normal Prop.t * bool)
|
||
7 years ago
|
: Prop.normal Prop.t =
|
||
9 years ago
|
let can_abstract data =
|
||
|
let r = can_abstract_ data in
|
||
7 years ago
|
if r then array_abstraction_performed := true ;
|
||
|
r
|
||
|
in
|
||
10 years ago
|
let find_strexp_to_abstract p0 =
|
||
8 years ago
|
let find sigma = StrexpMatch.find tenv sigma can_abstract in
|
||
8 years ago
|
let matchings_cur = find p0.Prop.sigma in
|
||
|
let matchings_fp = find p0.Prop.sigma_fp in
|
||
7 years ago
|
(matchings_cur, matchings_fp)
|
||
|
in
|
||
10 years ago
|
let match_select_next (matchings_cur, matchings_fp) =
|
||
7 years ago
|
match (matchings_cur, matchings_fp) with
|
||
7 years ago
|
| [], [] ->
|
||
|
raise Not_found
|
||
|
| matched :: cur', fp' ->
|
||
|
(matched, false, (cur', fp'))
|
||
|
| [], matched :: fp' ->
|
||
|
(matched, true, ([], fp'))
|
||
7 years ago
|
in
|
||
10 years ago
|
let rec match_abstract p0 matchings_cur_fp =
|
||
|
try
|
||
|
let matched, footprint_part, matchings_cur_fp' = match_select_next matchings_cur_fp in
|
||
8 years ago
|
let n = List.length (snd matchings_cur_fp') + 1 in
|
||
7 years ago
|
if Config.trace_absarray then L.d_strln ("Num of fp candidates " ^ string_of_int n) ;
|
||
8 years ago
|
let strexp_data = StrexpMatch.get_data tenv matched in
|
||
10 years ago
|
let p1, changed = do_abstract footprint_part p0 strexp_data in
|
||
7 years ago
|
if changed then (p1, true) else match_abstract p0 matchings_cur_fp'
|
||
|
with Not_found -> (p0, false)
|
||
|
in
|
||
10 years ago
|
let rec find_then_abstract bound p0 =
|
||
8 years ago
|
if Int.equal bound 0 then p0
|
||
7 years ago
|
else (
|
||
|
if Config.trace_absarray then (
|
||
|
L.d_strln ("Applying " ^ abstraction_name ^ " to") ;
|
||
|
Prop.d_prop p0 ;
|
||
|
L.d_ln () ;
|
||
|
L.d_ln () ) ;
|
||
10 years ago
|
let matchings_cur_fp = find_strexp_to_abstract p0 in
|
||
|
let p1, changed = match_abstract p0 matchings_cur_fp in
|
||
7 years ago
|
if changed then find_then_abstract (bound - 1) p1 else p0 )
|
||
|
in
|
||
10 years ago
|
let matchings_cur, matchings_fp = find_strexp_to_abstract p_in in
|
||
7 years ago
|
let num_matches = List.length matchings_cur + List.length matchings_fp in
|
||
|
find_then_abstract num_matches p_in
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Return [true] if there's a pointer to the index *)
|
||
8 years ago
|
let index_is_pointed_to tenv (p: Prop.normal Prop.t) (path: StrexpMatch.path) (index: Exp.t) : bool =
|
||
10 years ago
|
let indices =
|
||
7 years ago
|
let index_plus_one = Exp.BinOp (Binop.PlusA, index, Exp.one) in
|
||
|
[index; index_plus_one]
|
||
|
in
|
||
10 years ago
|
let add_index_to_paths =
|
||
|
let elist_path = StrexpMatch.path_to_exps path in
|
||
7 years ago
|
let add_index i e = Prop.exp_normalize_prop tenv p (Exp.Lindex (e, i)) in
|
||
|
fun i -> List.map ~f:(add_index i) elist_path
|
||
|
in
|
||
8 years ago
|
let pointers = List.concat_map ~f:add_index_to_paths indices in
|
||
10 years ago
|
let filter = function
|
||
7 years ago
|
| Sil.Hpointsto (_, Sil.Eexp (e, _), _) ->
|
||
|
List.exists ~f:(Exp.equal e) pointers
|
||
|
| _ ->
|
||
|
false
|
||
7 years ago
|
in
|
||
8 years ago
|
List.exists ~f:filter p.Prop.sigma
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Given [p] containing an array at [path], blur [index] in it *)
|
||
7 years ago
|
let blur_array_index tenv (p: Prop.normal Prop.t) (path: StrexpMatch.path) (index: Exp.t)
|
||
|
: Prop.normal Prop.t =
|
||
10 years ago
|
try
|
||
8 years ago
|
let fresh_index =
|
||
7 years ago
|
Exp.Var (Ident.create_fresh (if !Config.footprint then Ident.kfootprint else Ident.kprimed))
|
||
|
in
|
||
10 years ago
|
let p2 =
|
||
|
try
|
||
|
if !Config.footprint then
|
||
7 years ago
|
let sigma_fp = p.Prop.sigma_fp in
|
||
|
let matched_fp = StrexpMatch.find_path sigma_fp path in
|
||
|
let sigma_fp' = StrexpMatch.replace_index tenv true matched_fp index fresh_index in
|
||
|
Prop.set p ~sigma_fp:sigma_fp'
|
||
10 years ago
|
else Prop.expose p
|
||
7 years ago
|
with Not_found -> Prop.expose p
|
||
|
in
|
||
10 years ago
|
let p3 =
|
||
8 years ago
|
let matched = StrexpMatch.find_path p.Prop.sigma path in
|
||
8 years ago
|
let sigma' = StrexpMatch.replace_index tenv false matched index fresh_index in
|
||
7 years ago
|
Prop.set p2 ~sigma:sigma'
|
||
|
in
|
||
10 years ago
|
let p4 =
|
||
7 years ago
|
let index_next = Exp.BinOp (Binop.PlusA, index, Exp.one) in
|
||
8 years ago
|
let fresh_index_next = Exp.BinOp (Binop.PlusA, fresh_index, Exp.one) in
|
||
10 years ago
|
let map = [(index, fresh_index); (index_next, fresh_index_next)] in
|
||
7 years ago
|
prop_replace_path_index tenv p3 path map
|
||
|
in
|
||
8 years ago
|
Prop.normalize tenv p4
|
||
10 years ago
|
with Not_found -> p
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Given [p] containing an array at [root], blur [indices] in it *)
|
||
7 years ago
|
let blur_array_indices tenv (p: Prop.normal Prop.t) (root: StrexpMatch.path) (indices: Exp.t list)
|
||
|
: Prop.normal Prop.t * bool =
|
||
8 years ago
|
let f prop index = blur_array_index tenv prop root index in
|
||
8 years ago
|
(List.fold ~f ~init:p indices, List.length indices > 0)
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Given [p] containing an array at [root], only keep [indices] in it *)
|
||
7 years ago
|
let keep_only_indices tenv (p: Prop.normal Prop.t) (path: StrexpMatch.path) (indices: Exp.t list)
|
||
|
: Prop.normal Prop.t * bool =
|
||
10 years ago
|
let prune_sigma footprint_part sigma =
|
||
|
try
|
||
|
let matched = StrexpMatch.find_path sigma path in
|
||
7 years ago
|
let _, se, _ = StrexpMatch.get_data tenv matched in
|
||
10 years ago
|
match se with
|
||
7 years ago
|
| Sil.Earray (len, esel, inst) ->
|
||
|
let esel', esel_leftover' =
|
||
7 years ago
|
List.partition_tf ~f:(fun (e, _) -> List.exists ~f:(Exp.equal e) indices) esel
|
||
|
in
|
||
8 years ago
|
if List.is_empty esel_leftover' then (sigma, false)
|
||
7 years ago
|
else
|
||
9 years ago
|
let se' = Sil.Earray (len, esel', inst) in
|
||
8 years ago
|
let sigma' = StrexpMatch.replace_strexp tenv footprint_part matched se' in
|
||
10 years ago
|
(sigma', true)
|
||
7 years ago
|
| _ ->
|
||
|
(sigma, false)
|
||
7 years ago
|
with Not_found -> (sigma, false)
|
||
|
in
|
||
8 years ago
|
prop_update_sigma_and_fp_sigma tenv p prune_sigma
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** If the type is array, check whether we should do abstraction *)
|
||
7 years ago
|
let array_typ_can_abstract {Typ.desc} =
|
||
|
match desc with
|
||
7 years ago
|
| Tarray {elt= {desc= Tptr ({desc= Tfun _}, _)}} ->
|
||
7 years ago
|
false (* don't abstract arrays of pointers *)
|
||
|
| _ ->
|
||
|
true
|
||
|
|
||
10 years ago
|
|
||
|
(** This function checks whether we can apply an abstraction to a strexp *)
|
||
7 years ago
|
let strexp_can_abstract ((_, se, typ): StrexpMatch.strexp_data) : bool =
|
||
|
let can_abstract_se =
|
||
|
match se with
|
||
7 years ago
|
| Sil.Earray (_, esel, _) ->
|
||
|
let len = List.length esel in
|
||
10 years ago
|
len > 1
|
||
7 years ago
|
| _ ->
|
||
|
false
|
||
7 years ago
|
in
|
||
10 years ago
|
can_abstract_se && array_typ_can_abstract typ
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** This function abstracts a strexp *)
|
||
7 years ago
|
let strexp_do_abstract tenv footprint_part p ((path, se_in, _): StrexpMatch.strexp_data)
|
||
|
: Prop.normal Prop.t * bool =
|
||
|
if Config.trace_absarray && footprint_part then (
|
||
7 years ago
|
L.d_str "strexp_do_abstract (footprint)" ;
|
||
|
L.d_ln () ) ;
|
||
7 years ago
|
if Config.trace_absarray && not footprint_part then (
|
||
7 years ago
|
L.d_str "strexp_do_abstract (nonfootprint)" ;
|
||
|
L.d_ln () ) ;
|
||
10 years ago
|
let prune_and_blur d_keys keep blur path keep_keys blur_keys =
|
||
|
let p2, changed2 =
|
||
7 years ago
|
if Config.trace_absarray then ( L.d_str "keep " ; d_keys keep_keys ; L.d_ln () ) ;
|
||
|
keep p path keep_keys
|
||
|
in
|
||
10 years ago
|
let p3, changed3 =
|
||
8 years ago
|
if List.is_empty blur_keys then (p2, false)
|
||
7 years ago
|
else (
|
||
|
if Config.trace_absarray then ( L.d_str "blur " ; d_keys blur_keys ; L.d_ln () ) ;
|
||
|
blur p2 path blur_keys )
|
||
|
in
|
||
|
if Config.trace_absarray then ( L.d_strln "Returns" ; Prop.d_prop p3 ; L.d_ln () ; L.d_ln () ) ;
|
||
|
(p3, changed2 || changed3)
|
||
|
in
|
||
10 years ago
|
let prune_and_blur_indices =
|
||
7 years ago
|
prune_and_blur Sil.d_exp_list (keep_only_indices tenv) (blur_array_indices tenv)
|
||
|
in
|
||
10 years ago
|
let partition_abstract should_keep abstract ksel default_keys =
|
||
8 years ago
|
let keep_ksel, remove_ksel = List.partition_tf ~f:should_keep ksel in
|
||
9 years ago
|
let keep_keys, _, _ =
|
||
7 years ago
|
(List.map ~f:fst keep_ksel, List.map ~f:fst remove_ksel, List.map ~f:fst ksel)
|
||
|
in
|
||
8 years ago
|
let keep_keys' = if List.is_empty keep_keys then default_keys else keep_keys in
|
||
7 years ago
|
abstract keep_keys' keep_keys'
|
||
|
in
|
||
10 years ago
|
let do_array_footprint esel =
|
||
|
(* array case footprint: keep only the last index, and blur it *)
|
||
8 years ago
|
let should_keep (i0, _) = index_is_pointed_to tenv p path i0 in
|
||
10 years ago
|
let abstract = prune_and_blur_indices path in
|
||
|
let default_indices =
|
||
7 years ago
|
match List.map ~f:fst esel with [] -> [] | indices -> [List.last_exn indices]
|
||
|
(* keep last key at least *)
|
||
|
in
|
||
|
partition_abstract should_keep abstract esel default_indices
|
||
|
in
|
||
10 years ago
|
let do_footprint () =
|
||
7 years ago
|
match se_in with Sil.Earray (_, esel, _) -> do_array_footprint esel | _ -> assert false
|
||
|
in
|
||
10 years ago
|
let filter_abstract d_keys should_keep abstract ksel default_keys =
|
||
8 years ago
|
let keep_ksel = List.filter ~f:should_keep ksel in
|
||
8 years ago
|
let keep_keys = List.map ~f:fst keep_ksel in
|
||
8 years ago
|
let keep_keys' = if List.is_empty keep_keys then default_keys else keep_keys in
|
||
7 years ago
|
if Config.trace_absarray then ( L.d_str "keep " ; d_keys keep_keys' ; L.d_ln () ) ;
|
||
|
abstract keep_keys' []
|
||
|
in
|
||
10 years ago
|
let do_array_reexecution esel =
|
||
|
(* array case re-execution: remove and blur constant and primed indices *)
|
||
8 years ago
|
let is_pointed index = index_is_pointed_to tenv p path index in
|
||
7 years ago
|
let should_keep (index, _) =
|
||
|
match index with
|
||
7 years ago
|
| Exp.Const _ ->
|
||
|
is_pointed index
|
||
|
| Exp.Var id ->
|
||
|
Ident.is_normal id || is_pointed index
|
||
|
| _ ->
|
||
|
false
|
||
7 years ago
|
in
|
||
10 years ago
|
let abstract = prune_and_blur_indices path in
|
||
7 years ago
|
filter_abstract Sil.d_exp_list should_keep abstract esel []
|
||
|
in
|
||
10 years ago
|
let do_reexecution () =
|
||
7 years ago
|
match se_in with Sil.Earray (_, esel, _) -> do_array_reexecution esel | _ -> assert false
|
||
|
in
|
||
|
if !Config.footprint then do_footprint () else do_reexecution ()
|
||
10 years ago
|
|
||
7 years ago
|
|
||
7 years ago
|
let strexp_abstract tenv (p: Prop.normal Prop.t) : Prop.normal Prop.t =
|
||
8 years ago
|
generic_strexp_abstract tenv "strexp_abstract" p strexp_can_abstract (strexp_do_abstract tenv)
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
let report_error prop =
|
||
7 years ago
|
L.d_strln "Check after array abstraction: FAIL" ;
|
||
|
Prop.d_prop prop ;
|
||
|
L.d_ln () ;
|
||
10 years ago
|
assert false
|
||
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Check performed after the array abstraction to see whether it was successful. Raise assert false in case of failure *)
|
||
8 years ago
|
let check_after_array_abstraction tenv prop =
|
||
8 years ago
|
let lookup = Tenv.lookup tenv in
|
||
10 years ago
|
let check_index root offs (ind, _) =
|
||
|
if !Config.footprint then
|
||
|
let path = StrexpMatch.path_from_exp_offsets root offs in
|
||
8 years ago
|
index_is_pointed_to tenv prop path ind
|
||
7 years ago
|
else not (Exp.free_vars ind |> Sequence.exists ~f:Ident.is_primed)
|
||
7 years ago
|
in
|
||
10 years ago
|
let rec check_se root offs typ = function
|
||
7 years ago
|
| Sil.Eexp _ ->
|
||
|
()
|
||
|
| Sil.Earray (_, esel, _) ->
|
||
|
(* check that no more than 2 elements are in the array *)
|
||
8 years ago
|
let typ_elem = Typ.array_elem (Some (Typ.mk Tvoid)) typ in
|
||
8 years ago
|
if List.length esel > 2 && array_typ_can_abstract typ then
|
||
7 years ago
|
if List.for_all ~f:(check_index root offs) esel then () else report_error prop
|
||
|
else
|
||
|
List.iter
|
||
8 years ago
|
~f:(fun (ind, se) -> check_se root (offs @ [Sil.Off_index ind]) typ_elem se)
|
||
|
esel
|
||
7 years ago
|
| Sil.Estruct (fsel, _) ->
|
||
|
List.iter
|
||
7 years ago
|
~f:(fun (f, se) ->
|
||
8 years ago
|
let typ_f = Typ.Struct.fld_typ ~lookup ~default:(Typ.mk Tvoid) f typ in
|
||
7 years ago
|
check_se root (offs @ [Sil.Off_fld (f, typ)]) typ_f se )
|
||
7 years ago
|
fsel
|
||
|
in
|
||
10 years ago
|
let check_hpred = function
|
||
7 years ago
|
| Sil.Hpointsto (root, se, texp) ->
|
||
|
let typ = Exp.texp_to_typ (Some (Typ.mk Tvoid)) texp in
|
||
10 years ago
|
check_se root [] typ se
|
||
7 years ago
|
| Sil.Hlseg _ | Sil.Hdllseg _ ->
|
||
|
()
|
||
7 years ago
|
in
|
||
8 years ago
|
let check_sigma sigma = List.iter ~f:check_hpred sigma in
|
||
10 years ago
|
(* check_footprint_pure prop; *)
|
||
7 years ago
|
check_sigma prop.Prop.sigma ; check_sigma prop.Prop.sigma_fp
|
||
10 years ago
|
|
||
7 years ago
|
|
||
10 years ago
|
(** Apply array abstraction and check the result *)
|
||
8 years ago
|
let abstract_array_check tenv p =
|
||
|
let p_res = strexp_abstract tenv p in
|
||
7 years ago
|
check_after_array_abstraction tenv p_res ;
|
||
|
p_res
|
||
|
|
||
10 years ago
|
|
||
|
(** remove redundant elements in an array *)
|
||
8 years ago
|
let remove_redundant_elements tenv prop =
|
||
7 years ago
|
Prop.d_prop prop ;
|
||
|
L.d_ln () ;
|
||
|
let occurs_at_most_once : Ident.t -> bool =
|
||
7 years ago
|
let fav_curr =
|
||
|
let ( @@@ ) = Sequence.append in
|
||
|
Sil.exp_subst_free_vars prop.Prop.sub @@@ Prop.pi_free_vars prop.Prop.pi
|
||
|
@@@ Prop.sigma_free_vars prop.Prop.sigma
|
||
|
in
|
||
|
let fav_foot =
|
||
|
Sequence.append (Prop.pi_free_vars prop.Prop.pi_fp) (Prop.sigma_free_vars prop.Prop.sigma_fp)
|
||
|
in
|
||
|
let at_most_once seq id =
|
||
|
Sequence.filter seq ~f:(Ident.equal id) |> Sequence.length_is_bounded_by ~max:1
|
||
|
in
|
||
|
let at_most_once_in_curr_or_foot v = at_most_once fav_curr v && at_most_once fav_foot v in
|
||
|
at_most_once_in_curr_or_foot
|
||
7 years ago
|
in
|
||
10 years ago
|
let modified = ref false in
|
||
|
let filter_redundant_e_se fp_part (e, se) =
|
||
|
let remove () =
|
||
7 years ago
|
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
|
||
|
| Exp.Const Const.Cint i, Sil.Eexp (Exp.Var id, _)
|
||
7 years ago
|
when (not fp_part || IntLit.iszero i) && not (Ident.is_normal id) && occurs_at_most_once id ->
|
||
|
remove () (* unknown value can be removed in re-execution mode or if the index is zero *)
|
||
|
| Exp.Var id, Sil.Eexp _ when not (Ident.is_normal id) && occurs_at_most_once id ->
|
||
|
remove () (* index unknown can be removed *)
|
||
|
| _ ->
|
||
|
true
|
||
7 years ago
|
in
|
||
10 years ago
|
let remove_redundant_se fp_part = function
|
||
7 years ago
|
| Sil.Earray (len, esel, inst) ->
|
||
|
let esel' = List.filter ~f:(filter_redundant_e_se fp_part) esel in
|
||
9 years ago
|
Sil.Earray (len, esel', inst)
|
||
7 years ago
|
| se ->
|
||
|
se
|
||
7 years ago
|
in
|
||
10 years ago
|
let remove_redundant_hpred fp_part = function
|
||
7 years ago
|
| Sil.Hpointsto (e, se, te) ->
|
||
|
let se' = remove_redundant_se fp_part se in
|
||
10 years ago
|
Sil.Hpointsto (e, se', te)
|
||
7 years ago
|
| hpred ->
|
||
|
hpred
|
||
7 years ago
|
in
|
||
8 years ago
|
let remove_redundant_sigma fp_part sigma = List.map ~f:(remove_redundant_hpred fp_part) sigma in
|
||
8 years ago
|
let sigma' = remove_redundant_sigma false prop.Prop.sigma in
|
||
|
let sigma_fp' = remove_redundant_sigma true prop.Prop.sigma_fp in
|
||
10 years ago
|
if !modified then
|
||
7 years ago
|
let prop' = Prop.set prop ~sigma:sigma' ~sigma_fp:sigma_fp' in
|
||
8 years ago
|
Prop.normalize tenv prop'
|
||
10 years ago
|
else prop
|