This repository has been archived by the owner on Sep 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflowInference.ml
2074 lines (1876 loc) · 73.6 KB
/
flowInference.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
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
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
open Ast
open Sexplib.Std
open Std
open Std.StateMonad
open SimpleTypes
module SM = StringMap
module SS = StringSet
module P = Paths
module OI = OwnershipInference
module RT = RefinementTypes
open (val Log.located ~where:"FLOW" : Log.LocatedD) [@@ocaml.warning "-33"]
type z3_types =
| ZBool
| ZInt [@@deriving sexp]
type relation_source =
| Expr of int
| Fun of string * [`In | `Out]
| FunMu of string * [`In | `Out] * P.concr_ap
| ExprMu of int * P.concr_ap * [`Null | `Join | `Flow]
| AliasUnfold of int
| Start [@@deriving sexp]
type flow =
| Havoc of P.concr_ap
| Copy of P.concr_ap * P.concr_ap
| Const of P.concr_ap * int
| NullConst of P.concr_ap * bool
type relation = string * (P.concr_ap * z3_types) list * relation_source [@@deriving sexp]
type concr_arg =
| Ap of P.concr_ap
| BConst of bool
| IConst of int
| NondetChoice of concr_arg list
| KeyedChoice of P.concr_ap * concr_arg * concr_arg [@@deriving sexp]
type clause =
| PRelation of relation * ((P.concr_ap * concr_arg) list) * int option
| Relation of (concr_arg,concr_arg) RT.relation * z3_types
| NamedRel of string * (concr_arg list)
| NullCons of concr_arg * concr_arg
type fltype = [
| `IntArray
| `Ref of bool * fltype
| `Int
| `Tuple of fltype list
| `Mu of fltype
| `TVar
]
let rec fltype_to_string = function
| `IntArray -> "[int]"
| `Mu t -> Printf.sprintf "(%s %s.%s)" Greek.mu Greek.alpha @@ fltype_to_string t
| `Ref (_,t) -> Printf.sprintf "%s ref" @@ fltype_to_string t
| `Int -> "int"
| `Tuple tl -> Printf.sprintf "(%s)" @@ String.concat ", " @@ List.map fltype_to_string tl
| `TVar -> Greek.alpha
type funtype = fltype _funtyp
type recursive_ref_map = relation P.PathMap.t
type function_info = {
in_rel : relation;
out_rel : relation;
f_type: funtype;
in_recursive_rel: recursive_ref_map;
out_recursive_rel : recursive_ref_map;
}
type state_snapshot = {
mu_relations : recursive_ref_map;
gamma : (string * fltype) list;
relation : relation
}
type ctxt = {
relations: relation list;
impl: (clause list * clause) list;
o_hints: float OI.ownership_ops;
fenv: function_info SM.t;
curr_fun : string option;
let_types: fltype IntMap.t;
bif_types : RefinementTypes.funtype SM.t;
havoc_set : P.PathSet.t;
unfold_iso: IntSet.t;
fold_iso: IntSet.t;
recursive_rel : recursive_ref_map;
snapshots : state_snapshot IntMap.t
}
let rec unfold_fltype subst = function
| `TVar -> subst
| `Mu _ -> assert false
| `Int -> `Int
| `Ref (false,t) -> `Ref (false,unfold_fltype subst t)
| `Ref (true,_) -> failwith "Already unfolded ref under a mu binder?!?!?"
| `Tuple tl -> `Tuple (List.map (unfold_fltype subst) tl)
| `IntArray -> `IntArray
let rec deep_type_normalization = function
| `Mu (`Ref (false,t)) ->
`Ref (true,unfold_fltype (`Mu (`Ref (false,t))) t)
| `Mu (`Ref (true,_)) -> failwith "broken invariant"
| `Mu _ -> assert false
| `Int -> `Int
| `Tuple tl -> `Tuple (List.map deep_type_normalization tl)
| `Ref (b,t) -> `Ref (b, deep_type_normalization t)
| `IntArray -> `IntArray
| `TVar -> assert false
let rec simple_to_fltype ?tvar = function
| `Mu (id,t) ->
assert (tvar = None);
`Mu (simple_to_fltype ~tvar:id t)
| `Int -> `Int
| `Array `Int -> `IntArray
| `Ref t -> `Ref (false, simple_to_fltype ?tvar t)
| `TVar id ->
assert (Option.map ((=) id) tvar |> Option.value ~default:false);
`TVar
| `Tuple tl -> `Tuple (List.map (simple_to_fltype ?tvar) tl)
let%lq get_function_type f_name ctxt =
let { f_type; _ } = StringMap.find f_name ctxt.fenv in
f_type
let%lq get_in_relation f_name ctxt =
let { in_rel = ir; _} = StringMap.find f_name ctxt.fenv in
ir
let%lq get_out_relation f_name ctxt =
let { out_rel = o_rel; _ } = StringMap.find f_name ctxt.fenv in
o_rel
let%lq get_function_info f_name ctxt =
StringMap.find f_name ctxt.fenv
let%lq copy_state ctxt = ctxt
let%lm set_havoc_state havoc_state ctxt = { ctxt with havoc_set = havoc_state }
let%lq get_havoc_state ctxt = ctxt.havoc_set
let%lq get_bound_type e_id ctxt = IntMap.find e_id ctxt.let_types
let mk_relation lhs op rhs = RT.({
rel_op1 = lhs;
rel_cond = op;
rel_op2 = rhs
})
let rel ~ty k = Relation (k,ty)
(* TODO: make this function actually useful... *)
let path_type p = if P.is_nullity p then ZBool else ZInt
let%lm add_assert op1 cond op2 curr_relation ctxt =
let ante = [ PRelation (curr_relation,[],None) ] in
let relation = rel ~ty:ZInt @@ mk_relation op1 cond op2 in
{ ctxt with impl = (ante,relation)::ctxt.impl }
let add_assert_cond assert_cond curr_relation =
let lift_to_imm = function
| IVar v -> Ap (P.var v)
| IInt i -> IConst i
in
add_assert
(lift_to_imm assert_cond.rop1)
assert_cond.cond
(lift_to_imm assert_cond.rop2)
curr_relation
let rec havoc_oracle ctxt ml p =
Log.debug ~src:"FLOW-OWN" !"Looking for %{P} @ %{sexp:OI.magic_loc}" p ml;
let from_path p_ =
let o = OI.GenMap.find (ml,p_) ctxt.o_hints.OI.gen in
o = 0.0
in
match P.tail p with
| Some `Deref
| Some `Ind
| Some `Elem -> from_path @@ P.parent p
| Some _ -> havoc_oracle ctxt ml @@ P.parent p
| None -> false
let%lq split_oracle sl ctxt =
let from_path p =
Log.debug ~src:"FLOW-OWN" !"Splitting %{P} @ %{sexp:OI.split_loc}" p sl;
let (f1,f2) = OI.SplitMap.find (sl,p) ctxt.o_hints.OI.splits in
let to_flag b = if b then `Havoc else `Stable in
(to_flag (f1 = 0.0), to_flag (f2 = 0.0))
in
let rec loop p1 p2 =
match (P.tail p1),(P.tail p2) with
| None,_ -> (`Trivial,`Trivial)
| _,None -> (`Trivial, `Trivial)
| Some a,Some b ->
let () = assert (a = b) in
if a = `Deref || a = `Ind || a = `Elem then
from_path @@ P.parent p1
else
loop (P.parent p1) (P.parent p2)
in
loop
let%lq gen_for_alias e_id ctxt =
havoc_oracle ctxt (OI.MAlias e_id)
let%lq gen_oracle ml ctxt =
havoc_oracle ctxt ml
let rec lift_refinement ?(map=Fun.id) ?nu_arg =
let lift_symbolic_ap = function
| `Sym _ -> assert false
| `Concr cap -> map cap
in
let lift_symbolic_imm = function
| RT.RConst i -> IConst i
| RT.RAp p -> Ap (lift_symbolic_ap p)
in
RT.(function
| Top -> []
| And (r1, r2) -> lift_refinement ~map ?nu_arg r1 @ lift_refinement ~map ?nu_arg r2
| ConstEq i ->
[ rel ~ty:ZInt @@ mk_relation (Ap (Option.get nu_arg)) "=" (IConst i) ]
| Relation r when r.rel_op1 = Nu ->
[ rel ~ty:ZInt { r with rel_op1 = Ap (Option.get nu_arg); rel_op2 = lift_symbolic_imm r.rel_op2 } ]
| Relation ({ rel_op1 = RImm i; _ } as r) ->
[ rel ~ty:ZInt { r with rel_op1 = (lift_symbolic_imm i); rel_op2 = lift_symbolic_imm r.rel_op2 } ]
| NamedPred (nm,sym_names) ->
let nu_arg = Option.get nu_arg in
let named_args = List.map lift_symbolic_ap sym_names in
let val_args = List.map (fun l -> Ap l) @@ nu_arg::named_args in
[ NamedRel (nm,val_args) ]
| _ -> failwith "Refinement form not supported")
let path_simple_type tyenv path =
let rec loop (path: P.path) k =
match P.tail path with
| None -> begin
match (path : P.path :> P.root * 'a * 'b) with
| (P.Var v,_,_) -> k @@ List.assoc v tyenv
| _ -> failwith "not supported"
end
| Some `Deref ->
loop (P.parent path) (function
| `Ref (_,t) -> k t
| t -> failwith @@ Printf.sprintf "Unexpected simple type %s for path %s" (fltype_to_string t) (P.to_z3_ident path)
)
| Some (`Proj i) ->
loop (P.parent path) (function
| `Tuple tl -> List.nth tl i
| _ -> assert false
)
| _ -> failwith "Not supported"
in
loop path (fun i -> i)
let is_null_flag = true
let is_nonnull_flag = false
let null_pre (_,args,_) subst =
let subst_ap p =
if List.mem_assoc p subst then
List.assoc p subst
else
Ap p
in
let rec parent_null_loop p =
match P.tail p with
| None -> None
| Some `Deref ->
Some (P.to_null @@ P.parent p)
| Some _ -> parent_null_loop @@ P.parent p
in
List.filter_map (fun (p,_) ->
if not @@ P.is_nullity p then
None
else
P.parent p
|> parent_null_loop
|> Option.map (fun parent ->
(NullCons (subst_ap parent, subst_ap p))
)
) args
let to_havoc d = Printf.sprintf "havoc!%d!%s" d
let havoc_ap d p =
P.map_root (to_havoc d) @@
if P.is_template p then
P.root_at ~child:p ~parent:(P.var "$tmp")
else
p
let to_pass_ap = P.map_root (fun s -> "pass!" ^ s)
let lift_copy (s,d) = (s, Ap d)
let%lm add_implication ante conseq ctxt =
{ctxt with impl = (ante,conseq)::ctxt.impl }
let flow_to_subst i = function
| Havoc p -> p,Ap (havoc_ap i p)
| Const (p,c) -> (p,IConst c)
| Copy (p1,p2) -> (p2,Ap p1)
| NullConst (p,b) -> (p,BConst b)
let%lm add_relation_flow ?out_ctxt ?(pre=[]) subst in_rel out_rel ctxt =
let lifted_subst = List.mapi flow_to_subst subst in
let ante = PRelation (in_rel,[],None)::pre in
let conseq = PRelation (out_rel,lifted_subst,out_ctxt) in
{
ctxt with impl = ((ante,conseq)::ctxt.impl)
}
let type_to_paths ?(pre=false) root (ty: fltype) =
let add_path pre under_ref under_mu acc p =
let paths =
(if under_ref && not under_mu && pre then
[P.pre p] else []) @
[p]
in
paths @ acc
in
let rec loop under_ref under_mu acc p = function
| `Int -> add_path pre under_ref under_mu acc p
| `Ref (_,t) ->
let acc = add_path pre under_ref under_mu acc (P.to_null p) in
loop true under_mu acc (P.deref p) t
| `Tuple tl ->
fold_lefti (fun i acc t ->
loop under_ref under_mu acc (P.t_ind p i) t
) acc tl
| `IntArray ->
List.fold_left (add_path false under_ref under_mu) acc [(P.len p);(P.ind p);(P.elem p)]
| `Mu t -> loop under_ref true acc p t
| `TVar -> acc
in
loop false false [] root ty
let path_step k p = match k with
| `Ind -> P.ind p
| `Length -> P.len p
| `Elem -> P.elem p
| `Tuple i -> P.t_ind p i
| `Ref -> P.deref p
| `Null -> P.to_null p
(* pseudo-path elements *)
| `Mu -> p
| `Summ -> p
| `Array -> p
let rec walk_type ty step f st acc =
let continue ~acc mst k =
match mst with
| `K f_ -> f_ ty acc
| `ContWith (f,st) -> let acc = f ty acc in k st acc
| `Cont st -> k st acc
in
match ty with
| `Int -> f st acc
| `Ref (false, t) ->
let mst = step `Null st in
continue ~acc mst (fun st' acc ->
let acc = f st' acc in
let mst = step `Ref st in
continue ~acc mst (walk_type t step f)
)
| `Ref (true,t) ->
let mst = step `Null st in
continue ~acc mst (fun st' acc ->
let acc = f st' acc in
let mst = step `Summ st in
continue ~acc mst (fun st' acc ->
let mst = step `Ref st' in
continue ~acc mst (walk_type t step f)
)
)
| `Tuple tl ->
fold_lefti (fun i acc t ->
let mst = step (`Tuple i) st in
continue ~acc mst (walk_type t step f)
) acc tl
| `TVar -> acc
| `Mu t ->
let mst = step `Mu st in
continue ~acc mst (walk_type t step f)
| `IntArray ->
let mst = step `Length st in
continue ~acc mst (fun st' acc ->
let acc = f st' acc in
let mst = step `Array st in
continue ~acc mst (fun st acc ->
let mst = step `Ind st in
continue ~acc mst (fun st' acc ->
let acc = f st' acc in
let mst = step `Elem st in
continue ~acc mst f
)
)
)
let parallel_type_walk in_ap out_ap ty step f st acc =
walk_type ty (fun k (in_ap,out_ap,st) ->
let user_step constr kind =
let mst = step kind (in_ap,out_ap) st in
match mst with
| `K f -> `K f
| `ContWith (f,st) -> `ContWith (f,(constr in_ap, constr out_ap, st))
| `Cont st -> `Cont (constr in_ap,constr out_ap,st)
| `ContPath (in_ap,out_ap, st) -> `Cont (in_ap, out_ap, st)
in
let parallel_step constr =
`Cont (constr in_ap, constr out_ap, st)
in
match k with
| `Ref -> user_step P.deref `Ref
| `Mu -> user_step Fun.id `Mu
| `Array -> user_step Fun.id `Array
| `Ind -> parallel_step P.ind
| `Elem -> parallel_step P.elem
| `Length -> parallel_step P.len
| `Tuple i -> parallel_step ((Fun.flip P.t_ind) i)
| `Null -> parallel_step P.to_null
| `Summ -> user_step Fun.id `Summ
) (fun (in_ap, out_ap, st) acc ->
f st in_ap out_ap acc
) (in_ap, out_ap, st) acc
module RecRelations = struct
module MuChain = struct
let to_concr (s,p) =
Option.fold ~none:s ~some:(fun parent ->
P.root_at ~child:s ~parent
) p
let step_mu (s,p) =
assert (p = None);
(P.template, Some s)
let get_mu_path (s,p) =
Option.map (fun p ->
(s,p)
) p
let raw_stepper k (s,p) = match k with
| `Mu -> `Cont (step_mu (s,p))
| _ -> `Cont (path_step k s, p)
end
let get_recursive_roots root ty =
walk_type ty (fun k s ->
match k with
| `Summ -> `K (fun ty acc -> (s,ty)::acc)
| _ -> `Cont (path_step k s)
) (fun _ acc -> acc) root []
let get_mu_binders root ty =
walk_type ty (fun k s ->
match k with
| `Mu -> `K (fun _ acc -> s::acc)
| _ -> `Cont (path_step k s)
) (fun _ acc -> acc) root []
let mu_transposition_of ~ty path =
get_mu_binders P.template ty |> List.map (fun binders ->
P.root_at ~child:path ~parent:binders
)
let update_rel_map dst rel =
P.PathMap.add dst rel
let set_recursive_rel dst rel ctxt =
{ ctxt with recursive_rel = update_rel_map dst rel ctxt.recursive_rel }
let recursive_rel_for ~e_id ty dst ctxt =
let name =
Printf.sprintf !"%{P}-%d-%s" dst e_id "mu"
in
let args = type_to_paths P.template ty |> List.map (fun p -> p, path_type p) in
let rel = (name, args, ExprMu (e_id, dst, `Flow)) in
let ctxt = set_recursive_rel dst rel @@ { ctxt with relations = rel :: ctxt.relations } in
ctxt,rel
let recursive_havoc_subst ty havoc_paths =
havoc_paths |> ListMonad.bind (fun p ->
p::(mu_transposition_of ~ty p)
) |> List.mapi (fun i s -> (s, Ap (havoc_ap i s)))
let impl_with_havoc ?out_shift ~by_havoc ~ante dst_root dst_rel ctxt =
let havoc_subst =
P.PathMap.find_opt dst_root by_havoc
|> Option.map (fun (ty,havoc_paths) ->
recursive_havoc_subst ty @@ P.PathSet.elements havoc_paths
)
|> Option.value ~default:[]
in
{ ctxt with impl = (ante, PRelation (dst_rel, havoc_subst, out_shift))::ctxt.impl }
let recursive_rel_with_havoc ~by_havoc ~e_id ~ante dst_root ty (ctxt : ctxt) =
let ctxt,rel = recursive_rel_for ~e_id ty dst_root ctxt in
impl_with_havoc ~by_havoc ~ante dst_root rel ctxt
let%lm null_for_var ~e_id ~ty var ctxt =
get_recursive_roots (P.var var) ty
|> List.fold_left (fun ctxt (root,ty) ->
let args = type_to_paths P.template ty |> List.map (fun p -> p, path_type p) in
get_mu_binders root ty |> List.fold_left (fun ctxt bind ->
let name = Printf.sprintf !"null-%d-%{P}-%s" e_id bind "mu" in
let rel = (name, args, ExprMu (e_id, bind, `Null)) in
let grounding_subst = List.filter_map (fun (s, p) ->
match p with
| ZBool ->
let () = assert (P.is_nullity s) in
Some (s, BConst is_null_flag)
| _ -> None
) args in
set_recursive_rel
bind rel
{ ctxt with
relations = rel::ctxt.relations;
impl = ([], PRelation (rel, grounding_subst, None))::ctxt.impl
}
) ctxt
) ctxt
end
type copy_spec = {
direct_copies: (P.concr_ap * P.concr_ap) list;
weak_copies: (P.concr_ap * P.concr_ap * fltype) list
}
let empty_copy = {
direct_copies = [];
weak_copies = [];
}
let add_weak src dst ty cs = {
cs with weak_copies = (src,dst,ty)::cs.weak_copies
}
let add_direct src dst cs = {
cs with direct_copies = (src,dst)::cs.direct_copies
}
let ccat a1 a2 = {
direct_copies = a1.direct_copies @ a2.direct_copies;
weak_copies = a1.weak_copies @ a2.weak_copies
}
let compute_copies in_ap out_ap ty : copy_spec =
parallel_type_walk in_ap out_ap ty (fun k (in_ap,out_ap) () ->
match k with
| `Summ -> `K (fun ty -> add_weak in_ap out_ap ty)
| _ -> `Cont ()
) (fun () -> add_direct) () empty_copy
let to_copy_stream { direct_copies; weak_copies } =
let l = List.map (fun (s,d) ->
s,d,`Direct
) direct_copies
in
List.fold_left (fun acc (s,d,ty) ->
parallel_type_walk s d ty (fun k (in_ap,dst_ap) stack ->
match k with
| `Mu -> `ContPath (P.template, dst_ap, Some (in_ap, dst_ap))
| `Summ | `Ref | `Array -> `Cont stack
) (fun stack in_ap out_ap acc ->
let s = (in_ap, Option.map fst stack) in
let concr_in = RecRelations.MuChain.to_concr s in
let acc = (concr_in, out_ap, `Direct)::acc in
Option.fold ~none:acc ~some:(fun (in_mu,out_mu) ->
(concr_in,out_ap, `Mu (in_ap, in_mu,out_mu, ty))::acc
) stack
) None acc
) l weak_copies
let to_mu_copies { weak_copies; _ } =
ListMonad.bind (fun (s,d,ty) ->
RecRelations.get_mu_binders P.template ty
|> List.map (fun st ->
(st,
P.root_at ~child:st ~parent:s,
P.root_at ~child:st ~parent:d,ty)
)
) weak_copies
let compute_patt_copies path patt ty =
let rec loop patt path ty acc =
match patt,ty with
| PNone,_ -> acc
| PVar v,`Int ->
add_direct path (P.var v) acc
| PVar v,ty ->
ccat (compute_copies path (P.var v) ty) acc
| PTuple t,`Tuple tl ->
fold_left2i (fun i acc p t ->
loop p (P.t_ind path i) t acc
) acc t tl
| PTuple _,_ -> assert false
in
loop patt path ty empty_copy
module Havoc = struct
type mu_havoc = (fltype * P.PathSet.t) P.PathMap.t
type havoc_state = {
havoc: P.PathSet.t;
stable: P.PathSet.t;
mu_havoc: mu_havoc
}
let empty_havoc_state = {
havoc = P.PathSet.empty;
stable = P.PathSet.empty;
mu_havoc = P.PathMap.empty;
}
let add_havoc p f m =
if f then
{ m with havoc = P.PathSet.add p m.havoc }
else
{ m with stable = P.PathSet.add p m.stable }
let add_mu_havoc ~binder ~ty p f m =
if f then
{ m with mu_havoc =
P.PathMap.update binder (function
| None -> Some (ty, P.PathSet.singleton p)
| Some (ty',set) ->
let () = assert (ty = ty') in
Some (ty, P.PathSet.add p set)
) m.mu_havoc
}
else
m
let to_rec_havoc { mu_havoc; _ } = mu_havoc
let%lm update_null_havoc ~e_id ~ty var ctxt =
let ref_havoc p = (OI.GenMap.find (OI.MGen e_id, p) ctxt.o_hints.OI.gen) = 0.0 in
let hstate =
walk_type ty (fun k (f,p) ->
let f =
match k with
| `Ref | `Array -> ref_havoc p
| _ -> f
in
`Cont (f, path_step k p)
) (fun (f, p) acc ->
add_havoc p f acc
) (false, P.var var) empty_havoc_state
in
{ ctxt with havoc_set = P.PathSet.union hstate.havoc @@ P.PathSet.diff ctxt.havoc_set hstate.stable }
end
module H = Havoc
let augment_havocs flows havoc_set =
List.filter (fun (p,_) -> not (P.PathSet.mem p havoc_set)) flows |> P.PathSet.fold (fun p acc ->
(p, Ap (havoc_ap 0 p))::acc
) havoc_set
let compute_flows ~sl copies ctxt =
let _,split_oracle = split_oracle sl ctxt in
to_copy_stream copies |> List.fold_left (fun (hstate,rename_copy,copy_subst,out_subst) (src,dst,k) ->
let (h_in,h_out) = split_oracle src dst in
let add_havoc p f ostate =
if f = `Trivial then ostate
else Havoc.add_havoc p (f = `Havoc) ostate
in
let hstate = add_havoc src h_in @@ add_havoc dst h_out hstate in
match k with
| `Direct -> hstate,rename_copy,copy_subst,(dst, Ap src)::out_subst
| `Mu (under_mu_path, s, d, ty) ->
let () =
assert (h_in <> `Trivial);
assert (h_out <> `Trivial)
in
let hstate =
Havoc.add_mu_havoc ~binder:s ~ty under_mu_path (h_in = `Havoc) hstate
|> Havoc.add_mu_havoc ~binder:d ~ty under_mu_path (h_out = `Havoc)
in
hstate,(dst, Ap (to_pass_ap src))::rename_copy,(src, Ap dst)::copy_subst, out_subst
) (Havoc.empty_havoc_state,[],[],[])
let copy_rel ~by_havoc ~e_id ~ty src_rel dst_root ctxt =
if P.PathMap.mem dst_root by_havoc then
RecRelations.recursive_rel_with_havoc ~by_havoc ~e_id ~ante:([
PRelation (src_rel, [], None)
]) dst_root ty ctxt
else
RecRelations.set_recursive_rel dst_root src_rel ctxt
let%lm apply_copies ?out_rec_rel ~havoc:havoc_flag ~sl ?(flows=[]) ?pre copies in_rel out_rel ctxt =
let hstate,rename_copy,copy_flows,out_flows = compute_flows ~sl copies ctxt in
let mu_copies = to_mu_copies copies in
let havoc_set = ctxt.havoc_set in
let havoc_set = P.PathSet.union hstate.H.havoc @@ P.PathSet.diff havoc_set hstate.H.stable in
let applied_havocs = if havoc_flag then havoc_set else hstate.H.havoc in
let flows = List.mapi flow_to_subst flows in
let out_flows = augment_havocs (flows @ out_flows) applied_havocs in
let pre =
Option.value ~default:[] pre @
if (List.compare_length_with copy_flows 0) > 0 then
[
PRelation(in_rel, rename_copy, None);
PRelation(in_rel, rename_copy @ copy_flows, None)
]
else
[ PRelation(in_rel, [], None) ]
in
let conseq = PRelation(out_rel, out_flows, None) in
let ctxt =
let by_havoc = H.to_rec_havoc hstate in
List.fold_left (fun ctxt (_,src,dst,ty) ->
let () =
assert (P.PathMap.mem src ctxt.recursive_rel);
in
let e_id =
match sl with
| OI.SRet e_id
| OI.SBind e_id
| OI.SCall e_id -> e_id
in
let src_rel = P.PathMap.find src ctxt.recursive_rel in
let ctxt = copy_rel ~by_havoc ~e_id ~ty src_rel src ctxt in
match out_rec_rel with
| Some (direct_flow,_) ->
P.PathMap.find_opt dst direct_flow
|> Option.fold ~none:ctxt ~some:(fun rel ->
RecRelations.impl_with_havoc ~by_havoc ~ante:[
PRelation (src_rel, [], None)
] dst rel ctxt
)
| None -> copy_rel ~by_havoc ~e_id ~ty src_rel dst ctxt
) ctxt mu_copies
in
let ctxt =
match out_rec_rel with
| None -> ctxt
| Some (_,extra_rec_flows) ->
List.fold_left (fun ctxt (src,rel) ->
let in_rel = P.PathMap.find src ctxt.recursive_rel in
{ ctxt with impl = ([ PRelation(in_rel, [], None)], PRelation (rel, [], None))::ctxt.impl }
) ctxt extra_rec_flows
in
{ ctxt with havoc_set; impl = (pre, conseq)::ctxt.impl }
module IdMap(M: Map.S) : sig
type t
val id_map : t
val find : M.key -> t -> M.key
val add : M.key -> M.key -> t -> t
val mem : M.key -> t -> bool
val empty : t
end = struct
type t = (M.key M.t) option
let empty = Some M.empty
let id_map = None
let add k v = function
| None -> failwith "Cannot add to infinite map"
| Some w -> Some (M.add k v w)
let q f = fun k -> Option.fold ~none:true ~some:(f k)
let mem = q M.mem
let l f = fun k -> Option.fold ~none:k ~some:(f k)
let find = l M.find
end
module PPMap = IdMap(P.PathMap)
module RecursiveRefinements = struct
type mu_frag = P.path * fltype
type rooted_mu = {
hd: mu_frag;
recurse: mu_frag P.PathMap.t;
}
module Subst = struct
let rec concr_arg_to_string = function
| Ap p -> P.to_z3_ident p
| BConst b -> Bool.to_string b
| IConst i -> string_of_int i
| NondetChoice l -> List.map concr_arg_to_string l |> String.concat ", " |> Printf.sprintf "{?%s}"
| KeyedChoice (p,a1,a2) ->
Printf.sprintf "[? %s %s %s]" (P.to_z3_ident p) (concr_arg_to_string a1) (concr_arg_to_string a2)
let to_string l = List.map (fun (p,a) ->
Printf.sprintf !"%{P} |-> %s" p @@ concr_arg_to_string a
) l |> String.concat "; " |> Printf.sprintf "[ %s ]"
[@@ocaml.warning "-32"]
end
let to_mu_template ty =
let recurse = walk_type ty (fun k ap ->
match k with
| `Summ -> `Cont ap
| `Mu -> `K (fun ty acc -> (ap,ty)::acc)
| _ -> `Cont (path_step k ap)
) (fun _ acc -> acc) P.template []
in
let recurse = List.fold_left (fun acc (p,ty) ->
P.PathMap.add p (p,ty) acc
) P.PathMap.empty recurse
in
{
hd = (P.template, ty);
recurse
}
let root_template_at tmpl path =
let root_frag (p,ty) =
(P.root_at ~child:p ~parent:path, ty)
in
{
hd = root_frag tmpl.hd;
recurse = P.PathMap.map root_frag tmpl.recurse
}
let to_multi_subst substs indexed_subst =
fold_lefti (fun i acc sub_group ->
let indexed_sub = List.fold_left (fun acc (ind,substs) ->
List.fold_left (fun acc (s,d) ->
P.PathMap.update s (fun curr ->
Some ((ind,d)::(Option.value ~default:[] curr))
) acc
) acc substs
) P.PathMap.empty sub_group in
let keyed_ap j =
(P.var @@ Printf.sprintf "star!%d!%d" i j)
in
let to_unary_subst sorted =
match sorted with
| [] -> assert false
| l ->
let l = List.sort (fun (k1,_) (k2,_) ->
k2 - k1
) l
in
let (_,hd) = List.hd l in
let tl = List.tl l in
fold_lefti (fun i acc (_,ap) ->
KeyedChoice (keyed_ap i, Ap ap, acc)
) (Ap hd) tl
in
P.PathMap.fold (fun s dlist acc ->
(s,to_unary_subst dlist)::acc
) indexed_sub acc
) (List.map lift_copy substs) indexed_subst
let compute_frag_subst ?(acc=[]) ~skip_ref src_frag dst_frag =
let ty,src,dst =
if skip_ref then
match snd src_frag with
| `Ref (_,t) -> t,(P.deref @@ fst src_frag),(fst dst_frag)
| _ -> assert false
else
(snd src_frag),fst src_frag,fst dst_frag
in
let ty = match ty with
| `Mu t -> t
| _ -> ty
in
parallel_type_walk src dst ty (fun k _ () ->
match k with
| `Mu -> `K (fun _ acc -> acc)
| `Summ | `Array | `Ref -> `Cont ()
) (fun () in_ap out_ap acc ->
(in_ap, out_ap)::acc
) () acc
let compute_mu_template_subst { hd = (path,ty); _ } =
parallel_type_walk P.template path ty (fun _ _ () -> `Cont ()) (fun () in_ap out_ap acc ->
(in_ap, Ap out_ap)::acc
) () []
let compute_mu_multi_subst ?(skip_hd_ref=false) src_templ ~hd:hfrag ~tl:tfrags =
let subst = compute_frag_subst ~skip_ref:skip_hd_ref src_templ.hd hfrag in
let indexed_subst = P.PathMap.fold (fun bind_path src_frag acc ->
let dst_frags = P.PathMap.find bind_path tfrags in
let sub_group = fold_lefti (fun i acc dst_frag ->
let s = compute_frag_subst ~skip_ref:false src_frag dst_frag in
(i,s)::acc
) [] dst_frags in
sub_group::acc
) src_templ.recurse []
in
to_multi_subst subst indexed_subst
let compute_mu_subst src_templ ~hd:hfrag ~tl:tfrags =
let subst = compute_frag_subst ~skip_ref:false src_templ.hd hfrag in
P.PathMap.fold (fun path src_frag acc ->
let dst_frag = P.PathMap.find path tfrags in
compute_frag_subst ~skip_ref:false ~acc src_frag dst_frag
) src_templ.recurse subst
|> List.map lift_copy
let partial_subst src dst ty =
parallel_type_walk src dst ty (fun k _ () -> match k with
| `Mu -> `K (fun _ acc -> acc)
| _ -> `Cont ()
) (fun () in_ap out_ap acc -> (in_ap, out_ap)::acc) () []
let relation_subst rel subst =
PRelation (rel, subst, None)
let unfold_to ~with_havoc ?(out_mapping=PPMap.id_map) ~e_id ref_ty root target in_rel out_rel ctxt =
let normalize_havoc pset = P.PathSet.filter (fun p ->
PPMap.mem p out_mapping
) pset |> P.PathSet.map (fun p ->
PPMap.find p out_mapping
)
in
let normalize_ap p =
if PPMap.mem p out_mapping then
PPMap.find p out_mapping
else
p
in
let rec deep_normalize = function
| Ap p -> Ap (normalize_ap p)
| BConst c -> BConst c
| IConst i -> IConst i
| NondetChoice c -> NondetChoice (List.map deep_normalize c)
| KeyedChoice (p,a1,a2) ->
KeyedChoice (normalize_ap p, deep_normalize a1, deep_normalize a2)
in
let normalize_subst subst =
List.map (fun (s,d) ->
(s, deep_normalize d)
) subst
in
let cont_ty = match ref_ty with
| `Ref (_,t) -> t
| _ -> assert false
in
let source_path = P.var root in
let template = to_mu_template ref_ty in
let source_mu = root_template_at template source_path in
let target_path = P.var target in
(* Roughly speaking, we unfold the relation R(hd, tl) as follows:
R(hd, tl!pass) /\ R(hd, tl) /\ R(hd, tl->tl) /\ hd(tl, tl->tl) => R(hd, tl!pass, hd, tl, tl->tl)
hd(x,y) => tl(x, y)
R here encodes a relationship between hd and all tail elements, i.e. elements reachable from the tail pointer of hd.
hd(x,y) encodes the recursive refinement, i.e., after unfolding, what relationship should hold between tl and all
the elements reachable from tl, represented in the above by tl->tl.
*)
let mu_mapping = parallel_type_walk (P.deref @@ P.template) target_path cont_ty (fun k (in_ap,out_ap) () ->
match k with
| `Mu -> `K (fun _ acc ->
(in_ap, root_template_at template out_ap)::acc
)
| _ -> `Cont ()
) (fun _ _ _ acc -> acc) () []
in
(* now we compute the R(hd, tl) /\ R(hd, tl->tl) component. We use the multi_subst operation for this *)
let unfolding_mu = List.fold_left (fun acc (src_mu, target_mu) ->
let l = target_mu.hd :: (P.PathMap.bindings target_mu.recurse |> List.map snd) in
P.PathMap.add src_mu l acc
) P.PathMap.empty mu_mapping
in
let rec combination_loop l =
match l with
| [] -> [P.PathMap.empty]
| (src_mu,possibilities)::t ->
let comb = combination_loop t in
ListMonad.bind (fun mu ->
List.map (fun m ->
P.PathMap.add src_mu mu m
) comb
) possibilities
in
let combinatorial_subst =
P.PathMap.bindings unfolding_mu
|> combination_loop
|> List.map (fun m ->