Skip to content

Commit 8608e19

Browse files
Merge pull request #1442 from Red-Panda64/widening-gas
Widening gas
2 parents bce9f92 + a76946d commit 8608e19

File tree

13 files changed

+572
-86
lines changed

13 files changed

+572
-86
lines changed

src/config/options.schema.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,6 +2401,20 @@
24012401
"enum": ["never", "always", "sides", "cycle", "unstable_called", "unstable_self", "sides-pp","sides-local"],
24022402
"default": "sides"
24032403
},
2404+
"side_widen_gas": {
2405+
"title": "solvers.td3.side_widen_gas",
2406+
"description":
2407+
"Delay widening globals through side-effects. With a widening gas of N, the solver will perform widening only once unknown has grown through side-effects N times and the widening condition according to the side_widen policy has been met. A value of 0 behaves as before the feature was introduced.",
2408+
"type": "integer",
2409+
"default": 0
2410+
},
2411+
"widen_gas": {
2412+
"title": "solvers.td3.widen_gas",
2413+
"description":
2414+
"Delay widening non-globals, i.e. unknowns with right hand sides. With a widening gas of N, the solver will perform widening only once unknown has grown N times. A value of 0 behaves as before the feature was introduced.",
2415+
"type": "integer",
2416+
"default": 0
2417+
},
24042418
"space": {
24052419
"title": "solvers.td3.space",
24062420
"description":

src/solver/goblint_solver.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ module PostSolver = PostSolver
3030
module LocalFixpoint = LocalFixpoint
3131
module SolverStats = SolverStats
3232
module SolverBox = SolverBox
33+
34+
module SideWPointSelect = SideWPointSelect

src/solver/sideWPointSelect.ml

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
(** Strategies for widening leaf unknowns *)
2+
3+
open Batteries
4+
open ConstrSys
5+
open Messages
6+
7+
module type S =
8+
functor (S:EqConstrSys) ->
9+
functor (HM:Hashtbl.S with type key = S.v) ->
10+
functor (VS:Set.S with type elt = S.v) ->
11+
sig
12+
type data
13+
14+
(** Create data required by this widening point selection strategy.
15+
The parameters are not necessarily used by all strategies.
16+
@param is_stable This callback should return whether an unknown is stable.
17+
@param add_infl Allows the strategy to record additional influences.
18+
This is mainly intended for strategies like unstable-self,
19+
which records the influence of a side-effecting unknown x to the leaf y.
20+
*)
21+
val create_data: (S.v -> bool) -> (S.v -> S.v -> unit) -> data
22+
23+
(** Notifies this strategy that a side-effect has occured.
24+
This allows the strategy to adapt its internal data structure.
25+
@param data The internal state of this strategy
26+
@param x The optional source of the side-effect
27+
@param y The leaf receiving the side-effect
28+
*)
29+
val notify_side: data -> S.v option -> S.v -> unit
30+
31+
(** Whether the destabilization of the side-effected var should record the destabilization
32+
of called variables and start variables. This information should be passed to [should_mark_wpoint]
33+
by the solver.
34+
*)
35+
val record_destabilized_vs: bool
36+
37+
(** This strategy can decide to prevent widening.
38+
Note that, if this strategy does not veto, this does not mean that widening
39+
will necessarily be performed. Nor does a call to this function imply that
40+
the value of the leaf has grown.
41+
@param data The internal state of this strategy
42+
@param called Set of called unknowns
43+
@param old_sides Prior side-effects to leaf y
44+
@param x Optional source of the side-effect
45+
@param y Side-effected leaf y
46+
@return [true]: widening will not be applied; [false]: widening might be applied
47+
*)
48+
val veto_widen: data -> unit HM.t -> VS.t -> S.v option -> S.v -> bool
49+
50+
(** The value of the leaf has grown. Should it be marked a widening point?
51+
Widening points are widened when their value grows, unless vetoed.
52+
Even if this function is called, leaf y might already be a widening point
53+
from an earlier side-effect.
54+
@param data The internal state of this strategy
55+
@param called Set of called unknowns
56+
@param old_sides Prior side-effects to leaf y
57+
@param x Optional source of the side-effect
58+
@param y Side-effected leaf y
59+
@param destabilized_vs Optional destabilization info, described in [record_destabilized_vs]
60+
@return [true]: mark as widening point; [false]: do not mark as widening point
61+
62+
*)
63+
val should_mark_wpoint: data -> unit HM.t -> VS.t -> S.v option -> S.v -> bool option -> bool
64+
end
65+
66+
(** Any side-effect after the first one will be widened which will unnecessarily lose precision. *)
67+
module Always : S =
68+
functor (S:EqConstrSys) ->
69+
functor (HM:Hashtbl.S with type key = S.v) ->
70+
functor (VS:Set.S with type elt = S.v) ->
71+
struct
72+
type data = unit
73+
74+
let create_data _ _ = ()
75+
let notify_side _ _ _ = ()
76+
let record_destabilized_vs = false
77+
let veto_widen _ _ _ _ _ = false
78+
let should_mark_wpoint _ _ _ _ _ _ = true
79+
end
80+
81+
(* On side-effect cycles, this should terminate via the outer `solver` loop. TODO check. *)
82+
(** Never widen side-effects. *)
83+
module Never : S =
84+
functor (S:EqConstrSys) ->
85+
functor (HM:Hashtbl.S with type key = S.v) ->
86+
functor (VS:Set.S with type elt = S.v) ->
87+
struct
88+
type data = unit
89+
90+
let create_data _ _ = ()
91+
let notify_side _ _ _ = ()
92+
let record_destabilized_vs = false
93+
let veto_widen _ _ _ _ _ = false
94+
let should_mark_wpoint _ _ _ _ _ _ = false
95+
end
96+
97+
(** Widening check happens by checking sides.
98+
Only widen if value increases and there has already been a side-effect from the same source *)
99+
module SidesLocal : S =
100+
functor (S:EqConstrSys) ->
101+
functor (HM:Hashtbl.S with type key = S.v) ->
102+
functor (VS:Set.S with type elt = S.v) ->
103+
struct
104+
type data = unit
105+
106+
let create_data _ _ = ()
107+
let notify_side _ _ _ = ()
108+
let record_destabilized_vs = false
109+
let veto_widen state called old_sides x y =
110+
match x with
111+
| None -> false
112+
| Some x when VS.mem x old_sides -> false
113+
| _ -> true
114+
let should_mark_wpoint _ _ _ _ _ _ = true
115+
end
116+
117+
(** If there was already a `side x y d` from the same program point and now again, make y a widening point.
118+
Different from `Sides` in that it will not distinguish between side-effects from different contexts,
119+
only the program point matters. *)
120+
module SidesPP : S =
121+
functor (S:EqConstrSys) ->
122+
functor (HM:Hashtbl.S with type key = S.v) ->
123+
functor (VS:Set.S with type elt = S.v) ->
124+
struct
125+
type data = unit
126+
let create_data _ _ = ()
127+
let notify_side _ _ _ = ()
128+
let record_destabilized_vs = false
129+
let veto_widen state called old_sides x y = false
130+
let should_mark_wpoint state called old_sides x y _ = match x with
131+
| Some x ->
132+
let n = S.Var.node x in
133+
VS.exists (fun v -> Node.equal (S.Var.node v) n) old_sides
134+
| None -> false
135+
(* TODO: This is consistent with the previous implementation, but if multiple side-effects come in with x = None,
136+
the leaf will never be widened. This is different from SidesLocal *)
137+
end
138+
139+
(** If there already was a `side x y d` that changed rho[y] and now again, we make y a wpoint.
140+
x caused more than one update to y. >=3 partial context calls will be precise since sides come from different x. TODO this has 8 instead of 5 phases of `solver` for side_cycle.c *)
141+
module Sides : S =
142+
functor (S:EqConstrSys) ->
143+
functor (HM:Hashtbl.S with type key = S.v) ->
144+
functor (VS:Set.S with type elt = S.v) ->
145+
struct
146+
type data = unit
147+
148+
let create_data _ _ = ()
149+
let notify_side _ _ _ = ()
150+
let record_destabilized_vs = false
151+
let veto_widen state called old_sides x y = false
152+
let should_mark_wpoint state called old_sides x y _ = match x with | Some(x) -> VS.mem x old_sides | None -> true
153+
end
154+
155+
(* TODO: The following two don't check if a vs got destabilized which may be a problem. *)
156+
157+
(* TODO test/remove. Check for which examples this is problematic! *)
158+
(** Side to y destabilized itself via some infl-cycle. Records influences from unknowns to globals *)
159+
module UnstableSelf : S =
160+
functor (S:EqConstrSys) ->
161+
functor (HM:Hashtbl.S with type key = S.v) ->
162+
functor (VS:Set.S with type elt = S.v) ->
163+
struct
164+
type data = { is_stable: S.v -> bool; add_infl: S.v -> S.v -> unit }
165+
166+
let create_data is_stable add_infl = { is_stable; add_infl }
167+
let notify_side data x y = (match x with None -> () | Some x -> data.add_infl x y)
168+
let record_destabilized_vs = false
169+
let veto_widen _ _ _ _ _ = false
170+
let should_mark_wpoint state called old_sides x y _ = not (state.is_stable y)
171+
end
172+
173+
(* TODO test/remove. *)
174+
(** Widen if any called var (not just y) is no longer stable. Expensive! *)
175+
module UnstableCalled : S =
176+
functor (S:EqConstrSys) ->
177+
functor (HM:Hashtbl.S with type key = S.v) ->
178+
functor (VS:Set.S with type elt = S.v) ->
179+
struct
180+
type data = { is_stable: S.v -> bool }
181+
182+
let create_data is_stable _ = { is_stable }
183+
let notify_side _ _ _ = ()
184+
let record_destabilized_vs = false
185+
let veto_widen state called old_sides y x = false
186+
let should_mark_wpoint state called old_sides y x _ = HM.exists (fun k _ -> not (state.is_stable k)) called (* this is very expensive since it iterates over called! see https://github.com/goblint/analyzer/issues/265#issuecomment-880748636 *)
187+
end
188+
189+
(** Destabilized a called or start var. Problem: two partial context calls will be precise, but third call will widen the state.
190+
If this side destabilized some of the initial unknowns vs, there may be a side-cycle between vs and we should make y a wpoint *)
191+
module Cycle : S =
192+
functor (S:EqConstrSys) ->
193+
functor (HM:Hashtbl.S with type key = S.v) ->
194+
functor (VS:Set.S with type elt = S.v) ->
195+
struct
196+
type data = unit
197+
198+
let create_data _ _ = ()
199+
let notify_side _ _ _ = ()
200+
let record_destabilized_vs = true
201+
let veto_widen state called old_sides x y = false
202+
let should_mark_wpoint state called old_sides x y cycle =
203+
match cycle with
204+
| Some cycle ->
205+
if tracing && cycle then trace "side_widen" "cycle: should mark wpoint %a" S.Var.pretty_trace y;
206+
cycle
207+
| None ->
208+
failwith "destabilize_vs information not provided to side_widen cycle strategy";
209+
end
210+
211+
let choose_impl: unit -> (module S) = fun () ->
212+
let conf = GobConfig.get_string "solvers.td3.side_widen" in
213+
match conf with
214+
| "always" -> (module Always)
215+
| "never" -> (module Never)
216+
| "sides-local" -> (module SidesLocal)
217+
| "sides" -> (module Sides)
218+
| "sides-pp" -> (module SidesPP)
219+
| "unstable-self" -> (module UnstableSelf)
220+
| "unstable-called" -> (module UnstableCalled)
221+
| "cycle" -> (module Cycle)
222+
| _ -> failwith ("Unknown value '" ^ conf ^ "' for option solvers.td3.side_widen!")

0 commit comments

Comments
 (0)