Summary: Instead of having to remember to update both the inferbo and the concrete intervals domains of pulse, hide these details under a unified API. This should help the transition to adding a third(!) numerical domain later on (pudge!). Reviewed By: ezgicicek Differential Revision: D21022920 fbshipit-source-id: 783157464master
parent
0a8ad85596
commit
6247437296
@ -0,0 +1,193 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open PulseBasicInterface
|
||||
open PulseDomainInterface
|
||||
|
||||
(** {2 Building arithmetic constraints} *)
|
||||
|
||||
let and_nonnegative trace v astate =
|
||||
AddressAttributes.add_one v (BoItv Itv.ItvPure.nat) astate
|
||||
|> AddressAttributes.add_one v (CItv (CItv.zero_inf, trace))
|
||||
|
||||
|
||||
let and_positive trace v astate =
|
||||
AddressAttributes.add_one v (BoItv Itv.ItvPure.pos) astate
|
||||
|> AddressAttributes.add_one v (CItv (CItv.ge_to IntLit.one, trace))
|
||||
|
||||
|
||||
let and_eq_int trace v i astate =
|
||||
AddressAttributes.add_one v (BoItv (Itv.ItvPure.of_int_lit i)) astate
|
||||
|> AddressAttributes.add_one v (CItv (CItv.equal_to i, trace))
|
||||
|
||||
|
||||
(** {2 Operations} *)
|
||||
|
||||
type operand = LiteralOperand of IntLit.t | AbstractValueOperand of AbstractValue.t
|
||||
|
||||
let eval_arith_operand location binop_addr binop_hist bop op_lhs op_rhs astate =
|
||||
let arith_of_op op astate =
|
||||
match op with
|
||||
| LiteralOperand i ->
|
||||
Some (CItv.equal_to i)
|
||||
| AbstractValueOperand v ->
|
||||
AddressAttributes.get_citv v astate |> Option.map ~f:fst
|
||||
in
|
||||
match
|
||||
Option.both (arith_of_op op_lhs astate) (arith_of_op op_rhs astate)
|
||||
|> Option.bind ~f:(fun (addr_lhs, addr_rhs) -> CItv.binop bop addr_lhs addr_rhs)
|
||||
with
|
||||
| None ->
|
||||
astate
|
||||
| Some binop_a ->
|
||||
let binop_trace = Trace.Immediate {location; history= binop_hist} in
|
||||
let astate = AddressAttributes.add_one binop_addr (CItv (binop_a, binop_trace)) astate in
|
||||
astate
|
||||
|
||||
|
||||
let eval_bo_itv_binop binop_addr bop op_lhs op_rhs astate =
|
||||
let bo_itv_of_op op astate =
|
||||
match op with
|
||||
| LiteralOperand i ->
|
||||
Itv.ItvPure.of_int_lit i
|
||||
| AbstractValueOperand v ->
|
||||
AddressAttributes.get_bo_itv v astate
|
||||
in
|
||||
let bo_itv =
|
||||
Itv.ItvPure.arith_binop bop (bo_itv_of_op op_lhs astate) (bo_itv_of_op op_rhs astate)
|
||||
in
|
||||
AddressAttributes.add_one binop_addr (BoItv bo_itv) astate
|
||||
|
||||
|
||||
let eval_binop location binop op_lhs op_rhs binop_hist astate =
|
||||
let binop_addr = AbstractValue.mk_fresh () in
|
||||
let astate =
|
||||
eval_arith_operand location binop_addr binop_hist binop op_lhs op_rhs astate
|
||||
|> eval_bo_itv_binop binop_addr binop op_lhs op_rhs
|
||||
in
|
||||
(astate, (binop_addr, binop_hist))
|
||||
|
||||
|
||||
let eval_unop_arith location unop_addr unop operand_addr unop_hist astate =
|
||||
match
|
||||
AddressAttributes.get_citv operand_addr astate
|
||||
|> Option.bind ~f:(function a, _ -> CItv.unop unop a)
|
||||
with
|
||||
| None ->
|
||||
astate
|
||||
| Some unop_a ->
|
||||
let unop_trace = Trace.Immediate {location; history= unop_hist} in
|
||||
AddressAttributes.add_one unop_addr (CItv (unop_a, unop_trace)) astate
|
||||
|
||||
|
||||
let eval_unop_bo_itv unop_addr unop operand_addr astate =
|
||||
match Itv.ItvPure.arith_unop unop (AddressAttributes.get_bo_itv operand_addr astate) with
|
||||
| None ->
|
||||
astate
|
||||
| Some itv ->
|
||||
AddressAttributes.add_one unop_addr (BoItv itv) astate
|
||||
|
||||
|
||||
let eval_unop location unop addr unop_hist astate =
|
||||
let unop_addr = AbstractValue.mk_fresh () in
|
||||
let astate =
|
||||
eval_unop_arith location unop_addr unop addr unop_hist astate
|
||||
|> eval_unop_bo_itv unop_addr unop addr
|
||||
in
|
||||
(astate, (unop_addr, unop_hist))
|
||||
|
||||
|
||||
let prune_with_bop ~negated v_opt arith bop arith' astate =
|
||||
match
|
||||
Option.both v_opt (if negated then Binop.negate bop else Some bop)
|
||||
|> Option.map ~f:(fun (v, positive_bop) -> (v, Itv.ItvPure.prune_binop positive_bop arith arith')
|
||||
)
|
||||
with
|
||||
| None ->
|
||||
(astate, true)
|
||||
| Some (_, Bottom) ->
|
||||
(astate, false)
|
||||
| Some (v, NonBottom arith_pruned) ->
|
||||
let attr_arith = Attribute.BoItv arith_pruned in
|
||||
let astate =
|
||||
AddressAttributes.abduce_attribute v attr_arith astate
|
||||
|> AddressAttributes.add_one v attr_arith
|
||||
in
|
||||
(astate, true)
|
||||
|
||||
|
||||
let eval_operand location astate = function
|
||||
| LiteralOperand i ->
|
||||
( None
|
||||
, Some
|
||||
(CItv.equal_to i, Trace.Immediate {location; history= [ValueHistory.Assignment location]})
|
||||
, Itv.ItvPure.of_int_lit i )
|
||||
| AbstractValueOperand v ->
|
||||
(Some v, AddressAttributes.get_citv v astate, AddressAttributes.get_bo_itv v astate)
|
||||
|
||||
|
||||
let record_abduced event location addr_opt orig_arith_hist_opt arith_opt astate =
|
||||
match Option.both addr_opt arith_opt with
|
||||
| None ->
|
||||
astate
|
||||
| Some (addr, arith) ->
|
||||
let trace =
|
||||
match orig_arith_hist_opt with
|
||||
| None ->
|
||||
Trace.Immediate {location; history= [event]}
|
||||
| Some (_, trace) ->
|
||||
Trace.add_event event trace
|
||||
in
|
||||
let attribute = Attribute.CItv (arith, trace) in
|
||||
AddressAttributes.abduce_attribute addr attribute astate
|
||||
|> AddressAttributes.add_one addr attribute
|
||||
|
||||
|
||||
let bind_satisfiable ~satisfiable astate ~f = if satisfiable then f astate else (astate, false)
|
||||
|
||||
let prune_binop ~is_then_branch if_kind location ~negated bop lhs_op rhs_op astate =
|
||||
let value_lhs_opt, arith_lhs_opt, bo_itv_lhs = eval_operand location astate lhs_op in
|
||||
let value_rhs_opt, arith_rhs_opt, bo_itv_rhs = eval_operand location astate rhs_op in
|
||||
match
|
||||
CItv.abduce_binop_is_true ~negated bop (Option.map ~f:fst arith_lhs_opt)
|
||||
(Option.map ~f:fst arith_rhs_opt)
|
||||
with
|
||||
| Unsatisfiable ->
|
||||
(astate, false)
|
||||
| Satisfiable (abduced_lhs, abduced_rhs) ->
|
||||
let event = ValueHistory.Conditional {is_then_branch; if_kind; location} in
|
||||
let astate =
|
||||
record_abduced event location value_lhs_opt arith_lhs_opt abduced_lhs astate
|
||||
|> record_abduced event location value_rhs_opt arith_rhs_opt abduced_rhs
|
||||
in
|
||||
let satisfiable =
|
||||
match Itv.ItvPure.arith_binop bop bo_itv_lhs bo_itv_rhs |> Itv.ItvPure.to_boolean with
|
||||
| False ->
|
||||
negated
|
||||
| True ->
|
||||
not negated
|
||||
| Top ->
|
||||
true
|
||||
| Bottom ->
|
||||
false
|
||||
in
|
||||
let astate, satisfiable =
|
||||
bind_satisfiable ~satisfiable astate ~f:(fun astate ->
|
||||
prune_with_bop ~negated value_lhs_opt bo_itv_lhs bop bo_itv_rhs astate )
|
||||
in
|
||||
Option.value_map (Binop.symmetric bop) ~default:(astate, satisfiable) ~f:(fun bop' ->
|
||||
bind_satisfiable ~satisfiable astate ~f:(fun astate ->
|
||||
prune_with_bop ~negated value_rhs_opt bo_itv_rhs bop' bo_itv_lhs astate ) )
|
||||
|
||||
|
||||
(** {2 Queries} *)
|
||||
|
||||
let is_known_zero astate v =
|
||||
( AddressAttributes.get_citv v astate
|
||||
|> function Some (arith, _) -> CItv.is_equal_to_zero arith | None -> false )
|
||||
|| Itv.ItvPure.is_zero (AddressAttributes.get_bo_itv v astate)
|
@ -0,0 +1,58 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open PulseBasicInterface
|
||||
open PulseDomainInterface
|
||||
|
||||
(** {2 Building arithmetic constraints} *)
|
||||
|
||||
val and_nonnegative : Trace.t -> AbstractValue.t -> AbductiveDomain.t -> AbductiveDomain.t
|
||||
(** [and_nonnegative trace v astate] is [astate ∧ v≥0] *)
|
||||
|
||||
val and_positive : Trace.t -> AbstractValue.t -> AbductiveDomain.t -> AbductiveDomain.t
|
||||
(** [and_positive v astate] is [astate ∧ v>0] *)
|
||||
|
||||
val and_eq_int : Trace.t -> AbstractValue.t -> IntLit.t -> AbductiveDomain.t -> AbductiveDomain.t
|
||||
(** [and_eq_int v i astate] is [astate ∧ v=i] *)
|
||||
|
||||
(** {2 Operations} *)
|
||||
|
||||
type operand = LiteralOperand of IntLit.t | AbstractValueOperand of AbstractValue.t
|
||||
|
||||
val eval_binop :
|
||||
Location.t
|
||||
-> Binop.t
|
||||
-> operand
|
||||
-> operand
|
||||
-> ValueHistory.t
|
||||
-> AbductiveDomain.t
|
||||
-> AbductiveDomain.t * (AbstractValue.t * ValueHistory.t)
|
||||
|
||||
val eval_unop :
|
||||
Location.t
|
||||
-> Unop.t
|
||||
-> AbstractValue.t
|
||||
-> ValueHistory.t
|
||||
-> AbductiveDomain.t
|
||||
-> AbductiveDomain.t * (AbstractValue.t * ValueHistory.t)
|
||||
|
||||
val prune_binop :
|
||||
is_then_branch:bool
|
||||
-> Sil.if_kind
|
||||
-> Location.t
|
||||
-> negated:bool
|
||||
-> Binop.t
|
||||
-> operand
|
||||
-> operand
|
||||
-> AbductiveDomain.t
|
||||
-> AbductiveDomain.t * bool
|
||||
|
||||
(** {2 Queries} *)
|
||||
|
||||
val is_known_zero : AbductiveDomain.t -> AbstractValue.t -> bool
|
||||
(** [is_known_zero astate t] returns [true] if [astate |- t = 0], [false] if we don't know for sure *)
|
Loading…
Reference in new issue