@ -19,7 +19,7 @@ module Stack : sig
val push_jump : Var . Set . t -> t -> t
val push_jump : Var . Set . t -> t -> t
val push_call :
val push_call :
Var. Set . t
Llair. block
-> retreating : bool
-> retreating : bool
-> bound : int
-> bound : int
-> return : Llair . jump
-> return : Llair . jump
@ -31,14 +31,14 @@ module Stack : sig
val pop_return :
val pop_return :
t
t
-> init : ' a
-> init : ' a
-> f : ( Var . Set . t -> Domain . from_call -> ' a -> ' b )
-> f : ( Var . t option -> Var . Set . t -> Domain . from_call -> ' a -> ' a )
-> ( t * ' b * Llair . jump ) option
-> ( Llair . jump * ' a * t ) option
val pop_throw :
val pop_throw :
t
t
-> init : ' a
-> init : ' a
-> f : ( Var . Set . t -> Domain . from_call -> ' a -> ' a )
-> f : ( Var . t option -> Var . Set . t -> Domain . from_call -> ' a -> ' a )
-> ( t * ' a * Llair . jump ) option
-> ( Llair . jump * ' a * t ) option
end = struct
end = struct
type t =
type t =
| Locals of Var . Set . t * t
| Locals of Var . Set . t * t
@ -46,6 +46,8 @@ end = struct
{ retreating : bool
{ retreating : bool
(* * return from a call not known to be nonrecursive *)
(* * return from a call not known to be nonrecursive *)
; dst : Llair . Jump . t
; dst : Llair . Jump . t
; freturn : Var . t option
; fthrow : Var . t
; from_call : Domain . from_call
; from_call : Domain . from_call
; stk : t }
; stk : t }
| Throw of Llair . Jump . t * t
| Throw of Llair . Jump . t * t
@ -110,14 +112,16 @@ end = struct
( if Set . is_empty lcls then stk else Locals ( lcls , stk ) )
( if Set . is_empty lcls then stk else Locals ( lcls , stk ) )
| > check invariant
| > check invariant
let push_return ~ retreating dst from_call stk =
let push_return ~ retreating dst freturn fthrow from_call stk =
Return { retreating ; dst ; from_call ; stk } | > check invariant
Return { retreating ; dst ; freturn ; fthrow ; from_call ; stk }
| > check invariant
let push_throw jmp stk =
let push_throw jmp stk =
( match jmp with None -> stk | Some jmp -> Throw ( jmp , stk ) )
( match jmp with None -> stk | Some jmp -> Throw ( jmp , stk ) )
| > check invariant
| > check invariant
let push_call locals ~ retreating ~ bound ~ return from_call ? throw stk =
let push_call { Llair . locals ; parent = { freturn ; fthrow } } ~ retreating ~ bound
~ return from_call ? throw stk =
[ % Trace . call fun { pf } -> pf " %a " print_abbrev stk ]
[ % Trace . call fun { pf } -> pf " %a " print_abbrev stk ]
;
;
let rec count_f_in_stack acc f = function
let rec count_f_in_stack acc f = function
@ -132,7 +136,8 @@ end = struct
else
else
Some
Some
( push_jump locals
( push_jump locals
( push_throw throw ( push_return ~ retreating return from_call stk ) ) )
( push_throw throw
( push_return ~ retreating return freturn fthrow from_call stk ) ) )
)
)
| >
| >
[ % Trace . retn fun { pf } _ ->
[ % Trace . retn fun { pf } _ ->
@ -142,8 +147,13 @@ end = struct
let rec pop_return_ scope = function
let rec pop_return_ scope = function
| Locals ( locals , stk ) -> pop_return_ ( Set . union locals scope ) stk
| Locals ( locals , stk ) -> pop_return_ ( Set . union locals scope ) stk
| Throw ( _ , stk ) -> pop_return_ scope stk
| Throw ( _ , stk ) -> pop_return_ scope stk
| Return { dst ; from_call ; stk } ->
| Return { dst ; freturn ; from_call ; stk } ->
Some ( stk , f scope from_call init , dst )
let dst =
match freturn with
| Some freturn -> { dst with args = Exp . var freturn :: dst . args }
| None -> dst
in
Some ( dst , f freturn scope from_call init , stk )
| Empty -> None
| Empty -> None
in
in
pop_return_ Var . Set . empty stk
pop_return_ Var . Set . empty stk
@ -152,10 +162,11 @@ end = struct
let rec pop_throw_ scope state = function
let rec pop_throw_ scope state = function
| Locals ( locals , stk ) ->
| Locals ( locals , stk ) ->
pop_throw_ ( Set . union locals scope ) state stk
pop_throw_ ( Set . union locals scope ) state stk
| Return { from_call ; stk } ->
| Return { freturn ; from_call ; stk } ->
pop_throw_ Var . Set . empty ( f scope from_call state ) stk
pop_throw_ Var . Set . empty ( f freturn scope from_call state ) stk
| Throw ( jmp , Return { from_call ; stk } ) ->
| Throw ( dst , Return { fthrow ; from_call ; stk } ) ->
Some ( stk , f scope from_call state , jmp )
let dst = { dst with args = Exp . var fthrow :: dst . args } in
Some ( dst , f ( Some fthrow ) scope from_call state , stk )
| Empty -> None
| Empty -> None
| Throw _ as stk -> violates invariant stk
| Throw _ as stk -> violates invariant stk
in
in
@ -269,30 +280,29 @@ let exec_goto stk state block ({dst; retreating} : Llair.jump) =
Work . add ~ prev : block ~ retreating stk state dst
Work . add ~ prev : block ~ retreating stk state dst
let exec_jump stk state block ( { dst ; args } as jmp : Llair . jump ) =
let exec_jump stk state block ( { dst ; args } as jmp : Llair . jump ) =
let state , _ = Domain . call state args dst . params dst . locals in
let state , _ = Domain . call args dst . params dst . locals state in
exec_goto stk state block jmp
exec_goto stk state block jmp
let exec_call ~ opts stk state block ( { dst ; args ; retreating } : Llair . jump )
let exec_call ~ opts stk state block ( { dst ; args ; retreating } : Llair . jump )
return throw =
return throw =
let state , from_call = Domain . call state args dst . params dst . locals in
let state , from_call = Domain . call args dst . params dst . locals state in
match
match
Stack . push_call ~ bound : opts . bound dst . locals ~ retreating ~ return
Stack . push_call ~ bound : opts . bound dst ~ retreating ~ return from_call
from_call ? throw stk
? throw stk
with
with
| Some stk -> Work . add stk ~ prev : block ~ retreating state dst
| Some stk -> Work . add stk ~ prev : block ~ retreating state dst
| None -> Work . skip
| None -> Work . skip
let exec_return stk state block exp =
let exec_pop pop stk state block exp =
match Stack . pop_return stk ~ init : state ~ f : Domain . retn with
match pop stk ~ init : state ~ f : ( Domain . retn exp ) with
| Some ( stk , state , ( { args } as jmp ) ) ->
| Some ( jmp , state , stk ) -> exec_jump stk state block jmp
exec_jump stk state block { jmp with args = Option . cons exp args }
| None -> Work . skip
| None -> Work . skip
let exec_return stk state block exp =
exec_pop Stack . pop_return stk state block exp
let exec_throw stk state block exc =
let exec_throw stk state block exc =
match Stack . pop_throw stk ~ init : state ~ f : Domain . retn with
exec_pop Stack . pop_throw stk state block ( Some exc )
| Some ( stk , state , ( { args } as jmp ) ) ->
exec_jump stk state block { jmp with args = exc :: args }
| None -> Work . skip
let exec_skip_func :
let exec_skip_func :
Stack . t -> Domain . t -> Llair . block -> Llair . jump -> Work . x =
Stack . t -> Domain . t -> Llair . block -> Llair . jump -> Work . x =
@ -405,7 +415,7 @@ let harness : Llair.t -> (int -> Work.t) option =
| Some { entry = { params = [] } as block } ->
| Some { entry = { params = [] } as block } ->
Some
Some
( Work . init
( Work . init
( fst ( Domain . call ( Domain . init pgm . globals ) [] [] block . locals ) )
( fst ( Domain . call [] [] block . locals ( Domain . init pgm . globals ) ) )
block )
block )
| _ -> None
| _ -> None