[sledge] Functorize worklist, separate out domain-specific logic

Summary: Add support for future development of new abstract domains by eliminating hard-wired dependencies from the worklist into the symbolic heap domain.  Also includes an implementation of a trivial unit domain and a CLI flag to enable its use, for debugging purposes.

Reviewed By: jberdine

Differential Revision: D17281681

fbshipit-source-id: 5858fd420
master
Benno Stein 5 years ago committed by Facebook Github Bot
parent 7132a84b0d
commit 2acb1c3dee

@ -55,6 +55,8 @@ Analyze code in a buck target. This is a convenience wrapper for the sequence `s
output if <file> is `-` output if <file> is `-`
[-skip-throw] do not explore past throwing an exception [-skip-throw] do not explore past throwing an exception
[-trace <spec>] enable debug tracing [-trace <spec>] enable debug tracing
[-unit-domain] use unit domain (experimental, debugging purposes
only)
[-help] print this help text and exit [-help] print this help text and exit
(alias: -?) (alias: -?)
@ -132,6 +134,8 @@ Analyze code in one or more LLVM bitcode files. This is a convenience wrapper fo
[-margin <cols>] wrap debug tracing at <cols> columns [-margin <cols>] wrap debug tracing at <cols> columns
[-skip-throw] do not explore past throwing an exception [-skip-throw] do not explore past throwing an exception
[-trace <spec>] enable debug tracing [-trace <spec>] enable debug tracing
[-unit-domain] use unit domain (experimental, debugging purposes
only)
[-help] print this help text and exit [-help] print this help text and exit
(alias: -?) (alias: -?)
@ -171,6 +175,7 @@ The <input> file must be binary LLAIR, such as produced by `sledge translate`.
[-margin <cols>] wrap debug tracing at <cols> columns [-margin <cols>] wrap debug tracing at <cols> columns
[-skip-throw] do not explore past throwing an exception [-skip-throw] do not explore past throwing an exception
[-trace <spec>] enable debug tracing [-trace <spec>] enable debug tracing
[-unit-domain] use unit domain (experimental, debugging purposes only)
[-help] print this help text and exit [-help] print this help text and exit
(alias: -?) (alias: -?)

@ -11,6 +11,7 @@
type exec_opts = {bound: int; skip_throw: bool; function_summaries: bool} type exec_opts = {bound: int; skip_throw: bool; function_summaries: bool}
module Make (Dom : Domain_sig.Dom) = struct
module Stack : sig module Stack : sig
type t type t
type as_inlined_location = t [@@deriving compare, sexp_of] type as_inlined_location = t [@@deriving compare, sexp_of]
@ -18,15 +19,15 @@ module Stack : sig
val empty : t val empty : t
val push_call : val push_call :
Llair.func Llair.call -> bound:int -> Domain.from_call -> t -> t option Llair.func Llair.call -> bound:int -> Dom.from_call -> t -> t option
val pop_return : t -> (Domain.from_call * Llair.jump * t) option val pop_return : t -> (Dom.from_call * Llair.jump * t) option
val pop_throw : val pop_throw :
t t
-> init:'a -> init:'a
-> unwind:(Var.t list -> Var.Set.t -> Domain.from_call -> 'a -> 'a) -> unwind:(Var.t list -> Var.Set.t -> Dom.from_call -> 'a -> 'a)
-> (Domain.from_call * Llair.jump * t * 'a) option -> (Dom.from_call * Llair.jump * t * 'a) option
end = struct end = struct
type t = type t =
| Return of | Return of
@ -34,7 +35,7 @@ end = struct
; dst: Llair.Jump.t ; dst: Llair.Jump.t
; params: Var.t list ; params: Var.t list
; locals: Var.Set.t ; locals: Var.Set.t
; from_call: Domain.from_call ; from_call: Dom.from_call
; stk: t } ; stk: t }
| Throw of Llair.Jump.t * t | Throw of Llair.Jump.t * t
| Empty | Empty
@ -43,10 +44,10 @@ end = struct
type as_inlined_location = t [@@deriving sexp_of] type as_inlined_location = t [@@deriving sexp_of]
(* Treat a stack as a code location in a hypothetical expansion of the (* Treat a stack as a code location in a hypothetical expansion of the
program where all non-recursive functions have been completely inlined. program where all non-recursive functions have been completely
In particular, this means to compare stacks as if all Return frames for inlined. In particular, this means to compare stacks as if all Return
recursive calls had been removed. Additionally, the from_call info in frames for recursive calls had been removed. Additionally, the
Return frames is ignored. *) from_call info in Return frames is ignored. *)
let rec compare_as_inlined_location x y = let rec compare_as_inlined_location x y =
if x == y then 0 if x == y then 0
else else
@ -135,7 +136,7 @@ end
module Work : sig module Work : sig
type t type t
val init : Domain.t -> Llair.block -> int -> t val init : Dom.t -> Llair.block -> int -> t
type x type x
@ -146,11 +147,11 @@ module Work : sig
?prev:Llair.block ?prev:Llair.block
-> retreating:bool -> retreating:bool
-> Stack.t -> Stack.t
-> Domain.t -> Dom.t
-> Llair.block -> Llair.block
-> x -> x
val run : f:(Stack.t -> Domain.t -> Llair.block -> x) -> t -> unit val run : f:(Stack.t -> Dom.t -> Llair.block -> x) -> t -> unit
end = struct end = struct
module Edge = struct module Edge = struct
module T = struct module T = struct
@ -186,11 +187,13 @@ end = struct
type priority = int * Edge.t [@@deriving compare] type priority = int * Edge.t [@@deriving compare]
type priority_queue = priority Fheap.t type priority_queue = priority Fheap.t
type waiting_states = (Domain.t * Depths.t) list Map.M(Llair.Block).t type waiting_states = (Dom.t * Depths.t) list Map.M(Llair.Block).t
type t = priority_queue * waiting_states * int type t = priority_queue * waiting_states * int
type x = Depths.t -> t -> t type x = Depths.t -> t -> t
let empty_waiting_states : waiting_states = Map.empty (module Llair.Block) let empty_waiting_states : waiting_states =
Map.empty (module Llair.Block)
let pp_priority fs (n, e) = Format.fprintf fs "%i: %a" n Edge.pp e let pp_priority fs (n, e) = Format.fprintf fs "%i: %a" n Edge.pp e
let pp fs pq = let pp fs pq =
@ -225,7 +228,7 @@ end = struct
| Some ((_, ({Edge.dst; stk} as edge)), pq) -> ( | Some ((_, ({Edge.dst; stk} as edge)), pq) -> (
match Map.find_and_remove ws dst with match Map.find_and_remove ws dst with
| Some (state :: states, ws) -> | Some (state :: states, ws) ->
let join (qa, da) (q, d) = (Domain.join q qa, Depths.join d da) in let join (qa, da) (q, d) = (Dom.join q qa, Depths.join d da) in
let qs, depths = List.fold ~f:join ~init:state states in let qs, depths = List.fold ~f:join ~init:state states in
run ~f (f stk qs dst depths (pq, ws, bnd)) run ~f (f stk qs dst depths (pq, ws, bnd))
| _ -> | _ ->
@ -246,10 +249,10 @@ let exec_call opts stk state block call globals =
pf "%a from %a" Var.pp name.var Var.pp return.dst.parent.name.var] pf "%a from %a" Var.pp name.var Var.pp return.dst.parent.name.var]
; ;
let dnf_states = let dnf_states =
if opts.function_summaries then Domain.dnf state else [state] if opts.function_summaries then Dom.dnf state else [state]
in in
let domain_call = let domain_call =
Domain.call args areturn params (Set.add_option freturn locals) globals Dom.call args areturn params (Set.add_option freturn locals) globals
in in
List.fold ~init:Work.skip dnf_states ~f:(fun acc state -> List.fold ~init:Work.skip dnf_states ~f:(fun acc state ->
match match
@ -258,11 +261,10 @@ let exec_call opts stk state block call globals =
let maybe_summary_post = let maybe_summary_post =
let state = fst (domain_call ~summaries:false state) in let state = fst (domain_call ~summaries:false state) in
Hashtbl.find summary_table name.var Hashtbl.find summary_table name.var
>>= List.find_map ~f:(Domain.apply_summary state) >>= List.find_map ~f:(Dom.apply_summary state)
in in
[%Trace.info [%Trace.info
"Maybe summary post: %a" "Maybe summary post: %a" (Option.pp "%a" Dom.pp)
(Option.pp "%a" Domain.pp)
maybe_summary_post] ; maybe_summary_post] ;
maybe_summary_post maybe_summary_post
with with
@ -271,7 +273,9 @@ let exec_call opts stk state block call globals =
domain_call ~summaries:opts.function_summaries state domain_call ~summaries:opts.function_summaries state
in in
Work.seq acc Work.seq acc
( match Stack.push_call call ~bound:opts.bound from_call stk with ( match
Stack.push_call call ~bound:opts.bound from_call stk
with
| Some stk -> | Some stk ->
Work.add stk ~prev:block ~retreating:recursive state entry Work.add stk ~prev:block ~retreating:recursive state entry
| None -> Work.skip ) | None -> Work.skip )
@ -284,7 +288,7 @@ let pp_st () =
"@[<v>%t@]" (fun fs -> "@[<v>%t@]" (fun fs ->
Hashtbl.iteri summary_table ~f:(fun ~key ~data -> Hashtbl.iteri summary_table ~f:(fun ~key ~data ->
Format.fprintf fs "@[<v>%a:@ @[%a@]@]@ " Var.pp key Format.fprintf fs "@[<v>%a:@ @[%a@]@]@ " Var.pp key
(List.pp "@," State_domain.pp_function_summary) (List.pp "@," Dom.pp_summary)
data ) )] data ) )]
let exec_return ~opts stk pre_state (block : Llair.block) exp globals = let exec_return ~opts stk pre_state (block : Llair.block) exp globals =
@ -296,11 +300,11 @@ let exec_return ~opts stk pre_state (block : Llair.block) exp globals =
let exit_state = let exit_state =
match (freturn, exp) with match (freturn, exp) with
| Some freturn, Some return_val -> | Some freturn, Some return_val ->
Domain.exec_move pre_state freturn return_val Dom.exec_move pre_state freturn return_val
| None, None -> pre_state | None, None -> pre_state
| _ -> violates Llair.Func.invariant block.parent | _ -> violates Llair.Func.invariant block.parent
in in
let post_state = Domain.post locals from_call exit_state in let post_state = Dom.post locals from_call exit_state in
let post_state = let post_state =
if opts.function_summaries then ( if opts.function_summaries then (
let globals = let globals =
@ -308,7 +312,7 @@ let exec_return ~opts stk pre_state (block : Llair.block) exp globals =
(Vector.map globals ~f:(fun (g : Global.t) -> g.var)) (Vector.map globals ~f:(fun (g : Global.t) -> g.var))
in in
let function_summary, post_state = let function_summary, post_state =
Domain.create_summary ~locals post_state Dom.create_summary ~locals post_state
~formals:(Set.union (Var.Set.of_list params) globals) ~formals:(Set.union (Var.Set.of_list params) globals)
in in
Hashtbl.add_multi summary_table ~key:name.var Hashtbl.add_multi summary_table ~key:name.var
@ -317,7 +321,7 @@ let exec_return ~opts stk pre_state (block : Llair.block) exp globals =
post_state ) post_state )
else post_state else post_state
in in
let retn_state = Domain.retn params freturn from_call post_state in let retn_state = Dom.retn params freturn from_call post_state in
exec_jump stk retn_state block retn_site exec_jump stk retn_state block retn_site
| None -> Work.skip ) | None -> Work.skip )
|> |>
@ -328,16 +332,16 @@ let exec_throw stk pre_state (block : Llair.block) exc =
[%Trace.call fun {pf} -> pf "from %a" Var.pp func.name.var] [%Trace.call fun {pf} -> pf "from %a" Var.pp func.name.var]
; ;
let unwind params scope from_call state = let unwind params scope from_call state =
Domain.retn params (Some func.fthrow) from_call Dom.retn params (Some func.fthrow) from_call
(Domain.post scope from_call state) (Dom.post scope from_call state)
in in
( match Stack.pop_throw stk ~unwind ~init:pre_state with ( match Stack.pop_throw stk ~unwind ~init:pre_state with
| Some (from_call, retn_site, stk, unwind_state) -> | Some (from_call, retn_site, stk, unwind_state) ->
let fthrow = func.fthrow in let fthrow = func.fthrow in
let exit_state = Domain.exec_move unwind_state fthrow exc in let exit_state = Dom.exec_move unwind_state fthrow exc in
let post_state = Domain.post func.locals from_call exit_state in let post_state = Dom.post func.locals from_call exit_state in
let retn_state = let retn_state =
Domain.retn func.params func.freturn from_call post_state Dom.retn func.params func.freturn from_call post_state
in in
exec_jump stk retn_state block retn_site exec_jump stk retn_state block retn_site
| None -> Work.skip ) | None -> Work.skip )
@ -346,30 +350,30 @@ let exec_throw stk pre_state (block : Llair.block) exc =
let exec_skip_func : let exec_skip_func :
Stack.t Stack.t
-> Domain.t -> Dom.t
-> Llair.block -> Llair.block
-> Var.t option -> Var.t option
-> Llair.jump -> Llair.jump
-> Work.x = -> Work.x =
fun stk state block areturn return -> fun stk state block areturn return ->
Report.unknown_call block.term ; Report.unknown_call block.term ;
let state = Option.fold ~f:Domain.exec_kill ~init:state areturn in let state = Option.fold ~f:Dom.exec_kill ~init:state areturn in
exec_jump stk state block return exec_jump stk state block return
let exec_term : let exec_term :
exec_opts -> Llair.t -> Stack.t -> Domain.t -> Llair.block -> Work.x = exec_opts -> Llair.t -> Stack.t -> Dom.t -> Llair.block -> Work.x =
fun opts pgm stk state block -> fun opts pgm stk state block ->
[%Trace.info "exec %a" Llair.Term.pp block.term] ; [%Trace.info "exec %a" Llair.Term.pp block.term] ;
match block.term with match block.term with
| Switch {key; tbl; els} -> | Switch {key; tbl; els} ->
Vector.fold tbl Vector.fold tbl
~f:(fun x (case, jump) -> ~f:(fun x (case, jump) ->
match Domain.exec_assume state (Exp.eq key case) with match Dom.exec_assume state (Exp.eq key case) with
| Some state -> exec_jump stk state block jump |> Work.seq x | Some state -> exec_jump stk state block jump |> Work.seq x
| None -> x ) | None -> x )
~init: ~init:
( match ( match
Domain.exec_assume state Dom.exec_assume state
(Vector.fold tbl ~init:(Exp.bool true) (Vector.fold tbl ~init:(Exp.bool true)
~f:(fun b (case, _) -> Exp.and_ (Exp.dq key case) b)) ~f:(fun b (case, _) -> Exp.and_ (Exp.dq key case) b))
with with
@ -378,7 +382,7 @@ let exec_term :
| Iswitch {ptr; tbl} -> | Iswitch {ptr; tbl} ->
Vector.fold tbl ~init:Work.skip ~f:(fun x (jump : Llair.jump) -> Vector.fold tbl ~init:Work.skip ~f:(fun x (jump : Llair.jump) ->
match match
Domain.exec_assume state Dom.exec_assume state
(Exp.eq ptr (Exp.eq ptr
(Exp.label (Exp.label
~parent:(Var.name jump.dst.parent.name.var) ~parent:(Var.name jump.dst.parent.name.var)
@ -391,19 +395,21 @@ let exec_term :
let lookup name = let lookup name =
Option.to_list (Llair.Func.find pgm.functions name) Option.to_list (Llair.Func.find pgm.functions name)
in in
Domain.resolve_callee lookup callee state Dom.resolve_callee lookup callee state
with with
| [] -> exec_skip_func stk state block areturn return | [] -> exec_skip_func stk state block areturn return
| callees -> | callees ->
List.fold callees ~init:Work.skip ~f:(fun x callee -> List.fold callees ~init:Work.skip ~f:(fun x callee ->
( match ( match
Domain.exec_intrinsic ~skip_throw:opts.skip_throw state Dom.exec_intrinsic ~skip_throw:opts.skip_throw state
areturn callee.name.var args areturn callee.name.var args
with with
| Some (Error ()) -> | Some (Error ()) ->
Report.invalid_access_term (Domain.project state) block.term ; Report.invalid_access_term
(Dom.report_fmt_thunk state)
block.term ;
Work.skip Work.skip
| Some (Ok state) when Domain.is_false state -> Work.skip | Some (Ok state) when Dom.is_false state -> Work.skip
| Some (Ok state) -> exec_jump stk state block return | Some (Ok state) -> exec_jump stk state block return
| None when Llair.Func.is_undefined callee -> | None when Llair.Func.is_undefined callee ->
exec_skip_func stk state block areturn return exec_skip_func stk state block areturn return
@ -413,23 +419,24 @@ let exec_term :
|> Work.seq x ) ) |> Work.seq x ) )
| Return {exp} -> exec_return ~opts stk state block exp pgm.globals | Return {exp} -> exec_return ~opts stk state block exp pgm.globals
| Throw {exc} -> | Throw {exc} ->
if opts.skip_throw then Work.skip else exec_throw stk state block exc if opts.skip_throw then Work.skip
else exec_throw stk state block exc
| Unreachable -> Work.skip | Unreachable -> Work.skip
let exec_inst : let exec_inst : Dom.t -> Llair.inst -> (Dom.t, Dom.t * Llair.inst) result
Domain.t -> Llair.inst -> (Domain.t, Domain.t * Llair.inst) result = =
fun state inst -> fun state inst ->
Domain.exec_inst state inst Dom.exec_inst state inst
|> Result.map_error ~f:(fun () -> (state, inst)) |> Result.map_error ~f:(fun () -> (state, inst))
let exec_block : let exec_block :
exec_opts -> Llair.t -> Stack.t -> Domain.t -> Llair.block -> Work.x = exec_opts -> Llair.t -> Stack.t -> Dom.t -> Llair.block -> Work.x =
fun opts pgm stk state block -> fun opts pgm stk state block ->
[%Trace.info "exec %a" Llair.Block.pp block] ; [%Trace.info "exec %a" Llair.Block.pp block] ;
match Vector.fold_result ~f:exec_inst ~init:state block.cmnd with match Vector.fold_result ~f:exec_inst ~init:state block.cmnd with
| Ok state -> exec_term opts pgm stk state block | Ok state -> exec_term opts pgm stk state block
| Error (state, inst) -> | Error (state, inst) ->
Report.invalid_access_inst (Domain.project state) inst ; Report.invalid_access_inst (Dom.report_fmt_thunk state) inst ;
Work.skip Work.skip
let harness : exec_opts -> Llair.t -> (int -> Work.t) option = let harness : exec_opts -> Llair.t -> (int -> Work.t) option =
@ -442,8 +449,8 @@ let harness : exec_opts -> Llair.t -> (int -> Work.t) option =
Some Some
(Work.init (Work.init
(fst (fst
(Domain.call ~summaries:opts.function_summaries [] None [] (Dom.call ~summaries:opts.function_summaries [] None []
locals pgm.globals (Domain.init pgm.globals))) locals pgm.globals (Dom.init pgm.globals)))
entry) entry)
| _ -> None | _ -> None
@ -456,3 +463,4 @@ let exec_pgm : exec_opts -> Llair.t -> unit =
| None -> fail "no applicable harness" () ) | None -> fail "no applicable harness" () )
|> |>
[%Trace.retn fun {pf} _ -> pf ""] [%Trace.retn fun {pf} _ -> pf ""]
end

@ -12,4 +12,6 @@ type exec_opts =
; skip_throw: bool (** Treat throw as unreachable *) ; skip_throw: bool (** Treat throw as unreachable *)
; function_summaries: bool (** Use function summarisation *) } ; function_summaries: bool (** Use function summarisation *) }
module Make (Dom : Domain_sig.Dom) : sig
val exec_pgm : exec_opts -> Llair.t -> unit val exec_pgm : exec_opts -> Llair.t -> unit
end

@ -0,0 +1,57 @@
(*
* 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.
*)
(** Abstract Domain *)
module type Dom = sig
type t
val pp : t pp
val report_fmt_thunk : t -> Formatter.t -> unit
val init : Global.t vector -> t
val join : t -> t -> t
val is_false : t -> bool
val exec_assume : t -> Exp.t -> t option
val exec_kill : t -> Var.t -> t
val exec_move : t -> Var.t -> Exp.t -> t
val exec_inst : t -> Llair.inst -> (t, unit) result
val exec_intrinsic :
skip_throw:bool
-> t
-> Var.t option
-> Var.t
-> Exp.t list
-> (t, unit) result option
type from_call [@@deriving sexp_of]
val call :
summaries:bool
-> Exp.t list
-> Var.t option
-> Var.t list
-> Var.Set.t
-> Global.t vector
-> t
-> t * from_call
val post : Var.Set.t -> from_call -> t -> t
val retn : Var.t list -> Var.t option -> from_call -> t -> t
val dnf : t -> t list
val resolve_callee :
(Var.t -> Llair.func list) -> Exp.t -> t -> Llair.func list
type summary
val pp_summary : summary pp
val create_summary :
locals:Var.Set.t -> formals:Var.Set.t -> t -> summary * t
val apply_summary : t -> summary -> t option
end

@ -22,18 +22,17 @@ let unknown_call call =
| _ -> () ) | _ -> () )
call Llair.Term.pp call] call Llair.Term.pp call]
let invalid_access state pp access loc = let invalid_access fmt_thunk pp access loc =
let rep fs = let rep fs =
Format.fprintf fs "%a Invalid memory access@;<1 2>@[%a@]" Loc.pp Format.fprintf fs "%a Invalid memory access@;<1 2>@[%a@]" Loc.pp
(loc access) pp access (loc access) pp access
in in
Format.printf "@\n@[<v 2>%t@]@." rep ; Format.printf "@\n@[<v 2>%t@]@." rep ;
[%Trace.printf [%Trace.printf "@\n@[<v 2>%t@;<1 2>@[{ %t@ }@]@]@." rep fmt_thunk] ;
"@\n@[<v 2>%t@;<1 2>@[{ %a@ }@]@]@." rep State_domain.pp state] ;
Stop.on_invalid_access () Stop.on_invalid_access ()
let invalid_access_inst state inst = let invalid_access_inst fmt_thunk inst =
invalid_access state Llair.Inst.pp inst Llair.Inst.loc invalid_access fmt_thunk Llair.Inst.pp inst Llair.Inst.loc
let invalid_access_term state term = let invalid_access_term fmt_thunk term =
invalid_access state Llair.Term.pp term Llair.Term.loc invalid_access fmt_thunk Llair.Term.pp term Llair.Term.loc

@ -8,5 +8,5 @@
(** Issue reporting *) (** Issue reporting *)
val unknown_call : Llair.term -> unit val unknown_call : Llair.term -> unit
val invalid_access_inst : State_domain.t -> Llair.inst -> unit val invalid_access_inst : (Formatter.t -> unit) -> Llair.inst -> unit
val invalid_access_term : State_domain.t -> Llair.term -> unit val invalid_access_term : (Formatter.t -> unit) -> Llair.term -> unit

@ -13,6 +13,9 @@ open Command.Let_syntax
type 'a param = 'a Command.Param.t type 'a param = 'a Command.Param.t
module Sh_executor = Control.Make (Sh_domain)
module Unit_executor = Control.Make (Unit_domain)
(* reverse application in the Command.Param applicative *) (* reverse application in the Command.Param applicative *)
let ( |*> ) : 'a param -> ('a -> 'b) param -> 'b param = let ( |*> ) : 'a param -> ('a -> 'b) param -> 'b param =
fun x f -> x |> Command.Param.apply f fun x f -> x |> Command.Param.apply f
@ -75,9 +78,15 @@ let analyze =
and function_summaries = and function_summaries =
flag "function-summaries" no_arg flag "function-summaries" no_arg
~doc:"use function summaries (in development)" ~doc:"use function summaries (in development)"
and unit_domain =
flag "unit-domain" no_arg
~doc:"use unit domain (experimental, debugging purposes only)"
in
let exec =
if unit_domain then Unit_executor.exec_pgm else Sh_executor.exec_pgm
in in
fun program () -> fun program () ->
Control.exec_pgm {bound; skip_throw; function_summaries} (program ()) exec {bound; skip_throw; function_summaries} (program ())
let analyze_cmd = let analyze_cmd =
let summary = "analyze LLAIR code" in let summary = "analyze LLAIR code" in

@ -10,12 +10,12 @@
type t = State_domain.t * State_domain.t type t = State_domain.t * State_domain.t
let embed q = (q, q) let embed q = (q, q)
let project (_, curr) = curr
let pp fs (entry, current) = let pp fs (entry, current) =
Format.fprintf fs "@[<v 1> entry: %a@;current: %a@]" State_domain.pp entry Format.fprintf fs "@[<v 1> entry: %a@;current: %a@]" State_domain.pp entry
State_domain.pp current State_domain.pp current
let report_fmt_thunk (_, curr) fs = State_domain.pp fs curr
let init globals = embed (State_domain.init globals) let init globals = embed (State_domain.init globals)
let join (entry_a, current_a) (entry_b, current_b) = let join (entry_a, current_a) (entry_b, current_b) =
@ -95,6 +95,10 @@ let dnf (entry, current) =
let resolve_callee f e (_, current) = let resolve_callee f e (_, current) =
State_domain.resolve_callee f e current State_domain.resolve_callee f e current
type summary = State_domain.summary
let pp_summary = State_domain.pp_summary
let create_summary ~locals ~formals (entry, current) = let create_summary ~locals ~formals (entry, current) =
let fs, current = let fs, current =
State_domain.create_summary ~locals ~formals ~entry ~current State_domain.create_summary ~locals ~formals ~entry ~current

@ -9,8 +9,8 @@
type t type t
val project : t -> State_domain.t
val pp : t pp val pp : t pp
val report_fmt_thunk : t -> Formatter.t -> unit
val init : Global.t vector -> t val init : Global.t vector -> t
val join : t -> t -> t val join : t -> t -> t
val is_false : t -> bool val is_false : t -> bool
@ -28,16 +28,16 @@ val exec_intrinsic :
-> (t, unit) result option -> (t, unit) result option
type from_call [@@deriving sexp_of] type from_call [@@deriving sexp_of]
type summary
val pp_summary : summary pp
(* formals should include all the parameters of the summary. That is both (* formals should include all the parameters of the summary. That is both
formals and globals.*) formals and globals.*)
val create_summary : val create_summary :
locals:Var.Set.t locals:Var.Set.t -> formals:Var.Set.t -> t -> summary * t
-> formals:Var.Set.t
-> t
-> State_domain.function_summary * t
val apply_summary : t -> State_domain.function_summary -> t option val apply_summary : t -> summary -> t option
val call : val call :
summaries:bool summaries:bool

@ -152,9 +152,9 @@ let resolve_callee lookup ptr _ =
| Some callee_name -> lookup callee_name | Some callee_name -> lookup callee_name
| None -> [] | None -> []
type function_summary = {xs: Var.Set.t; foot: t; post: t} type summary = {xs: Var.Set.t; foot: t; post: t}
let pp_function_summary fs {xs; foot; post} = let pp_summary fs {xs; foot; post} =
Format.fprintf fs "@[<v>xs: @[%a@]@ foot: %a@ post: %a @]" Var.Set.pp xs Format.fprintf fs "@[<v>xs: @[%a@]@ foot: %a@ post: %a @]" Var.Set.pp xs
pp foot pp post pp foot pp post
@ -184,10 +184,10 @@ let create_summary ~locals ~formals ~entry ~current:(post : Sh.t) =
let current = Sh.extend_us xs post in let current = Sh.extend_us xs post in
({xs; foot; post}, current) ({xs; foot; post}, current)
|> |>
[%Trace.retn fun {pf} (fs, _) -> pf "@,%a" pp_function_summary fs] [%Trace.retn fun {pf} (fs, _) -> pf "@,%a" pp_summary fs]
let apply_summary ({xs; foot; post} as fs) q = let apply_summary ({xs; foot; post} as fs) q =
[%Trace.call fun {pf} -> pf "fs: %a@ q: %a" pp_function_summary fs pp q] [%Trace.call fun {pf} -> pf "fs: %a@ q: %a" pp_summary fs pp q]
; ;
let xs_in_q = Set.inter xs q.Sh.us in let xs_in_q = Set.inter xs q.Sh.us in
let xs_in_fv_q = Set.inter xs (Sh.fv q) in let xs_in_fv_q = Set.inter xs (Sh.fv q) in

@ -38,10 +38,10 @@ val call :
-> t -> t
-> t * from_call -> t * from_call
type function_summary = {xs: Var.Set.t; foot: t; post: t} type summary = {xs: Var.Set.t; foot: t; post: t}
val pp_function_summary : Format.formatter -> function_summary -> unit val pp_summary : summary pp
val apply_summary : function_summary -> t -> t option val apply_summary : summary -> t -> t option
(* formals should include all the parameters of the summary. That is both (* formals should include all the parameters of the summary. That is both
formals and globals.*) formals and globals.*)
@ -50,7 +50,7 @@ val create_summary :
-> formals:Var.Set.t -> formals:Var.Set.t
-> entry:t -> entry:t
-> current:t -> current:t
-> function_summary * t -> summary * t
val post : Var.Set.t -> t -> t val post : Var.Set.t -> t -> t
val retn : Var.t list -> Var.t option -> from_call -> t -> t val retn : Var.t list -> Var.t option -> from_call -> t -> t

@ -0,0 +1,39 @@
(*
* 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.
*)
(** "Unit" abstract domain *)
type t = unit
let pp fs () = Format.pp_print_string fs "()"
let report_fmt_thunk () fs = pp fs ()
let init _ = ()
let join () () = ()
let is_false _ = false
let exec_assume () _ = Some ()
let exec_kill () _ = ()
let exec_move () _ _ = ()
let exec_inst () _ = Ok ()
let exec_intrinsic ~skip_throw:_ _ _ _ _ : (t, unit) result option = None
type from_call = unit [@@deriving compare, equal, sexp]
let call ~summaries:_ _ _ _ _ _ _ = ((), ())
let post _ _ () = ()
let retn _ _ _ _ = ()
let dnf () = [()]
let resolve_callee lookup ptr _ =
match Var.of_exp ptr with
| Some callee_name -> lookup callee_name
| None -> []
type summary = unit
let pp_summary fs () = Format.pp_print_string fs "()"
let create_summary ~locals:_ ~formals:_ _ = ((), ())
let apply_summary _ _ = Some ()

@ -0,0 +1,10 @@
(*
* 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.
*)
(** "Unit" abstract domain *)
include Domain_sig.Dom
Loading…
Cancel
Save