-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathmCP.ml
More file actions
633 lines (585 loc) · 26.4 KB
/
mCP.ml
File metadata and controls
633 lines (585 loc) · 26.4 KB
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
(** MCP analysis specification. *)
open Batteries
open GoblintCil
open GobConfig
open Analyses
include MCPRegistry
module MCP2 : Analyses.Spec
with module D = DomListLattice (LocalDomainListSpec)
and module G = DomVariantLattice (GlobalDomainListSpec)
and module C = DomListPrintable (ContextListSpec)
and module V = DomVariantSysVar (VarListSpec)
and module P = DomListRepresentative (PathListSpec) =
struct
module D = DomListLattice (LocalDomainListSpec)
module G = DomVariantLattice (GlobalDomainListSpec)
module C = DomListPrintable (ContextListSpec)
module V = DomVariantSysVar (VarListSpec)
module P = DomListRepresentative (PathListSpec)
open List
let v_of n v = (n, Obj.repr v)
let g_of n g = `Lifted (n, Obj.repr g)
let g_to n = function
| `Lifted (n', g) ->
assert (n = n');
g
| `Bot ->
let module S = (val GlobalDomainListSpec.assoc_dom n) in
Obj.repr (S.bot ())
| `Top ->
failwith "MCP2.g_to: top"
let name () = "MCP2"
let path_sens = ref []
let act_cont_sens = ref Set.empty
let base_id = ref (-1)
let topo_sort deps circ_msg =
let rec f res stack = function
| [] -> res
| x::xs when mem x stack -> circ_msg x
| x::xs when mem x res -> f res stack xs
| x::xs -> f (x :: f res (x::stack) (deps x)) stack xs
in rev % f [] []
let topo_sort_an xs =
let msg (x,_) = failwith ("Analyses have circular dependencies, conflict for "^find_spec_name x^".") in
let deps (y,_) = map (fun x -> x, assoc x xs) @@ map find_id @@ (find_spec y).dep in
topo_sort deps msg xs
let check_deps xs =
let check_dep x yn =
let y = find_id yn in
if not (exists (fun (y',_) -> y=y') xs) then begin
let xn = find_spec_name x in
Logs.error "Activated analysis '%s' depends on '%s' and '%s' is not activated." xn yn yn;
raise Stdlib.Exit
end
in
let deps (x,_) = iter (check_dep x) @@ (find_spec x).dep in
iter deps xs
type marshal = Obj.t list
let init marshal =
let map' f =
let f x =
try f x
with Not_found -> raise @@ ConfigError ("Analysis '"^x^"' not found. Abort!")
in
List.map f
in
let xs = get_string_list "ana.activated" in
let xs = map' find_id xs in
base_id := find_id "base";
activated := map (fun s -> s, find_spec s) xs;
path_sens := map' find_id @@ get_string_list "ana.path_sens";
check_deps !activated;
activated := topo_sort_an !activated;
begin
match get_string_list "ana.ctx_sens" with
| [] -> (* use values of "ana.ctx_insens" (blacklist) *)
let cont_inse = map' find_id @@ get_string_list "ana.ctx_insens" in
activated_context_sens := List.filter (fun (n, _) -> not (List.mem n cont_inse)) !activated;
| sens -> (* use values of "ana.ctx_sens" (whitelist) *)
let cont_sens = map' find_id @@ sens in
activated_context_sens := List.filter (fun (n, _) -> List.mem n cont_sens) !activated;
end;
act_cont_sens := Set.of_list (List.map (fun (n,p) -> n) !activated_context_sens);
activated_path_sens := List.filter (fun (n, _) -> List.mem n !path_sens) !activated;
match marshal with
| Some marshal ->
iter2 (fun (_,{spec=(module S:MCPSpec); _}) marshal -> S.init (Some (Obj.obj marshal))) !activated marshal
| None ->
iter (fun (_,{spec=(module S:MCPSpec); _}) -> S.init None) !activated
let finalize () = map (fun (_,{spec=(module S:MCPSpec); _}) -> Obj.repr (S.finalize ())) !activated
let spec x = (find_spec x).spec
let spec_list xs =
map (fun (n,x) -> (n,spec n,x)) xs
let spec_list2 xs ys =
map2 (fun (n,x) (n',y) -> assert (n = n'); (n,spec n,(x,y))) xs ys
let map_deadcode f xs =
let dead = ref false in
let one_el xs (n,(module S:MCPSpec),d) = try f xs (n,(module S:MCPSpec),d) :: xs with Deadcode -> dead:=true; (n,Obj.repr @@ S.D.bot ()) :: xs in
let ys = fold_left one_el [] xs in
List.rev ys, !dead
let exitstate v = map (fun (n,{spec=(module S:MCPSpec); _}) -> n, Obj.repr @@ S.exitstate v) !activated
let startstate v = map (fun (n,{spec=(module S:MCPSpec); _}) -> n, Obj.repr @@ S.startstate v) !activated
let morphstate v x = map (fun (n,(module S:MCPSpec),d) -> n, Obj.repr @@ S.morphstate v (Obj.obj d)) (spec_list x)
let startcontext () =
filter_map (fun (n,{spec=(module S:MCPSpec); _}) ->
if Set.is_empty !act_cont_sens || not (Set.mem n !act_cont_sens) then (*n is insensitive*)
None
else
Some (n, Obj.repr @@ S.startcontext ())
) !activated
let rec assoc_replace (n,c) = function
| [] -> failwith "assoc_replace"
| (n',c')::xs -> if n=n' then (n,c)::xs else (n',c') :: assoc_replace (n,c) xs
(** [assoc_split_eq (=) 1 [(1,a);(1,b);(2,x)] = ([a,b],[(2,x)])] *)
let assoc_split_eq eq (k:'a) (xs:('a * 'b) list) : ('b list) * (('a * 'b) list) =
let rec f a b = function
| [] -> a, b
| (k',v)::xs when eq k k' -> f (v::a) b xs
| x::xs -> f a (x::b) xs
in
f [] [] xs
(** [group_assoc_eq (=) [(1,a);(1,b);(2,x);(2,y)] = [(1,[a,b]),(2,[x,y])]] *)
let group_assoc_eq eq (xs: ('a * 'b) list) : ('a * ('b list)) list =
let rec f a = function
| [] -> a
| (k,v)::xs ->
let a', b = assoc_split_eq eq k xs in
f ((k,v::a')::a) b
in f [] xs
let do_spawns man (xs:(varinfo * (lval option * exp list * bool)) list) =
let spawn_one v d =
List.iter (fun (lval, args, multiple) -> man.spawn ~multiple lval v args) d
in
if get_bool "exp.single-threaded" then
M.msg_final Error ~category:Unsound "Thread not spawned" (* TODO: non-final error *) (* TODO: only final error if xs is non-empty *)
else
iter (uncurry spawn_one) @@ group_assoc_eq Basetype.Variables.equal xs
let do_sideg man (xs:(V.t * (WideningTokenLifter.TS.t * G.t)) list) =
let side_one v dts =
let side_one_ts ts d =
(* Do side effects with the tokens that were active at the time.
Transfer functions have exited the with_side_token wrappers by now. *)
let old_side_tokens = !WideningTokenLifter.side_tokens in
WideningTokenLifter.side_tokens := ts;
Fun.protect (fun () ->
man.sideg v @@ fold_left G.join (G.bot ()) d
) ~finally:(fun () ->
WideningTokenLifter.side_tokens := old_side_tokens
)
in
iter (uncurry side_one_ts) @@ group_assoc_eq WideningTokenLifter.TS.equal dts
in
iter (uncurry side_one) @@ group_assoc_eq V.equal xs
let rec do_splits man pv (xs:(int * (Obj.t * Events.t list)) list) emits =
let split_one n (d,emits') =
let nv = assoc_replace (n,d) pv in
(* Do split-specific emits before general emits.
[emits] and [do_emits] are in reverse order.
[emits'] is in normal order. *)
man.split (do_emits man (emits @ List.rev emits') nv false) []
in
iter (uncurry split_one) xs
and do_emits man emits xs dead =
let oman = man in
let man_with_local man local' =
(* let rec man' =
{ man with
local = local';
ask = ask
}
and ask q = query man' q
in
man' *)
{man with local = local'}
in
let do_emit man = function
| Events.SplitBranch (exp, tv) ->
man_with_local man (branch man exp tv)
| Events.Assign {lval; exp} ->
man_with_local man (assign man lval exp)
| e ->
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in (* why do we need to collect these instead of calling man.sideg directly? *)
let emits = ref [] in
let man'' = outer_man "do_emits" ~spawns ~sides ~emits man in
let oman'' = outer_man "do_emits" ~spawns ~sides ~emits oman in
let f post_all (n,(module S:MCPSpec),(d,od)) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "do_emits" ~splits ~post_all man'' n d in
let oman' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "do_emits" ~splits ~post_all oman'' n od in
n, Obj.repr @@ S.event man' e oman'
in
if M.tracing then M.traceli "event" "%a\n before: %a" Events.pretty e D.pretty man.local;
let d, q = map_deadcode f @@ spec_list2 man.local oman.local in
if M.tracing then M.traceu "event" "%a\n after:%a" Events.pretty e D.pretty d;
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else man_with_local man d
in
if M.tracing then M.traceli "event" "do_emits:";
let emits =
if dead then
List.filter Events.emit_on_deadcode emits
else
emits
in
(* [emits] is in reverse order. *)
let man' = List.fold_left do_emit (man_with_local man xs) (List.rev emits) in
if M.tracing then M.traceu "event" "";
man'.local
and context man fd x =
let man'' = outer_man "context_computation" man in
let x = spec_list x in
filter_map (fun (n,(module S:MCPSpec),d) ->
if Set.is_empty !act_cont_sens || not (Set.mem n !act_cont_sens) then (*n is insensitive*)
None
else
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "context_computation" man'' n d in
Some (n, Obj.repr @@ S.context man' fd (Obj.obj d))
) x
and branch (man:(D.t, G.t, C.t, V.t) man) (e:exp) (tv:bool) =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in (* why do we need to collect these instead of calling man.sideg directly? *)
let emits = ref [] in
let man'' = outer_man "branch" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "branch" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.branch man' e tv
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
(* Explicitly polymorphic type required here for recursive GADT call in ask. *)
and query': type a. querycache:Obj.t Queries.Hashtbl.t -> Queries.Set.t -> (D.t, G.t, C.t, V.t) man -> a Queries.t -> a Queries.result = fun ~querycache asked man q ->
let anyq = Queries.Any q in
if M.tracing then M.traceli "query" "query %a" Queries.Any.pretty anyq;
let r = match Queries.Hashtbl.find_option querycache anyq with
| Some r ->
if M.tracing then M.trace "query" "cached";
Obj.obj r
| None ->
let module Result = (val Queries.Result.lattice q) in
if Queries.Set.mem anyq asked then (
if M.tracing then M.trace "query" "cycle";
Result.top () (* query cycle *)
)
else
let asked' = Queries.Set.add anyq asked in
let sides = ref [] in
let man'' = outer_man "query" ~sides man in
let f ~q a (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "query" man'' n d in
(* sideg is discouraged in query, because they would bypass sides grouping in other transfer functions.
See https://github.com/goblint/analyzer/pull/214. *)
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man =
{ man' with
ask = (fun (type b) (q: b Queries.t) -> query' ~querycache asked' man q)
}
in
(* meet results so that precision from all analyses is combined *)
let res = S.query man' q in
if M.tracing then M.trace "queryanswers" "analysis %s query %a -> answer %a" (S.name ()) Queries.Any.pretty anyq Result.pretty res;
Result.meet a @@ res
in
match q with
| Queries.WarnGlobal g ->
(* WarnGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *)
let (n, g): V.t = Obj.obj g in
f ~q:(WarnGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n man.local)
| Queries.InvariantGlobal g ->
(* InvariantGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *)
let (n, g): V.t = Obj.obj g in
f ~q:(InvariantGlobal (Obj.repr g)) (Result.top ()) (n, spec n, assoc n man.local)
| Queries.YamlEntryGlobal (g, task) ->
(* YamlEntryGlobal is special: it only goes to corresponding analysis and the argument variant is unlifted for it *)
let (n, g): V.t = Obj.obj g in
f ~q:(YamlEntryGlobal (Obj.repr g, task)) (Result.top ()) (n, spec n, assoc n man.local)
| Queries.PartAccess a ->
Obj.repr (access man a)
| Queries.IterSysVars (vq, fi) ->
(* IterSysVars is special: argument function is lifted for each analysis *)
iter (fun ((n,(module S:MCPSpec),d) as t) ->
let fi' x = fi (Obj.repr (v_of n x)) in
let q' = Queries.IterSysVars (vq, fi') in
f ~q:q' () t
) @@ spec_list man.local
(* | EvalInt e ->
(* TODO: only query others that actually respond to EvalInt *)
(* 2x speed difference on SV-COMP nla-digbench-scaling/ps6-ll_valuebound5.c *)
f (Result.top ()) (!base_id, spec !base_id, assoc !base_id man.local) *)
| Queries.DYojson ->
`Lifted (D.to_yojson man.local)
| Queries.GasExhausted f ->
if (get_int "ana.context.gas_value" >= 0) then
(* There is a lifter above this that will answer it, save to ask *)
man.ask (Queries.GasExhausted f)
else
(* Abort to avoid infinite recursion *)
false
| _ ->
let r = fold_left (f ~q) (Result.top ()) @@ spec_list man.local in
do_sideg man !sides;
Queries.Hashtbl.replace querycache anyq (Obj.repr r);
r
in
if M.tracing then (
let module Result = (val Queries.Result.lattice q) in
M.traceu "query" "-> %a" Result.pretty r
);
r
and query: type a. (D.t, G.t, C.t, V.t) man -> a Queries.t -> a Queries.result = fun man q ->
let querycache = Queries.Hashtbl.create 13 in
query' ~querycache Queries.Set.empty man q
and access (man:(D.t, G.t, C.t, V.t) man) a: MCPAccess.A.t =
let man'' = outer_man "access" man in
let f (n, (module S: MCPSpec), d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "access" man'' n d in
(n, Obj.repr (S.access man' a))
in
BatList.map f (spec_list man.local) (* map without deadcode *)
and outer_man tfname ?spawns ?sides ?emits man =
let spawn = match spawns with
| Some spawns -> (fun ?(multiple=false) l v a -> spawns := (v,(l,a,multiple)) :: !spawns)
| None -> (fun ?(multiple=false) v d -> failwith ("Cannot \"spawn\" in " ^ tfname ^ " context."))
in
let sideg = match sides with
| Some sides -> (fun v g -> sides := (v, (!WideningTokenLifter.side_tokens, g)) :: !sides)
| None -> (fun v g -> failwith ("Cannot \"sideg\" in " ^ tfname ^ " context."))
in
let emit = match emits with
| Some emits -> (fun e -> emits := e :: !emits) (* [emits] is in reverse order. *)
| None -> (fun _ -> failwith ("Cannot \"emit\" in " ^ tfname ^ " context."))
in
let querycache = Queries.Hashtbl.create 13 in
(* TODO: make rec? *)
{ man with
ask = (fun (type a) (q: a Queries.t) -> query' ~querycache Queries.Set.empty man q)
; emit
; spawn
; sideg
}
(* Explicitly polymorphic type required here for recursive call in branch. *)
and inner_man: type d g c v. string -> ?splits:(int * (Obj.t * Events.t list)) list ref -> ?post_all:(int * Obj.t) list -> (D.t, G.t, C.t, V.t) man -> int -> Obj.t -> (d, g, c, v) man = fun tfname ?splits ?(post_all=[]) man n d ->
let split = match splits with
| Some splits -> (fun d es -> splits := (n,(Obj.repr d,es)) :: !splits)
| None -> (fun _ _ -> failwith ("Cannot \"split\" in " ^ tfname ^ " context."))
in
{ man with
local = Obj.obj d
; context = (fun () -> man.context () |> assoc n |> Obj.obj)
; global = (fun v -> man.global (v_of n v) |> g_to n |> Obj.obj)
; split
; sideg = (fun v g -> man.sideg (v_of n v) (g_of n g))
}
and assign (man:(D.t, G.t, C.t, V.t) man) l e =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "assign" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "assign" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.assign man' l e
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let vdecl (man:(D.t, G.t, C.t, V.t) man) v =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "vdecl" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "vdecl" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.vdecl man' v
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let body (man:(D.t, G.t, C.t, V.t) man) f =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "body" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "body" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.body man' f
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let return (man:(D.t, G.t, C.t, V.t) man) e f =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "return" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "return" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.return man' e f
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let asm (man:(D.t, G.t, C.t, V.t) man) =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "asm" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "asm" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.asm man'
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let skip (man:(D.t, G.t, C.t, V.t) man) =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "skip" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "skip" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.skip man'
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let special (man:(D.t, G.t, C.t, V.t) man) r f a =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "special" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "special" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.special man' r f a
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let sync (man:(D.t, G.t, C.t, V.t) man) reason =
let spawns = ref [] in
let splits = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "sync" ~spawns ~sides ~emits man in
let f post_all (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "sync" ~splits ~post_all man'' n d in
n, Obj.repr @@ S.sync man' reason
in
let d, q = map_deadcode f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
do_splits man d !splits !emits;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let enter (man:(D.t, G.t, C.t, V.t) man) r f a =
let spawns = ref [] in
let sides = ref [] in
let man'' = outer_man "enter" ~spawns ~sides man in
let f (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "enter" man'' n d in
map (fun (c,d) -> ((n, Obj.repr c), (n, Obj.repr d))) @@ S.enter man' r f a
in
let css = map f @@ spec_list man.local in
do_sideg man !sides;
do_spawns man !spawns;
map (fun xs -> (topo_sort_an @@ map fst xs, topo_sort_an @@ map snd xs)) @@ n_cartesian_product css
let combine_env (man:(D.t, G.t, C.t, V.t) man) r fe f a fc fd f_ask =
let spawns = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "combine_env" ~spawns ~sides ~emits man in
(* Like spec_list2 but for three lists. Tail recursion like map3_rev would have.
Due to context-insensitivity, second list is optional and may only contain a subset of analyses
in the same order, so some skipping needs to happen to align the three lists.
See https://github.com/goblint/analyzer/pull/578/files#r794376508. *)
let rec spec_list3_rev_acc acc l1 l2_opt l3 = match l1, l2_opt, l3 with
| [], _, [] -> acc
| (n, x) :: l1, Some ((n', y) :: l2), (n'', z) :: l3 when n = n' -> (* context-sensitive *)
assert (n = n'');
spec_list3_rev_acc ((n, spec n, (x, Some y, z)) :: acc) l1 (Some l2) l3
| (n, x) :: l1, l2_opt, (n'', z) :: l3 -> (* context-insensitive *)
assert (n = n'');
spec_list3_rev_acc ((n, spec n, (x, None, z)) :: acc) l1 l2_opt l3
| _, _, _ -> invalid_arg "MCP.spec_list3_rev_acc"
in
let f post_all (n,(module S:MCPSpec),(d,fc,fd)) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "combine_env" ~post_all man'' n d in
n, Obj.repr @@ S.combine_env man' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask
in
let d, q = map_deadcode f @@ List.rev @@ spec_list3_rev_acc [] man.local fc fd in
do_sideg man !sides;
do_spawns man !spawns;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let combine_assign (man:(D.t, G.t, C.t, V.t) man) r fe f a fc fd f_ask =
let spawns = ref [] in
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "combine_assign" ~spawns ~sides ~emits man in
(* Like spec_list2 but for three lists. Tail recursion like map3_rev would have.
Due to context-insensitivity, second list is optional and may only contain a subset of analyses
in the same order, so some skipping needs to happen to align the three lists.
See https://github.com/goblint/analyzer/pull/578/files#r794376508. *)
let rec spec_list3_rev_acc acc l1 l2_opt l3 = match l1, l2_opt, l3 with
| [], _, [] -> acc
| (n, x) :: l1, Some ((n', y) :: l2), (n'', z) :: l3 when n = n' -> (* context-sensitive *)
assert (n = n'');
spec_list3_rev_acc ((n, spec n, (x, Some y, z)) :: acc) l1 (Some l2) l3
| (n, x) :: l1, l2_opt, (n'', z) :: l3 -> (* context-insensitive *)
assert (n = n'');
spec_list3_rev_acc ((n, spec n, (x, None, z)) :: acc) l1 l2_opt l3
| _, _, _ -> invalid_arg "MCP.spec_list3_rev_acc"
in
let f post_all (n,(module S:MCPSpec),(d,fc,fd)) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "combine_assign" ~post_all man'' n d in
n, Obj.repr @@ S.combine_assign man' r fe f a (Option.map Obj.obj fc) (Obj.obj fd) f_ask
in
let d, q = map_deadcode f @@ List.rev @@ spec_list3_rev_acc [] man.local fc fd in
do_sideg man !sides;
do_spawns man !spawns;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let threadenter (man:(D.t, G.t, C.t, V.t) man) ~multiple lval f a =
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "threadenter" ~sides ~emits man in
let f (n,(module S:MCPSpec),d) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadenter" man'' n d in
map (fun d -> (n, Obj.repr d)) @@ (S.threadenter ~multiple) man' lval f a
in
let css = map f @@ spec_list man.local in
do_sideg man !sides;
(* TODO: this do_emits is now different from everything else *)
map (fun d -> do_emits man !emits d false) @@ map topo_sort_an @@ n_cartesian_product css
let threadspawn (man:(D.t, G.t, C.t, V.t) man) ~multiple lval f a fman =
let sides = ref [] in
let emits = ref [] in
let man'' = outer_man "threadspawn" ~sides ~emits man in
let fman'' = outer_man "threadspawn" ~sides ~emits fman in
let f post_all (n,(module S:MCPSpec),(d,fd)) =
let man' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadspawn" ~post_all man'' n d in
let fman' : (S.D.t, S.G.t, S.C.t, S.V.t) man = inner_man "threadspawn" ~post_all fman'' n fd in
n, Obj.repr @@ S.threadspawn ~multiple man' lval f a fman'
in
let d, q = map_deadcode f @@ spec_list2 man.local fman.local in
do_sideg man !sides;
let d = do_emits man !emits d q in
if q then raise Deadcode else d
let event (man:(D.t, G.t, C.t, V.t) man) e _ = do_emits man [e] man.local false
(* Just to satisfy signature *)
let paths_as_set man = [man.local]
end