Source file topDown_space_cache_term.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
(** Terminating top-down solver, which supports space-efficiency and caching ([topdown_space_cache_term]).
Simpler version of {!Td3} without incremental. *)
open Batteries
open ConstrSys
open Messages
module WP =
functor (S:EqConstrSys) ->
functor (HM:Hashtbl.S with type key = S.v) ->
struct
include Generic.SolverStats (S) (HM)
module VS = Set.Make (S.Var)
module P =
struct
type t = S.Var.t * S.Var.t [@@deriving eq, hash]
end
type phase = Widen | Narrow
let solve st vs =
let stable = HM.create 10 in
let infl = HM.create 10 in
let called = HM.create 10 in
let rho = HM.create 10 in
let rho' = HM.create 10 in
let cache_sizes = ref [] in
let add_infl y x =
if tracing then trace "sol2" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x;
HM.replace infl y (VS.add x (try HM.find infl y with Not_found -> VS.empty))
in
let rec destabilize x =
if tracing then trace "sol2" "destabilize %a" S.Var.pretty_trace x;
let w = HM.find_default infl x VS.empty in
HM.replace infl x VS.empty;
VS.iter (fun y -> HM.remove stable y; if not (HM.mem called y) then destabilize y) w
and solve x phase =
if tracing then trace "sol2" "solve %a, called: %b, stable: %b" S.Var.pretty_trace x (HM.mem called x) (HM.mem stable x);
if not (HM.mem called x || HM.mem stable x) then (
HM.replace stable x ();
HM.replace called x ();
let old = HM.find rho x in
let l = HM.create 10 in
let tmp = eq x (eval l x) (side l) in
let tmp = S.Dom.join tmp (try HM.find rho' x with Not_found -> S.Dom.bot ()) in
if tracing then trace "sol" "Var: %a" S.Var.pretty_trace x ;
if tracing then trace "sol" "Contrib:%a" S.Dom.pretty tmp;
HM.remove called x;
let tmp = match phase with Widen -> S.Dom.widen old (S.Dom.join old tmp) | Narrow -> S.Dom.narrow old tmp in
if tracing then trace "cache" "cache size %d for %a" (HM.length l) S.Var.pretty_trace x;
cache_sizes := HM.length l :: !cache_sizes;
if not (S.Dom.equal old tmp) then (
update_var_event x old tmp;
if tracing then trace "sol" "New Value:%a" S.Dom.pretty tmp;
HM.replace rho x tmp;
destabilize x;
(solve[@tailcall]) x phase;
) else if not (HM.mem stable x) then (
(solve[@tailcall]) x Widen;
) else if phase = Widen then (
HM.remove stable x;
(solve[@tailcall]) x Narrow;
);
)
and eq x get set =
if tracing then trace "sol2" "eq %a" S.Var.pretty_trace x;
eval_rhs_event x;
match S.system x with
| None -> S.Dom.bot ()
| Some f -> f get set
and simple_solve l x y =
if tracing then trace "sol2" "simple_solve %a (rhs: %b)" S.Var.pretty_trace y (S.system y <> None);
if S.system y = None then init y;
if HM.mem rho y then (solve y Widen; HM.find rho y) else
if HM.mem called y then (init y; HM.remove l y; HM.find rho y) else
if HM.mem l y then HM.find l y
else (
HM.replace called y ();
let tmp = eq y (eval l x) (side l) in
HM.remove called y;
if HM.mem rho y then (HM.remove l y; solve y Widen; HM.find rho y)
else (HM.replace l y tmp; tmp)
)
and eval l x y =
if tracing then trace "sol2" "eval %a ## %a" S.Var.pretty_trace x S.Var.pretty_trace y;
get_var_event y;
let tmp = simple_solve l x y in
if HM.mem rho y then add_infl y x;
tmp
and side l y d =
if tracing then trace "sol2" "side to %a (wpx: %b) ## value: %a" S.Var.pretty_trace y (HM.mem rho y) S.Dom.pretty d;
let old = try HM.find rho' y with Not_found -> S.Dom.bot () in
if not (S.Dom.leq d old) then (
HM.replace rho' y (S.Dom.join old d);
HM.remove l y;
HM.remove stable y;
init y;
solve y Widen;
)
and init x =
if tracing then trace "sol2" "init %a" S.Var.pretty_trace x;
if not (HM.mem rho x) then (
new_var_event x;
HM.replace rho x (S.Dom.bot ())
)
in
let set_start (x,d) =
if tracing then trace "sol2" "set_start %a ## %a" S.Var.pretty_trace x S.Dom.pretty d;
init x;
HM.replace rho x d;
HM.replace rho' x d;
solve x Widen
in
start_event ();
List.iter set_start st;
List.iter init vs;
List.iter (fun x -> solve x Widen) vs;
let rec solve_sidevs () =
let non_stable = List.filter (neg (HM.mem stable)) vs in
if non_stable <> [] then (
List.iter (fun x -> solve x Widen) non_stable;
solve_sidevs ()
)
in
solve_sidevs ();
let visited = HM.create 10 in
let rec get x =
if HM.mem visited x then (
if not (HM.mem rho x) then (
Logs.warn "Found an unknown that should be a widening point: %a" S.Var.pretty_trace x;
S.Dom.top ()
) else
HM.find rho x
) else (
HM.replace visited x ();
let check_side y d =
let d' = try HM.find rho y with Not_found -> S.Dom.bot () in
if not (S.Dom.leq d d') then Logs.error "Fixpoint not reached in restore step at side-effected variable %a: %a not leq %a" S.Var.pretty_trace y S.Dom.pretty d S.Dom.pretty d'
in
let eq x =
match S.system x with
| None -> if HM.mem rho x then HM.find rho x else S.Dom.bot ()
| Some f -> f get check_side
in
if HM.mem rho x then (
let d1 = HM.find rho x in
let d2 = eq x in
if not (S.Dom.leq d2 d1) then
Logs.error "Fixpoint not reached in restore step at %a\n @[Variable:\n%a\nRight-Hand-Side:\n%a\nCalculating one more step changes: %a\n@]" S.Var.pretty_trace x S.Dom.pretty d1 S.Dom.pretty d2 S.Dom.pretty_diff (d1,d2);
d1
) else (
let d = eq x in
HM.replace rho x d;
d
)
)
in
if GobConfig.get_bool "solvers.wp.restore" then (
Logs.debug "Restoring missing values.";
let restore () =
let get x =
let d = get x in
if tracing then trace "sol2" "restored var %a ## %a" S.Var.pretty_trace x S.Dom.pretty d
in
List.iter get vs
in
Timing.wrap "restore" restore ();
Logs.debug "Solved %d vars. Total of %d vars after restore." !SolverStats.vars (HM.length rho);
);
let avg xs = float_of_int (BatList.sum xs) /. float_of_int (List.length xs) in
if tracing then trace "cache" "#caches: %d, max: %d, avg: %.2f" (List.length !cache_sizes) (List.max !cache_sizes) (avg !cache_sizes);
stop_event ();
HM.clear stable;
HM.clear infl ;
rho
end
let _ =
Selector.add_solver ("topdown_space_cache_term", (module PostSolver.EqIncrSolverFromEqSolver (WP)));