@ -9,13 +9,7 @@
The analysis' semantics of control flow . * )
type exec_opts =
{ bound : int
; function_summaries : bool
; entry_points : string list
; globals : Domain_used_globals . r }
module Make ( Dom : Domain_intf . Dom ) = struct
module Make ( Opts : Domain_intf . Opts ) ( Dom : Domain_intf . Dom ) = struct
module Stack : sig
type t
@ -24,10 +18,7 @@ module Make (Dom : Domain_intf.Dom) = struct
type as_inlined_location = t [ @@ deriving compare , sexp_of ]
val empty : t
val push_call :
Llair . func Llair . call -> bound : int -> Dom . from_call -> t -> t option
val push_call : Llair . func Llair . call -> Dom . from_call -> t -> t option
val pop_return : t -> ( Dom . from_call * Llair . jump * t ) option
val pop_throw :
@ -107,7 +98,7 @@ module Make (Dom : Domain_intf.Dom) = struct
( match jmp with None -> stk | Some jmp -> Throw ( jmp , stk ) )
| > check invariant
let push_call ( Llair . { return ; throw } as call ) ~ bound from_call stk =
let push_call ( Llair . { return ; throw } as call ) from_call stk =
[ % Trace . call fun { pf } -> pf " %a " pp stk ]
;
let rec count_f_in_stack acc f = function
@ -118,7 +109,7 @@ module Make (Dom : Domain_intf.Dom) = struct
| _ -> acc
in
let n = count_f_in_stack 0 return stk in
( if n > bound then (
( if n > Opts . bound then (
Report . hit_bound n ;
None )
else Some ( push_throw throw ( push_return call from_call stk ) ) )
@ -146,7 +137,7 @@ module Make (Dom : Domain_intf.Dom) = struct
module Work : sig
type t
val init : Dom . t -> Llair . block -> int -> t
val init : Dom . t -> Llair . block -> t
type x
@ -264,32 +255,32 @@ module Make (Dom : Domain_intf.Dom) = struct
Some ( top , elts , { queue ; removed } )
end
type t = PrioQueue . t * int
type t = PrioQueue . t
type x = Depths . t -> t -> t
let skip _ w = w
let seq x y d w = y d ( x d w )
let add ? prev ~ retreating stk state curr depths ( queue , bound ) =
let add ? prev ~ retreating stk state curr depths queue =
let edge = { Edge . dst = curr ; src = prev ; stk } in
let depth = Option . value ( Depths . find edge depths ) ~ default : 0 in
let depth = if retreating then depth + 1 else depth in
if depth < = bound then (
if depth < = Opts . bound then (
[ % Trace . info
" @[<6>enqueue %i: %a [%a]@ | %a@] " depth Edge . pp edge Stack . pp
edge . stk PrioQueue . pp queue ] ;
let depths = Depths . add ~ key : edge ~ data : depth depths in
( PrioQueue . add { depth ; edge ; state ; depths } queue , bound ) )
PrioQueue . add { depth ; edge ; state ; depths } queue )
else (
[ % Trace . info " prune: %i: %a " depth Edge . pp edge ] ;
Report . hit_bound bound ;
( queue , bound ) )
Report . hit_bound Opts . bound ;
queue )
let init state curr bound =
let init state curr =
add ~ retreating : false Stack . empty state curr Depths . empty
( PrioQueue . create () , bound )
( PrioQueue . create () )
let rec run ~ f ( queue , bound ) =
let rec run ~ f queue =
match PrioQueue . pop queue with
| Some ( { depth ; edge ; state ; depths } , elts , queue ) ->
[ % Trace . info
@ -305,7 +296,7 @@ module Make (Dom : Domain_intf.Dom) = struct
( state , depths , queue )
| None -> ( state , depths , queue ) )
in
run ~ f ( f edge . stk state edge . dst depths ( queue , bound ) )
run ~ f ( f edge . stk state edge . dst depths queue )
| None ->
[ % Trace . info " queue empty " ] ;
()
@ -316,7 +307,7 @@ module Make (Dom : Domain_intf.Dom) = struct
let summary_table = Llair . Function . Tbl . create ()
let exec_call opts stk state block call globals =
let exec_call stk state block call globals =
let Llair . { callee ; actuals ; areturn ; return ; recursive } = call in
let Llair . { name ; formals ; freturn ; locals ; entry } = callee in
[ % Trace . call fun { pf } ->
@ -324,14 +315,14 @@ module Make (Dom : Domain_intf.Dom) = struct
Llair . Function . pp return . dst . parent . name Dom . pp state ]
;
let dnf_states =
if opts .function_summaries then Dom . dnf state else [ state ]
if Opts .function_summaries then Dom . dnf state else [ state ]
in
let domain_call =
Dom . call ~ globals ~ actuals ~ areturn ~ formals ~ freturn ~ locals
in
List . fold dnf_states Work . skip ~ f : ( fun state acc ->
match
if not opts .function_summaries then None
if not Opts .function_summaries then None
else
let maybe_summary_post =
let state = fst ( domain_call ~ summaries : false state ) in
@ -345,12 +336,10 @@ module Make (Dom : Domain_intf.Dom) = struct
with
| None ->
let state , from_call =
domain_call ~ summaries : opts .function_summaries state
domain_call ~ summaries : Opts .function_summaries state
in
Work . seq acc
( match
Stack . push_call call ~ bound : opts . bound from_call stk
with
( match Stack . push_call call from_call stk with
| Some stk ->
Work . add stk ~ prev : block ~ retreating : recursive state entry
| None -> (
@ -373,11 +362,11 @@ module Make (Dom : Domain_intf.Dom) = struct
let state = Option . fold ~ f : Dom . exec_kill areturn state in
exec_jump stk state block return
let exec_call opts stk state block
( { Llair . callee ; areturn ; return ; _ } as call ) globals =
let exec_call stk state block ( { Llair . callee ; areturn ; return ; _ } as call )
globals =
if Llair . Func . is_undefined callee then
exec_skip_func stk state block areturn return
else exec_call opts stk state block call globals
else exec_call stk state block call globals
let pp_st () =
[ % Trace . printf
@ -387,12 +376,12 @@ module Make (Dom : Domain_intf.Dom) = struct
( List . pp " @, " Dom . pp_summary )
data ) ) ]
let exec_return ~ opts stk pre_state ( block : Llair . block ) exp =
let exec_return stk pre_state ( block : Llair . block ) exp =
let Llair . { name ; formals ; freturn ; locals } = block . parent in
[ % Trace . call fun { pf } -> pf " from: %a " Llair . Function . pp name ]
;
let summarize post_state =
if not opts .function_summaries then post_state
if not Opts .function_summaries then post_state
else
let function_summary , post_state =
Dom . create_summary ~ locals ~ formals post_state
@ -417,10 +406,10 @@ module Make (Dom : Domain_intf.Dom) = struct
| None ->
(* Create and store a function summary for main *)
if
opts .function_summaries
Opts .function_summaries
&& List . mem ~ eq : String . equal
( Llair . Function . name name )
opts .entry_points
Opts .entry_points
then summarize exit_state | > ( ignore : Dom . t -> unit ) ;
Work . skip )
| >
@ -449,14 +438,9 @@ module Make (Dom : Domain_intf.Dom) = struct
| >
[ % Trace . retn fun { pf } _ -> pf " " ]
let exec_term :
exec_opts
-> Llair . program
-> Stack . t
-> Dom . t
-> Llair . block
-> Work . x =
fun opts pgm stk state block ->
let exec_term : Llair . program -> Stack . t -> Dom . t -> Llair . block -> Work . x
=
fun pgm stk state block ->
[ % Trace . info
" @[<2>exec term@ \n @[%a@]@ \n %a@] " Dom . pp state Llair . Term . pp block . term ] ;
Report . step_term block . term ;
@ -487,18 +471,18 @@ module Make (Dom : Domain_intf.Dom) = struct
| Some state -> exec_jump stk state block jump | > Work . seq x
| None -> x )
| Call ( { callee } as call ) ->
exec_call opts stk state block call
( Domain_used_globals . by_function opts .globals callee . name )
exec_call stk state block call
( Domain_used_globals . by_function Opts .globals callee . name )
| ICall ( { callee ; areturn ; return } as call ) -> (
let lookup name = Llair . Func . find name pgm . functions in
match Dom . resolve_callee lookup callee state with
| [] -> exec_skip_func stk state block areturn return
| callees ->
List . fold callees Work . skip ~ f : ( fun callee x ->
exec_call opts stk state block { call with callee }
( Domain_used_globals . by_function opts .globals callee . name )
exec_call stk state block { call with callee }
( Domain_used_globals . by_function Opts .globals callee . name )
| > Work . seq x ) )
| Return { exp } -> exec_return ~ opts stk state block exp
| Return { exp } -> exec_return stk state block exp
| Throw { exc } -> exec_throw stk state block exc
| Unreachable -> Work . skip
@ -513,13 +497,8 @@ module Make (Dom : Domain_intf.Dom) = struct
| Some state -> Result . Ok state | None -> Result . Error ( state , inst )
let exec_block :
exec_opts
-> Llair . program
-> Stack . t
-> Dom . t
-> Llair . block
-> Work . x =
fun opts pgm stk state block ->
Llair . program -> Stack . t -> Dom . t -> Llair . block -> Work . x =
fun pgm stk state block ->
[ % trace ]
~ call : ( fun { pf } ->
pf " #%i %%%s in %a " block . sort_index block . lbl Llair . Function . pp
@ -531,39 +510,39 @@ module Make (Dom : Domain_intf.Dom) = struct
match
Iter . fold_result ~ f : exec_inst ( IArray . to_iter block . cmnd ) state
with
| Ok state -> exec_term opts pgm stk state block
| Ok state -> exec_term pgm stk state block
| Error ( state , inst ) ->
Report . invalid_access_inst ( Dom . report_fmt_thunk state ) inst ;
Work . skip
let harness : exec_opts -> Llair . program -> ( int -> Work . t ) option =
fun opts pgm ->
let harness : Llair . program -> Work . t option =
fun pgm ->
List . find_map
~ f : ( fun entry_point -> Llair . Func . find entry_point pgm . functions )
opts .entry_points
Opts .entry_points
| > function
| Some { name ; formals ; freturn ; locals ; entry }
when IArray . is_empty formals ->
Some
( Work . init
( fst
( Dom . call ~ summaries : opts .function_summaries
( Dom . call ~ summaries : Opts .function_summaries
~ globals :
( Domain_used_globals . by_function opts .globals name )
( Domain_used_globals . by_function Opts .globals name )
~ actuals : IArray . empty ~ areturn : None ~ formals : IArray . empty
~ freturn ~ locals ( Dom . init pgm . globals ) ) )
entry )
| _ -> None
let exec_pgm : exec_opts -> Llair . program -> unit =
fun opts pgm ->
match harness opts pgm with
| Some work -> Work . run ~ f : ( exec_block opts pgm) ( work opts . bound )
let exec_pgm : Llair . program -> unit =
fun pgm ->
match harness pgm with
| Some work -> Work . run ~ f : ( exec_block pgm) work
| None -> fail " no applicable harness " ()
let compute_summaries opts pgm : Dom . summary list Llair . Function . Map . t =
assert opts .function_summaries ;
exec_pgm opts pgm ;
let compute_summaries pgm : Dom . summary list Llair . Function . Map . t =
assert Opts .function_summaries ;
exec_pgm pgm ;
Llair . Function . Tbl . fold summary_table Llair . Function . Map . empty
~ f : ( fun ~ key ~ data map ->
match data with