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