summaryrefslogtreecommitdiff
path: root/backend/Linearizeproof.v
blob: b80acb4d8d06fc63c63d63b576e9e91302df37db (plain)
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
(** Correctness proof for code linearization *)

Require Import Coqlib.
Require Import Maps.
Require Import AST.
Require Import Integers.
Require Import Values.
Require Import Mem.
Require Import Globalenvs.
Require Import Op.
Require Import Locations.
Require Import LTL.
Require Import Linear.
Require Import Linearize.
Require Import Lattice.

Section LINEARIZATION.

Variable prog: LTL.program.
Let tprog := transf_program prog.

Let ge := Genv.globalenv prog.
Let tge := Genv.globalenv tprog.

Lemma functions_translated:
  forall v f,
  Genv.find_funct ge v = Some f ->
  Genv.find_funct tge v = Some (transf_function f).
Proof (@Genv.find_funct_transf _ _ transf_function prog).

Lemma function_ptr_translated:
  forall v f,
  Genv.find_funct_ptr ge v = Some f ->
  Genv.find_funct_ptr tge v = Some (transf_function f).
Proof (@Genv.find_funct_ptr_transf _ _ transf_function prog).

Lemma symbols_preserved:
  forall id,
  Genv.find_symbol tge id = Genv.find_symbol ge id.
Proof (@Genv.find_symbol_transf _ _ transf_function prog).

(** * Correctness of reachability analysis *)

(** The entry point of the function is reachable. *)

Lemma reachable_entrypoint:
  forall f, (reachable f)!!(f.(fn_entrypoint)) = true.
Proof.
  intros. unfold reachable.
  caseEq (reachable_aux f).
  unfold reachable_aux; intros reach A.
  assert (LBoolean.ge reach!!(f.(fn_entrypoint)) true).
  eapply DS.fixpoint_entry. eexact A. auto with coqlib.
  unfold LBoolean.ge in H. tauto.
  intros. apply PMap.gi.
Qed.

(** The successors of a reachable basic block are reachable. *)

Lemma reachable_successors:
  forall f pc pc',
  f.(LTL.fn_code)!pc <> None ->
  In pc' (successors f pc) ->
  (reachable f)!!pc = true ->
  (reachable f)!!pc' = true.
