|
|
|
@ -28,141 +28,140 @@ struct
|
|
|
|
|
loc : Location.t;
|
|
|
|
|
trace : trace;
|
|
|
|
|
id : string }
|
|
|
|
|
[@@deriving compare]
|
|
|
|
|
and trace = Intra of Typ.Procname.t
|
|
|
|
|
| Inter of Typ.Procname.t * Typ.Procname.t * Location.t
|
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
|
|
and astate = t
|
|
|
|
|
|
|
|
|
|
let set_size_pos : t -> t
|
|
|
|
|
= fun c ->
|
|
|
|
|
if Itv.Bound.lt (Itv.lb c.size) Itv.Bound.zero
|
|
|
|
|
then { c with size = Itv.make Itv.Bound.zero (Itv.ub c.size) }
|
|
|
|
|
else c
|
|
|
|
|
|
|
|
|
|
let string_of_location : Location.t -> string
|
|
|
|
|
= fun loc ->
|
|
|
|
|
let fname = SourceFile.to_string loc.Location.file in
|
|
|
|
|
let pos = Location.to_string loc in
|
|
|
|
|
F.fprintf F.str_formatter "%s:%s" fname pos;
|
|
|
|
|
F.flush_str_formatter ()
|
|
|
|
|
|
|
|
|
|
let pp_location : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt c ->
|
|
|
|
|
F.fprintf fmt "%s" (string_of_location c.loc)
|
|
|
|
|
[@@deriving compare]
|
|
|
|
|
and trace = Intra of Typ.Procname.t
|
|
|
|
|
| Inter of Typ.Procname.t * Typ.Procname.t * Location.t
|
|
|
|
|
[@@deriving compare]
|
|
|
|
|
|
|
|
|
|
and astate = t
|
|
|
|
|
|
|
|
|
|
let set_size_pos : t -> t
|
|
|
|
|
= fun c ->
|
|
|
|
|
if Itv.Bound.lt (Itv.lb c.size) Itv.Bound.zero
|
|
|
|
|
then { c with size = Itv.make Itv.Bound.zero (Itv.ub c.size) }
|
|
|
|
|
else c
|
|
|
|
|
|
|
|
|
|
let string_of_location : Location.t -> string
|
|
|
|
|
= fun loc ->
|
|
|
|
|
let fname = SourceFile.to_string loc.Location.file in
|
|
|
|
|
let pos = Location.to_string loc in
|
|
|
|
|
F.fprintf F.str_formatter "%s:%s" fname pos;
|
|
|
|
|
F.flush_str_formatter ()
|
|
|
|
|
|
|
|
|
|
let pp_location : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt c ->
|
|
|
|
|
F.fprintf fmt "%s" (string_of_location c.loc)
|
|
|
|
|
|
|
|
|
|
let pp : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt c ->
|
|
|
|
|
let c = set_size_pos c in
|
|
|
|
|
if Config.bo_debug <= 1 then
|
|
|
|
|
F.fprintf fmt "%a < %a at %a" Itv.pp c.idx Itv.pp c.size pp_location c
|
|
|
|
|
else
|
|
|
|
|
match c.trace with
|
|
|
|
|
Inter (_, pname, loc) ->
|
|
|
|
|
let pp : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt c ->
|
|
|
|
|
let c = set_size_pos c in
|
|
|
|
|
if Config.bo_debug <= 1 then
|
|
|
|
|
F.fprintf fmt "%a < %a at %a" Itv.pp c.idx Itv.pp c.size pp_location c
|
|
|
|
|
else
|
|
|
|
|
match c.trace with
|
|
|
|
|
Inter (_, pname, loc) ->
|
|
|
|
|
let pname = Typ.Procname.to_string pname in
|
|
|
|
|
F.fprintf fmt "%a < %a at %a by call %s() at %s"
|
|
|
|
|
Itv.pp c.idx Itv.pp c.size pp_location c pname (string_of_location loc)
|
|
|
|
|
| Intra _ -> F.fprintf fmt "%a < %a at %a" Itv.pp c.idx Itv.pp c.size pp_location c
|
|
|
|
|
|
|
|
|
|
let get_location : t -> Location.t
|
|
|
|
|
= fun c -> c.loc
|
|
|
|
|
|
|
|
|
|
let get_trace : t -> trace
|
|
|
|
|
= fun c -> c.trace
|
|
|
|
|
|
|
|
|
|
let get_proc_name : t -> Typ.Procname.t
|
|
|
|
|
= fun c -> c.proc_name
|
|
|
|
|
|
|
|
|
|
let make : Typ.Procname.t -> Location.t -> string -> idx:Itv.t -> size:Itv.t -> t
|
|
|
|
|
= fun proc_name loc id ~idx ~size ->
|
|
|
|
|
{ proc_name; idx; size; loc; id ; trace = Intra proc_name }
|
|
|
|
|
|
|
|
|
|
let filter1 : t -> bool
|
|
|
|
|
= fun c ->
|
|
|
|
|
Itv.eq c.idx Itv.top || Itv.eq c.size Itv.top
|
|
|
|
|
|| Itv.Bound.eq (Itv.lb c.idx) Itv.Bound.MInf
|
|
|
|
|
|| Itv.Bound.eq (Itv.lb c.size) Itv.Bound.MInf
|
|
|
|
|
|| (Itv.eq c.idx Itv.nat && Itv.eq c.size Itv.nat)
|
|
|
|
|
|
|
|
|
|
let filter2 : t -> bool
|
|
|
|
|
= fun c ->
|
|
|
|
|
(* basically, alarms involving infinity are filtered *)
|
|
|
|
|
(not (Itv.is_finite c.idx) || not (Itv.is_finite c.size))
|
|
|
|
|
&& (* except the following cases *)
|
|
|
|
|
not ((Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb < 0 *)
|
|
|
|
|
Itv.Bound.lt (Itv.lb c.idx) Itv.Bound.zero)
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb > size lb *)
|
|
|
|
|
(Itv.Bound.gt (Itv.lb c.idx) (Itv.lb c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb > size ub *)
|
|
|
|
|
(Itv.Bound.gt (Itv.lb c.idx) (Itv.ub c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.ub c.idx) && (* idx non-infty ub > size lb *)
|
|
|
|
|
(Itv.Bound.gt (Itv.ub c.idx) (Itv.lb c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.ub c.idx) && (* idx non-infty ub > size ub *)
|
|
|
|
|
(Itv.Bound.gt (Itv.ub c.idx) (Itv.ub c.size))))
|
|
|
|
|
|
|
|
|
|
(* check buffer overrun and return its confidence *)
|
|
|
|
|
let check : t -> string option
|
|
|
|
|
= fun c ->
|
|
|
|
|
(* idx = [il, iu], size = [sl, su], we want to check that 0 <= idx < size *)
|
|
|
|
|
let c' = set_size_pos c in (* if sl < 0, use sl' = 0 *)
|
|
|
|
|
let not_overrun = Itv.lt_sem c'.idx c'.size in
|
|
|
|
|
let not_underrun = Itv.le_sem Itv.zero c'.idx in
|
|
|
|
|
(* il >= 0 and iu < sl, definitely not an error *)
|
|
|
|
|
if Itv.eq not_overrun Itv.one && Itv.eq not_underrun Itv.one then
|
|
|
|
|
None
|
|
|
|
|
| Intra _ -> F.fprintf fmt "%a < %a at %a" Itv.pp c.idx Itv.pp c.size pp_location c
|
|
|
|
|
|
|
|
|
|
let get_location : t -> Location.t
|
|
|
|
|
= fun c -> c.loc
|
|
|
|
|
|
|
|
|
|
let get_trace : t -> trace
|
|
|
|
|
= fun c -> c.trace
|
|
|
|
|
|
|
|
|
|
let get_proc_name : t -> Typ.Procname.t
|
|
|
|
|
= fun c -> c.proc_name
|
|
|
|
|
|
|
|
|
|
let make : Typ.Procname.t -> Location.t -> string -> idx:Itv.t -> size:Itv.t -> t
|
|
|
|
|
= fun proc_name loc id ~idx ~size ->
|
|
|
|
|
{ proc_name; idx; size; loc; id ; trace = Intra proc_name }
|
|
|
|
|
|
|
|
|
|
let filter1 : t -> bool
|
|
|
|
|
= fun c ->
|
|
|
|
|
Itv.eq c.idx Itv.top || Itv.eq c.size Itv.top
|
|
|
|
|
|| Itv.Bound.eq (Itv.lb c.idx) Itv.Bound.MInf
|
|
|
|
|
|| Itv.Bound.eq (Itv.lb c.size) Itv.Bound.MInf
|
|
|
|
|
|| (Itv.eq c.idx Itv.nat && Itv.eq c.size Itv.nat)
|
|
|
|
|
|
|
|
|
|
let filter2 : t -> bool
|
|
|
|
|
= fun c ->
|
|
|
|
|
(* basically, alarms involving infinity are filtered *)
|
|
|
|
|
(not (Itv.is_finite c.idx) || not (Itv.is_finite c.size))
|
|
|
|
|
&& (* except the following cases *)
|
|
|
|
|
not ((Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb < 0 *)
|
|
|
|
|
Itv.Bound.lt (Itv.lb c.idx) Itv.Bound.zero)
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb > size lb *)
|
|
|
|
|
(Itv.Bound.gt (Itv.lb c.idx) (Itv.lb c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.lb c.idx) && (* idx non-infty lb > size ub *)
|
|
|
|
|
(Itv.Bound.gt (Itv.lb c.idx) (Itv.ub c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.ub c.idx) && (* idx non-infty ub > size lb *)
|
|
|
|
|
(Itv.Bound.gt (Itv.ub c.idx) (Itv.lb c.size)))
|
|
|
|
|
||
|
|
|
|
|
(Itv.Bound.is_not_infty (Itv.ub c.idx) && (* idx non-infty ub > size ub *)
|
|
|
|
|
(Itv.Bound.gt (Itv.ub c.idx) (Itv.ub c.size))))
|
|
|
|
|
|
|
|
|
|
(* check buffer overrun and return its confidence *)
|
|
|
|
|
let check : t -> string option
|
|
|
|
|
= fun c ->
|
|
|
|
|
(* idx = [il, iu], size = [sl, su], we want to check that 0 <= idx < size *)
|
|
|
|
|
let c' = set_size_pos c in (* if sl < 0, use sl' = 0 *)
|
|
|
|
|
let not_overrun = Itv.lt_sem c'.idx c'.size in
|
|
|
|
|
let not_underrun = Itv.le_sem Itv.zero c'.idx in
|
|
|
|
|
(* il >= 0 and iu < sl, definitely not an error *)
|
|
|
|
|
if Itv.eq not_overrun Itv.one && Itv.eq not_underrun Itv.one then
|
|
|
|
|
None
|
|
|
|
|
(* iu < 0 or il >= su, definitely an error *)
|
|
|
|
|
else if Itv.eq not_overrun Itv.zero || Itv.eq not_underrun Itv.zero then
|
|
|
|
|
Some Localise.BucketLevel.b1
|
|
|
|
|
else if Itv.eq not_overrun Itv.zero || Itv.eq not_underrun Itv.zero then
|
|
|
|
|
Some Localise.BucketLevel.b1
|
|
|
|
|
(* su <= iu < +oo, most probably an error *)
|
|
|
|
|
else if Itv.Bound.is_not_infty (Itv.ub c.idx)
|
|
|
|
|
&& Itv.Bound.le (Itv.ub c.size) (Itv.ub c.idx) then
|
|
|
|
|
Some Localise.BucketLevel.b2
|
|
|
|
|
else if Itv.Bound.is_not_infty (Itv.ub c.idx)
|
|
|
|
|
&& Itv.Bound.le (Itv.ub c.size) (Itv.ub c.idx) then
|
|
|
|
|
Some Localise.BucketLevel.b2
|
|
|
|
|
(* symbolic il >= sl, probably an error *)
|
|
|
|
|
else if Itv.Bound.is_symbolic (Itv.lb c.idx)
|
|
|
|
|
&& Itv.Bound.le (Itv.lb c'.size) (Itv.lb c.idx) then
|
|
|
|
|
Some Localise.BucketLevel.b3
|
|
|
|
|
else if Itv.Bound.is_symbolic (Itv.lb c.idx)
|
|
|
|
|
&& Itv.Bound.le (Itv.lb c'.size) (Itv.lb c.idx) then
|
|
|
|
|
Some Localise.BucketLevel.b3
|
|
|
|
|
(* other symbolic bounds are probably too noisy *)
|
|
|
|
|
else if Config.bo_debug <= 1 && (Itv.is_symbolic c.idx || Itv.is_symbolic c.size) then
|
|
|
|
|
None
|
|
|
|
|
else if filter1 c then
|
|
|
|
|
Some Localise.BucketLevel.b5
|
|
|
|
|
else if filter2 c then
|
|
|
|
|
Some Localise.BucketLevel.b3
|
|
|
|
|
else
|
|
|
|
|
Some Localise.BucketLevel.b2
|
|
|
|
|
|
|
|
|
|
let invalid : t -> bool
|
|
|
|
|
= fun x -> Itv.invalid x.idx || Itv.invalid x.size
|
|
|
|
|
|
|
|
|
|
let to_string : t -> string
|
|
|
|
|
= fun c ->
|
|
|
|
|
let c = set_size_pos c in
|
|
|
|
|
"Offset: " ^ Itv.to_string c.idx ^ " Size: " ^ Itv.to_string c.size
|
|
|
|
|
^ " @ " ^ string_of_location c.loc
|
|
|
|
|
^ (match c.trace with
|
|
|
|
|
Inter (_, pname, _) ->
|
|
|
|
|
" by call "
|
|
|
|
|
^ MF.monospaced_to_string (Typ.Procname.to_string pname ^ "()") ^ " "
|
|
|
|
|
| Intra _ -> "")
|
|
|
|
|
|
|
|
|
|
let subst : t -> Itv.Bound.t Itv.SubstMap.t -> Typ.Procname.t -> Typ.Procname.t -> Location.t -> t
|
|
|
|
|
= fun c subst_map caller_pname callee_pname loc ->
|
|
|
|
|
if Itv.is_symbolic c.idx || Itv.is_symbolic c.size then
|
|
|
|
|
{ c with idx = Itv.subst c.idx subst_map;
|
|
|
|
|
size = Itv.subst c.size subst_map;
|
|
|
|
|
trace = Inter (caller_pname, callee_pname, loc) }
|
|
|
|
|
else c
|
|
|
|
|
else if Config.bo_debug <= 1 && (Itv.is_symbolic c.idx || Itv.is_symbolic c.size) then
|
|
|
|
|
None
|
|
|
|
|
else if filter1 c then
|
|
|
|
|
Some Localise.BucketLevel.b5
|
|
|
|
|
else if filter2 c then
|
|
|
|
|
Some Localise.BucketLevel.b3
|
|
|
|
|
else
|
|
|
|
|
Some Localise.BucketLevel.b2
|
|
|
|
|
|
|
|
|
|
let invalid : t -> bool
|
|
|
|
|
= fun x -> Itv.invalid x.idx || Itv.invalid x.size
|
|
|
|
|
|
|
|
|
|
let to_string : t -> string
|
|
|
|
|
= fun c ->
|
|
|
|
|
let c = set_size_pos c in
|
|
|
|
|
"Offset: " ^ Itv.to_string c.idx ^ " Size: " ^ Itv.to_string c.size
|
|
|
|
|
^ " @ " ^ string_of_location c.loc
|
|
|
|
|
^ (match c.trace with
|
|
|
|
|
Inter (_, pname, _) ->
|
|
|
|
|
" by call "
|
|
|
|
|
^ MF.monospaced_to_string (Typ.Procname.to_string pname ^ "()") ^ " "
|
|
|
|
|
| Intra _ -> "")
|
|
|
|
|
|
|
|
|
|
let subst : t -> Itv.Bound.t Itv.SubstMap.t -> Typ.Procname.t -> Typ.Procname.t -> Location.t -> t
|
|
|
|
|
= fun c subst_map caller_pname callee_pname loc ->
|
|
|
|
|
if Itv.is_symbolic c.idx || Itv.is_symbolic c.size then
|
|
|
|
|
{ c with idx = Itv.subst c.idx subst_map;
|
|
|
|
|
size = Itv.subst c.size subst_map;
|
|
|
|
|
trace = Inter (caller_pname, callee_pname, loc) }
|
|
|
|
|
else c
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
module ConditionSet =
|
|
|
|
|
struct
|
|
|
|
|
module PPSet = PrettyPrintable.MakePPSet (Condition)
|
|
|
|
|
include AbstractDomain.FiniteSet (PPSet)
|
|
|
|
|
include AbstractDomain.FiniteSet (Condition)
|
|
|
|
|
|
|
|
|
|
module Map = Caml.Map.Make (struct
|
|
|
|
|
type t = Location.t [@@deriving compare]
|
|
|
|
@ -180,8 +179,8 @@ struct
|
|
|
|
|
let group : t -> t Map.t
|
|
|
|
|
= fun x ->
|
|
|
|
|
fold (fun cond map ->
|
|
|
|
|
let old_set = try Map.find cond.loc map with _ -> empty in
|
|
|
|
|
Map.add cond.loc (add cond old_set) map) x Map.empty
|
|
|
|
|
let old_set = try Map.find cond.loc map with _ -> empty in
|
|
|
|
|
Map.add cond.loc (add cond old_set) map) x Map.empty
|
|
|
|
|
|
|
|
|
|
let pp_summary : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt x ->
|
|
|
|
@ -247,9 +246,9 @@ struct
|
|
|
|
|
|
|
|
|
|
let rec joins : t list -> t
|
|
|
|
|
= function
|
|
|
|
|
| [] -> bot
|
|
|
|
|
| [a] -> a
|
|
|
|
|
| a :: b -> join a (joins b)
|
|
|
|
|
| [] -> bot
|
|
|
|
|
| [a] -> a
|
|
|
|
|
| a :: b -> join a (joins b)
|
|
|
|
|
|
|
|
|
|
let get_itv : t -> Itv.t
|
|
|
|
|
= fun x -> x.itv
|
|
|
|
@ -403,7 +402,7 @@ struct
|
|
|
|
|
let subst : t -> Itv.Bound.t Itv.SubstMap.t -> t
|
|
|
|
|
= fun x subst_map ->
|
|
|
|
|
{ x with itv = Itv.subst x.itv subst_map;
|
|
|
|
|
arrayblk = ArrayBlk.subst x.arrayblk subst_map }
|
|
|
|
|
arrayblk = ArrayBlk.subst x.arrayblk subst_map }
|
|
|
|
|
|
|
|
|
|
let get_symbols : t -> Itv.Symbol.t list
|
|
|
|
|
= fun x ->
|
|
|
|
@ -419,28 +418,7 @@ end
|
|
|
|
|
|
|
|
|
|
module Stack =
|
|
|
|
|
struct
|
|
|
|
|
module PPMap =
|
|
|
|
|
struct
|
|
|
|
|
include PrettyPrintable.MakePPMap (Loc)
|
|
|
|
|
|
|
|
|
|
let pp_collection
|
|
|
|
|
: pp_item:(F.formatter -> 'a -> unit) -> F.formatter -> 'a list -> unit
|
|
|
|
|
= fun ~pp_item fmt c ->
|
|
|
|
|
let pp_sep fmt () = F.fprintf fmt ",@," in
|
|
|
|
|
F.pp_print_list ~pp_sep pp_item fmt c
|
|
|
|
|
|
|
|
|
|
let pp
|
|
|
|
|
: pp_value:(F.formatter -> 'a -> unit) -> F.formatter -> 'a t -> unit
|
|
|
|
|
= fun ~pp_value fmt m ->
|
|
|
|
|
let pp_item fmt (k, v) =
|
|
|
|
|
F.fprintf fmt "%a -> %a" Loc.pp k pp_value v
|
|
|
|
|
in
|
|
|
|
|
F.fprintf fmt "@[<v 2>{ ";
|
|
|
|
|
pp_collection ~pp_item fmt (bindings m);
|
|
|
|
|
F.fprintf fmt " }@]"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
include AbstractDomain.Map (PPMap) (Val)
|
|
|
|
|
include AbstractDomain.Map (Loc) (Val)
|
|
|
|
|
|
|
|
|
|
let bot = empty
|
|
|
|
|
|
|
|
|
@ -493,7 +471,7 @@ struct
|
|
|
|
|
F.fprintf fmt " }@]"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
include AbstractDomain.Map (PPMap) (Val)
|
|
|
|
|
include AbstractDomain.Map (Loc) (Val)
|
|
|
|
|
|
|
|
|
|
let bot = empty
|
|
|
|
|
|
|
|
|
@ -549,49 +527,49 @@ struct
|
|
|
|
|
match M.find k rhs with
|
|
|
|
|
| v' -> Pvar.equal v v'
|
|
|
|
|
| exception Not_found -> false
|
|
|
|
|
in
|
|
|
|
|
M.for_all is_in_rhs lhs
|
|
|
|
|
|
|
|
|
|
let join : t -> t -> t
|
|
|
|
|
= fun x y ->
|
|
|
|
|
let join_v _ v1_opt v2_opt =
|
|
|
|
|
match v1_opt, v2_opt with
|
|
|
|
|
| None, None -> None
|
|
|
|
|
| Some v, None
|
|
|
|
|
| None, Some v -> Some v
|
|
|
|
|
| Some v1, Some v2 -> if Pvar.equal v1 v2 then Some v1 else assert false
|
|
|
|
|
in
|
|
|
|
|
M.merge join_v x y
|
|
|
|
|
|
|
|
|
|
let widen : prev:t -> next:t -> num_iters:int -> t
|
|
|
|
|
= fun ~prev ~next ~num_iters:_ -> join prev next
|
|
|
|
|
in
|
|
|
|
|
M.for_all is_in_rhs lhs
|
|
|
|
|
|
|
|
|
|
let pp : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt x ->
|
|
|
|
|
let pp_sep fmt () = F.fprintf fmt ", @," in
|
|
|
|
|
let pp1 fmt (k, v) =
|
|
|
|
|
F.fprintf fmt "%a=%a" (Ident.pp Pp.text) k (Pvar.pp Pp.text) v
|
|
|
|
|
in
|
|
|
|
|
(* F.fprintf fmt "@[<v 0>Logical Variables :@,"; *)
|
|
|
|
|
F.fprintf fmt "@[<hov 2>{ @,";
|
|
|
|
|
F.pp_print_list ~pp_sep pp1 fmt (M.bindings x);
|
|
|
|
|
F.fprintf fmt " }@]";
|
|
|
|
|
F.fprintf fmt "@]"
|
|
|
|
|
|
|
|
|
|
let load : Ident.t -> Exp.t -> t -> t
|
|
|
|
|
= fun id exp m ->
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.Lvar x -> M.add id x m
|
|
|
|
|
| _ -> m
|
|
|
|
|
|
|
|
|
|
let store : Exp.t -> Exp.t -> t -> t
|
|
|
|
|
= fun e _ m ->
|
|
|
|
|
match e with
|
|
|
|
|
| Exp.Lvar x -> M.filter (fun _ y -> not (Pvar.equal x y)) m
|
|
|
|
|
| _ -> m
|
|
|
|
|
|
|
|
|
|
let find : Ident.t -> t -> Pvar.t option
|
|
|
|
|
= fun k m -> try Some (M.find k m) with Not_found -> None
|
|
|
|
|
let join : t -> t -> t
|
|
|
|
|
= fun x y ->
|
|
|
|
|
let join_v _ v1_opt v2_opt =
|
|
|
|
|
match v1_opt, v2_opt with
|
|
|
|
|
| None, None -> None
|
|
|
|
|
| Some v, None
|
|
|
|
|
| None, Some v -> Some v
|
|
|
|
|
| Some v1, Some v2 -> if Pvar.equal v1 v2 then Some v1 else assert false
|
|
|
|
|
in
|
|
|
|
|
M.merge join_v x y
|
|
|
|
|
|
|
|
|
|
let widen : prev:t -> next:t -> num_iters:int -> t
|
|
|
|
|
= fun ~prev ~next ~num_iters:_ -> join prev next
|
|
|
|
|
|
|
|
|
|
let pp : F.formatter -> t -> unit
|
|
|
|
|
= fun fmt x ->
|
|
|
|
|
let pp_sep fmt () = F.fprintf fmt ", @," in
|
|
|
|
|
let pp1 fmt (k, v) =
|
|
|
|
|
F.fprintf fmt "%a=%a" (Ident.pp Pp.text) k (Pvar.pp Pp.text) v
|
|
|
|
|
in
|
|
|
|
|
(* F.fprintf fmt "@[<v 0>Logical Variables :@,"; *)
|
|
|
|
|
F.fprintf fmt "@[<hov 2>{ @,";
|
|
|
|
|
F.pp_print_list ~pp_sep pp1 fmt (M.bindings x);
|
|
|
|
|
F.fprintf fmt " }@]";
|
|
|
|
|
F.fprintf fmt "@]"
|
|
|
|
|
|
|
|
|
|
let load : Ident.t -> Exp.t -> t -> t
|
|
|
|
|
= fun id exp m ->
|
|
|
|
|
match exp with
|
|
|
|
|
| Exp.Lvar x -> M.add id x m
|
|
|
|
|
| _ -> m
|
|
|
|
|
|
|
|
|
|
let store : Exp.t -> Exp.t -> t -> t
|
|
|
|
|
= fun e _ m ->
|
|
|
|
|
match e with
|
|
|
|
|
| Exp.Lvar x -> M.filter (fun _ y -> not (Pvar.equal x y)) m
|
|
|
|
|
| _ -> m
|
|
|
|
|
|
|
|
|
|
let find : Ident.t -> t -> Pvar.t option
|
|
|
|
|
= fun k m -> try Some (M.find k m) with Not_found -> None
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
module MemReach =
|
|
|
|
|