forked from achlipala/frap
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AbstractInterpretation.v
2167 lines (1882 loc) · 65.9 KB
/
AbstractInterpretation.v
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
(** Formal Reasoning About Programs <http://adam.chlipala.net/frap/>
* Chapter 9: Abstract Interpretation and Dataflow Analysis
* Author: Adam Chlipala
* License: https://creativecommons.org/licenses/by-nc-nd/4.0/ *)
Require Import Frap Imp.
Set Implicit Arguments.
(* With model checking, we were able to find invariants automatically for
* nontrivial transition systems. With operational semantics, we're able to
* build transition systems automatically from program syntax. Let's put these
* two sorts of examples together into an even more computationally efficient
* technique: abstract interpretation. *)
(* Apologies for jumping right into abstract formal details, but that's what the
* medium of Coq forces on us! We will apply abstract interpretation to the
* imperative language that we formalized in the last chapter. Here's a record
* capturing the idea of an abstract interpretation for that language. *)
Record absint := {
Domain :> Set;
(* We will represent concrete values (natural numbers) with this alternative,
* abstract set. This [:>] notation lets us treat any [absint] as its
* [Domain], automatically. See below for examples (e.g., return type of
* [absint_interp]). *)
Top : Domain;
(* A universal (least informative) element, describing *all* concrete
* values *)
Constant : nat -> Domain;
(* Most accurate representation of a constant *)
Add : Domain -> Domain -> Domain;
Subtract : Domain -> Domain -> Domain;
Multiply : Domain -> Domain -> Domain;
(* Abstract versions of arithmetic operators *)
Join : Domain -> Domain -> Domain;
(* Returns some new element that covers all cases of each of its inputs *)
Represents : nat -> Domain -> Prop
(* Which elements represent which numbers? *)
}.
(* That was a mouthful, but we still need to say what makes one of these
* reasonable. *)
Record absint_sound (a : absint) : Prop := {
TopSound : forall n, a.(Represents) n a.(Top);
ConstSound : forall n, a.(Represents) n (a.(Constant) n);
AddSound : forall n na m ma, a.(Represents) n na
-> a.(Represents) m ma
-> a.(Represents) (n + m) (a.(Add) na ma);
SubtractSound: forall n na m ma, a.(Represents) n na
-> a.(Represents) m ma
-> a.(Represents) (n - m) (a.(Subtract) na ma);
MultiplySound : forall n na m ma, a.(Represents) n na
-> a.(Represents) m ma
-> a.(Represents) (n * m) (a.(Multiply) na ma);
AddMonotone : forall na na' ma ma', (forall n, a.(Represents) n na -> a.(Represents) n na')
-> (forall n, a.(Represents) n ma -> a.(Represents) n ma')
-> (forall n, a.(Represents) n (a.(Add) na ma)
-> a.(Represents) n (a.(Add) na' ma'));
SubtractMonotone : forall na na' ma ma', (forall n, a.(Represents) n na -> a.(Represents) n na')
-> (forall n, a.(Represents) n ma -> a.(Represents) n ma')
-> (forall n, a.(Represents) n (a.(Subtract) na ma)
-> a.(Represents) n (a.(Subtract) na' ma'));
MultiplyMonotone : forall na na' ma ma', (forall n, a.(Represents) n na -> a.(Represents) n na')
-> (forall n, a.(Represents) n ma -> a.(Represents) n ma')
-> (forall n, a.(Represents) n (a.(Multiply) na ma)
-> a.(Represents) n (a.(Multiply) na' ma'));
JoinSoundLeft : forall x y n, a.(Represents) n x
-> a.(Represents) n (a.(Join) x y);
JoinSoundRight : forall x y n, a.(Represents) n y
-> a.(Represents) n (a.(Join) x y)
}.
(* Here's a "bonus" condition that we'll sometimes use and sometimes not:
* [Join] gives a *least* upper bound of its two arguments, such that any other
* upper bound is also at or above the join. *)
Definition absint_complete (a : absint) :=
forall x y z n, a.(Represents) n (a.(Join) x y)
-> (forall n, a.(Represents) n x -> a.(Represents) n z)
-> (forall n, a.(Represents) n y -> a.(Represents) n z)
-> a.(Represents) n z.
(* Let's ask [eauto] to try all of the above soundness rules automatically. *)
Hint Resolve TopSound ConstSound AddSound SubtractSound MultiplySound
AddMonotone SubtractMonotone MultiplyMonotone
JoinSoundLeft JoinSoundRight : core.
(** * Example: even-odd analysis *)
Inductive parity := Even | Odd | Either.
(* In this interpretation, every value either has known parity or not. *)
Definition isEven (n : nat) := exists k, n = k * 2.
Definition isOdd (n : nat) := exists k, n = k * 2 + 1.
(* Here are some convenient definitions of the parities. *)
(* BEGIN SPAN OF BORING THEOREMS ABOUT PARITY, WHICH WE WON'T EXPLAIN. *)
Theorem decide_parity : forall n, isEven n \/ isOdd n.
Proof.
induct n; simplify; propositional.
left; exists 0; linear_arithmetic.
invert H.
right.
exists x; linear_arithmetic.
invert H.
left.
exists (x + 1); linear_arithmetic.
Qed.
Theorem notEven_odd : forall n, ~isEven n -> isOdd n.
Proof.
simplify.
assert (isEven n \/ isOdd n).
apply decide_parity.
propositional.
Qed.
Theorem odd_notEven : forall n, isOdd n -> ~isEven n.
Proof.
propositional.
invert H.
invert H0.
linear_arithmetic.
Qed.
Theorem isEven_0 : isEven 0.
Proof.
exists 0; linear_arithmetic.
Qed.
Theorem isEven_1 : ~isEven 1.
Proof.
propositional; invert H; linear_arithmetic.
Qed.
Theorem isEven_S_Even : forall n, isEven n -> ~isEven (S n).
Proof.
propositional; invert H; invert H0; linear_arithmetic.
Qed.
Theorem isEven_S_Odd : forall n, ~isEven n -> isEven (S n).
Proof.
propositional.
apply notEven_odd in H.
invert H.
exists (x + 1); linear_arithmetic.
Qed.
Hint Resolve isEven_0 isEven_1 isEven_S_Even isEven_S_Odd : core.
(* END SPAN OF BORING THEOREMS ABOUT PARITY. *)
(* Next, we are ready to implement the operators of the abstract
* interpretation. *)
Definition parity_flip (p : parity) :=
match p with
| Even => Odd
| Odd => Even
| Either => Either
end.
Fixpoint parity_const (n : nat) :=
match n with
| O => Even
| S n' => parity_flip (parity_const n')
end.
Definition parity_add (x y : parity) :=
match x, y with
| Even, Even => Even
| Odd, Odd => Even
| Even, Odd => Odd
| Odd, Even => Odd
| _, _ => Either
end.
Definition parity_subtract (x y : parity) :=
match x, y with
| Even, Even => Even
| Odd, Odd => Even
| _, _ => Either
end.
(* Note subtleties with [Either]s above, to deal with underflow at zero! *)
Definition parity_multiply (x y : parity) :=
match x, y with
| Even, _ => Even
| Odd, Odd => Odd
| _, Even => Even
| _, _ => Either
end.
Definition parity_join (x y : parity) :=
match x, y with
| Even, Even => Even
| Odd, Odd => Odd
| _, _ => Either
end.
(* What does it mean for a parity to classify a number correctly? *)
Inductive parity_rep : nat -> parity -> Prop :=
| PrEven : forall n,
isEven n
-> parity_rep n Even
| PrOdd : forall n,
~isEven n
-> parity_rep n Odd
| PrEither : forall n,
parity_rep n Either.
Hint Constructors parity_rep : core.
(* Putting it all together: *)
Definition parity_absint := {|
Top := Either;
Constant := parity_const;
Add := parity_add;
Subtract := parity_subtract;
Multiply := parity_multiply;
Join := parity_join;
Represents := parity_rep
|}.
(* Now we prove soundness. *)
Lemma parity_const_sound : forall n,
parity_rep n (parity_const n).
Proof.
induct n; simplify; eauto.
cases (parity_const n); simplify; eauto.
invert IHn; eauto.
invert IHn; eauto.
Qed.
Hint Resolve parity_const_sound : core.
Lemma even_not_odd :
(forall n, parity_rep n Even -> parity_rep n Odd)
-> False.
Proof.
simplify.
specialize (H 0).
assert (parity_rep 0 Even) by eauto.
apply H in H0.
invert H0.
apply H1.
auto.
Qed.
Lemma odd_not_even :
(forall n, parity_rep n Odd -> parity_rep n Even)
-> False.
Proof.
simplify.
specialize (H 1).
assert (parity_rep 1 Odd) by eauto.
apply H in H0.
invert H0.
invert H1.
linear_arithmetic.
Qed.
Hint Resolve even_not_odd odd_not_even : core.
Lemma parity_join_complete : forall n x y,
parity_rep n (parity_join x y)
-> parity_rep n x \/ parity_rep n y.
Proof.
simplify; cases x; cases y; simplify; propositional.
assert (isEven n \/ isOdd n) by apply decide_parity.
propositional; eauto using odd_notEven.
assert (isEven n \/ isOdd n) by apply decide_parity.
propositional; eauto using odd_notEven.
Qed.
Hint Resolve parity_join_complete : core.
(* The final proof uses some automation that we won't explain, to descend down
* to the hearts of the interesting cases. *)
Theorem parity_sound : absint_sound parity_absint.
Proof.
constructor; simplify; eauto;
repeat match goal with
| [ H : parity_rep _ _ |- _ ] => invert H
| [ H : ~isEven _ |- _ ] => apply notEven_odd in H; invert H
| [ H : isEven _ |- _ ] => invert H
| [ p : parity |- _ ] => cases p; simplify; try equality
end; try solve [ exfalso; eauto ]; try (constructor; try apply odd_notEven).
(* We finish up by instantiating all those existential quantifiers in uses of
* [isEven] and [isOdd]. *)
exists (x0 + x); ring.
exists (x0 + x); ring.
exists (x0 + x); ring.
exists (x0 + x + 1); ring.
exists (x - x0); linear_arithmetic.
exists (x - x0); linear_arithmetic.
exists (x * x0 * 2); ring.
exists ((x * 2 + 1) * x0); ring.
exists (n * x); ring.
exists ((x * 2 + 1) * x0); ring.
exists (2 * x * x0 + x + x0); ring.
exists (x * m); ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x; ring.
exists x0; ring.
exists x0; ring.
Qed.
Theorem parity_complete : absint_complete parity_absint.
Proof.
unfold absint_complete; simplify; eauto;
repeat match goal with
| [ H : parity_rep _ _ |- _ ] => invert H
| [ H : ~isEven _ |- _ ] => apply notEven_odd in H; invert H
| [ H : isEven _ |- _ ] => invert H
| [ p : parity |- _ ] => cases p; simplify; try equality
end; try solve [ exfalso; eauto ]; try (constructor; try apply odd_notEven).
exists x0; ring.
exists x0; ring.
Qed.
(** * Flow-insensitive analysis *)
(* So there's an example of an abstract interpretation, but how do we put it to
* use to prove a theorem about a program? Model checking was an example of a
* *path-sensitive* analysis, where we accumulated a finite set of reachable
* states of a system. Abstract interpretation is usually *path-insensitive*,
* and it may even be *flow-insensitive*, which means that we will find an
* invariant that *ignores the current command altogether*. Instead, the
* invariant talks just about the valuation. To help us do that, here's a type
* definition: *)
Definition astate (a : absint) := fmap var a.
(* An abstract state maps variables to abstract elements. The idea is that each
* variable should take on a concrete value represented by its associated
* abstract value. These are only finite maps, so missing variables are allowed
* to take arbitrary values. *)
(* An easy thing to do with an [astate] is evaluate an expression into another
* abstract element. *)
Fixpoint absint_interp (e : arith) a (s : astate a) : a :=
match e with
| Const n => a.(Constant) n
| Var x => match s $? x with
| None => a.(Top)
| Some xa => xa
end
| Plus e1 e2 => a.(Add) (absint_interp e1 s) (absint_interp e2 s)
| Minus e1 e2 => a.(Subtract) (absint_interp e1 s) (absint_interp e2 s)
| Times e1 e2 => a.(Multiply) (absint_interp e1 s) (absint_interp e2 s)
end.
(* To automate finding a suitable path-insensitive invariant, it's helpful to
* compute the set of all assignments in a command. *)
Fixpoint assignmentsOf (c : cmd) : set (var * arith) :=
match c with
| Skip => {}
| Assign x e => {(x, e)}
| Sequence c1 c2 => assignmentsOf c1 \cup assignmentsOf c2
| If _ c1 c2 => assignmentsOf c1 \cup assignmentsOf c2
| While _ c1 => assignmentsOf c1
end.
(* Any step in a program can be matched by either doing nothing or running one
* of the assignments. *)
Theorem assignmentsOf_ok : forall v c v' c',
step (v, c) (v', c')
-> v' = v \/ exists x e, (x, e) \in assignmentsOf c
/\ v' = v $+ (x, interp e v).
Proof.
induct 1; unfold Sets.In; simplify; eauto 10.
first_order.
Qed.
(* Taking a step never adds new possible assignments. *)
Theorem assignmentsOf_monotone : forall v c v' c',
step (v, c) (v', c')
-> assignmentsOf c' \subseteq assignmentsOf c.
Proof.
induct 1; simplify; sets.
(* [sets]: simplify a goal involving set-theory operators. *)
Qed.
(* OK, now we know all the assignments that could happen. We can start with
* some initial [astate] and repeatedly pick some assignment and execute it to
* modify [astate]. The trouble would be if, for instance in our even-odd
* example, the program had two assignments ["x" <- 0] and ["x" <- 1]. We could
* alternate between these assignments and waste our time forever, switching
* ["x"] back and forth between [Even] and [Odd]! Instead, when we run an
* assignment, we want to merge the new abstract value with the old, getting a
* new value guaranteed to overapproximate both of them. [merge_astate] applies
* that combination to all variables in two abstract states, using a finite-map
* function [merge] from the book library. Any variable found in at most one of
* the two input maps is absent from the output map, and any variable found in
* both maps is now mapped to the *join* of the original values, using a crucial
* component of any abstract interpretation. *)
Definition merge_astate a : astate a -> astate a -> astate a :=
merge (fun x y =>
match x with
| None => None
| Some x' =>
match y with
| None => None
| Some y' => Some (a.(Join) x' y')
end
end).
(* With [merge_astate] in place, we can define a new transition system,
* capturing the essence of flow-insensitive abstract interpretations. We can
* either do nothing or we can pick an assignment, run it, and merge the result
* into our growing candidate invariant. *)
Inductive flow_insensitive_step a (c : cmd) : astate a -> astate a -> Prop :=
| InsensitiveNothing : forall s,
flow_insensitive_step c s s
| InsensitiveStep : forall x e s,
(x, e) \in assignmentsOf c
-> flow_insensitive_step c s (merge_astate s (s $+ (x, absint_interp e s))).
Hint Constructors flow_insensitive_step : core.
Definition flow_insensitive_trsys a (s : astate a) (c : cmd) := {|
Initial := {s};
Step := flow_insensitive_step (a := a) c
|}.
(* Now we revisit our studies of *simulation* from model checking. After all,
* that abstraction principle worked for transition systems in general,
* regardless of whether we want to model-check afterward. To define our
* simulation relation, let's start with what makes an abstract state compatible
* with a concrete valuation. *)
Definition insensitive_compatible a (s : astate a) (v : valuation) : Prop :=
forall x xa, s $? x = Some xa
-> (exists n, v $? x = Some n
/\ a.(Represents) n xa)
\/ (forall n, a.(Represents) n xa).
(* That is, when a variable is mapped to some abstract element, either that
* variable has a compatible concrete value, or the variable has no value and
* that element actually accepts all values (i.e., is probably [Top]). *)
(* Concrete state matches abstract when:
* (1) The abstract state and valuation match up.
* (2) The command's assignment set is contained within the set of the overall
* program [c]. *)
Inductive Rinsensitive a (c : cmd) : valuation * cmd -> astate a -> Prop :=
| RInsensitive : forall v s c',
insensitive_compatible s v
-> assignmentsOf c' \subseteq assignmentsOf c
-> Rinsensitive c (v, c') s.
Hint Constructors Rinsensitive : core.
(* A helpful decomposition property for compatibility *)
Lemma insensitive_compatible_add : forall a (s : astate a) v x na n,
insensitive_compatible s v
-> a.(Represents) n na
-> insensitive_compatible (s $+ (x, na)) (v $+ (x, n)).
Proof.
unfold insensitive_compatible; simplify.
cases (x ==v x0); simplify; eauto.
invert H1; eauto.
Qed.
(* Interpretation of expressions is compatible with [Represents]. *)
Theorem absint_interp_ok : forall a, absint_sound a
-> forall (s : astate a) v e,
insensitive_compatible s v
-> a.(Represents) (interp e v) (absint_interp e s).
Proof.
induct e; simplify; eauto.
cases (s $? x); auto.
unfold insensitive_compatible in H0.
apply H0 in Heq.
invert Heq.
invert H1.
propositional.
rewrite H1.
assumption.
eauto.
Qed.
Hint Resolve insensitive_compatible_add absint_interp_ok : core.
(* With that, let's show that the flow-insensitive version of a program
* *simulates* the original program, w.r.t. any sound abstract
* interpretation. *)
Theorem insensitive_simulates : forall a (s : astate a) v c,
absint_sound a
-> insensitive_compatible s v
-> simulates (Rinsensitive (a := a) c) (trsys_of v c) (flow_insensitive_trsys s c).
Proof.
simplify.
constructor; simplify.
exists s; propositional.
subst.
constructor.
assumption.
sets.
invert H1.
cases st1'.
assert (assignmentsOf c0 \subseteq assignmentsOf c).
apply assignmentsOf_monotone in H2.
sets.
apply assignmentsOf_ok in H2.
propositional; subst.
eauto.
invert H5.
invert H2.
propositional; subst.
exists (merge_astate st2 (st2 $+ (x, absint_interp x0 st2))).
propositional; eauto.
econstructor; eauto.
unfold insensitive_compatible in *; simplify.
unfold merge_astate in *; simplify.
cases (st2 $? x1); simplify; try equality.
cases (x ==v x1); simplify; try equality.
invert H5; eauto 6.
rewrite Heq in H5.
invert H5; eauto.
apply H3 in Heq; propositional; eauto.
invert H5; propositional; eauto.
Qed.
(* Now we need a way to come up with an invariant, in the form of one unifying
* [astate]. This predicate formalizes one step from our informal recipe above:
* run every possible assignment on an [astate] in some order, at each step
* merging the new state with the old. *)
Inductive runAllAssignments a : set (var * arith) -> astate a -> astate a -> Prop :=
| RunDone : forall s,
runAllAssignments {} s s
| RunStep : forall x e xes s s',
runAllAssignments (constant xes) (merge_astate s (s $+ (x, absint_interp e s))) s'
-> runAllAssignments (constant ((x, e) :: xes)) s s'.
(* Finally, we can iterate that process to take an initial state to a final one
* that covers all possible executions. *)
Inductive iterate a (c : cmd) : astate a -> astate a -> Prop :=
(* If [runAllAssignments] has no effect, then we're done. *)
| IterateDone : forall s,
runAllAssignments (assignmentsOf c) s s
-> iterate c s s
(* Otherwise, use it to evolve one step and continue from there. *)
| IterateStep : forall s s' s'',
runAllAssignments (assignmentsOf c) s s'
-> iterate c s' s''
-> iterate c s s''.
(* What does it mean for [s2] to capture all concrete states captured by
* [s1]? *)
Definition subsumed a (s1 s2 : astate a) :=
forall x, match s1 $? x with
| None => s2 $? x = None
| Some xa1 =>
forall xa2, s2 $? x = Some xa2
-> forall n, a.(Represents) n xa1
-> a.(Represents) n xa2
end.
(* A few basic properties of subsumption *)
Theorem subsumed_refl : forall a (s : astate a),
subsumed s s.
Proof.
unfold subsumed; simplify.
cases (s $? x); equality.
Qed.
Hint Resolve subsumed_refl : core.
Lemma subsumed_use : forall a (s s' : astate a) x n t0 t,
s $? x = Some t0
-> subsumed s s'
-> s' $? x = Some t
-> Represents a n t0
-> Represents a n t.
Proof.
unfold subsumed; simplify.
specialize (H0 x).
rewrite H in H0.
eauto.
Qed.
Lemma subsumed_use_empty : forall a (s s' : astate a) x n t0 t,
s $? x = None
-> subsumed s s'
-> s' $? x = Some t
-> Represents a n t0
-> Represents a n t.
Proof.
unfold subsumed; simplify.
specialize (H0 x).
rewrite H in H0.
equality.
Qed.
Hint Resolve subsumed_use subsumed_use_empty : core.
Lemma subsumed_trans : forall a (s1 s2 s3 : astate a),
subsumed s1 s2
-> subsumed s2 s3
-> subsumed s1 s3.
Proof.
unfold subsumed; simplify.
specialize (H x); specialize (H0 x).
cases (s1 $? x); simplify.
cases (s2 $? x); eauto.
cases (s2 $? x); eauto.
equality.
Qed.
Lemma subsumed_merge_left : forall a, absint_sound a
-> forall s1 s2 : astate a,
subsumed s1 (merge_astate s1 s2).
Proof.
unfold subsumed, merge_astate; simplify.
cases (s1 $? x); trivial.
cases (s2 $? x); simplify; try equality.
invert H0; eauto.
Qed.
Hint Resolve subsumed_merge_left : core.
Lemma subsumed_merge_both : forall a, absint_sound a
-> absint_complete a
-> forall s1 s2 s : astate a,
subsumed s1 s
-> subsumed s2 s
-> subsumed (merge_astate s1 s2) s.
Proof.
unfold subsumed, merge_astate; simplify.
specialize (H1 x).
specialize (H2 x).
cases (s1 $? x); auto.
cases (s2 $? x); auto.
simplify.
unfold absint_complete in *; eauto.
Qed.
Lemma subsumed_add : forall a, absint_sound a
-> forall (s1 s2 : astate a) x v1 v2,
subsumed s1 s2
-> (forall n, a.(Represents) n v1 -> a.(Represents) n v2)
-> subsumed (s1 $+ (x, v1)) (s2 $+ (x, v2)).
Proof.
unfold subsumed; simplify.
cases (x ==v x0); subst; simplify; eauto.
invert H2; eauto.
specialize (H0 x0); eauto.
Qed.
Hint Resolve subsumed_add : core.
(* A key property of interpreting expressions abstractly: it's *monotone*, in
* the sense that moving up to a less precise [astate] leads to a less precise
* interpretation result. *)
Lemma absint_interp_monotone : forall a, absint_sound a
-> forall (s : astate a) e s' n,
a.(Represents) n (absint_interp e s)
-> subsumed s s'
-> a.(Represents) n (absint_interp e s').
Proof.
induct e; simplify; eauto.
cases (s' $? x); eauto.
cases (s $? x); eauto.
Qed.
Hint Resolve absint_interp_monotone : core.
(* [runAllAssignments] also respects the subsumption order, in going from inputs
* to outputs. *)
Lemma runAllAssignments_monotone : forall a, absint_sound a
-> forall xes (s s' : astate a),
runAllAssignments xes s s'
-> subsumed s s'.
Proof.
induct 2; simplify; eauto using subsumed_trans.
Qed.
Hint Resolve runAllAssignments_monotone : core.
(* The output of [runAllAssignments] subsumes every state reachable by running a
* single command. *)
Lemma runAllAssignments_ok : forall a, absint_sound a
-> forall xes (s s' : astate a),
runAllAssignments xes s s'
-> forall x e, (x, e) \in xes
-> subsumed (s $+ (x, absint_interp e s)) s'.
Proof.
induct 2; unfold Sets.In; simplify; propositional.
invert H2.
apply subsumed_trans with (s2 := merge_astate s (s $+ (x0, absint_interp e0 s))); eauto.
unfold subsumed, merge_astate; simplify.
cases (x0 ==v x); subst; simplify.
cases (s $? x); try equality.
invert H1; eauto.
cases (s $? x); try equality.
invert 1; eauto.
eapply subsumed_trans; try apply IHrunAllAssignments; eauto.
Qed.
(* Let's skip past this lemma to the theorem it supports. *)
Lemma iterate_ok' : forall a, absint_sound a
-> absint_complete a
-> forall c (s0 s s' : astate a),
iterate c s s'
-> subsumed s0 s
-> invariantFor (flow_insensitive_trsys s0 c) (fun s'' => subsumed s'' s').
Proof.
induct 3; simplify.
apply invariant_induction; simplify; propositional; subst; auto.
invert H4; auto.
eapply runAllAssignments_ok in H5; eauto.
apply subsumed_merge_both; auto.
unfold subsumed, merge_astate; simplify.
assert (subsumed s1 s) by assumption.
specialize (H4 x0).
specialize (H5 x0).
cases (x ==v x0); subst; simplify; eauto.
eauto using subsumed_trans.
Qed.
(* In a sound and complete abstract interpretation, [iterate] produces a genuine
* invariant for the flow-insensitive execution of a command, phrased in terms
* of subsumption. *)
Theorem iterate_ok : forall a, absint_sound a
-> absint_complete a
-> forall c (s0 s : astate a),
iterate c s0 s
-> invariantFor (flow_insensitive_trsys s0 c) (fun s' => subsumed s' s).
Proof.
eauto using iterate_ok'.
Qed.
(* Here's our corral of automatic tactics for the day. These do the boring
* parts of iteration for us. *)
Ltac insensitive_simpl := unfold merge_astate; simplify; repeat simplify_map.
Ltac runAllAssignments := repeat (constructor; insensitive_simpl).
Ltac iterate1 := eapply IterateStep; [ simplify; runAllAssignments | ].
Ltac iterate_done := eapply IterateDone; simplify; runAllAssignments.
(* Here's a good first program to try our analysis on. *)
Definition straightline :=
"a" <- 7;;
"b" <- "b" + 2 * "a";;
"a" <- "a" + "b".
(* A useful property, capturing the intended meaning of [Even] *)
Lemma final_even : forall (s s' : astate parity_absint) v x,
insensitive_compatible s v
-> subsumed s s'
-> s' $? x = Some Even
-> exists n, v $? x = Some n /\ isEven n.
Proof.
unfold insensitive_compatible, subsumed; simplify.
specialize (H x); specialize (H0 x).
cases (s $? x); simplify.
rewrite Heq in *.
assert (Some d = Some d) by equality.
apply H in H2.
first_order.
eapply H0 in H1.
invert H1.
eauto.
assumption.
specialize (H2 1).
invert H2; try (exfalso; eauto).
rewrite Heq in *.
equality.
Qed.
(* Now we can verify the example program, saying that, when the program
* finishes, variable ["b"] holds an even number. *)
Theorem straightline_even :
invariantFor (trsys_of ($0 $+ ("a", 0) $+ ("b", 0)) straightline)
(fun p => snd p = Skip
-> exists n, fst p $? "b" = Some n /\ isEven n).
Proof.
(* We start off much as with model checking: strengthening the invariant. *)
simplify.
eapply invariant_weaken.
(* Now we use simulation to switch to analyzing the flow-insensitive
* program. *)
unfold straightline.
eapply invariant_simulates.
apply insensitive_simulates with (s := $0 $+ ("a", Even) $+ ("b", Even))
(a := parity_absint).
apply parity_sound.
unfold insensitive_compatible; simplify.
cases (x ==v "b"); simplify.
invert H; eauto.
cases (x ==v "a"); simplify.
invert H; eauto.
equality.
(* Now we apply the general principle that iteration is a sound way to find an
* invariant for a flow-insensitive program. *)
apply iterate_ok.
apply parity_sound.
apply parity_complete.
(* Time to iterate! It only takes one round of run-all-commands to reach an
* [astate] that covers all reachable states. Watch the [astate] change as we
* iterate. *)
iterate1.
iterate_done.
(* Now the routine step of showing that our calculated invariant implies the
* original one from the theorem statement. *)
invert 1.
invert H0; simplify.
eapply final_even; eauto; simplify; equality.
Qed.
(* Everything still works with programs that have conditions. *)
Definition less_straightline :=
"a" <- 7;;
when "c" then
"b" <- "b" + 2 * "a"
else
"b" <- 18
done.
Theorem less_straightline_even :
invariantFor (trsys_of ($0 $+ ("a", 0) $+ ("b", 0)) less_straightline)
(fun p => snd p = Skip
-> exists n, fst p $? "b" = Some n /\ isEven n).
Proof.
simplify.
eapply invariant_weaken.
unfold less_straightline.
eapply invariant_simulates.
apply insensitive_simulates with (s := $0 $+ ("a", Even) $+ ("b", Even))
(a := parity_absint).
apply parity_sound.
unfold insensitive_compatible; simplify.
cases (x ==v "b"); simplify.
invert H; eauto.
cases (x ==v "a"); simplify.
invert H; eauto.
equality.
apply iterate_ok.
apply parity_sound.
apply parity_complete.
iterate1.
iterate_done.
invert 1.
invert H0; simplify.
eapply final_even; eauto; simplify; equality.
Qed.
(* It works for loops, too. *)
Definition loopy :=
"n" <- 100;;
"a" <- 0;;
while "n" loop
"a" <- "a" + "n";;
"n" <- "n" - 2
done.
Theorem loopy_even :
invariantFor (trsys_of ($0 $+ ("n", 0) $+ ("a", 0)) loopy)
(fun p => snd p = Skip
-> exists n, fst p $? "n" = Some n /\ isEven n).
Proof.
simplify.
eapply invariant_weaken.
unfold loopy.
eapply invariant_simulates.
apply insensitive_simulates with (s := $0 $+ ("n", Even) $+ ("a", Even))
(a := parity_absint).
apply parity_sound.
unfold insensitive_compatible; simplify.
cases (x ==v "a"); simplify.
invert H; eauto.
cases (x ==v "n"); simplify.
invert H; eauto.
equality.
apply iterate_ok.
apply parity_sound.
apply parity_complete.
(* This time, the original [astate] is already fully general. *)
iterate_done.
invert 1.
invert H0; simplify.
eapply final_even; eauto; simplify; equality.
Qed.
(** * Flow-sensitive analysis *)
(* Flow-insensitive parity analysis will get stuck on a program like this one,
* assuming, like before, that all variables start out initialized to zero. *)
Definition simple :=
"a" <- 7;;
"b" <- 8;;
"a" <- "a" + "b";;
"b" <- ("a" + 1) * ("b" + 1).
(* Unfortunately, some variables get assigned odd values in this program.
* However the parity of a variable is still uniquely determined, given
* *where we are in the program*! Flow-sensitive analysis attaches a different
* [astate] to every intermediate program, allowing us to handle such
* potentially tricky cases. We will be able to do a fully accurate
* flow-sensitive parity analysis on this program. *)
(* Here, we can get away with a simpler definition of compatibility than last
* time. *)
Definition compatible a (s : astate a) (v : valuation) : Prop :=
forall x xa, s $? x = Some xa
-> exists n, v $? x = Some n
/\ a.(Represents) n xa.
Lemma compatible_add : forall a (s : astate a) v x na n,
compatible s v
-> a.(Represents) n na
-> compatible (s $+ (x, na)) (v $+ (x, n)).
Proof.
unfold compatible; simplify.
cases (x ==v x0); simplify; eauto.
invert H1; eauto.
Qed.
Hint Resolve compatible_add : core.
(* A similar result follows about soundness of expression interpretation. *)
Theorem absint_interp_ok2 : forall a, absint_sound a
-> forall (s : astate a) v e,
compatible s v
-> a.(Represents) (interp e v) (absint_interp e s).
Proof.
induct e; simplify; eauto.
cases (s $? x); auto.
unfold compatible in H0.
apply H0 in Heq.
invert Heq.
propositional.
rewrite H2.
assumption.
Qed.
Hint Resolve absint_interp_ok2 : core.
(* The new type of invariant we calculate as we go: a map from commands to
* [astate]s. The idea is that we populate this map with the commands that show
* up as we step the program through full executions. *)
Definition astates (a : absint) := fmap cmd (astate a).
(* Here's an executable version of executing a command for a single step
* abstractly. The function is to return an [astates] capturing all the "places