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.

120 lines
3.6 KiB

(*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module F = Format
module L = Logging
module Reverse = struct
module Dead = AbstractDomain.BooleanAnd
module VarSet = AbstractDomain.FiniteSet (Var)
module DeadAndVarSet = AbstractDomain.Pair (Dead) (VarSet)
module M = AbstractDomain.Map (Var) (DeadAndVarSet)
(* invariant: sets are not empty *)
type t = M.t
let empty = M.empty
let add k v rm =
M.update k
(function
| None -> Some (false, VarSet.singleton v) | Some (_, s) -> Some (false, VarSet.add v s))
rm
let mem = M.mem
let deadify k rm =
M.update k
(function (None | Some (true, _)) as vo -> vo | Some (false, s) -> Some (true, s))
rm
let remove k v rm =
match M.find_opt k rm with
| None ->
(rm, false)
| Some (dead, s) ->
let s = VarSet.remove v s in
if VarSet.is_empty s then (M.remove k rm, dead) else (M.add k (dead, s) rm, false)
let pp = M.pp
let join = M.join
let widen = M.widen
end
type t =
{ resolve: IdAccessPathMapDomain.t
; reverse: Reverse.t
(** there is a [x -> y] mapping in reverse for each variable [y] appearing in [resolve x] *)
}
let empty = {resolve= IdAccessPathMapDomain.empty; reverse= Reverse.empty}
let add id ap {resolve; reverse} =
if (Config.debug_exceptions || Config.write_html) && Reverse.mem id reverse then
(if Config.debug_exceptions then L.(die InternalError) else L.d_printfln ?color:None)
"Variable %a appearing on both sides of bindings" Var.pp id ;
let resolve = IdAccessPathMapDomain.add id ap resolve in
let reverse =
HilExp.AccessExpression.fold_vars ap ~init:reverse ~f:(fun acc var_in_ap ->
Reverse.add var_in_ap id acc )
in
{resolve; reverse}
let exit_scope id bindings =
let {resolve; reverse} = bindings in
match IdAccessPathMapDomain.find_opt id resolve with
| None ->
if Reverse.mem id reverse then
let reverse = Reverse.deadify id reverse in
({resolve; reverse}, [])
else (bindings, [id])
| Some ap ->
let resolve = IdAccessPathMapDomain.remove id resolve in
let reverse, vars =
HilExp.AccessExpression.fold_vars ap ~init:(reverse, [])
~f:(fun (reverse, vars) var_in_ap ->
let reverse, dead = Reverse.remove var_in_ap id reverse in
let vars = if dead then var_in_ap :: vars else vars in
(reverse, vars) )
in
({resolve; reverse}, vars)
let resolve {resolve} id = IdAccessPathMapDomain.find_opt id resolve
let fold {resolve} ~init ~f = IdAccessPathMapDomain.fold f resolve init
let pp f {resolve; reverse} =
F.fprintf f "{@[<v1> resolve=@[<hv>%a@];@;reverse=@[<hv>%a@]@]}" IdAccessPathMapDomain.pp resolve
Reverse.pp reverse
let ( <= ) ~lhs ~rhs = IdAccessPathMapDomain.( <= ) ~lhs:lhs.resolve ~rhs:rhs.resolve
let join bindings1 bindings2 =
if phys_equal bindings1 bindings2 then bindings1
else
let {resolve= resolve1; reverse= reverse1} = bindings1 in
let {resolve= resolve2; reverse= reverse2} = bindings2 in
{resolve= IdAccessPathMapDomain.join resolve1 resolve2; reverse= Reverse.join reverse1 reverse2}
let widen ~prev ~next ~num_iters =
if phys_equal prev next then prev
else
let {resolve= resolve1; reverse= reverse1} = prev in
let {resolve= resolve2; reverse= reverse2} = next in
{ resolve= IdAccessPathMapDomain.widen ~prev:resolve1 ~next:resolve2 ~num_iters
; reverse= Reverse.widen ~prev:reverse1 ~next:reverse2 ~num_iters }