-
Notifications
You must be signed in to change notification settings - Fork 0
/
Regalloc.v
495 lines (427 loc) · 16.2 KB
/
Regalloc.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
Require Import Coq.Program.Equality.
Require Import FSets.
From SF Require Import Imp.
From SF Require Import Semantics.
From SF Require Import Deadcode.
Import VSdecide.
(** In this chapter: a study of register allocation viewed as
renaming the variables of an IMP program in a semantic-preserving
manner.*)
(** * 1. Renaming variables in a program *)
(** Recognition of expressions that are just variables. *)
Definition expr_is_var (a: aexp): option id :=
match a with AId x => Some x | _ => None end.
Lemma expr_is_var_correct:
forall a x, expr_is_var a = Some x -> a = AId x.
Proof.
unfold expr_is_var; intros. destruct a; congruence.
Qed.
Section RENAMING.
Variable f: id -> id.
(** Renaming variables in an expression. *)
Fixpoint rename_aexp (a: aexp) : aexp :=
match a with
| ANum n => ANum n
| AId x => AId (f x)
| APlus a1 a2 => APlus (rename_aexp a1) (rename_aexp a2)
| AMinus a1 a2 => AMinus (rename_aexp a1) (rename_aexp a2)
| AMult a1 a2 => AMult (rename_aexp a1) (rename_aexp a2)
end.
Fixpoint rename_bexp (b: bexp) : bexp :=
match b with
| BTrue => BTrue
| BFalse => BFalse
| BEq a1 a2 => BEq (rename_aexp a1) (rename_aexp a2)
| BLe a1 a2 => BLe (rename_aexp a1) (rename_aexp a2)
| BNot b1 => BNot (rename_bexp b1)
| BAnd b1 b2 => BAnd (rename_bexp b1) (rename_bexp b2)
end.
(** Transformation of a command:
- renaming the variables
- dead code elimination as in the previous chapter
- coalescing: useless variable-to-variable copies [x ::= y] where
[f x = f y]are turned into [SKIP].
*)
Fixpoint transf_com (c: com) (L: VS.t): com :=
match c with
| SKIP => SKIP
| x ::= a =>
if VS.mem x L then
match expr_is_var a with
| None => (f x) ::= (rename_aexp a)
| Some y => if beq_id (f x) (f y) then SKIP else (f x) ::= (rename_aexp a)
end
else SKIP
| (c1; c2) =>
(transf_com c1 (live c2 L); transf_com c2 L)
| IFB b THEN c1 ELSE c2 FI =>
IFB rename_bexp b THEN transf_com c1 L ELSE transf_com c2 L FI
| WHILE b DO c END =>
WHILE rename_bexp b DO transf_com c (live (WHILE b DO c END) L) END
end.
(** * 2. Semantic preservation *)
(** We adapt the notion of agreement between two states over a given
set of live variables that was introduced in chapter [Deadcode]. *)
Definition agree (L: VS.t) (s1 s2: state) : Prop :=
forall x, VS.In x L -> s1 x = s2 (f x).
(** Monotonicity property. *)
Lemma agree_mon:
forall L L' s1 s2,
agree L' s1 s2 -> VS.Subset L L' -> agree L s1 s2.
Proof.
unfold VS.Subset, agree; intros. auto.
Qed.
Lemma agree_extensional:
forall L s1 s2 s3,
agree L s1 s2 -> (forall x, s2 x = s3 x) -> agree L s1 s3.
Proof.
unfold agree; intros. transitivity (s2 (f x)); auto.
Qed.
(** Agreement on the free variables of an expression implies that this
expression and its renaming evaluates identically in both states. *)
Lemma aeval_agree:
forall L s1 s2, agree L s1 s2 ->
forall a, VS.Subset (fv_aexp a) L ->
aeval s1 a = aeval s2 (rename_aexp a).
Proof.
induction a; simpl; intros.
auto.
apply H. generalize H0; fsetdec.
f_equal. apply IHa1. generalize H0; fsetdec. apply IHa2. generalize H0; fsetdec.
f_equal. apply IHa1. generalize H0; fsetdec. apply IHa2. generalize H0; fsetdec.
f_equal. apply IHa1. generalize H0; fsetdec. apply IHa2. generalize H0; fsetdec.
Qed.
Lemma beval_agree:
forall L s1 s2, agree L s1 s2 ->
forall b, VS.Subset (fv_bexp b) L ->
beval s1 b = beval s2 (rename_bexp b).
Proof.
induction b; simpl; intros.
auto.
auto.
repeat rewrite (aeval_agree L s1 s2); auto; generalize H0; fsetdec.
repeat rewrite (aeval_agree L s1 s2); auto; generalize H0; fsetdec.
f_equal; auto.
f_equal. apply IHb1. generalize H0; fsetdec. apply IHb2. generalize H0; fsetdec.
Qed.
(** Agreement is preserved by unilateral assignment to a dead variable. *)
Lemma agree_update_dead:
forall s1 s2 L x v,
agree L s1 s2 -> ~VS.In x L ->
agree L (update s1 x v) s2.
Proof.
intros; red; intros. unfold update. remember (beq_id x x0). destruct b.
assert (x = x0). apply beq_id_eq. auto. subst x0. contradiction.
apply H; auto.
Qed.
(** Agreement is preserved by simultaneous assignment to a live variable
[x] and its renaming [f x], provided none of the other live variables
[z] is renamed to [f x] (non-interference property). *)
Lemma agree_update_live:
forall s1 s2 L x v,
agree (VS.remove x L) s1 s2 ->
(forall z, VS.In z L -> z <> x -> f z <> f x) ->
agree L (update s1 x v) (update s2 (f x) v).
Proof.
intros; red; intros. unfold update. remember (beq_id x x0). destruct b.
(* Case x = x0 *)
assert (x = x0). apply beq_id_eq. auto. subst x0.
rewrite <- beq_id_refl. auto.
(* Case x <> x0 *)
assert (x <> x0). apply beq_id_false_not_eq; auto.
assert (f x0 <> f x). apply H0; auto.
rewrite not_eq_beq_id_false.
apply H. apply VS.remove_2; auto.
auto.
Qed.
(** A special case of [agree_update_live] where the value being assigned
is not arbitrary, but is the value of another variable [y].
In this case, we can relax the non-interference hypothesis
(Chaitin's observation). *)
Lemma agree_update_move:
forall s1 s2 L x y,
agree (VS.union (VS.remove x L) (VS.singleton y)) s1 s2 ->
(forall z, VS.In z L -> z <> x -> z <> y -> f z <> f x) ->
agree L (update s1 x (s1 y)) (update s2 (f x) (s2 (f y))).
Proof.
intros; red; intros. unfold update. remember (beq_id x x0). destruct b.
(* Case x = x0 *)
assert (x = x0). apply beq_id_eq. auto. subst x0.
rewrite <- beq_id_refl. apply H. fsetdec.
(* Case x <> x0 *)
assert (x <> x0). apply beq_id_false_not_eq; auto.
remember (beq_id x0 y). destruct b.
(* SCase x0 = y *)
assert (x0 = y). apply beq_id_eq. auto. subst x0.
transitivity (s2 (f y)).
apply H. fsetdec.
destruct (beq_id (f x) (f y)); auto.
(* SCase x0 <> y *)
assert (x0 <> y). apply beq_id_false_not_eq; auto.
assert (f x0 <> f x). apply H0; auto.
rewrite not_eq_beq_id_false.
apply H. apply VS.union_2. apply VS.remove_2; auto.
auto.
Qed.
(** A special case of [agree_update_move] where [f x = f y].
In this case, the assignment in the transformed program
can be optimized away. *)
Lemma agree_update_coalesced_move:
forall s1 s2 L x y,
agree (VS.union (VS.remove x L) (VS.singleton y)) s1 s2 ->
(forall z, VS.In z L -> z <> x -> z <> y -> f z <> f x) ->
f x = f y ->
agree L (update s1 x (s1 y)) s2.
Proof.
intros. apply agree_extensional with (update s2 (f x) (s2 (f y))).
apply agree_update_move; auto.
intros. rewrite H1. unfold update. remember (beq_id (f y) x0). destruct b.
assert (f y = x0). apply beq_id_eq; auto.
congruence.
auto.
Qed.
(** Gathering together the various non-interference conditions above,
we obtain the following correctness criterion for a candidate
renaming [f]. Note that this is a boolean-valued function, not
a predicate; therefore, it can be used as a validator for
a renaming computed by an untrusted heuristic. *)
Fixpoint correct_allocation (c: com) (L: VS.t) : bool :=
match c with
| SKIP =>
true
| x ::= a =>
if VS.mem x L then
match expr_is_var a with
| Some y =>
VS.for_all (fun z => beq_id z x || beq_id z y || negb (beq_id (f z) (f x))) L
| None =>
VS.for_all (fun z => beq_id z x || negb (beq_id (f z) (f x))) L
end
else true
| (c1; c2) =>
correct_allocation c1 (live c2 L) && correct_allocation c2 L
| IFB b THEN c1 ELSE c2 FI =>
correct_allocation c1 L && correct_allocation c2 L
| WHILE b DO c END =>
correct_allocation c (live (WHILE b DO c END) L)
end.
Remark correct_allocation_assign_1:
forall x y L,
VS.for_all (fun z => beq_id z x || beq_id z y || negb (beq_id (f z) (f x))) L = true ->
forall z, VS.In z L -> z <> x -> z <> y -> f z <> f x.
Proof.
intros.
set (g := fun z => beq_id z x || beq_id z y || negb (beq_id (f z) (f x))) in *.
assert (VS.For_all (fun x => g x = true) L).
apply VS.for_all_2.
red; intros. congruence.
auto.
red in H3. generalize (H3 z H0). unfold g.
rewrite not_eq_beq_id_false; auto.
rewrite not_eq_beq_id_false; auto.
simpl. remember (beq_id (f z) (f x)). destruct b; simpl; intros.
congruence.
apply beq_id_false_not_eq; auto.
Qed.
Remark correct_allocation_assign_2:
forall x L,
VS.for_all (fun z => beq_id z x || negb (beq_id (f z) (f x))) L = true ->
forall z, VS.In z L -> z <> x -> f z <> f x.
Proof.
intros.
set (g := fun z => beq_id z x || negb (beq_id (f z) (f x))) in *.
assert (VS.For_all (fun x => g x = true) L).
apply VS.for_all_2.
red; intros. congruence.
auto.
red in H2. generalize (H2 z H0). unfold g.
rewrite not_eq_beq_id_false; auto.
simpl. remember (beq_id (f z) (f x)). destruct b; simpl; intros.
congruence.
apply beq_id_false_not_eq; auto.
Qed.
(** The proof of semantic preservation is a straightforward adaptation
of the proof for dead code elimination. There are more cases to
be considered in the assignment case [x ::= a], corresponding
to variable-to-variable moves [x ::= y], either coalesced or not. *)
Theorem transf_correct_terminating:
forall st c st', c / st ==> st' ->
forall L st1,
agree (live c L) st st1 ->
correct_allocation c L = true ->
exists st1', transf_com c L / st1 ==> st1' /\ agree L st' st1'.
Proof.
induction 1; intros L st1 AG CORR; simpl; simpl in CORR.
(*Case skip *)
exists st1; split. constructor. auto.
(* := *)
rename l into x. simpl in AG. remember (VS.mem x L) as is_live. destruct is_live.
(* SSCase x is live after *)
assert (aeval st1 (rename_aexp a1) = n).
rewrite <- H. symmetry. eapply aeval_agree. eauto. fsetdec.
remember (expr_is_var a1) as is_var. destruct is_var as [y | ].
(* SSCasemove x := y *)
assert (a1 = AId y). apply expr_is_var_correct; auto. subst a1. simpl in *.
remember (beq_id (f x) (f y)) as coalesced. destruct coalesced.
(* SSCase coalesced move *)
assert (f x = f y). apply beq_id_eq; auto.
exists st1; split.
apply E_Skip.
rewrite <- H. apply agree_update_coalesced_move; auto.
apply correct_allocation_assign_1; auto.
(* SSCase preserved move *)
exists (update st1 (f x) n); split.
apply E_Ass. simpl. auto.
rewrite <- H at 1. rewrite <- H0.
apply agree_update_move; auto.
apply correct_allocation_assign_1; auto.
(* SSCase not a move *)
exists (update st1 (f x) n); split.
apply E_Ass. auto.
apply agree_update_live.
eapply agree_mon; eauto. fsetdec.
apply correct_allocation_assign_2; auto.
(* SCase x is dead after *)
exists st1; split.
apply E_Skip.
apply agree_update_dead. auto.
red; intros. assert (VS.mem x L = true). apply VS.mem_1; auto. congruence.
(* Case seq *)
simpl in AG. destruct (andb_true _ _ CORR).
destruct (IHceval1 _ _ AG H1) as [st1' [E1 A1]].
destruct (IHceval2 _ _ A1 H2) as [st2' [E2 A2]].
exists st2'; split.
apply E_Seq with st1'; auto.
auto.
(* Case if true *)
simpl in AG. destruct (andb_true _ _ CORR).
assert (beval st1 (rename_bexp b1) = true).
rewrite <- H. symmetry. eapply beval_agree; eauto. fsetdec.
destruct (IHceval L st1) as [st1' [E A]].
eapply agree_mon; eauto. fsetdec.
auto.
exists st1'; split.
apply E_IfTrue; auto.
auto.
(* Case if false *)
simpl in AG. destruct (andb_true _ _ CORR).
assert (beval st1 (rename_bexp b1) = false).
rewrite <- H. symmetry. eapply beval_agree; eauto. fsetdec.
destruct (IHceval L st1) as [st1' [E A]].
eapply agree_mon; eauto. fsetdec.
auto.
exists st1'; split.
apply E_IfFalse; auto.
auto.
(* Case while end *)
destruct (live_while_charact b1 c1 L) as [P [Q R]].
assert (beval st1 (rename_bexp b1) = false).
rewrite <- H. symmetry. eapply beval_agree; eauto.
exists st1; split.
apply E_WhileEnd. auto.
eapply agree_mon; eauto.
(* Case while loop *)
destruct (live_while_charact b1 c1 L) as [P [Q R]].
assert (beval st1 (rename_bexp b1) = true).
rewrite <- H. symmetry. eapply beval_agree; eauto.
destruct (IHceval1 (live (CWhile b1 c1) L) st1) as [st2 [E1 A1]].
eapply agree_mon; eauto.
auto.
destruct (IHceval2 L st2) as [st3 [E2 A2]].
auto. auto.
exists st3; split.
apply E_WhileLoop with st2; auto.
auto.
Qed.
Theorem transf_correct_diverging:
forall st c L st1,
c / st ==> ∞ ->
agree (live c L) st st1 ->
correct_allocation c L = true ->
transf_com c L / st1 ==> ∞.
Proof.
cofix COINDHYP; intros until st1; intros DIV AG CORR; inv DIV.
(* Case seq left *)
simpl in *. destruct (andb_prop _ _ CORR).
apply Einf_Seq_1. apply COINDHYP with st. auto. auto. auto.
(* Case seq right *)
simpl in *. destruct (andb_prop _ _ CORR).
destruct (transf_correct_terminating _ _ _ H _ _ AG H1) as [st1' [E A]].
apply Einf_Seq_2 with st1'. auto. apply COINDHYP with st2; auto.
(* Case if true *)
simpl in *. destruct (andb_prop _ _ CORR).
assert (beval st1 (rename_bexp b) = true).
rewrite <- H. symmetry. eapply beval_agree; eauto. fsetdec.
apply Einf_IfTrue. auto.
apply COINDHYP with st. auto. eapply agree_mon; eauto. fsetdec. auto.
(* Case if false *)
simpl in *. destruct (andb_prop _ _ CORR).
assert (beval st1 (rename_bexp b) = false).
rewrite <- H. symmetry. eapply beval_agree; eauto. fsetdec.
apply Einf_IfFalse. auto.
apply COINDHYP with st. auto. eapply agree_mon; eauto. fsetdec. auto.
(* Case while body *)
destruct (live_while_charact b c1 L) as [P [Q R]].
assert (beval st1 (rename_bexp b) = true).
rewrite <- H. symmetry. eapply beval_agree; eauto.
apply Einf_WhileBody. auto.
apply COINDHYP with st. auto. eapply agree_mon; eauto. auto.
(* while loop *)
destruct (live_while_charact b c1 L) as [P [Q R]].
assert (beval st1 (rename_bexp b) = true).
rewrite <- H. symmetry. eapply beval_agree; eauto.
destruct (transf_correct_terminating _ _ _ H0 (live (CWhile b c1) L) st1) as [st2 [E1 A1]].
eapply agree_mon; eauto. auto.
apply Einf_WhileLoop with st2. auto. apply E1.
apply (COINDHYP st' (WHILE b DO c1 END) L st2). auto. auto. auto.
Qed.
End RENAMING.
(** * 3. Examples *)
(** If we give it the identity renaming, our code transformation
behaves exactly like dead code elimination. *)
Definition trivial_alloc := fun (x: id) => x.
Eval vm_compute in (correct_allocation trivial_alloc prog (VS.singleton vr)).
Eval vm_compute in (transf_com trivial_alloc prog (VS.singleton vr)).
(** Result is:
<<
r := a; ===> r := a;
q := 0; skip;
while b <= r do while b <= r do
r := r - b; r := r - b;
q := q + 1; skip;
done done
>>
*)
(** Here is another allocation that collapses variables [r] and [a].*)
Definition my_alloc := fun (x: id) => if beq_id x vr then va else x.
Eval vm_compute in (correct_allocation my_alloc prog (VS.singleton vr)).
Eval vm_compute in (transf_com my_alloc prog (VS.singleton vr)).
(** Here is the result. Note the removal of [r := a].
<<
r := a; ===> skip;
q := 0; skip;
while b <= r do while b <= a do
r := r - b; a := a - b;
q := q + 1; skip;
done done
>>
*)
(** Finally, if we try to collapse variable [r] and [b], the validation
function [correct_allocation] rejects the allocation. *)
Definition wrong_alloc := fun (x: id) => if beq_id x vr then vb else x.
Eval vm_compute in (correct_allocation wrong_alloc prog (VS.singleton vr)).
(** * 4. Putting it all together *)
(** We assume given a register allocation heuristic that computes a candidate
allocation. *)
Parameter regalloc_heuristic: com -> VS.t -> (id -> id).
(** Typically, this heuristic will be written in Caml and linked with
the Caml code extracted from the present Coq development. *)
(** We then define the register allocation pass. [prog] is the program to be
transformed and [obs] is the set of variables we want to observe at the
end of its execution. This compilation pass can fail at compile-time,
in which case it returns [None]. *)
Definition regalloc (prog: com) (obs: VS.t) : option com :=
let f := regalloc_heuristic prog obs in
if correct_allocation f prog obs
then Some(transf_com f prog obs)
else None.