Proof.
  intro f. unfold reachable.
  caseEq (reachable_aux f).
  unfold reachable_aux. intro reach; intros.
  assert (LBoolean.ge reach!!pc' reach!!pc).
  change (reach!!pc) with ((fun pc r => r) pc (reach!!pc)).
  eapply DS.fixpoint_solution. eexact H.
  elim (fn_code_wf f pc); intro. auto. contradiction.
  auto. 
  elim H3; intro. congruence. auto.
  intros. apply PMap.gi.
Qed.

(* If we have a valid LTL transition from [pc] to [pc'], and
  [pc] is reachable, then [pc'] is reachable. *)

Lemma reachable_correct_1:
  forall f sp pc rs m pc' rs' m' b,
  f.(LTL.fn_code)!pc = Some b ->
  exec_block ge sp b rs m (Cont pc') rs' m' ->
  (reachable f)!!pc = true ->
  (reachable f)!!pc' = true.
Proof.
  intros. apply reachable_successors with pc; auto.
  congruence.
  eapply successors_correct; eauto.
Qed.

Lemma reachable_correct_2:
  forall c sp pc rs m out rs' m',
  exec_blocks ge c sp pc rs m out rs' m' ->  
  forall f pc',
  c = f.(LTL.fn_code) ->
  out = Cont pc' ->
  (reachable f)!!pc = true ->
  (reachable f)!!pc' = true.
Proof.
  induction 1; intros.
  congruence.
  eapply reachable_correct_1. rewrite <- H1; eauto. 
  subst out; eauto. auto.
  auto.
Qed.

(** * Properties of node enumeration *)

(** An enumeration of CFG nodes is correct if the following conditions hold:
- All nodes for reachable basic blocks must be in the list.
- The function entry point is the first element in the list.
- The list is without repetition (so that no code duplication occurs).

We prove that our [enumerate] function satisfies all three. *)

Lemma enumerate_complete:
  forall f pc i,
  f.(LTL.fn_code)!pc = Some i ->
  (reachable f)!!pc = true ->
  In pc (enumerate f).
Proof.
  intros. 
  assert (forall p, 
    Plt p (Psucc f.(fn_entrypoint)) ->
    (reachable f)!!p = true ->
    In p (enumerate f)).
  unfold enumerate. pattern (Psucc (fn_entrypoint f)).
  apply positive_Peano_ind. 
  intros. compute in H1. destruct p; discriminate. 
  intros. rewrite positive_rec_succ. elim (Plt_succ_inv _ _ H2); intro.
  case (reachable f)!!x. apply in_cons. auto. auto.
  subst x. rewrite H3. apply in_eq. 
  elim (LTL.fn_code_wf f pc); intro. auto. congruence.
Qed. 

Lemma enumerate_head:
  forall f, exists l, enumerate f = f.(LTL.fn_entrypoint) :: l.
Proof.
  intro. unfold enumerate. rewrite positive_rec_succ. 
  rewrite reachable_entrypoint.
  exists (positive_rec (list node) nil
    (fun (pc : positive) (nodes : list node) =>
      if (reachable f) !! pc then pc :: nodes else nodes) 
    (fn_entrypoint f) ).
  auto.
Qed.

Lemma enumerate_norepet:
  forall f, list_norepet (enumerate f).
Proof.
  intro. 
  unfold enumerate. pattern (Psucc (fn_entrypoint f)).
  apply positive_Peano_ind.  
  rewrite positive_rec_base. constructor.
  intros. rewrite positive_rec_succ.
  case (reachable f)!!x. auto.
  constructor. 
  assert (forall y,
    In y (positive_rec
     (list node) nil
     (fun (pc : positive) (nodes : list node) =>
      if (reachable f) !! pc then pc :: nodes else nodes) x) ->
    Plt y x).
    pattern x. apply positive_Peano_ind. 
    rewrite positive_rec_base. intros. elim H0.
    intros until y. rewrite positive_rec_succ. 
    case (reachable f)!!x0. 
    simpl. intros [A|B].
    subst x0. apply Plt_succ.
    apply Plt_trans_succ. auto. 
    intro. apply Plt_trans_succ. auto.
  red; intro. generalize (H0 x H1). exact (Plt_strict x). auto.
  auto.
Qed.

(** * Correctness of the cleanup pass *)

(** If labels are globally unique and the Linear code [c] contains
  a subsequence [Llabel lbl :: c1], [find_label lbl c] returns [c1].
*)

Fixpoint unique_labels (c: code) : Prop :=
  match c with
  | nil => True
  | Llabel lbl :: c => ~(In (Llabel lbl) c) /\ unique_labels c
  | i :: c => unique_labels c
  end.

Inductive is_tail: code -> code -> Prop :=
  | is_tail_refl:
      forall c, is_tail c c
  | is_tail_cons:
      forall i c1 c2, is_tail c1 c2 -> is_tail c1 (i :: c2).

Lemma is_tail_in:
  forall i c1 c2, is_tail (i :: c1) c2 -> In i c2.
Proof.
  induction c2; simpl; intros.
  inversion H.
  inversion H. tauto. right; auto.
Qed.

Lemma is_tail_cons_left:
  forall i c1 c2, is_tail (i :: c1) c2 -> is_tail c1 c2.
Proof.
  induction c2; intros; inversion H.
  constructor. constructor. constructor. auto. 
Qed.

Lemma find_label_unique:
  forall lbl c1 c2 c3,
  is_tail (Llabel lbl :: c1) c2 ->
  unique_labels c2 ->
  find_label lbl c2 = Some c3 ->
  c1 = c3.
Proof.
  induction c2.
  simpl; intros; discriminate.
  intros c3 TAIL UNIQ. simpl.
  generalize (is_label_correct lbl a). case (is_label lbl a); intro ISLBL.
  subst a. intro. inversion TAIL. congruence. 
  elim UNIQ; intros. elim H4. apply is_tail_in with c1; auto.
  inversion TAIL. congruence. apply IHc2. auto. 
  destruct a; simpl in UNIQ; tauto.
Qed.

(** Correctness of the [starts_with] test. *)

Lemma starts_with_correct:
  forall lbl c1 c2 c3 f sp ls m,
  is_tail c1 c2 ->
  unique_labels c2 ->
  starts_with lbl c1 = true ->
  find_label lbl c2 = Some c3 ->
  exec_instrs tge f sp (cleanup_code c1) ls m 
                       (cleanup_code c3) ls m.
Proof.
  induction c1.
  simpl; intros; discriminate.
  simpl starts_with. destruct a; try (intros; discriminate).
  intros. apply exec_trans with (cleanup_code c1) ls m.
  simpl. 
  constructor. constructor. 
  destruct (peq lbl l).
  subst l. replace c3 with c1. constructor.
  apply find_label_unique with lbl c2; auto.
  apply IHc1 with c2; auto. eapply is_tail_cons_left; eauto.
Qed.

(** Code cleanup preserves the labeling of the code. *)

Lemma find_label_cleanup_code:
  forall lbl c c',
  find_label lbl c = Some c' ->
  find_label lbl (cleanup_code c) = Some (cleanup_code c').
Proof.
  induction c.  
  simpl. intros; discriminate.
  generalize (is_label_correct lbl a). 
  simpl find_label. case (is_label lbl a); intro.
  subst a. intros. injection H; intros. subst c'. 
  simpl. rewrite peq_true. auto.
  intros. destruct a; auto. 
  simpl. rewrite peq_false. auto.
  congruence. 
  case (starts_with l c). auto. simpl. auto.
Qed.

(** One transition in the original Linear code corresponds to zero
  or one transitions in the cleaned-up code. *)

Lemma cleanup_code_correct_1:
  forall f sp c1 ls1 m1 c2 ls2 m2,
  exec_instr tge f sp c1 ls1 m1 c2 ls2 m2 ->
  is_tail c1 f.(fn_code) ->
  unique_labels f.(fn_code) ->
  exec_instrs tge (cleanup_function f) sp 
                       (cleanup_code c1) ls1 m1
                       (cleanup_code c2) ls2 m2.
Proof.
  induction 1; simpl; intros;
  try (apply exec_one; econstructor; eauto; fail).
  (* goto *)
  caseEq (starts_with lbl b); intro SW.
  eapply starts_with_correct; eauto.
  eapply is_tail_cons_left; eauto.
  constructor. constructor. 
  unfold cleanup_function; simpl. 
  apply find_label_cleanup_code. auto.
  (* cond, taken *)
  constructor. apply exec_Lcond_true. auto.
  unfold cleanup_function; simpl. 
  apply find_label_cleanup_code. auto.
  (* cond, not taken *)
  constructor. apply exec_Lcond_false. auto.
Qed. 

Lemma is_tail_find_label:
  forall lbl c2 c1,
  find_label lbl c1 = Some c2 -> is_tail c2 c1.
Proof.
  induction c1; simpl.
  intros; discriminate.
  case (is_label lbl a). intro. injection H; intro. subst c2.
  constructor. constructor.
  intro. constructor. auto. 
Qed.

Lemma is_tail_exec_instr:
  forall f sp c1 ls1 m1 c2 ls2 m2,
  exec_instr tge f sp c1 ls1 m1 c2 ls2 m2 ->
  is_tail c1 f.(fn_code) -> is_tail c2 f.(fn_code).
Proof.
  induction 1; intros;
  try (eapply is_tail_cons_left; eauto; fail).
  eapply is_tail_find_label; eauto.
  eapply is_tail_find_label; eauto.
Qed.

Lemma is_tail_exec_instrs:
  forall f sp c1 ls1 m1 c2 ls2 m2,
  exec_instrs tge f sp c1 ls1 m1 c2 ls2 m2 ->
  is_tail c1 f.(fn_code) -> is_tail c2 f.(fn_code).
Proof.
  induction 1; intros.
  auto.
  eapply is_tail_exec_instr; eauto.
  auto.
Qed.

(** Zero, one or several transitions in the original Linear code correspond
  to zero, one or several transitions in the cleaned-up code. *)

Lemma cleanup_code_correct_2:
  forall f sp c1 ls1 m1 c2 ls2 m2,
  exec_instrs tge f sp c1 ls1 m1 c2 ls2 m2 ->
  is_tail c1 f.(fn_code) ->
  unique_labels f.(fn_code) ->
  exec_instrs tge (cleanup_function f) sp 
                       (cleanup_code c1) ls1 m1
                       (cleanup_code c2) ls2 m2.
Proof.
  induction 1; simpl; intros.
  apply exec_refl.
  eapply cleanup_code_correct_1; eauto.
  apply exec_trans with (cleanup_code b2) rs2 m2.
  auto. 
  apply IHexec_instrs2; auto.
  eapply is_tail_exec_instrs; eauto.
Qed.

Lemma cleanup_function_correct:
  forall f ls1 m1 ls2 m2,
  exec_function tge f ls1 m1 ls2 m2 ->
  unique_labels f.(fn_code) ->
  exec_function tge (cleanup_function f) ls1 m1 ls2 m2. 
Proof.
  induction 1; intro.
  generalize (cleanup_code_correct_2 _ _ _ _ _ _ _ _ H0 (is_tail_refl _) H1).
  simpl. intro.
  econstructor; eauto.
Qed.

(** * Properties of linearized code *)

(** We now state useful properties of the Linear code generated by
  the naive LTL-to-Linear translation. *)

(** Connection between [find_label] and linearization. *)

Lemma find_label_lin_block:
  forall lbl k b,
  find_label lbl (linearize_block b k) = find_label lbl k.
Proof.
  induction b; simpl; auto.
  case (starts_with n k); reflexivity.
Qed.

Lemma find_label_lin_rec:
  forall f enum pc b,
  In pc enum ->
  f.(LTL.fn_code)!pc = Some b ->
  exists k,
  find_label pc (linearize_body f enum) = Some (linearize_block b k).
Proof.
  induction enum; intros.
  elim H.
  case (peq a pc); intro.
  subst a. exists (linearize_body f enum).
  simpl. rewrite H0. simpl. rewrite peq_true. auto.
  assert (In pc enum). simpl in H. tauto.
  elim (IHenum pc b H1 H0). intros k FIND.
  exists k. simpl. destruct (LTL.fn_code f)!a. 
  simpl. rewrite peq_false. rewrite find_label_lin_block. auto. auto.
  auto.
Qed.

Lemma find_label_lin:
  forall f pc b,
  f.(LTL.fn_code)!pc = Some b ->
  (reachable f)!!pc = true ->
  exists k,
  find_label pc (fn_code (linearize_function f)) = Some (linearize_block b k).
Proof.
  intros. unfold linearize_function; simpl. apply find_label_lin_rec.
  eapply enumerate_complete; eauto. auto.
Qed.

(** Unique label property for linearized code. *)

Lemma label_in_lin_block:
  forall lbl k b,
  In (Llabel lbl) (linearize_block b k) -> In (Llabel lbl) k.
Proof.
  induction b; simpl; try (intuition congruence).
  case (starts_with n k); simpl; intuition congruence.
Qed.

Lemma label_in_lin_rec:
  forall f lbl enum,
  In (Llabel lbl) (linearize_body f enum) -> In lbl enum.
Proof.
  induction enum.
  simpl; auto.
  simpl. destruct (LTL.fn_code f)!a. 
  simpl. intros [A|B]. left; congruence. 
  right. apply IHenum. eapply label_in_lin_block; eauto.
  intro; right; auto.
Qed.

Lemma unique_labels_lin_block:
  forall k b,
  unique_labels k -> unique_labels (linearize_block b k).
Proof.
  induction b; simpl; auto.
  case (starts_with n k); simpl; auto.
Qed.

Lemma unique_labels_lin_rec:
  forall f enum,
  list_norepet enum ->
  unique_labels (linearize_body f enum).
Proof.
  induction enum.
  simpl; auto.
  intro. simpl. destruct (LTL.fn_code f)!a. 
  simpl. split. red. intro. inversion H. elim H3.
  apply label_in_lin_rec with f. 
  apply label_in_lin_block with b. auto.
  apply unique_labels_lin_block. apply IHenum. inversion H; auto.
  apply IHenum. inversion H; auto.
Qed.

Lemma unique_labels_lin_function:
  forall f,
  unique_labels (fn_code (linearize_function f)).
Proof.
  intros. unfold linearize_function; simpl.
  apply unique_labels_lin_rec. apply enumerate_norepet. 
Qed.

(** * Correctness of linearization *)

(** The outcome of an LTL [exec_block] or [exec_blocks] execution
  is valid if it corresponds to a reachable, existing basic block.
  All outcomes occurring in an LTL program execution are actually
  valid, but we will prove that along with the main simulation proof. *)

Definition valid_outcome (f: LTL.function) (out: outcome) :=
  match out with
  | Cont s =>
      (reachable f)!!s = true /\ exists b, f.(LTL.fn_code)!s = Some b
  | Return => True
  end.

(** Auxiliary lemma used to establish the [valid_outcome] property. *)

Lemma exec_blocks_valid_outcome:
  forall c sp pc ls1 m1 out ls2 m2,
  exec_blocks ge c sp pc ls1 m1 out ls2 m2 ->
  forall f,
  c = f.(LTL.fn_code) ->
  (reachable f)!!pc = true ->
  valid_outcome f out ->
  valid_outcome f (Cont pc).
Proof.
  induction 1.
  auto.
  intros. simpl. split. auto. exists b. congruence. 
  intros. apply IHexec_blocks1. auto. auto.
  apply IHexec_blocks2. auto. 
  eapply reachable_correct_2. eexact H. auto. auto. auto.
  auto.
Qed.

(** Correspondence between an LTL outcome and a fragment of generated
  Linear code. *)

Inductive cont_for_outcome: LTL.function -> outcome -> code -> Prop :=
  | co_return:
      forall f k,
      cont_for_outcome f Return (Lreturn :: k)
  | co_goto:
      forall f s k,
      find_label s (linearize_function f).(fn_code) = Some k ->
      cont_for_outcome f (Cont s) k.

(** The simulation properties used in the inductive proof.
  They are parameterized by an LTL execution, and state
  that a matching execution is possible in the generated Linear code. *)

Definition exec_instr_prop 
  (sp: val) (b1: block) (ls1: locset) (m1: mem)
            (b2: block) (ls2: locset) (m2: mem) : Prop :=
  forall f k,
  exec_instr tge (linearize_function f) sp
                 (linearize_block b1 k) ls1 m1
                 (linearize_block b2 k) ls2 m2.

Definition exec_instrs_prop
  (sp: val) (b1: block) (ls1: locset) (m1: mem)
            (b2: block) (ls2: locset) (m2: mem) : Prop :=
  forall f k,
  exec_instrs tge (linearize_function f) sp
                  (linearize_block b1 k) ls1 m1
                  (linearize_block b2 k) ls2 m2.

Definition exec_block_prop
  (sp: val) (b: block) (ls1: locset) (m1: mem) 
            (out: outcome) (ls2: locset) (m2: mem): Prop :=
  forall f k,
  valid_outcome f out ->
  exists k',
  exec_instrs tge (linearize_function f) sp
                  (linearize_block b k) ls1 m1
                  k' ls2 m2
  /\ cont_for_outcome f out k'.

Definition exec_blocks_prop
  (c: LTL.code) (sp: val) (pc: node) (ls1: locset) (m1: mem) 
                      (out: outcome) (ls2: locset) (m2: mem): Prop :=
  forall f k,
  c = f.(LTL.fn_code) ->
  (reachable f)!!pc = true ->
  find_label pc (fn_code (linearize_function f)) = Some k ->
  valid_outcome f out ->
  exists k',
  exec_instrs tge (linearize_function f) sp
                  k ls1 m1
                  k' ls2 m2
  /\ cont_for_outcome f out k'.

Definition exec_function_prop
  (f: LTL.function) (ls1: locset) (m1: mem) (ls2: locset) (m2: mem): Prop :=
  exec_function tge (transf_function f) ls1 m1 ls2 m2.

Scheme exec_instr_ind5 := Minimality for LTL.exec_instr Sort Prop
  with exec_instrs_ind5 := Minimality for LTL.exec_instrs Sort Prop
  with exec_block_ind5 := Minimality for LTL.exec_block Sort Prop
  with exec_blocks_ind5 := Minimality for LTL.exec_blocks Sort Prop
  with exec_function_ind5 := Minimality for LTL.exec_function Sort Prop.

(** The obligatory proof by structural induction on the LTL evaluation
  derivation. *)

Lemma transf_function_correct:
  forall f ls1 m1 ls2 m2,
  LTL.exec_function ge f ls1 m1 ls2 m2 ->
  exec_function_prop f ls1 m1 ls2 m2.
Proof.
  apply (exec_function_ind5 ge
           exec_instr_prop
           exec_instrs_prop
           exec_block_prop
           exec_blocks_prop
           exec_function_prop);
  intros; red; intros; simpl.
  (* getstack *)
  constructor.
  (* setstack *)
  constructor.
  (* op *)
  constructor. rewrite <- H. apply eval_operation_preserved.
  exact symbols_preserved.
  (* load *)
  apply exec_Lload with a. 
  rewrite <- H. apply eval_addressing_preserved.
  exact symbols_preserved.
  auto.
  (* store *)
  apply exec_Lstore with a.
  rewrite <- H. apply eval_addressing_preserved.
  exact symbols_preserved.
  auto.
  (* call *)
  apply exec_Lcall with (transf_function f).
  generalize H. destruct ros; simpl.
  intro; apply functions_translated; auto.
  rewrite symbols_preserved. destruct (Genv.find_symbol ge i).
  intro; apply function_ptr_translated; auto. congruence.
  rewrite H0; reflexivity.
  apply H2. 
  (* instr_refl *)
  apply exec_refl.
  (* instr_one *)
  apply exec_one. apply H0. 
  (* instr_trans *)
  apply exec_trans with (linearize_block b2 k) rs2 m2.
  apply H0. apply H2.
  (* goto *)
  elim H1. intros REACH [b2 AT2]. 
  generalize (H0 f k). simpl. intro.
  elim (find_label_lin f s b2 AT2 REACH). intros k2 FIND.
  exists (linearize_block b2 k2).
  split. 
  eapply exec_trans. eexact H2. constructor. constructor. auto.
  constructor. auto. 
  (* cond, true *)
  elim H2. intros REACH [b2 AT2]. 
  elim (find_label_lin f ifso b2 AT2 REACH). intros k2 FIND.
  exists (linearize_block b2 k2).
  split.
  generalize (H0 f k). simpl. 
  case (starts_with ifso k); intro.
  eapply exec_trans. eexact H3. 
  eapply exec_trans. apply exec_one. apply exec_Lcond_false.
  change false with (negb true). apply eval_negate_condition. auto.
  apply exec_one. apply exec_Lgoto. auto. 
  eapply exec_trans. eexact H3. 
  apply exec_one. apply exec_Lcond_true.
  auto. auto. 
  constructor; auto.
  (* cond, false *)
  elim H2. intros REACH [b2 AT2]. 
  elim (find_label_lin f ifnot b2 AT2 REACH). intros k2 FIND.
  exists (linearize_block b2 k2).
  split.
  generalize (H0 f k). simpl. 
  case (starts_with ifso k); intro.
  eapply exec_trans. eexact H3. 
  apply exec_one. apply exec_Lcond_true. 
  change true with (negb false). apply eval_negate_condition. auto.
  auto. 
  eapply exec_trans. eexact H3.
  eapply exec_trans. apply exec_one. apply exec_Lcond_false. auto.
  apply exec_one. apply exec_Lgoto. auto. 
  constructor; auto.
  (* return *)
  exists (Lreturn :: k). split. 
  exact (H0 f k). constructor.
  (* refl blocks *)
  exists k. split. apply exec_refl. constructor. auto.
  (* one blocks *)
  subst c. elim (find_label_lin f pc b H H3). intros k' FIND.
  assert (k = linearize_block b k'). congruence. subst k.
  elim (H1 f k' H5). intros k'' [EXEC CFO].
  exists k''. tauto.
  (* trans blocks *)
  assert ((reachable f)!!pc2 = true).
    eapply reachable_correct_2. eexact H. auto. auto. auto.
  assert (valid_outcome f (Cont pc2)).
    eapply exec_blocks_valid_outcome; eauto.
  generalize (H0 f k H3 H4 H5 H8). intros [k1 [EX1 CFO2]].
  inversion CFO2. 
  generalize (H2 f k1 H3 H7 H11 H6). intros [k2 [EX2 CFO3]].
  exists k2. split. eapply exec_trans; eauto. auto.
  (* function -- TA-DA! *)
  assert (REACH0: (reachable f)!!(fn_entrypoint f) = true).
    apply reachable_entrypoint.
  assert (VO2: valid_outcome f Return). simpl; auto.
  assert (VO1: valid_outcome f (Cont (fn_entrypoint f))).
    eapply exec_blocks_valid_outcome; eauto.
  assert (exists k, fn_code (linearize_function f) = Llabel f.(fn_entrypoint) :: k).
    unfold linearize_function; simpl. 
    elim (enumerate_head f). intros tl EN. rewrite EN. 
    simpl. elim VO1. intros REACH [b EQ]. rewrite EQ. 
    exists (linearize_block b (linearize_body f tl)). auto.
  elim H2; intros k EQ.
  assert (find_label (fn_entrypoint f) (fn_code (linearize_function f)) =
            Some k).
    rewrite EQ. simpl. rewrite peq_true. auto.
  generalize (H1 f k (refl_equal _) REACH0 H3 VO2). 
  intros [k' [EX CO]].
  inversion CO. subst k'. 
  unfold transf_function. apply cleanup_function_correct.
  econstructor. eauto. rewrite EQ. eapply exec_trans.
  apply exec_one. constructor. eauto.
  apply unique_labels_lin_function.
Qed.

End LINEARIZATION.

Theorem transf_program_correct:
  forall (p: LTL.program) (r: val),
  LTL.exec_program p r ->
  Linear.exec_program (transf_program p) r.
Proof.
  intros p r [b [f [ls [m [FIND1 [FIND2 [SIG [EX RES]]]]]]]].
  red. exists b; exists (transf_function f); exists ls; exists m.
  split. change (prog_main (transf_program p)) with (prog_main p).
  rewrite symbols_preserved; auto.
  split. apply function_ptr_translated. auto.
  split. auto.
  split. apply transf_function_correct. 
  unfold transf_program. rewrite Genv.init_mem_transf. auto.
  exact RES.
Qed.