|
1 | | -(** {{!MonSystem} constraint system} signatures. *) |
| 1 | +(** {{!EqConstrSys} constraint system} signatures. *) |
2 | 2 |
|
3 | 3 | open Batteries |
4 | 4 |
|
|
21 | 21 | val relift : t -> t (* needed only for incremental+hashcons to re-hashcons contexts after loading *) |
22 | 22 | end |
23 | 23 |
|
24 | | -(** Abstract incremental change to constraint system. |
25 | | - @param 'v constrain system variable type *) |
26 | | -type 'v sys_change_info = { |
27 | | - obsolete: 'v list; (** Variables to destabilize. *) |
28 | | - delete: 'v list; (** Variables to delete. *) |
29 | | - reluctant: 'v list; (** Variables to solve reluctantly. *) |
30 | | - restart: 'v list; (** Variables to restart. *) |
31 | | -} |
32 | | - |
33 | | -(** A side-effecting system. *) |
34 | | -module type MonSystem = |
35 | | -sig |
36 | | - type v (* variables *) |
37 | | - type d (* values *) |
38 | | - type 'a m (* basically a monad carrier *) |
39 | | - |
40 | | - (** Variables must be hashable, comparable, etc. *) |
41 | | - module Var : VarType with type t = v |
42 | | - |
43 | | - (** Values must form a lattice. *) |
44 | | - module Dom : Lattice.S with type t = d |
45 | | - |
46 | | - (** The system in functional form. *) |
47 | | - val system : v -> ((v -> d) -> (v -> d -> unit) -> d) m |
48 | | - |
49 | | - (** Compute incremental constraint system change from old solution. *) |
50 | | - val sys_change: (v -> d) -> v sys_change_info |
51 | | - |
52 | | - (** List of unknowns that should be queried again when the argument unknown has shrunk to bot, to eagerly trigger (analysis-time!) abstract garbage collection idependently of reach-based pruning at the end. |
53 | | - @see <https://arxiv.org/abs/2504.06026> Stemmler, F., Schwarz, M., Erhard, J., Tilscher, S., Seidl, H. Taking out the Toxic Trash: Recovering Precision in Mixed Flow-Sensitive Static Analyses *) |
54 | | - val postmortem: v -> v list |
55 | | -end |
56 | | - |
57 | | -(** Any system of side-effecting equations over lattices. *) |
58 | | -module type EqConstrSys = MonSystem with type 'a m := 'a option |
59 | | - |
60 | | -(** A side-effecting system with globals. *) |
61 | | -module type GlobConstrSys = |
62 | | -sig |
63 | | - module LVar : VarType |
64 | | - module GVar : VarType |
65 | | - |
66 | | - module D : Lattice.S |
67 | | - module G : Lattice.S |
68 | | - val system : LVar.t -> ((LVar.t -> D.t) -> (LVar.t -> D.t -> unit) -> (GVar.t -> G.t) -> (GVar.t -> G.t -> unit) -> D.t) option |
69 | | - val iter_vars: (LVar.t -> D.t) -> (GVar.t -> G.t) -> VarQuery.t -> LVar.t VarQuery.f -> GVar.t VarQuery.f -> unit |
70 | | - val sys_change: (LVar.t -> D.t) -> (GVar.t -> G.t) -> [`L of LVar.t | `G of GVar.t] sys_change_info |
71 | | - val postmortem: LVar.t -> LVar.t list |
72 | | -end |
73 | | - |
74 | | -(** A solver is something that can translate a system into a solution (hash-table). |
75 | | - Incremental solver has data to be marshaled. *) |
76 | | -module type GenericEqIncrSolverBase = |
77 | | - functor (S:EqConstrSys) -> |
78 | | - functor (H:Hashtbl.S with type key=S.v) -> |
79 | | - sig |
80 | | - type marshal |
81 | | - |
82 | | - val copy_marshal: marshal -> marshal |
83 | | - val relift_marshal: marshal -> marshal |
84 | | - |
85 | | - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], |
86 | | - reached from starting values [xs]. |
87 | | - As a second component the solver returns data structures for incremental serialization. *) |
88 | | - val solve : (S.v*S.d) list -> S.v list -> marshal option -> S.d H.t * marshal |
89 | | - end |
90 | | - |
91 | | -(** (Incremental) solver argument, indicating which postsolving should be performed by the solver. *) |
92 | | -module type IncrSolverArg = |
93 | | -sig |
94 | | - val should_prune: bool |
95 | | - val should_verify: bool |
96 | | - val should_warn: bool |
97 | | - val should_save_run: bool |
98 | | -end |
99 | | - |
100 | | -(** An incremental solver takes the argument about postsolving. *) |
101 | | -module type GenericEqIncrSolver = |
102 | | - functor (Arg: IncrSolverArg) -> |
103 | | - GenericEqIncrSolverBase |
104 | | - |
105 | | -(** A solver is something that can translate a system into a solution (hash-table) *) |
106 | | -module type GenericEqSolver = |
107 | | - functor (S:EqConstrSys) -> |
108 | | - functor (H:Hashtbl.S with type key=S.v) -> |
109 | | - sig |
110 | | - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], |
111 | | - reached from starting values [xs]. *) |
112 | | - val solve : (S.v*S.d) list -> S.v list -> S.d H.t |
113 | | - end |
114 | | - |
115 | | -(** A solver is something that can translate a system into a solution (hash-table) *) |
116 | | -module type GenericGlobSolver = |
117 | | - functor (S:GlobConstrSys) -> |
118 | | - functor (LH:Hashtbl.S with type key=S.LVar.t) -> |
119 | | - functor (GH:Hashtbl.S with type key=S.GVar.t) -> |
120 | | - sig |
121 | | - type marshal |
122 | | - |
123 | | - val copy_marshal: marshal -> marshal |
124 | | - val relift_marshal: marshal -> marshal |
125 | | - |
126 | | - (** The hash-map that is the first component of [solve xs vs] is a local solution for interesting variables [vs], |
127 | | - reached from starting values [xs]. |
128 | | - As a second component the solver returns data structures for incremental serialization. *) |
129 | | - val solve : (S.LVar.t*S.D.t) list -> (S.GVar.t*S.G.t) list -> S.LVar.t list -> marshal option -> (S.D.t LH.t * S.G.t GH.t) * marshal |
130 | | - end |
131 | | - |
132 | | - |
133 | 24 | (** Combined variables so that we can also use the more common [EqConstrSys] |
134 | 25 | that uses only one kind of a variable. *) |
135 | 26 | module Var2 (LV:VarType) (GV:VarType) |
@@ -163,124 +54,52 @@ struct |
163 | 54 | | `G a -> GV.is_write_only a |
164 | 55 | end |
165 | 56 |
|
| 57 | +(** Abstract incremental change to constraint system. |
| 58 | + @param 'v constrain system variable type *) |
| 59 | +type 'v sys_change_info = { |
| 60 | + obsolete: 'v list; (** Variables to destabilize. *) |
| 61 | + delete: 'v list; (** Variables to delete. *) |
| 62 | + reluctant: 'v list; (** Variables to solve reluctantly. *) |
| 63 | + restart: 'v list; (** Variables to restart. *) |
| 64 | +} |
166 | 65 |
|
167 | | -(** Translate a [GlobConstrSys] into a [EqConstrSys] *) |
168 | | -module EqConstrSysFromGlobConstrSys (S:GlobConstrSys) |
169 | | - : EqConstrSys with type v = Var2(S.LVar)(S.GVar).t |
170 | | - and type d = Lattice.Lift2(S.G)(S.D).t |
171 | | - and module Var = Var2(S.LVar)(S.GVar) |
172 | | - and module Dom = Lattice.Lift2(S.G)(S.D) |
173 | | -= |
174 | | -struct |
175 | | - module Var = Var2(S.LVar)(S.GVar) |
176 | | - module Dom = |
177 | | - struct |
178 | | - include Lattice.Lift2 (S.G) (S.D) |
179 | | - let printXml f = function |
180 | | - | `Lifted1 a -> S.G.printXml f a |
181 | | - | `Lifted2 a -> S.D.printXml f a |
182 | | - | (`Bot | `Top) as x -> printXml f x |
183 | | - end |
184 | | - type v = Var.t |
185 | | - type d = Dom.t |
186 | | - |
187 | | - let getG = function |
188 | | - | `Lifted1 x -> x |
189 | | - | `Bot -> S.G.bot () |
190 | | - | `Top -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has top value" |
191 | | - | `Lifted2 _ -> failwith "EqConstrSysFromGlobConstrSys.getG: global variable has local value" |
192 | | - |
193 | | - let getL = function |
194 | | - | `Lifted2 x -> x |
195 | | - | `Bot -> S.D.bot () |
196 | | - | `Top -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has top value" |
197 | | - | `Lifted1 _ -> failwith "EqConstrSysFromGlobConstrSys.getL: local variable has global value" |
198 | | - |
199 | | - let l, g = (fun x -> `L x), (fun x -> `G x) |
200 | | - let lD, gD = (fun x -> `Lifted2 x), (fun x -> `Lifted1 x) |
| 66 | +(** A side-effecting system. *) |
| 67 | +module type EqConstrSys = |
| 68 | +sig |
| 69 | + type v (* variables *) |
| 70 | + type d (* values *) |
201 | 71 |
|
202 | | - let conv f get set = |
203 | | - f (getL % get % l) (fun x v -> set (l x) (lD v)) |
204 | | - (getG % get % g) (fun x v -> set (g x) (gD v)) |
205 | | - |> lD |
| 72 | + (** Variables must be hashable, comparable, etc. *) |
| 73 | + module Var : VarType with type t = v |
206 | 74 |
|
207 | | - let system = function |
208 | | - | `G _ -> None |
209 | | - | `L x -> Option.map conv (S.system x) |
| 75 | + (** Values must form a lattice. *) |
| 76 | + module Dom : Lattice.S with type t = d |
210 | 77 |
|
211 | | - let sys_change get = |
212 | | - S.sys_change (getL % get % l) (getG % get % g) |
| 78 | + (** The system in functional form. *) |
| 79 | + val system : v -> ((v -> d) -> (v -> d -> unit) -> d) option |
213 | 80 |
|
214 | | - let postmortem = function |
215 | | - | `L g -> List.map (fun x -> `L x) @@ S.postmortem g |
216 | | - | _ -> [] |
217 | | -end |
| 81 | + (** Compute incremental constraint system change from old solution. *) |
| 82 | + val sys_change: (v -> d) -> v sys_change_info |
218 | 83 |
|
219 | | -(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution with given [Hashtbl.S] for the [EqConstrSys]. *) |
220 | | -module GlobConstrSolFromEqConstrSolBase (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) (VH: Hashtbl.S with type key = Var2 (S.LVar) (S.GVar).t) = |
221 | | -struct |
222 | | - let split_solution hm = |
223 | | - let l' = LH.create 113 in |
224 | | - let g' = GH.create 113 in |
225 | | - let split_vars x d = match x with |
226 | | - | `L x -> |
227 | | - begin match d with |
228 | | - | `Lifted2 d -> LH.replace l' x d |
229 | | - (* | `Bot -> () *) |
230 | | - (* Since Verify2 is broken and only checks existing keys, add it with local bottom value. |
231 | | - This works around some cases, where Verify2 would not detect a problem due to completely missing variable. *) |
232 | | - | `Bot -> LH.replace l' x (S.D.bot ()) |
233 | | - | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has top value" |
234 | | - | `Lifted1 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: local variable has global value" |
235 | | - end |
236 | | - | `G x -> |
237 | | - begin match d with |
238 | | - | `Lifted1 d -> GH.replace g' x d |
239 | | - | `Bot -> () |
240 | | - | `Top -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has top value" |
241 | | - | `Lifted2 _ -> failwith "GlobConstrSolFromEqConstrSolBase.split_vars: global variable has local value" |
242 | | - end |
243 | | - in |
244 | | - VH.iter split_vars hm; |
245 | | - (l', g') |
| 84 | + (** List of unknowns that should be queried again when the argument unknown has shrunk to bot, to eagerly trigger (analysis-time!) abstract garbage collection idependently of reach-based pruning at the end. |
| 85 | + @see <https://arxiv.org/abs/2504.06026> Stemmler, F., Schwarz, M., Erhard, J., Tilscher, S., Seidl, H. Taking out the Toxic Trash: Recovering Precision in Mixed Flow-Sensitive Static Analyses *) |
| 86 | + val postmortem: v -> v list |
246 | 87 | end |
247 | 88 |
|
248 | | -(** Splits a [EqConstrSys] solution into a [GlobConstrSys] solution. *) |
249 | | -module GlobConstrSolFromEqConstrSol (S: GlobConstrSys) (LH: Hashtbl.S with type key = S.LVar.t) (GH: Hashtbl.S with type key = S.GVar.t) = |
250 | | -struct |
251 | | - module S2 = EqConstrSysFromGlobConstrSys (S) |
252 | | - module VH = Hashtbl.Make (S2.Var) |
| 89 | +(** A side-effecting system with globals. *) |
| 90 | +module type GlobConstrSys = |
| 91 | +sig |
| 92 | + module LVar : VarType |
| 93 | + module GVar : VarType |
253 | 94 |
|
254 | | - include GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) |
| 95 | + module D : Lattice.S |
| 96 | + module G : Lattice.S |
| 97 | + val system : LVar.t -> ((LVar.t -> D.t) -> (LVar.t -> D.t -> unit) -> (GVar.t -> G.t) -> (GVar.t -> G.t -> unit) -> D.t) option |
| 98 | + val iter_vars: (LVar.t -> D.t) -> (GVar.t -> G.t) -> VarQuery.t -> LVar.t VarQuery.f -> GVar.t VarQuery.f -> unit |
| 99 | + val sys_change: (LVar.t -> D.t) -> (GVar.t -> G.t) -> [`L of LVar.t | `G of GVar.t] sys_change_info |
| 100 | + val postmortem: LVar.t -> LVar.t list |
255 | 101 | end |
256 | 102 |
|
257 | | -(** Transforms a [GenericEqIncrSolver] into a [GenericGlobSolver]. *) |
258 | | -module GlobSolverFromEqSolver (Sol:GenericEqIncrSolverBase) |
259 | | - = functor (S:GlobConstrSys) -> |
260 | | - functor (LH:Hashtbl.S with type key=S.LVar.t) -> |
261 | | - functor (GH:Hashtbl.S with type key=S.GVar.t) -> |
262 | | - struct |
263 | | - module EqSys = EqConstrSysFromGlobConstrSys (S) |
264 | | - |
265 | | - module VH : Hashtbl.S with type key=EqSys.v = Hashtbl.Make(EqSys.Var) |
266 | | - module Sol' = Sol (EqSys) (VH) |
267 | | - |
268 | | - module Splitter = GlobConstrSolFromEqConstrSolBase (S) (LH) (GH) (VH) (* reuse EqSys and VH *) |
269 | | - |
270 | | - type marshal = Sol'.marshal |
271 | | - |
272 | | - let copy_marshal = Sol'.copy_marshal |
273 | | - let relift_marshal = Sol'.relift_marshal |
274 | | - |
275 | | - let solve ls gs l old_data = |
276 | | - let vs = List.map (fun (x,v) -> `L x, `Lifted2 v) ls |
277 | | - @ List.map (fun (x,v) -> `G x, `Lifted1 v) gs in |
278 | | - let sv = List.map (fun x -> `L x) l in |
279 | | - let hm, solver_data = Sol'.solve vs sv old_data in |
280 | | - Splitter.split_solution hm, solver_data |
281 | | - end |
282 | | - |
283 | | - |
284 | 103 | (** [EqConstrSys] where [current_var] indicates the variable whose right-hand side is currently being evaluated. *) |
285 | 104 | module CurrentVarEqConstrSys (S: EqConstrSys) = |
286 | 105 | struct |
|
0 commit comments