|
| 1 | +(** Top-down solver with side effects. Baseline for comparisons with td_parallel solvers ([td_simplified_ref]). |
| 2 | + This is the same as ([td_simplified]), but it uses records for solver that instead of multiple hashmaps. |
| 3 | +*) |
| 4 | + |
| 5 | +open Batteries |
| 6 | +open Goblint_constraint.ConstrSys |
| 7 | +open Goblint_constraint.SolverTypes |
| 8 | +open Messages |
| 9 | + |
| 10 | +module M = Messages |
| 11 | + |
| 12 | +module Base : GenericEqSolver = |
| 13 | + functor (S:EqConstrSys) -> |
| 14 | + functor (HM:Hashtbl.S with type key = S.v) -> |
| 15 | + struct |
| 16 | + open SolverBox.Warrow (S.Dom) |
| 17 | + include Generic.SolverStats (S) (HM) |
| 18 | + module VS = Set.Make (S.Var) |
| 19 | + |
| 20 | + type var_data = { |
| 21 | + infl: VS.t; |
| 22 | + value: S.Dom.t; |
| 23 | + wpoint: bool; |
| 24 | + stable: bool; |
| 25 | + called: bool; |
| 26 | + } |
| 27 | + |
| 28 | + let solve st vs = |
| 29 | + let (data : var_data ref HM.t) = HM.create 10 in |
| 30 | + |
| 31 | + let () = print_solver_stats := fun () -> |
| 32 | + Logs.debug "|data|=%d" (HM.length data); |
| 33 | + in |
| 34 | + |
| 35 | + let add_infl y x = |
| 36 | + if tracing then trace "infl" "add_infl %a %a" S.Var.pretty_trace y S.Var.pretty_trace x; |
| 37 | + let y_ref = HM.find data y in |
| 38 | + y_ref := { !y_ref with infl = VS.add x !y_ref.infl } |
| 39 | + in |
| 40 | + |
| 41 | + let init (x : S.v): var_data ref = |
| 42 | + let x_ref = HM.find_option data x in |
| 43 | + match x_ref with |
| 44 | + | Some r -> r |
| 45 | + | None -> |
| 46 | + begin |
| 47 | + new_var_event x; |
| 48 | + if tracing then trace "init" "init %a" S.Var.pretty_trace x; |
| 49 | + let data_x = ref { |
| 50 | + infl = VS.empty; |
| 51 | + value = S.Dom.bot (); |
| 52 | + wpoint = false; |
| 53 | + stable = false; |
| 54 | + called = false |
| 55 | + } in |
| 56 | + HM.replace data x data_x; |
| 57 | + data_x |
| 58 | + end |
| 59 | + in |
| 60 | + |
| 61 | + let eq x get set = |
| 62 | + if tracing then trace "eq" "eq %a" S.Var.pretty_trace x; |
| 63 | + match S.system x with |
| 64 | + | None -> S.Dom.bot () |
| 65 | + | Some f -> f get set |
| 66 | + in |
| 67 | + |
| 68 | + let rec destabilize x = |
| 69 | + if tracing then trace "destab" "destabilize %a" S.Var.pretty_trace x; |
| 70 | + let x_ref = HM.find data x in |
| 71 | + let w = !x_ref.infl in |
| 72 | + x_ref := { !x_ref with infl = VS.empty }; |
| 73 | + VS.iter (fun y -> |
| 74 | + if tracing then trace "destab" "stable remove %a" S.Var.pretty_trace y; |
| 75 | + let y_ref = HM.find data y in |
| 76 | + y_ref := { !y_ref with stable = false }; |
| 77 | + destabilize y |
| 78 | + ) w |
| 79 | + in |
| 80 | + |
| 81 | + let rec query x y = |
| 82 | + let y_ref = init y in |
| 83 | + if tracing then trace "sol_query" "entering query for %a; stable %b; called %b" S.Var.pretty_trace y (!y_ref.stable) (!y_ref.called); |
| 84 | + get_var_event y; |
| 85 | + if not (!y_ref.called) then ( |
| 86 | + if S.system y = None then ( |
| 87 | + y_ref := { !y_ref with stable = true }; |
| 88 | + ) else ( |
| 89 | + y_ref := { !y_ref with called = true }; |
| 90 | + if tracing then trace "iter" "iterate called from query"; |
| 91 | + iterate y; |
| 92 | + y_ref := { !y_ref with called = false };) |
| 93 | + ) else ( |
| 94 | + if tracing && not (!y_ref.wpoint) then trace "wpoint" "query adding wpoint %a" S.Var.pretty_trace y; |
| 95 | + y_ref := { !y_ref with wpoint = true }; |
| 96 | + ); |
| 97 | + let tmp = !y_ref.value in |
| 98 | + add_infl y x; |
| 99 | + if tracing then trace "answer" "exiting query for %a\nanswer: %a" S.Var.pretty_trace y S.Dom.pretty tmp; |
| 100 | + tmp |
| 101 | + |
| 102 | + and side x y d = (* side from x to y; only to variables y w/o rhs; x only used for trace *) |
| 103 | + assert (S.system y = None); |
| 104 | + let y_ref = init y in |
| 105 | + if tracing then trace "side" "side to %a (wpx: %b) from %a ## value: %a" S.Var.pretty_trace y (!y_ref.wpoint) S.Var.pretty_trace x S.Dom.pretty d; |
| 106 | + let widen a b = |
| 107 | + if M.tracing then M.trace "wpoint" "side widen %a" S.Var.pretty_trace y; |
| 108 | + S.Dom.widen a (S.Dom.join a b) |
| 109 | + in |
| 110 | + let op a b = if !y_ref.wpoint then widen a b else S.Dom.join a b |
| 111 | + in |
| 112 | + let old = !y_ref.value in |
| 113 | + let tmp = op old d in |
| 114 | + y_ref := { !y_ref with stable = true }; |
| 115 | + if not (S.Dom.leq tmp old) then ( |
| 116 | + if tracing && not (S.Dom.is_bot old) then trace "update" "side to %a (wpx: %b) from %a: %a -> %a" S.Var.pretty_trace y (!y_ref.wpoint) S.Var.pretty_trace x S.Dom.pretty old S.Dom.pretty tmp; |
| 117 | + y_ref := { !y_ref with value = tmp }; |
| 118 | + destabilize y; |
| 119 | + (* make y a widening point. This will only matter for the next side _ y. *) |
| 120 | + if tracing && not (!y_ref.wpoint) then trace "wpoint" "side adding wpoint %a" S.Var.pretty_trace y; |
| 121 | + y_ref := { !y_ref with wpoint = true }; |
| 122 | + ) |
| 123 | + |
| 124 | + and iterate x = (* ~(inner) solve in td3*) |
| 125 | + |
| 126 | + (* begining of iterate*) |
| 127 | + let x_ref = init x in |
| 128 | + if tracing then trace "iter" "iterate %a, called: %b, stable: %b, wpoint: %b" S.Var.pretty_trace x (!x_ref.called) (!x_ref.stable) (!x_ref.wpoint); |
| 129 | + assert (S.system x <> None); |
| 130 | + if not (!x_ref.stable) then ( |
| 131 | + x_ref := { !x_ref with stable = true }; |
| 132 | + let wp = !x_ref.wpoint in (* if x becomes a wpoint during eq, checking this will delay widening until next iterate *) |
| 133 | + let eqd = eq x (query x) (side x) in (* d from equation/rhs *) |
| 134 | + let old = !x_ref.value in (* d from older iterate *) |
| 135 | + let wpd = (* d after widen/narrow (if wp) *) |
| 136 | + if not wp then eqd |
| 137 | + else ( |
| 138 | + if M.tracing then M.trace "wpoint" "widen %a" S.Var.pretty_trace x; |
| 139 | + box old eqd) |
| 140 | + in |
| 141 | + if not (Timing.wrap "S.Dom.equal" (fun () -> S.Dom.equal old wpd) ()) then ( |
| 142 | + (* old != wpd *) |
| 143 | + if tracing && not (S.Dom.is_bot old) && !x_ref.wpoint then trace "solchange" "%a (wpx: %b): %a" S.Var.pretty_trace x (!x_ref.wpoint) S.Dom.pretty_diff (wpd, old); |
| 144 | + update_var_event x old wpd; |
| 145 | + x_ref := { !x_ref with value = wpd }; |
| 146 | + destabilize x; |
| 147 | + if tracing then trace "iter" "iterate changed %a" S.Var.pretty_trace x; |
| 148 | + (iterate[@tailcall]) x |
| 149 | + ) else ( |
| 150 | + (* old == wpd *) |
| 151 | + if not (!x_ref.stable) then ( |
| 152 | + (* value unchanged, but not stable, i.e. destabilized itself during rhs *) |
| 153 | + if tracing then trace "iter" "iterate still unstable %a" S.Var.pretty_trace x; |
| 154 | + (iterate[@tailcall]) x |
| 155 | + ) else ( |
| 156 | + (* this makes e.g. nested loops precise, ex. tests/regression/34-localization/01-nested.c - if we do not remove wpoint, the inner loop head will stay a wpoint and widen the outer loop variable. *) |
| 157 | + if tracing && (!x_ref.wpoint) then trace "wpoint" "iterate removing wpoint %a" S.Var.pretty_trace x; |
| 158 | + x_ref := { !x_ref with wpoint = false }; |
| 159 | + ) |
| 160 | + ) |
| 161 | + ) |
| 162 | + in |
| 163 | + |
| 164 | + let set_start (x,d) = |
| 165 | + let x_ref = init x in |
| 166 | + x_ref := { !x_ref with value = d; stable = true }; |
| 167 | + in |
| 168 | + |
| 169 | + (* beginning of main solve *) |
| 170 | + start_event (); |
| 171 | + |
| 172 | + List.iter set_start st; |
| 173 | + |
| 174 | + List.iter (fun x -> ignore @@ init x) vs; |
| 175 | + (* If we have multiple start variables vs, we might solve v1, then while solving v2 we side some global which v1 depends on with a new value. Then v1 is no longer stable and we have to solve it again. *) |
| 176 | + let i = ref 0 in |
| 177 | + let rec solver () = (* as while loop in paper *) |
| 178 | + incr i; |
| 179 | + let unstable_vs = List.filter (neg (fun x -> !(HM.find data x).stable)) vs in |
| 180 | + if unstable_vs <> [] then ( |
| 181 | + if Logs.Level.should_log Debug then ( |
| 182 | + if !i = 1 then Logs.newline (); |
| 183 | + Logs.debug "Unstable solver start vars in %d. phase:" !i; |
| 184 | + List.iter (fun v -> Logs.debug "\t%a" S.Var.pretty_trace v) unstable_vs; |
| 185 | + Logs.newline (); |
| 186 | + flush_all (); |
| 187 | + ); |
| 188 | + List.iter (fun x -> |
| 189 | + let x_ref = HM.find data x in |
| 190 | + x_ref := { !x_ref with called = true }; |
| 191 | + if tracing then trace "multivar" "solving for %a" S.Var.pretty_trace x; |
| 192 | + iterate x; |
| 193 | + x_ref := { !x_ref with called = false } |
| 194 | + ) unstable_vs; |
| 195 | + solver (); |
| 196 | + ) |
| 197 | + in |
| 198 | + solver (); |
| 199 | + (* After termination, only those variables are stable which are |
| 200 | + * - reachable from any of the queried variables vs, or |
| 201 | + * - effected by side-effects and have no constraints on their own (this should be the case for all of our analyses). *) |
| 202 | + |
| 203 | + stop_event (); |
| 204 | + if Logs.Level.should_log Debug then ( |
| 205 | + Logs.debug "Data after iterate completed"; |
| 206 | + Logs.debug "|data|=%d" (HM.length data); |
| 207 | + ); |
| 208 | + |
| 209 | + if GobConfig.get_bool "dbg.print_wpoints" then ( |
| 210 | + Logs.newline (); |
| 211 | + Logs.debug "Widening points:"; |
| 212 | + HM.filter (fun x_ref -> !x_ref.wpoint) data |> HM.iter (fun k x_ref -> |
| 213 | + Logs.debug "%a" S.Var.pretty_trace k) |
| 214 | + ); |
| 215 | + |
| 216 | + HM.map (fun x x_ref -> !x_ref.value) data |
| 217 | + end |
| 218 | + |
| 219 | +let () = |
| 220 | + Selector.add_solver ("td_simplified_ref", (module PostSolver.DemandEqIncrSolverFromEqSolver (Base))); |
0 commit comments