|
|
|
(*
|
|
|
|
* 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.
|
|
|
|
*)
|
|
|
|
|
|
|
|
(** The analysis' semantics of control flow. *)
|
|
|
|
|
|
|
|
open Domain_intf
|
|
|
|
open Control_intf
|
|
|
|
|
|
|
|
(** An element of work to be scheduled. The scheduling strategies have very
|
|
|
|
few dependencies on elements, mainly some just need to test if two
|
|
|
|
elements have the same destination. An example element instance is a
|
|
|
|
pair of a control-flow edge with a symbolic state that has reached the
|
|
|
|
source of the edge and has yet to be propagated to the destination. *)
|
|
|
|
module type Elt = sig
|
|
|
|
type t [@@deriving compare, equal, sexp_of]
|
|
|
|
|
|
|
|
val pp : t pp
|
|
|
|
val equal_destination : t -> t -> bool
|
|
|
|
val dnf : t -> t list
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Interface of analysis control scheduler "queues". *)
|
|
|
|
module type QueueS = sig
|
|
|
|
type elt
|
|
|
|
|
|
|
|
(** a "queue" of elements, which need not be FIFO *)
|
|
|
|
type t
|
|
|
|
|
|
|
|
val pp : t pp
|
|
|
|
|
|
|
|
val create : unit -> t
|
|
|
|
(** create an empty queue *)
|
|
|
|
|
|
|
|
val add : elt -> t -> t
|
|
|
|
(** add an element *)
|
|
|
|
|
|
|
|
val top : t -> (elt * elt list * t) option
|
|
|
|
(** [top q] is [None] if [q] is empty and otherwise is [Some (e, es, q')]
|
|
|
|
where [e] is the selected element in [q] and any elements [es] have
|
|
|
|
the same destination as [e]. [q'] is equivalent to [q] but possibly
|
|
|
|
more compactly represented. *)
|
|
|
|
|
|
|
|
val remove : elt -> elt list -> t -> t
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Type of a queue implementation, which is parameterized over elements. *)
|
|
|
|
module type Queue = functor (Elt : Elt) -> QueueS with type elt = Elt.t
|
|
|
|
|
|
|
|
(** A strategy that implements an iterative breadth-first exploration that
|
|
|
|
joins scheduled elements whenever possible, thereby DAGifying the
|
|
|
|
execution tree. *)
|
|
|
|
module PriorityQueue (Elt : Elt) : QueueS with type elt = Elt.t = struct
|
|
|
|
type elt = Elt.t
|
|
|
|
|
|
|
|
module Elts = Set.Make (Elt)
|
|
|
|
|
|
|
|
type t = {queue: Elt.t FHeap.t; removed: Elts.t}
|
|
|
|
|
|
|
|
let pp ppf {queue; removed} =
|
|
|
|
let rev_elts =
|
|
|
|
FHeap.fold queue ~init:[] ~f:(fun rev_elts elt ->
|
|
|
|
if Elts.mem elt removed then rev_elts else elt :: rev_elts )
|
|
|
|
in
|
|
|
|
Format.fprintf ppf "@[%a@]" (List.pp " ::@ " Elt.pp) (List.rev rev_elts)
|
|
|
|
|
|
|
|
let create () = {queue= FHeap.create ~cmp:Elt.compare; removed= Elts.empty}
|
|
|
|
|
|
|
|
let add elt {queue; removed} =
|
|
|
|
let removed' = Elts.remove elt removed in
|
|
|
|
if removed' == removed then {queue= FHeap.add queue elt; removed}
|
|
|
|
else {queue; removed= removed'}
|
|
|
|
|
|
|
|
let rec top {queue; removed} =
|
|
|
|
let* next = FHeap.top queue in
|
|
|
|
let removed' = Elts.remove next removed in
|
|
|
|
if removed' != removed then
|
|
|
|
let queue' = FHeap.remove_top_exn queue in
|
|
|
|
top {queue= queue'; removed= removed'}
|
|
|
|
else
|
|
|
|
let elts =
|
|
|
|
FHeap.fold queue ~init:[] ~f:(fun elts elt ->
|
|
|
|
if Elt.equal_destination next elt && not (Elts.mem elt removed)
|
|
|
|
then elt :: elts
|
|
|
|
else elts )
|
|
|
|
in
|
|
|
|
Some (next, elts, {queue; removed})
|
|
|
|
|
|
|
|
let remove top elts {queue; removed} =
|
|
|
|
assert (Elt.equal top (FHeap.top_exn queue)) ;
|
|
|
|
let queue = FHeap.remove_top_exn queue in
|
|
|
|
let removed = Elts.add_list elts removed in
|
|
|
|
{queue; removed}
|
|
|
|
end
|
|
|
|
|
|
|
|
module RandomQueue (Elt : Elt) : QueueS with type elt = Elt.t = struct
|
|
|
|
type elt = Elt.t
|
|
|
|
|
|
|
|
module M = Int.Map
|
|
|
|
|
|
|
|
(** The analyzer, after calling [create], performs a sequence of [add] and
|
|
|
|
[pop] operations. Implicitly, each [add] is for an element that is a
|
|
|
|
successor of the element that was returned by the last [pop]. This
|
|
|
|
module assumes this implicit protocol in order to infer the structure
|
|
|
|
of the execution tree, and uses it to assign weights aiming to
|
|
|
|
implement fair random sampling of paths.
|
|
|
|
|
|
|
|
Each edge of an execution tree conceptually has a "weight" [1/w]
|
|
|
|
(represented by just the denominator [w]) indicating that there is a
|
|
|
|
[1] in [w] chance of making the sequence of branching choices leading
|
|
|
|
to the edge. An execution tree starts with a single edge to the
|
|
|
|
initial control point with weight [1]. For an edge with weight [1/w]
|
|
|
|
that has [n] successors, the edge to each of the successors has weight
|
|
|
|
[1 / (w * n)].
|
|
|
|
|
|
|
|
The scheduling "frontier" is a set of edges with weights (represented
|
|
|
|
as a map from weights to lists of edges) that have been reached but
|
|
|
|
not followed.
|
|
|
|
|
|
|
|
Edges are selected from the frontier randomly using the weights to
|
|
|
|
simulate fair sampling of paths as follows. Let [{eᵢ}] be a sequence
|
|
|
|
of the edges of the frontier in decreasing weight order. The weight of
|
|
|
|
edge [eᵢ] is written [wᵢ]. The sum of the weights of the frontier
|
|
|
|
is [s = Σᵢ wᵢ]. Now choose a random number [0 ≤ n ≤ s]. This
|
|
|
|
determines an edge [eᵣ] where [r] is least such that
|
|
|
|
[Σᵢ₌₀ʳ wᵢ ≥ n].
|
|
|
|
|
|
|
|
The inferred structure of the execution tree is also used to schedule
|
|
|
|
the analysis to proceed depth-first where a random successor is chosen
|
|
|
|
at each point until no further progress is possible, at which point a
|
|
|
|
new path is sampled. Successors are added by the analyzer prior to
|
|
|
|
knowing whether they are feasible. For example executing a conditional
|
|
|
|
branch results in two [add] operations where the next instruction on
|
|
|
|
each is to assume the condition and the negation of the condition. To
|
|
|
|
avoid depth-first execution from being thwarted by choosing infeasible
|
|
|
|
branches in such cases, a [recent] list is maintained that contains
|
|
|
|
the successors of the last popped element. When an element is popped
|
|
|
|
from the recent list, it is not known whether or not it is immediately
|
|
|
|
infeasible. If it is, the next operation will be another [pop], and
|
|
|
|
this is also taken from the [recent] list. If the element was not
|
|
|
|
immediately infeasible, the next operation is an [add] (of a
|
|
|
|
successor), at which point the recent list is flushed to the
|
|
|
|
"frontier". In this way, each [pop] that requests the next branch to
|
|
|
|
explore is chosen from the successors of the last control point,
|
|
|
|
effecting depth-first exploration. Only when the recent list is empty,
|
|
|
|
is an element chosen from the "frontier" of untaken branches. *)
|
|
|
|
|
|
|
|
type t =
|
|
|
|
{ recent: Elt.t RAL.t (** elements added since last pop; add *)
|
|
|
|
; recent_weight: int (** combined weight of recent *)
|
|
|
|
; frontier: Elt.t RAL.t M.t (** weight-keyed elements to be explored *)
|
|
|
|
; frontier_weight: float (** combined weight of frontier *)
|
|
|
|
; last: last_operation (** single step of execution history *) }
|
|
|
|
|
|
|
|
and last_operation =
|
|
|
|
| Add_or_pop_frontier
|
|
|
|
(** last operation was either [add] or [pop] where [recent] was
|
|
|
|
empty *)
|
|
|
|
| Pop_recent of int
|
|
|
|
(** last operation was [pop] where [recent] was not empty, and the
|
|
|
|
returned element had given weight *)
|
|
|
|
|
|
|
|
let pp ppf {recent; frontier} =
|
|
|
|
Format.fprintf ppf "@[%a @@@ %a@]" (List.pp " ::@ " Elt.pp)
|
|
|
|
(RAL.to_list recent)
|
|
|
|
(M.pp Int.pp (RAL.pp " ::@ " Elt.pp))
|
|
|
|
frontier
|
|
|
|
|
|
|
|
let create () =
|
|
|
|
{ recent= RAL.empty
|
|
|
|
; recent_weight= 1
|
|
|
|
; frontier= M.empty
|
|
|
|
; frontier_weight= 0.
|
|
|
|
; last= Add_or_pop_frontier }
|
|
|
|
|
|
|
|
let add elt q =
|
|
|
|
let add_elt l = List.fold ~f:RAL.cons (Elt.dnf elt) l in
|
|
|
|
match q.last with
|
|
|
|
| Add_or_pop_frontier ->
|
|
|
|
(* elt is a sibling of the elements of recent, so extend recent *)
|
|
|
|
{q with recent= add_elt q.recent}
|
|
|
|
| Pop_recent elt_weight ->
|
|
|
|
(* elt is a successor of the last popped element (which is itself a
|
|
|
|
sibling of the elements of recent), so flush recent to frontier
|
|
|
|
and reset recent to the singleton elt with a combined weight
|
|
|
|
equal to that of the previously popped element *)
|
|
|
|
{ recent= add_elt RAL.empty
|
|
|
|
; recent_weight= elt_weight
|
|
|
|
; frontier=
|
|
|
|
( if RAL.is_empty q.recent then q.frontier
|
|
|
|
else
|
|
|
|
M.update elt_weight q.frontier ~f:(function
|
|
|
|
| Some data -> Some (RAL.append q.recent data)
|
|
|
|
| None -> Some q.recent ) )
|
|
|
|
; frontier_weight=
|
|
|
|
q.frontier_weight
|
|
|
|
+. Float.of_int (RAL.length q.recent)
|
|
|
|
/. Float.of_int elt_weight
|
|
|
|
; last= Add_or_pop_frontier }
|
|
|
|
|
|
|
|
let _pop q =
|
|
|
|
let num_recent = RAL.length q.recent in
|
|
|
|
if num_recent > 0 then
|
|
|
|
let elt, recent =
|
|
|
|
RAL.get_and_remove_exn q.recent (Random.int num_recent)
|
|
|
|
in
|
|
|
|
match q.last with
|
|
|
|
| Pop_recent _ ->
|
|
|
|
(* elt is sibling to last popped element, with same elt_weight *)
|
|
|
|
Some (elt, [], {q with recent})
|
|
|
|
| Add_or_pop_frontier ->
|
|
|
|
(* recent is now complete, and weight of each element can be
|
|
|
|
computed from combined weight and length *)
|
|
|
|
let elt_weight = q.recent_weight * num_recent in
|
|
|
|
Some (elt, [], {q with recent; last= Pop_recent elt_weight})
|
|
|
|
else
|
|
|
|
let random_weight = Random.float q.frontier_weight in
|
|
|
|
M.fold_until q.frontier 0.
|
|
|
|
~f:(fun ~key ~data prefix_weight ->
|
|
|
|
let len = RAL.length data in
|
|
|
|
let w = Float.of_int len /. Float.of_int key in
|
|
|
|
let prefix_weight = prefix_weight +. w in
|
|
|
|
if Float.(prefix_weight < random_weight) then
|
|
|
|
`Continue prefix_weight
|
|
|
|
else
|
|
|
|
let elt, data = RAL.get_and_remove_exn data (Random.int len) in
|
|
|
|
`Stop
|
|
|
|
(Some
|
|
|
|
( elt
|
|
|
|
, []
|
|
|
|
, { recent= RAL.empty
|
|
|
|
; recent_weight= key
|
|
|
|
; frontier=
|
|
|
|
( if RAL.is_empty data then M.remove key q.frontier
|
|
|
|
else M.add ~key ~data q.frontier )
|
|
|
|
; frontier_weight= q.frontier_weight -. w
|
|
|
|
; last= Add_or_pop_frontier } )) )
|
|
|
|
~finish:(fun _ ->
|
|
|
|
assert (M.is_empty q.frontier) ;
|
|
|
|
None )
|
|
|
|
|
|
|
|
let top _ = todo "concurrent sampling analysis" ()
|
|
|
|
let remove _ = todo "concurrent sampling analysis" ()
|
|
|
|
end
|
|
|
|
|
|
|
|
module Make (Config : Config) (D : Domain) (Queue : Queue) = struct
|
|
|
|
module Stack : sig
|
|
|
|
type t
|
|
|
|
|
|
|
|
val empty : t
|
|
|
|
val push_call : Llair.func Llair.call -> D.from_call -> t -> t
|
|
|
|
val pop_return : t -> (D.from_call * Llair.jump * t) option
|
|
|
|
|
|
|
|
val pop_throw :
|
|
|
|
t
|
|
|
|
-> 'a
|
|
|
|
-> unwind:
|
[sledge] Represent function formal parameters and actual arguments in order
Summary:
Previously, when LLAIR was in SSA form, blocks took parameters just
like functions, and it was sometimes necessary to partially apply a
block to some of the parameters. For example, blocks to which function
calls return would need to accept the return value as an argument, and
sometimes immediately jump to another block passing the rest of the
arguments as well. These "trampoline" blocks were partial applications
of the eventual block to all but the final, return value,
argument.
This partial application mechanism meant that function parameters and
arguments were represented as a stack, with the first argument at the
bottom, that is, in reverse order.
Now that LLAIR is free of SSA, this confusion is no longer needed, and
this diff changes the representation of function formal parameters and
actual arguments to be in the natural order. This also brings Call
instructions in line with Intrinsic instructions, which will make
changing the handling of intrinsics from Calls to Intrinsic less
error-prone.
Reviewed By: jvillard
Differential Revision: D25146163
fbshipit-source-id: d3ed07a45
4 years ago
|
|
|
( Llair.Reg.t iarray
|
|
|
|
-> Llair.Reg.Set.t
|
|
|
|
-> D.from_call
|
|
|
|
-> 'a
|
|
|
|
-> 'a)
|
|
|
|
-> (D.from_call * Llair.jump * t * 'a) option
|
|
|
|
|
|
|
|
type as_inlined_location = t [@@deriving compare, equal, sexp_of]
|
|
|
|
end = struct
|
|
|
|
type t =
|
|
|
|
| Return of
|
|
|
|
{ dst: Llair.Jump.t
|
[sledge] Represent function formal parameters and actual arguments in order
Summary:
Previously, when LLAIR was in SSA form, blocks took parameters just
like functions, and it was sometimes necessary to partially apply a
block to some of the parameters. For example, blocks to which function
calls return would need to accept the return value as an argument, and
sometimes immediately jump to another block passing the rest of the
arguments as well. These "trampoline" blocks were partial applications
of the eventual block to all but the final, return value,
argument.
This partial application mechanism meant that function parameters and
arguments were represented as a stack, with the first argument at the
bottom, that is, in reverse order.
Now that LLAIR is free of SSA, this confusion is no longer needed, and
this diff changes the representation of function formal parameters and
actual arguments to be in the natural order. This also brings Call
instructions in line with Intrinsic instructions, which will make
changing the handling of intrinsics from Calls to Intrinsic less
error-prone.
Reviewed By: jvillard
Differential Revision: D25146163
fbshipit-source-id: d3ed07a45
4 years ago
|
|
|
; formals: Llair.Reg.t iarray
|
|
|
|
; locals: Llair.Reg.Set.t
|
|
|
|
; from_call: D.from_call
|
|
|
|
; stk: t }
|
|
|
|
| Throw of Llair.Jump.t * t
|
|
|
|
| Empty
|
|
|
|
[@@deriving sexp_of]
|
|
|
|
|
|
|
|
let rec pp ppf stk =
|
|
|
|
let pp ppf = function
|
|
|
|
| Empty -> ()
|
|
|
|
| stk -> Format.fprintf ppf "; %a" pp stk
|
|
|
|
in
|
|
|
|
match stk with
|
|
|
|
| Return {dst; stk= s} ->
|
|
|
|
Format.fprintf ppf "R#%i%a" dst.dst.sort_index pp s
|
|
|
|
| Throw (dst, s) ->
|
|
|
|
Format.fprintf ppf "T#%i%a" dst.dst.sort_index pp s
|
|
|
|
| Empty -> ()
|
|
|
|
|
|
|
|
let invariant s =
|
|
|
|
let@ () = Invariant.invariant [%here] s [%sexp_of: t] in
|
|
|
|
match s with
|
|
|
|
| Return _ | Throw (_, Return _) | Empty -> ()
|
|
|
|
| Throw _ -> fail "malformed stack: %a" pp s ()
|
|
|
|
|
|
|
|
let empty = Empty |> check invariant
|
|
|
|
|
|
|
|
let push_return call from_call stk =
|
|
|
|
let Llair.{callee= {formals; locals}; return; _} = call in
|
|
|
|
Return {dst= return; formals; locals; from_call; stk}
|
|
|
|
|> check invariant
|
|
|
|
|
|
|
|
let push_throw call stk =
|
|
|
|
( match call.Llair.throw with
|
|
|
|
| None -> stk
|
|
|
|
| Some jmp -> Throw (jmp, stk) )
|
|
|
|
|> check invariant
|
|
|
|
|
|
|
|
let push_call call from_call stk =
|
|
|
|
push_throw call (push_return call from_call stk)
|
|
|
|
|
|
|
|
let rec pop_return = function
|
|
|
|
| Throw (_, stk) -> pop_return stk
|
|
|
|
| Return {from_call; dst; stk} -> Some (from_call, dst, stk)
|
|
|
|
| Empty -> None
|
|
|
|
|
|
|
|
let pop_throw stk state ~unwind =
|
|
|
|
let rec pop_throw_ state = function
|
|
|
|
| Return {formals; locals; from_call; stk} ->
|
|
|
|
pop_throw_ (unwind formals locals from_call state) stk
|
|
|
|
| Throw (dst, Return {from_call; stk}) ->
|
|
|
|
Some (from_call, dst, stk, state)
|
|
|
|
| Empty -> None
|
|
|
|
| Throw _ as stk -> violates invariant stk
|
|
|
|
in
|
|
|
|
pop_throw_ state stk
|
|
|
|
|
|
|
|
type as_inlined_location = t [@@deriving sexp_of]
|
|
|
|
|
|
|
|
(* Treat a stack as a code location in a hypothetical expansion of the
|
|
|
|
program where functions have been inlined. In particular, only the
|
|
|
|
dst and stk of Return frames is considered. *)
|
|
|
|
let rec compare_as_inlined_location x y =
|
|
|
|
if x == y then 0
|
|
|
|
else
|
|
|
|
match (x, y) with
|
|
|
|
| Return {dst= j; stk= x}, Return {dst= k; stk= y} -> (
|
|
|
|
match Llair.Jump.compare j k with
|
|
|
|
| 0 -> compare_as_inlined_location x y
|
|
|
|
| n -> n )
|
|
|
|
| Return _, _ -> -1
|
|
|
|
| _, Return _ -> 1
|
|
|
|
| Throw (j, x), Throw (k, y) -> (
|
|
|
|
match Llair.Jump.compare j k with
|
|
|
|
| 0 -> compare_as_inlined_location x y
|
|
|
|
| n -> n )
|
|
|
|
| Throw _, _ -> -1
|
|
|
|
| _, Throw _ -> 1
|
|
|
|
| Empty, Empty -> 0
|
|
|
|
|
|
|
|
let equal_as_inlined_location = [%compare.equal: as_inlined_location]
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Instruction Pointer, of a single thread so includes thread id.
|
|
|
|
Functions are treated as-if-inlined by including a call stack in each
|
|
|
|
instruction pointer, effectively copying the control-flow graph for
|
|
|
|
each calling context. *)
|
|
|
|
type ip = {ip: Llair.IP.t; stk: Stack.t; tid: ThreadID.t}
|
|
|
|
|
|
|
|
(** Instruction Pointer of a single thread *)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
module IP : sig
|
|
|
|
type t = ip [@@deriving compare, equal, sexp_of]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
val pp : t pp
|
|
|
|
end = struct
|
|
|
|
type t = ip =
|
|
|
|
{ip: Llair.IP.t; stk: Stack.as_inlined_location; tid: ThreadID.t}
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
[@@deriving compare, equal, sexp_of]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let pp ppf {ip} = Llair.IP.pp ppf ip
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Representation of a single thread, including identity and scheduling
|
|
|
|
state *)
|
|
|
|
module Thread : sig
|
|
|
|
type t = Runnable of IP.t | Terminated of ThreadID.t
|
|
|
|
[@@deriving equal, sexp_of]
|
|
|
|
|
|
|
|
val compare : t Ord.t
|
|
|
|
val compare_without_tid : t Ord.t
|
|
|
|
val pp : t pp
|
|
|
|
val id : t -> ThreadID.t
|
|
|
|
end = struct
|
|
|
|
(** Because [ip] needs to include [tid], this is represented as a sum of
|
|
|
|
products, but it may be more natural to think in terms of the
|
|
|
|
isomorphic representation using a product of a sum such as
|
|
|
|
[(Runnable of ... | Terminated ...) * ThreadID.t]. *)
|
|
|
|
type t = Runnable of IP.t | Terminated of ThreadID.t
|
|
|
|
[@@deriving sexp_of]
|
|
|
|
|
|
|
|
let pp ppf = function
|
|
|
|
| Runnable ip -> IP.pp ppf ip
|
|
|
|
| Terminated tid -> Format.fprintf ppf "T%i" tid
|
|
|
|
|
|
|
|
let id = function Runnable {tid} -> tid | Terminated tid -> tid
|
|
|
|
|
|
|
|
(* Note: Threads.inactive relies on comparing tid last *)
|
|
|
|
let compare_aux compare_tid x y =
|
|
|
|
let open Ord.Infix in
|
|
|
|
if x == y then 0
|
|
|
|
else
|
|
|
|
match (x, y) with
|
|
|
|
| Runnable x, Runnable y ->
|
|
|
|
Llair.IP.compare x.ip y.ip
|
|
|
|
<?> (Stack.compare_as_inlined_location, x.stk, y.stk)
|
|
|
|
<?> (compare_tid, x.tid, y.tid)
|
|
|
|
| Runnable _, _ -> -1
|
|
|
|
| _, Runnable _ -> 1
|
|
|
|
| Terminated x_tid, Terminated y_tid -> compare_tid x_tid y_tid
|
|
|
|
|
|
|
|
let compare = compare_aux ThreadID.compare
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
let compare_without_tid = compare_aux (fun _ _ -> 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Set of threads *)
|
|
|
|
module Threads : sig
|
|
|
|
type t [@@deriving compare, equal, sexp_of]
|
|
|
|
type inactive [@@deriving sexp_of]
|
|
|
|
|
|
|
|
val compare_inactive : inactive Ord.t
|
|
|
|
val compare_inactive_tids : inactive Ord.t
|
|
|
|
val init : t
|
|
|
|
val create : Llair.block -> t -> ThreadID.t * t
|
|
|
|
val after_step : Thread.t -> t -> t * inactive
|
|
|
|
val join : ThreadID.t -> t -> t option
|
|
|
|
val fold : t -> 's -> f:(Thread.t -> 's -> 's) -> 's
|
|
|
|
end = struct
|
|
|
|
module M = Map.Make (ThreadID)
|
|
|
|
|
|
|
|
type t = Thread.t M.t [@@deriving compare, equal, sexp_of]
|
|
|
|
type inactive = Thread.t array [@@deriving sexp_of]
|
|
|
|
|
|
|
|
let compare_inactive = Ord.array Thread.compare_without_tid
|
|
|
|
let compare_inactive_tids = Ord.(array (ThreadID.compare >|= Thread.id))
|
|
|
|
|
|
|
|
let inactive active_id threads =
|
|
|
|
let a = Iter.to_array (M.values (M.remove active_id threads)) in
|
|
|
|
Array.sort ~cmp:Thread.compare a ;
|
|
|
|
a
|
|
|
|
|
|
|
|
let init = M.empty
|
|
|
|
|
|
|
|
let fold threads s ~f =
|
|
|
|
M.fold threads s ~f:(fun ~key:_ ~data s -> f data s)
|
|
|
|
|
|
|
|
let create entry threads =
|
|
|
|
let ip = Llair.IP.mk entry in
|
|
|
|
let max_tid =
|
|
|
|
match M.max_binding threads with
|
|
|
|
| Some (tid, _) -> tid
|
|
|
|
| None -> ThreadID.init
|
|
|
|
in
|
|
|
|
let tid = max_tid + 1 in
|
|
|
|
let thread = Thread.Runnable {ip; stk= Stack.empty; tid} in
|
|
|
|
(tid, M.add ~key:tid ~data:thread threads)
|
|
|
|
|
|
|
|
let after_step active threads =
|
|
|
|
let tid = Thread.id active in
|
|
|
|
let inactive = inactive tid threads in
|
|
|
|
let threads = M.add ~key:tid ~data:active threads in
|
|
|
|
(threads, inactive)
|
|
|
|
|
|
|
|
let join tid threads =
|
|
|
|
match M.find tid threads with
|
|
|
|
| Some (Thread.Terminated _) -> Some (M.remove tid threads)
|
|
|
|
| _ -> None
|
|
|
|
end
|
|
|
|
|
|
|
|
(** A control-flow transition of a single thread. In the common case an
|
|
|
|
edge from block [src] to [dst = Runnable {ip; stk; tid}] represents a
|
|
|
|
transition by thread [tid] with call stack [stk] from (usually the
|
|
|
|
terminator of) block [src] to the instruction pointer [ip]. If a
|
|
|
|
scheduling point is encountered within a block, the represented
|
|
|
|
transition need not originate from the terminator of [src]. Edges can
|
|
|
|
also represent transitions that produce threads in non-[Runnable]
|
|
|
|
scheduling states, determined by the form of [dst]. *)
|
|
|
|
type edge = {dst: Thread.t; src: Llair.Block.t} [@@deriving sexp_of]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
module Edge = struct
|
|
|
|
type t = edge [@@deriving sexp_of]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let pp fs {dst; src= {sort_index; lbl}} =
|
|
|
|
Format.fprintf fs "%a <-t%i- #%i %%%s" Thread.pp dst (Thread.id dst)
|
|
|
|
sort_index lbl
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
(** Each retreating edge has a depth for each calling context, except
|
|
|
|
for recursive calls. Recursive call edges are instead compared
|
|
|
|
without considering their stacks. Bounding the depth of edges
|
|
|
|
therefore has the effect of bounding the number of recursive calls
|
|
|
|
in any calling context. *)
|
|
|
|
let compare_aux compare_tid x y =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let open Ord.Infix in
|
|
|
|
if x == y then 0
|
|
|
|
else
|
|
|
|
match (x, y) with
|
|
|
|
| {dst= Runnable x_t}, {dst= Runnable y_t} ->
|
|
|
|
let is_rec_call = function
|
|
|
|
| {Llair.term= Call {recursive= true}} -> true
|
|
|
|
| _ -> false
|
|
|
|
in
|
|
|
|
let compare_stk stk1 stk2 =
|
|
|
|
if is_rec_call x.src then 0
|
|
|
|
else Stack.compare_as_inlined_location stk1 stk2
|
|
|
|
in
|
|
|
|
Llair.IP.compare x_t.ip y_t.ip
|
|
|
|
<?> (Llair.Block.compare, x.src, y.src)
|
|
|
|
<?> (compare_stk, x_t.stk, y_t.stk)
|
|
|
|
<?> (compare_tid, x_t.tid, y_t.tid)
|
|
|
|
| {dst= Runnable _}, _ -> -1
|
|
|
|
| _, {dst= Runnable _} -> 1
|
|
|
|
| {dst= Terminated x_tid}, {dst= Terminated y_tid} ->
|
|
|
|
Llair.Block.compare x.src y.src <?> (compare_tid, x_tid, y_tid)
|
|
|
|
|
|
|
|
let compare = compare_aux ThreadID.compare
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
let compare_without_tid = compare_aux (fun _ _ -> 0)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
end
|
|
|
|
|
|
|
|
module Depths = struct
|
|
|
|
module M = Map.Make (Edge)
|
|
|
|
|
|
|
|
type t = int M.t [@@deriving compare, equal, sexp_of]
|
|
|
|
|
|
|
|
let empty = M.empty
|
|
|
|
let find = M.find
|
|
|
|
let add = M.add
|
|
|
|
|
|
|
|
let join x y =
|
|
|
|
M.merge x y ~f:(fun _ -> function
|
|
|
|
| `Left d | `Right d -> Some d
|
|
|
|
| `Both (d1, d2) -> Some (Int.max d1 d2) )
|
|
|
|
end
|
|
|
|
|
|
|
|
type switches = int [@@deriving compare, equal, sexp_of]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
(** Abstract memory, control, and history state, with a slot used for the
|
|
|
|
current "control position", such as an instruction pointer. Consists
|
|
|
|
of a symbolic [state] and a scheduling state of the [threads], plus a
|
|
|
|
coarse abstraction of the preceding execution history in the form of
|
|
|
|
the number of context [switches] and [depths] representing the number
|
|
|
|
of times retreating edges have been crossed. *)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
type 'a memory_control_history =
|
|
|
|
{ ctrl: 'a (** current control position *)
|
|
|
|
; state: D.t (** symbolic memory and register state *)
|
|
|
|
; threads: Threads.t (** scheduling state of the threads *)
|
|
|
|
; switches: switches (** count of preceding context switches *)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
; depths: Depths.t (** count of retreating edge crossings *) }
|
|
|
|
[@@deriving sexp_of]
|
|
|
|
|
|
|
|
(** An abstract machine state consists of the instruction pointer of the
|
|
|
|
active thread plus the memory, control, and history state. *)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
type ams = IP.t memory_control_history [@@deriving sexp_of]
|
|
|
|
|
|
|
|
(** A unit of analysis work is an abstract machine state from which
|
|
|
|
execution should continue, with additional control-flow [edge] info
|
|
|
|
used by the analysis scheduler. *)
|
|
|
|
type work = edge memory_control_history
|
|
|
|
|
|
|
|
(** An element of the frontier of execution is a control-flow [edge] that
|
|
|
|
has been executed by the thread [Thread.id edge.dst], yielding a
|
|
|
|
memory, control, and history state. The [threads] indicates the
|
|
|
|
scheduling state of the point after the transition indicated by the
|
|
|
|
edge. *)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
type elt = elt_ctrl memory_control_history [@@deriving sexp_of]
|
|
|
|
|
|
|
|
and elt_ctrl =
|
|
|
|
{ edge: Edge.t
|
|
|
|
; depth: int
|
|
|
|
(** pre-computed depth of [edge], for use by e.g. [Elt.compare] *)
|
|
|
|
; inactive: Threads.inactive
|
|
|
|
(** pre-computed summary of inactive thread scheduling states, for
|
|
|
|
use by e.g. [Elt.compare] *) }
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
|
|
|
|
module Work : sig
|
|
|
|
type t
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
val init : D.t -> Llair.block -> t
|
|
|
|
val add : retreating:bool -> work -> t -> t
|
|
|
|
val run : f:(ams -> t -> t) -> t -> unit
|
|
|
|
end = struct
|
|
|
|
(** Element of the frontier of execution, ordered for scheduler's
|
|
|
|
priority queue *)
|
|
|
|
module Elt = struct
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
type t = elt [@@deriving sexp_of]
|
|
|
|
|
|
|
|
let pp ppf {ctrl= {edge; depth}; switches} =
|
|
|
|
Format.fprintf ppf "%i,%i: %a" switches depth Edge.pp edge
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let compare x y =
|
|
|
|
let open Ord.Infix in
|
|
|
|
if x == y then 0
|
|
|
|
else
|
|
|
|
( (Int.compare >|= fun x -> x.switches)
|
|
|
|
@? (Int.compare >|= fun x -> x.ctrl.depth)
|
|
|
|
@? (Edge.compare_without_tid >|= fun x -> x.ctrl.edge)
|
|
|
|
@? (Threads.compare_inactive >|= fun x -> x.ctrl.inactive)
|
|
|
|
@? (ThreadID.compare >|= fun x -> Thread.id x.ctrl.edge.dst)
|
|
|
|
@? (Threads.compare_inactive_tids >|= fun x -> x.ctrl.inactive)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
@? (Depths.compare >|= fun x -> x.depths)
|
|
|
|
@? (D.compare >|= fun x -> x.state) )
|
|
|
|
x y
|
|
|
|
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
let equal_destination x y = Threads.equal x.threads y.threads
|
|
|
|
let dnf x = List.map ~f:(fun state -> {x with state}) (D.dnf x.state)
|
|
|
|
end
|
|
|
|
|
|
|
|
module Queue = Queue (Elt)
|
|
|
|
|
|
|
|
(** Concurrent state and history projection of abstract machine states.
|
|
|
|
Abstract machine states with the same [switches], [ip], and
|
|
|
|
[threads] fields have, as far as the scheduler is concerned, the
|
|
|
|
same concurrent state and history and can be joined. *)
|
|
|
|
module ConcSH = struct
|
|
|
|
module T = struct
|
|
|
|
type t = switches * IP.t * Threads.t
|
|
|
|
[@@deriving compare, equal, sexp_of]
|
|
|
|
end
|
|
|
|
|
|
|
|
include T
|
|
|
|
module Map = Map.Make (T)
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Sequential state and history projection of abstract machine states.
|
|
|
|
Complementary to [ConcSH], [SeqSH] represents the subset of [ams]
|
|
|
|
fields that can be joined across several executions that share the
|
|
|
|
same abstract concurrent state and history. *)
|
|
|
|
module SeqSH = struct
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
module T = struct
|
|
|
|
type t = D.t * Depths.t [@@deriving compare, equal, sexp_of]
|
|
|
|
end
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
include T
|
|
|
|
module Set = Set.Make (T)
|
|
|
|
|
|
|
|
let join s =
|
|
|
|
let states, depths =
|
|
|
|
Set.fold s ([], Depths.empty) ~f:(fun (q, d) (qs, ds) ->
|
|
|
|
let qqs =
|
|
|
|
match qs with
|
|
|
|
| q0 :: _ when D.equal q q0 -> qs
|
|
|
|
| _ -> q :: qs
|
|
|
|
in
|
|
|
|
(qqs, Depths.join d ds) )
|
|
|
|
in
|
|
|
|
(D.joinN states, depths)
|
|
|
|
end
|
|
|
|
|
|
|
|
(** Sequential states indexed by concurrent states. When sequential
|
|
|
|
states and histories are joined across executions that reach the
|
|
|
|
same abstract concurrent state and history, there are multiple
|
|
|
|
successor executions corresponding to which thread is selected to
|
|
|
|
execute. Executing some such successors can lead to additional
|
|
|
|
executions that reach the original abstract concurrent state and
|
|
|
|
history. These new executions also need to be joined with the old
|
|
|
|
ones. To handle this, the successors of a join are enumerated
|
|
|
|
lazily, returning them one-by-one from the scheduler and adding them
|
|
|
|
to the analysis worklist. The "cursor" that maintains the current
|
|
|
|
progress of this enumeration is a set of sequential states that is
|
|
|
|
indexed by concurrent states. *)
|
|
|
|
module Cursor = struct
|
|
|
|
type t = SeqSH.Set.t ConcSH.Map.t
|
|
|
|
|
|
|
|
let empty = ConcSH.Map.empty
|
|
|
|
let add = ConcSH.Map.add
|
|
|
|
let find = ConcSH.Map.find
|
|
|
|
end
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
(** Analysis exploration state *)
|
|
|
|
type t = Queue.t * Cursor.t
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let prune depth {ctrl= edge} wl =
|
|
|
|
[%Trace.info " %i: %a" depth Edge.pp edge] ;
|
|
|
|
Report.hit_bound Config.bound ;
|
|
|
|
wl
|
|
|
|
|
|
|
|
let enqueue depth ({ctrl= {dst} as edge; threads; depths} as elt)
|
|
|
|
(queue, cursor) =
|
|
|
|
[%Trace.info
|
|
|
|
" %i,%i: %a@ | %a" elt.switches depth Edge.pp edge Queue.pp queue] ;
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let depths = Depths.add ~key:edge ~data:depth depths in
|
|
|
|
let threads, inactive = Threads.after_step dst threads in
|
|
|
|
let queue =
|
|
|
|
Queue.add
|
|
|
|
{elt with ctrl= {edge; depth; inactive}; threads; depths}
|
|
|
|
queue
|
|
|
|
in
|
|
|
|
(queue, cursor)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
|
|
|
|
let init state curr =
|
|
|
|
let depth = 0 in
|
|
|
|
let ip = Llair.IP.mk curr in
|
|
|
|
let stk = Stack.empty in
|
|
|
|
let prev = curr in
|
|
|
|
let tid = ThreadID.init in
|
|
|
|
let edge = {dst= Runnable {ip; stk; tid}; src= prev} in
|
|
|
|
let threads = Threads.init in
|
|
|
|
let switches = 0 in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let depths = Depths.empty in
|
|
|
|
let queue = Queue.create () in
|
|
|
|
let cursor = Cursor.empty in
|
|
|
|
enqueue depth
|
|
|
|
{ctrl= edge; state; threads; switches; depths}
|
|
|
|
(queue, cursor)
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
|
|
|
|
let add ~retreating ({ctrl= edge; depths} as elt) wl =
|
|
|
|
let depth = Option.value (Depths.find edge depths) ~default:0 in
|
|
|
|
let depth = if retreating then depth + 1 else depth in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
if depth > Config.bound && Config.bound >= 0 then prune depth elt wl
|
|
|
|
else enqueue depth elt wl
|
|
|
|
|
|
|
|
module Succs = struct
|
|
|
|
module M = ConcSH.Map
|
|
|
|
|
|
|
|
let empty = M.empty
|
|
|
|
let add = M.add_multi
|
|
|
|
|
|
|
|
let find_first m ~f =
|
|
|
|
let exception Stop in
|
|
|
|
let found = ref None in
|
|
|
|
let hit_end = ref true in
|
|
|
|
( try
|
|
|
|
M.iteri m ~f:(fun ~key ~data ->
|
|
|
|
match !found with
|
|
|
|
| None -> (
|
|
|
|
match f ~key ~data with
|
|
|
|
| None -> ()
|
|
|
|
| Some r -> found := Some r )
|
|
|
|
| Some _ ->
|
|
|
|
hit_end := false ;
|
|
|
|
raise_notrace Stop )
|
|
|
|
with Stop -> () ) ;
|
|
|
|
(!found, !hit_end)
|
|
|
|
end
|
|
|
|
|
|
|
|
let rec dequeue (queue, cursor) =
|
|
|
|
let* ({threads} as top), elts, queue = Queue.top queue in
|
|
|
|
let succs =
|
|
|
|
List.fold (top :: elts) Succs.empty ~f:(fun incoming succs ->
|
|
|
|
let {ctrl= {edge= {dst}}; state; switches; depths} = incoming in
|
|
|
|
let incoming_tid = Thread.id dst in
|
|
|
|
Threads.fold threads succs ~f:(fun active succs ->
|
|
|
|
match active with
|
|
|
|
| Terminated _ -> succs
|
|
|
|
| Runnable ({tid} as ip) ->
|
|
|
|
let switches =
|
|
|
|
if tid = incoming_tid then switches else switches + 1
|
|
|
|
in
|
|
|
|
Succs.add ~key:(switches, ip, threads)
|
|
|
|
~data:(state, depths) succs ) )
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
in
|
|
|
|
let found, hit_end =
|
|
|
|
Succs.find_first succs
|
|
|
|
~f:(fun ~key:(switches, ip, threads) ~data:incoming ->
|
|
|
|
let next = (switches, ip, threads) in
|
|
|
|
let curr = SeqSH.Set.of_list incoming in
|
|
|
|
let+ next_states =
|
|
|
|
match Cursor.find next cursor with
|
|
|
|
| Some already_done ->
|
|
|
|
let next_states = SeqSH.Set.diff curr already_done in
|
|
|
|
if SeqSH.Set.is_empty next_states then None
|
|
|
|
else Some next_states
|
|
|
|
| None -> Some curr
|
|
|
|
in
|
|
|
|
( next
|
|
|
|
, next_states
|
|
|
|
, Cursor.add ~key:next ~data:next_states cursor ) )
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
in
|
|
|
|
let queue = if hit_end then Queue.remove top elts queue else queue in
|
|
|
|
match found with
|
|
|
|
| Some ((switches, ip, threads), next_states, cursor) ->
|
|
|
|
[%Trace.info
|
|
|
|
" %i,%i: %a@ | %a" switches top.ctrl.depth Edge.pp top.ctrl.edge
|
|
|
|
Queue.pp queue] ;
|
|
|
|
let state, depths = SeqSH.join next_states in
|
|
|
|
Some
|
|
|
|
({ctrl= ip; state; threads; switches; depths}, (queue, cursor))
|
|
|
|
| None -> dequeue (queue, cursor)
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let rec run ~f wl =
|
|
|
|
match dequeue wl with
|
|
|
|
| Some (ams, wl) -> run ~f (f ams wl)
|
|
|
|
| None -> ()
|
|
|
|
end
|
|
|
|
|
|
|
|
let summary_table = Llair.Function.Tbl.create ()
|
|
|
|
|
|
|
|
let pp_st () =
|
|
|
|
[%Trace.printf
|
|
|
|
"@[<v>%t@]" (fun fs ->
|
|
|
|
Llair.Function.Tbl.iteri summary_table ~f:(fun ~key ~data ->
|
|
|
|
Format.fprintf fs "@[<v>%a:@ @[%a@]@]@ " Llair.Function.pp key
|
|
|
|
(List.pp "@," D.pp_summary)
|
|
|
|
data ) )]
|
|
|
|
|
|
|
|
let exec_jump jump ({ctrl= {ip; stk; tid}} as ams) wl =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let src = Llair.IP.block ip in
|
|
|
|
let {Llair.dst; retreating} = jump in
|
|
|
|
let ip = Llair.IP.mk dst in
|
|
|
|
let edge = {dst= Runnable {ip; stk; tid}; src} in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
Work.add ~retreating {ams with ctrl= edge} wl
|
|
|
|
|
|
|
|
let exec_skip_func areturn return ({ctrl= {ip; tid}; state} as ams) wl =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
Report.unknown_call (Llair.IP.block ip).term ;
|
|
|
|
let state = Option.fold ~f:(D.exec_kill tid) areturn state in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
exec_jump return {ams with state} wl
|
|
|
|
|
|
|
|
let exec_call globals call ({ctrl= {stk; tid}; state} as ams) wl =
|
|
|
|
let Llair.{callee; actuals; areturn; return; recursive} = call in
|
|
|
|
let Llair.{name; formals; freturn; locals; entry} = callee in
|
|
|
|
[%Trace.call fun {pf} ->
|
|
|
|
pf " t%i@[<2>@ %a from %a with state@]@;<1 2>%a" tid
|
|
|
|
Llair.Func.pp_call call Llair.Function.pp return.dst.parent.name
|
|
|
|
D.pp state]
|
|
|
|
;
|
|
|
|
let dnf_states =
|
|
|
|
if Config.function_summaries then D.dnf state else [state]
|
|
|
|
in
|
|
|
|
let domain_call =
|
|
|
|
D.call tid ~globals ~actuals ~areturn ~formals ~freturn ~locals
|
|
|
|
in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
List.fold dnf_states wl ~f:(fun state wl ->
|
|
|
|
match
|
|
|
|
if not Config.function_summaries then None
|
|
|
|
else
|
|
|
|
let state = fst (domain_call ~summaries:false state) in
|
|
|
|
let* summary = Llair.Function.Tbl.find summary_table name in
|
|
|
|
List.find_map ~f:(D.apply_summary state) summary
|
|
|
|
with
|
|
|
|
| None ->
|
|
|
|
let state, from_call =
|
|
|
|
domain_call ~summaries:Config.function_summaries state
|
|
|
|
in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let ip = Llair.IP.mk entry in
|
|
|
|
let stk = Stack.push_call call from_call stk in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let src = Llair.IP.block ams.ctrl.ip in
|
|
|
|
let edge = {dst= Runnable {ip; stk; tid}; src} in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
Work.add ~retreating:recursive {ams with ctrl= edge; state} wl
|
|
|
|
| Some post -> exec_jump return {ams with state= post} wl )
|
|
|
|
|>
|
|
|
|
[%Trace.retn fun {pf} _ -> pf ""]
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let exec_call call ams wl =
|
|
|
|
let Llair.{callee= {name} as callee; areturn; return; _} = call in
|
|
|
|
if Llair.Func.is_undefined callee then
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
exec_skip_func areturn return ams wl
|
|
|
|
else
|
|
|
|
let globals = Domain_used_globals.by_function Config.globals name in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
exec_call globals call ams wl
|
|
|
|
|
|
|
|
let exec_return exp ({ctrl= {ip; stk; tid}; state} as ams) wl =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let block = Llair.IP.block ip in
|
|
|
|
let func = block.parent in
|
|
|
|
let Llair.{name; formals; freturn; locals} = func in
|
|
|
|
[%Trace.call fun {pf} -> pf " t%i@ from: %a" tid Llair.Function.pp name]
|
|
|
|
;
|
|
|
|
let summarize post_state =
|
|
|
|
if not Config.function_summaries then post_state
|
|
|
|
else
|
|
|
|
let function_summary, post_state =
|
|
|
|
D.create_summary tid ~locals ~formals post_state
|
|
|
|
in
|
|
|
|
Llair.Function.Tbl.add_multi ~key:name ~data:function_summary
|
|
|
|
summary_table ;
|
|
|
|
pp_st () ;
|
|
|
|
post_state
|
|
|
|
in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let pre_state = state in
|
|
|
|
let exit_state =
|
|
|
|
match (freturn, exp) with
|
|
|
|
| Some freturn, Some return_val ->
|
|
|
|
D.exec_move tid (IArray.of_ (freturn, return_val)) pre_state
|
|
|
|
| None, None -> pre_state
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
| _ -> violates Llair.Func.invariant func
|
|
|
|
in
|
|
|
|
( match Stack.pop_return stk with
|
|
|
|
| Some (from_call, retn_site, stk) ->
|
|
|
|
let post_state =
|
|
|
|
summarize (D.post tid locals from_call exit_state)
|
|
|
|
in
|
|
|
|
let retn_state = D.retn tid formals freturn from_call post_state in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
exec_jump retn_site
|
|
|
|
{ams with ctrl= {ams.ctrl with stk}; state= retn_state}
|
|
|
|
wl
|
|
|
|
| None ->
|
|
|
|
if Config.function_summaries then
|
|
|
|
summarize exit_state |> (ignore : D.t -> unit) ;
|
|
|
|
Work.add ~retreating:false
|
|
|
|
{ams with ctrl= {dst= Terminated tid; src= block}}
|
|
|
|
wl )
|
|
|
|
|>
|
|
|
|
[%Trace.retn fun {pf} _ -> pf ""]
|
|
|
|
|
|
|
|
let exec_throw exc ({ctrl= {ip; stk; tid}; state} as ams) wl =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let func = (Llair.IP.block ip).parent in
|
|
|
|
let Llair.{name; formals; freturn; fthrow; locals} = func in
|
|
|
|
[%Trace.call fun {pf} -> pf "@ from %a" Llair.Function.pp name]
|
|
|
|
;
|
|
|
|
let unwind formals scope from_call state =
|
|
|
|
D.retn tid formals (Some fthrow) from_call
|
|
|
|
(D.post tid scope from_call state)
|
|
|
|
in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let pre_state = state in
|
|
|
|
( match Stack.pop_throw stk ~unwind pre_state with
|
|
|
|
| Some (from_call, retn_site, stk, unwind_state) ->
|
|
|
|
let exit_state =
|
|
|
|
D.exec_move tid (IArray.of_ (fthrow, exc)) unwind_state
|
|
|
|
in
|
|
|
|
let post_state = D.post tid locals from_call exit_state in
|
|
|
|
let retn_state = D.retn tid formals freturn from_call post_state in
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
exec_jump retn_site
|
|
|
|
{ams with ctrl= {ams.ctrl with stk}; state= retn_state}
|
|
|
|
wl
|
|
|
|
| None -> wl )
|
|
|
|
|>
|
|
|
|
[%Trace.retn fun {pf} _ -> pf ""]
|
|
|
|
|
|
|
|
let exec_assume cond jump ({ctrl= {tid}; state} as ams) wl =
|
|
|
|
match D.exec_assume tid state cond with
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
| Some state -> exec_jump jump {ams with state} wl
|
|
|
|
| None ->
|
|
|
|
[%Trace.info " infeasible %a@\n@[%a@]" Llair.Exp.pp cond D.pp state] ;
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
wl
|
|
|
|
|
|
|
|
let exec_thread_create reg {Llair.entry; locals} return
|
|
|
|
({ctrl= {tid}; state; threads} as ams) wl =
|
|
|
|
let child_tid, threads = Threads.create entry threads in
|
|
|
|
let child =
|
|
|
|
Llair.Exp.integer (Llair.Reg.typ reg) (Z.of_int child_tid)
|
|
|
|
in
|
|
|
|
let state = D.exec_move tid (IArray.of_ (reg, child)) state in
|
|
|
|
let state = D.enter_scope child_tid locals state in
|
|
|
|
exec_jump return {ams with state; threads} wl
|
|
|
|
|
|
|
|
let exec_thread_join thread return ({ctrl= {tid}; state; threads} as ams)
|
|
|
|
wl =
|
|
|
|
List.fold (D.resolve_int tid state thread) wl ~f:(fun join_tid wl ->
|
|
|
|
match Threads.join join_tid threads with
|
|
|
|
| Some threads -> exec_jump return {ams with threads} wl
|
|
|
|
| None -> wl )
|
|
|
|
|
|
|
|
let resolve_callee (pgm : Llair.program) tid callee state =
|
|
|
|
let lookup name = Llair.Func.find name pgm.functions in
|
|
|
|
D.resolve_callee lookup tid callee state
|
|
|
|
|
|
|
|
let exec_term pgm ({ctrl= {ip; tid}; state} as ams) wl =
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let block = Llair.IP.block ip in
|
|
|
|
let term = block.term in
|
|
|
|
[%Trace.info
|
|
|
|
" t%i@\n@[%a@]@\n%a" tid D.pp state Llair.Term.pp block.term] ;
|
|
|
|
Report.step_term block ;
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
match (term : Llair.term) with
|
|
|
|
| Switch {key; tbl; els} ->
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let wl =
|
|
|
|
exec_assume
|
|
|
|
(IArray.fold tbl Llair.Exp.true_ ~f:(fun (case, _) b ->
|
|
|
|
Llair.Exp.and_ (Llair.Exp.dq key case) b ))
|
|
|
|
els ams wl
|
|
|
|
in
|
|
|
|
IArray.fold tbl wl ~f:(fun (case, jump) wl ->
|
|
|
|
exec_assume (Llair.Exp.eq key case) jump ams wl )
|
|
|
|
| Iswitch {ptr; tbl} ->
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
IArray.fold tbl wl ~f:(fun jump wl ->
|
|
|
|
exec_assume
|
|
|
|
(Llair.Exp.eq ptr
|
|
|
|
(Llair.Exp.label
|
|
|
|
~parent:(Llair.Function.name jump.dst.parent.name)
|
|
|
|
~name:jump.dst.lbl))
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
jump ams wl )
|
|
|
|
| Call ({callee; actuals; areturn; return} as call) -> (
|
|
|
|
match
|
|
|
|
(Llair.Function.name callee.name, IArray.to_array actuals, areturn)
|
|
|
|
with
|
|
|
|
| "sledge_thread_create", [|callee|], Some reg -> (
|
|
|
|
match resolve_callee pgm tid callee state with
|
|
|
|
| [] -> exec_skip_func areturn return ams wl
|
|
|
|
| callees ->
|
|
|
|
List.fold callees wl ~f:(fun callee wl ->
|
|
|
|
exec_thread_create reg callee return ams wl ) )
|
|
|
|
| "sledge_thread_join", [|thread|], None ->
|
|
|
|
exec_thread_join thread return ams wl
|
|
|
|
| _ -> exec_call call ams wl )
|
|
|
|
| ICall ({callee; areturn; return} as call) -> (
|
|
|
|
match resolve_callee pgm tid callee state with
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
| [] -> exec_skip_func areturn return ams wl
|
|
|
|
| callees ->
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
List.fold callees wl ~f:(fun callee wl ->
|
|
|
|
exec_call {call with callee} ams wl ) )
|
|
|
|
| Return {exp} -> exec_return exp ams wl
|
|
|
|
| Throw {exc} -> exec_throw exc ams wl
|
|
|
|
| Unreachable -> wl
|
|
|
|
|
|
|
|
let rec exec_ip pgm ({ctrl= {ip; stk; tid}; state} as ams) wl =
|
|
|
|
match Llair.IP.inst ip with
|
|
|
|
| Some inst -> (
|
|
|
|
[%Trace.info " t%i@\n@[%a@]@\n%a" tid D.pp state Llair.Inst.pp inst] ;
|
|
|
|
Report.step_inst ip ;
|
|
|
|
match D.exec_inst tid inst state with
|
|
|
|
| Ok state ->
|
|
|
|
let ip = Llair.IP.succ ip in
|
|
|
|
if Llair.IP.is_schedule_point ip then
|
|
|
|
let src = Llair.IP.block ip in
|
|
|
|
let edge = {dst= Runnable {ip; stk; tid}; src} in
|
|
|
|
Work.add ~retreating:false {ams with ctrl= edge; state} wl
|
|
|
|
else exec_ip pgm {ams with ctrl= {ams.ctrl with ip}; state} wl
|
|
|
|
| Error alarm ->
|
|
|
|
Report.alarm alarm ;
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
wl )
|
|
|
|
| None -> exec_term pgm ams wl
|
|
|
|
|
|
|
|
let call_entry_point pgm =
|
|
|
|
let+ {name; formals; freturn; locals; entry} =
|
|
|
|
List.find_map Config.entry_points ~f:(fun entry_point ->
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let* func = Llair.Func.find entry_point pgm.Llair.functions in
|
|
|
|
if IArray.is_empty func.formals then Some func else None )
|
|
|
|
in
|
|
|
|
let summaries = Config.function_summaries in
|
|
|
|
let globals = Domain_used_globals.by_function Config.globals name in
|
|
|
|
let actuals = IArray.empty in
|
|
|
|
let areturn = None in
|
|
|
|
let state, _ =
|
|
|
|
D.call ThreadID.init ~summaries ~globals ~actuals ~areturn ~formals
|
|
|
|
~freturn ~locals (D.init pgm.globals)
|
|
|
|
in
|
|
|
|
Work.init state entry
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let exec_pgm pgm =
|
|
|
|
match call_entry_point pgm with
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
| Some wl -> Work.run ~f:(exec_ip pgm) wl
|
|
|
|
| None -> fail "no entry point found" ()
|
|
|
|
|
[sledge] Rework Control to use an explicit abstract machine state
Summary:
This diff reworks the analysis scheduler to explicitly use a notion of
"abstract machine state" which makes the distinction with the state of
the analysis exploration algorithm more clear. The instruction
pointer, call stack, symbolic state, and retreating edge depths were,
prior to this change, passed individually to the various `exec_*`
functions. After this change, all this information is combined into an
abstract machine state value.
Additionally, this change explicitly factors out the commonality
between abstract machine states, on which symbolic execution operates,
and the elements of the frontier of exploration, that the analysis
scheduler maintaines. In short, an element of the frontier is simply
an abstract machine state with a control-flow edge instead of an
instruction pointer.
This change is almost entirely a non-functional refactoring. While
this serves as an improvement in code clarity, the main motivation is
that it establishes a code structure which minimizes the structural
changes needed when adding the concurrency analysis.
Differential Revision: D29441152
fbshipit-source-id: 01be87d4e
4 years ago
|
|
|
let compute_summaries pgm =
|
|
|
|
assert Config.function_summaries ;
|
|
|
|
exec_pgm pgm ;
|
|
|
|
Llair.Function.Tbl.fold summary_table Llair.Function.Map.empty
|
|
|
|
~f:(fun ~key ~data map ->
|
|
|
|
match data with
|
|
|
|
| [] -> map
|
|
|
|
| _ -> Llair.Function.Map.add ~key ~data map )
|
|
|
|
end
|
|
|
|
[@@inlined]
|