Module SSAfastliveutils

Require Import Bool.
Require Import List. Import ListNotations.
Require Import Sorting.
Require Import OrdersEx.
Require Import Relation_Operators.

Require Import Coqlib.
Require Import AST.
Require Import Maps.
Require Import Utils.
Require Import TrMaps2.
Require Import Kildall.
Require Import KildallComp.
Require Import SSA.
Require Import SSAutils.


Lemma fold_invariant :
  forall {A B} (f : A -> B -> A) l a (P:A->Prop),
  P a ->
  (forall x t, In x l -> P t -> P (f t x)) ->
  P (fold_left f l a).
Proof.
  intros A B f l.
  induction l; intros.
  - (* l = nil *)
    simpl. assumption.
  - (* l = a :: l *)
    simpl. apply IHl.
    + apply H0.
      left. reflexivity.
      assumption.
    + intros. apply H0.
      right. assumption.
      assumption.
Qed.

Lemma fold_becomes_true_growing :
  forall {A B : Type} (f : A -> B -> A) l a (P : A -> Prop),
  Exists (fun b => forall a, P (f a b)) l ->
  forall (f_growing : forall x t, In x l -> P t -> P (f t x)),
  P (List.fold_left f l a).
Proof.
  intros. revert a. induction H; intros.
  - simpl. apply fold_invariant. apply H.
    intros. apply f_growing. right; assumption. assumption.
  - apply IHExists. intros. apply f_growing. right; assumption. assumption.
Qed.

Lemma fold_becomes_true_growing_with_invariant :
  forall {A B : Type} (f : A -> B -> A) l a (P P0 : A -> Prop),
  P0 a ->
  (forall x t, In x l -> P0 t -> P0 (f t x)) ->
  Exists (fun b => forall a, P0 a -> P (f a b)) l ->
  forall (f_growing : forall x t, In x l -> P0 t -> P t -> P (f t x)),
  P (List.fold_left f l a).
Proof.
  intros. revert dependent a. induction H1; intros.
  - simpl.
    assert (P (fold_left f l (f a x)) /\ P0 (fold_left f l (f a x))) as Ha;
      [|apply Ha].
    apply fold_invariant.
    + split.
      * apply H. assumption.
      * apply H0. left; reflexivity. assumption.
    + intros. destruct H3 as (H31 & H32). split.
      * apply f_growing; try assumption. right; assumption.
      * apply H0. right; assumption. assumption.
  - apply IHExists.
    + intros. apply H0. right; assumption. assumption.
    + intros. apply f_growing; try assumption. right; assumption.
    + apply H0. left; reflexivity. assumption.
Qed.



Definition option_union (a b : option unit) : option unit :=
  match a with
  | None => b
  | Some tt => Some tt
  end.

Lemma union_strict_correct : forall m1 m2 x,
  (PTree.combine_union_strict option_union m1 m2) ! x = Some tt
  <-> m1 ! x = Some tt \/ m2 ! x = Some tt.
Proof.
  intros.
  rewrite PTree.gcombine_union_strict.
  - destruct (m1 !x) as [[]|], (m2!x) as [[]|]; simpl; tauto.
  - reflexivity.
  - destruct a as [[]|]; reflexivity.
Qed.

Lemma union_strict_wf : forall m1 m2,
  PTree.pt_wf m1 -> PTree.pt_wf m2 ->
  PTree.pt_wf (PTree.combine_union_strict option_union m1 m2).
Proof.
  intros. apply PTree.gcombine_union_strict_wf; try assumption.
  - destruct a as [[]|]; reflexivity.
  - intros [] *. discriminate.
Qed.

Definition option_diff (a b : option unit) : option unit :=
  match b with
  | None => a
  | Some _ => None
  end.

Lemma diff_correct : forall m1 m2 x,
  (PTree.combine_diff option_diff m1 m2) ! x = Some tt
  <-> m1 ! x = Some tt /\ m2 ! x = None.
Proof.
  intros.
  rewrite PTree.gcombine_diff.
  destruct (m2 ! x); simpl.
  - intuition congruence.
  - intuition.
  - destruct b; reflexivity.
  - reflexivity.
Qed.



Definition is_included '(a, b) '(c, d) :=
  (c <= a)%positive /\ (b <= d)%positive.

Lemma is_included_trans : forall a b c d e f,
  is_included (a, b) (c, d) ->
  is_included (c, d) (e, f) ->
  is_included (a, b) (e, f).
Proof.
  intros. unfold is_included in *. xomega.
Qed.

Lemma is_included_dec : forall a b c d,
  is_included (a, b) (c, d) \/
  ~ is_included (a, b) (c, d).
Proof.
  intros.
  unfold is_included. xomega.
Qed.

Definition test_is_included '(a, b) '(c, d) :=
  Pos.leb c a && Pos.leb b d.

Lemma test_is_included_spec : forall a b c d,
  test_is_included (a, b) (c, d) = true <-> is_included (a, b) (c, d).
Proof.
  intros.
  unfold test_is_included, is_included.
  rewrite andb_true_iff, !Pos.leb_le.
  reflexivity.
Qed.

Definition can_reach r (c:PTree.t (PTree.t unit)) u v :=
  match r ! v with
  | None => false
  | Some i_v =>
    match c ! u with
    | None => false
    | Some set =>
      PTree.fold (fun b w _ =>
        b || match r ! w with
             | None => false
             | Some i_w => test_is_included i_v i_w
             end
      ) set false
    end
  end.



Module SortWithIndex (X : Orders.UsualOrderedType).

  Section withIndex.

    Context {A : Type}.
    Context (index : A -> X.t).

    Fixpoint merge (l1 l2 : list (A*X.t)) :=
      let fix merge_aux l2 :=
        match l1, l2 with
        | [], _ => l2
        | _, [] => l1
        | (_, n1) as a1::l1', (_, n2) as a2::l2' =>
            match X.compare n1 n2 with
            | Eq => merge_aux l2'
            | Lt => a1 :: merge l1' l2
            | Gt => a2 :: merge_aux l2'
            end
        end
      in merge_aux l2.

    Fixpoint merge_list_to_stack stack l :=
      match stack with
      | [] => [Some l]
      | None :: stack' => Some l :: stack'
      | Some l' :: stack' => None :: merge_list_to_stack stack' (merge l' l)
      end.

    Fixpoint merge_stack stack :=
      match stack with
      | [] => []
      | None :: stack' => merge_stack stack'
      | Some l :: stack' => merge l (merge_stack stack')
      end.

    Fixpoint iter_merge stack l :=
      match l with
      | [] => merge_stack stack
      | a::l' => iter_merge (merge_list_to_stack stack [(a, index a)]) l'
      end.

    Definition sort_aux := iter_merge [].
    Definition sort l := List.map fst (sort_aux l).

    Local Notation Sorted := (LocallySorted (fun '((_, n1) : A * X.t) '(_, n2) => X.lt n1 n2)) (only parsing).

    Fixpoint SortedStack stack :=
      match stack with
      | [] => True
      | None :: stack' => SortedStack stack'
      | Some l :: stack' => Sorted l /\ SortedStack stack'
      end.

    Local Ltac invert H := inversion H; subst; clear H.

    Local Open Scope list_scope.

    Fixpoint flatten_stack (stack : list (option (list (A * X.t)))) :=
      match stack with
      | [] => []
      | None :: stack' => flatten_stack stack'
      | Some l :: stack' => l ++ flatten_stack stack'
      end.

    Lemma Sorted_merge_aux : forall x1 n1 l1 x2 n2 l2,
      Sorted ((x2, n2) :: l2) ->
      exists x3 n3 l3, merge ((x1, n1) :: l1) ((x2, n2) :: l2) = (x3, n3) :: l3
        /\ (X.eq n3 n1 \/ X.eq n3 n2).
Proof.
      intros. induction l2 as [|(x2', n2') l2].
      - simpl. destruct (X.compare_spec n1 n2).
        + eexists _, _, _. split. reflexivity.
          left. reflexivity.
        + eexists _, _, _. split. reflexivity.
          left. reflexivity.
        + eexists _, _, _. split. reflexivity.
          right. reflexivity.
      - simpl in *. destruct (X.compare_spec n1 n2).
        + destruct (X.compare_spec n1 n2').
          * apply IHl2. invert H.
            contradiction (RelationClasses.StrictOrder_Irreflexive _ H6).
          * eexists _, _, _. split. reflexivity.
            left. reflexivity.
          * rewrite H0 in H1.
            invert H. rewrite H1 in H6.
            contradiction (RelationClasses.StrictOrder_Irreflexive _ H6).
        + eexists _, _, _. split. reflexivity.
          left. reflexivity.
        + eexists _, _, _. split. reflexivity.
          right; reflexivity.
    Qed.

    Theorem Sorted_merge : forall l1 l2,
      Sorted l1 -> Sorted l2 -> Sorted (merge l1 l2).
Proof.
      induction l1 as [|(x1, n1) l1]; induction l2 as [|(x2, n2) l2]; intros; simpl; auto.
      destruct (X.compare_spec n1 n2).
      - eapply IHl2. assumption. invert H0. constructor. assumption.
      - invert H.
        + simpl. constructor; trivial.
        + destruct b as (x3, n3).
          assert (Sorted (merge ((x3, n3)::l) ((x2, n2)::l2))) by (apply IHl1; auto).
          destruct (Sorted_merge_aux x3 n3 l x2 n2 l2)
            as (x4 & n4 & l4 & H61 & H62).
          assumption.
          rewrite H61. constructor. rewrite <- H61. assumption.
          destruct H62. rewrite H2. assumption.
          rewrite H2. assumption.
      - invert H0.
        + constructor; trivial.
        + destruct b as (x3, n3).
          assert (Sorted (merge ((x1, n1)::l1) ((x3, n3)::l))) by (apply IHl2; auto).
          destruct (Sorted_merge_aux x1 n1 l1 x3 n3 l)
            as (x4 & n4 & l4 & H61 & H62).
          assumption.
          fold (merge ((x1, n1) :: l1) ((x3, n3) :: l)).
          simpl in H61.
          rewrite H61. constructor. rewrite <- H61. assumption.
          destruct H62. rewrite H2. assumption.
          rewrite H2. assumption.
    Qed.

    Theorem incl_merge : forall l1 l2, incl (merge l1 l2) (l1++l2).
Proof.
      induction l1 as [|(x1, n1) l1]; simpl merge; intro.
        assert (forall l, (fix merge_aux (l0 : list _) : list _ := l0) l = l)
        as -> by (destruct l; trivial). (* Technical lemma *)
        apply incl_refl.
      induction l2 as [|(x2, n2) l2].
        rewrite app_nil_r. apply incl_refl.
        destruct (X.compare_spec n1 n2).
          intros (x3, n3) H1. apply IHl2 in H1.
          destruct H1. left; assumption.
          right. rewrite in_app_iff in H0 |- *. destruct H0.
          left; assumption. right; right; assumption.
          apply incl_cons. left; reflexivity. apply incl_tl. apply IHl1.
          intros (x3, n3) H1. destruct H1.
          right. rewrite in_app_iff. right; left. assumption.
          apply IHl2 in H0. destruct H0. left; assumption.
          right. rewrite in_app_iff in H0 |- *. destruct H0.
          left; assumption. right; right; assumption.
    Qed.

    Theorem incl_merge_2 : forall l1 l2 x n,
      In (x, n) (l1 ++ l2) -> exists y, In (y, n) (merge l1 l2).
Proof.
      intros l1 l2. revert l1. induction l2 as [|(x2, n2) l2]; intros.
      - rewrite app_nil_r in H.
        replace (merge l1 []) with l1 by (destruct l1 as [|(?, ?) ?]; reflexivity).
        eexists; eassumption.
      - induction l1 as [|(x1, n1) l1].
        + eexists; eassumption.
        + simpl. destruct (X.compare_spec n1 n2).
          * subst n2. rewrite in_app_iff in H. destruct H.
            -- eapply IHl2 with (l1:=(x1, n1) :: l1). rewrite in_app_iff.
               left; eassumption.
            -- destruct H.
               ++ inv H.
                  eapply IHl2 with (l1:=(x1, n) :: l1). rewrite in_app_iff.
                  left; left; reflexivity.
               ++ eapply IHl2 with (l1:=(x1, n1) :: l1). rewrite in_app_iff.
                  right; eassumption.
          * destruct H.
            -- inv H. eexists. left; reflexivity.
            -- apply IHl1 in H. destruct H as (y & H).
               exists y. right; assumption.
          * assert ((x2, n2) = (x, n) \/ In (x, n) (((x1, n1) :: l1) ++ l2)) as Ha.
            { rewrite in_app_iff in H. destruct H.
              - right. rewrite in_app_iff. left; assumption.
              - destruct H. left; assumption.
                right; right. rewrite in_app_iff. right; assumption.
            }
            destruct Ha.
            -- eexists. left. eassumption.
            -- apply IHl2 in H1. destruct H1 as (y & H1).
               exists y. right. assumption.
    Qed.

    Theorem incl_merge_2_inj : forall l1 l2
      (number_inj : forall x1 x2 n, In (x1, n) l1 -> In (x2, n) l2 -> x1 = x2),
      incl (l1 ++ l2) (merge l1 l2).
Proof.
      intros l1 l2 ? (x, n) H.
      revert dependent l1. induction l2 as [|(x2, n2) l2]; intros.
      - rewrite app_nil_r in H.
        replace (merge l1 []) with l1 by (destruct l1 as [|(?, ?) ?]; reflexivity).
        assumption.
      - induction l1 as [|(x1, n1) l1].
        + assumption.
        + simpl. destruct (X.compare_spec n1 n2).
          * subst n2.
            assert (x1 = x2) as Ha.
            { eapply number_inj; left; reflexivity. }
            subst x2.
            rewrite in_app_iff in H. destruct H.
            -- eapply IHl2 with (l1:=(x1, n1) :: l1); eauto.
               rewrite in_app_iff. left; eassumption.
            -- destruct H.
               ++ inv H.
                  eapply IHl2 with (l1:=(x, n) :: l1); eauto.
                  rewrite in_app_iff. left; left; reflexivity.
               ++ eapply IHl2 with (l1:=(x1, n1) :: l1); eauto.
                  rewrite in_app_iff. right; eassumption.
          * destruct H.
            -- inv H. left; reflexivity.
            -- apply IHl1 in H; eauto.
          * assert ((x2, n2) = (x, n) \/ In (x, n) (((x1, n1) :: l1) ++ l2)) as Ha.
            { rewrite in_app_iff in H. destruct H.
              - right. rewrite in_app_iff. left; assumption.
              - destruct H. left; assumption.
                right; right. rewrite in_app_iff. right; assumption.
            }
            destruct Ha.
            -- left. assumption.
            -- apply IHl2 in H1; eauto.
    Qed.

    Theorem Sorted_merge_list_to_stack : forall stack l,
      SortedStack stack -> Sorted l -> SortedStack (merge_list_to_stack stack l).
Proof.
      induction stack as [|[|]]; intros; simpl.
        auto.
        apply IHstack. destruct H as (_,H1). fold SortedStack in H1. auto.
          apply Sorted_merge; auto; destruct H; auto.
          auto.
    Qed.

    Theorem incl_merge_list_to_stack : forall stack l,
      incl (flatten_stack (merge_list_to_stack stack l)) (l ++ flatten_stack stack).
Proof.
      induction stack as [|[]]; simpl; intros.
        apply incl_refl.
        intros (x, n) H. apply IHstack in H. rewrite app_assoc.
        rewrite in_app_iff in H |- *.
        destruct H. left. rewrite in_app_iff. rewrite or_comm.
        apply in_app_iff. apply incl_merge. assumption.
        right; assumption.
        apply incl_refl.
    Qed.

    Theorem incl_merge_list_to_stack_2 : forall stack l x n,
      In (x, n) (l ++ flatten_stack stack) ->
      exists y, In (y, n) (flatten_stack (merge_list_to_stack stack l)).
Proof.
      induction stack as [|[]]; simpl; intros.
      - eexists; eassumption.
      - rewrite app_assoc in H. rewrite in_app_iff in H. destruct H.
        + apply in_app_iff, or_comm, in_app_iff in H.
          apply incl_merge_2 in H. destruct H as (y & H).
          apply IHstack with (x:=y). rewrite in_app_iff. left; assumption.
        + eapply IHstack. rewrite in_app_iff. right; eassumption.
      - eexists; eassumption.
    Qed.

    Theorem incl_merge_list_to_stack_2_inj : forall stack l
      (number_inj : forall x1 x2 n, In (x1, n) (l ++ flatten_stack stack) -> In (x2, n) (l ++ flatten_stack stack) -> x1 = x2),
      incl (l ++ flatten_stack stack) (flatten_stack (merge_list_to_stack stack l)).
Proof.
      intros. intros (x, n) H. apply incl_merge_list_to_stack_2 in H as H0. destruct H0 as (y & H0).
      apply incl_merge_list_to_stack in H0 as H1. specialize (number_inj _ _ _ H H1). subst y. assumption.
    Qed.

    Theorem Sorted_merge_stack : forall stack,
      SortedStack stack -> Sorted (merge_stack stack).
Proof.
    induction stack as [|[|]]; simpl; intros.
      constructor; auto.
      apply Sorted_merge; tauto.
      auto.
    Qed.

    Theorem incl_merge_stack : forall stack,
      incl (merge_stack stack) (flatten_stack stack).
Proof.
      induction stack as [|[]]; simpl.
      apply incl_refl.
      intros (x, n) H. apply incl_merge in H.
      rewrite in_app_iff in H |- *. destruct H.
      left; assumption.
      right; apply IHstack; assumption.
      assumption.
    Qed.

    Theorem incl_merge_stack_2 : forall stack x n,
      In (x, n) (flatten_stack stack) ->
      exists y, In (y, n) (merge_stack stack).
Proof.
      intros. induction stack as [|[]]; simpl.
      - contradiction.
      - simpl in H. rewrite in_app_iff in H. destruct H.
        + eapply incl_merge_2. rewrite in_app_iff. left; eassumption.
        + apply IHstack in H. destruct H as (y & H).
          eapply incl_merge_2. rewrite in_app_iff. right; eassumption.
      - simpl in H. eauto.
    Qed.

    Theorem incl_merge_stack_2_inj : forall stack
      (number_inj : forall x1 x2 n, In (x1, n) (flatten_stack stack) ->
        In (x2, n) (flatten_stack stack) -> x1 = x2),
      incl (flatten_stack stack) (merge_stack stack).
Proof.
      intros. intros (x, n) H. apply incl_merge_stack_2 in H as H0. destruct H0 as (y & H0).
      apply incl_merge_stack in H0 as H1. specialize (number_inj _ _ _ H H1). subst y. assumption.
    Qed.

    Theorem Sorted_iter_merge : forall stack l,
      SortedStack stack -> Sorted (iter_merge stack l).
Proof.
      intros stack l H; induction l in stack, H |- *; simpl.
        auto using Sorted_merge_stack.
        assert (Sorted [(a, index a)]) by constructor.
        auto using Sorted_merge_list_to_stack.
    Qed.

    Theorem incl_iter_merge : forall l stack,
      incl (iter_merge stack l)
        (flatten_stack stack ++ List.map (fun a => (a, index a)) l).
Proof.
      induction l; simpl; intros.
        rewrite app_nil_r. apply incl_merge_stack.
        change ((a, index a)::map (fun a0 : A => (a0, index a0)) l)
          with ([(a, index a)]++map (fun a0 : A => (a0, index a0)) l).
        rewrite app_assoc.
        intros (x, n) H.
        apply IHl in H. rewrite in_app_iff in H |- *.
        destruct H.
        left. apply incl_merge_list_to_stack in H.
        apply in_app_iff, or_comm, in_app_iff. assumption.
        right; assumption.
    Qed.

    Theorem incl_iter_merge_2 : forall l stack x n,
      In (x, n) (flatten_stack stack ++ List.map (fun a => (a, index a)) l) ->
      exists y, In (y, n) (iter_merge stack l).
Proof.
      induction l; simpl; intros.
      - rewrite app_nil_r in H. eapply incl_merge_stack_2; eassumption.
      - change ((a, index a)::map (fun a0 : A => (a0, index a0)) l)
          with ([(a, index a)]++map (fun a0 : A => (a0, index a0)) l)
          in H.
        rewrite app_assoc in H. rewrite in_app_iff in H. destruct H.
        + apply in_app_iff, or_comm, in_app_iff in H.
          apply incl_merge_list_to_stack_2 in H. destruct H as (y & H).
          eapply IHl. rewrite in_app_iff. left; eassumption.
        + eapply IHl. rewrite in_app_iff. right; eassumption.
    Qed.

    Theorem incl_iter_merge_2_inj : forall l stack
      (number_inj : forall x1 x2 n,
        In (x1, n) (flatten_stack stack ++ List.map (fun a => (a, index a)) l) ->
        In (x2, n) (flatten_stack stack ++List.map (fun a => (a, index a)) l) -> x1 = x2),
      incl (flatten_stack stack ++ List.map (fun a => (a, index a)) l)
        (iter_merge stack l).
Proof.
      intros. intros (x, n) H. apply incl_iter_merge_2 in H as H0. destruct H0 as (y & H0).
      apply incl_iter_merge in H0 as H1. specialize (number_inj _ _ _ H H1). subst y. assumption.
    Qed.

    Theorem Sorted_sort_aux : forall l, Sorted (sort_aux l).
Proof.
      intro; apply Sorted_iter_merge. constructor.
    Qed.

    Corollary LocallySorted_sort_aux : forall l,
      Sorted.Sorted (fun '(_, n1) '(_, n2) => X.lt n1 n2) (sort_aux l).
Proof.
intro; eapply Sorted_LocallySorted_iff, Sorted_sort_aux; auto. Qed.

    Theorem incl_sort_aux : forall l,
      incl (sort_aux l) (List.map (fun a => (a, index a)) l).
Proof.
      intro; apply (incl_iter_merge l []).
    Qed.

    Theorem incl_sort_aux_2 : forall l x n,
      In (x, n) (List.map (fun a => (a, index a)) l) ->
      exists y, In (y, n) (sort_aux l).
Proof.
      intros. eapply (incl_iter_merge_2 l []); eauto.
    Qed.

    Theorem incl_sort_aux_2_inj : forall l
      (number_inj : forall x1 x2 n,
        In (x1, n) (List.map (fun a => (a, index a)) l) ->
        In (x2, n) (List.map (fun a => (a, index a)) l) -> x1 = x2),
      incl (List.map (fun a => (a, index a)) l) (sort_aux l).
Proof.
      intros. intros (x, n) H. apply incl_sort_aux_2 in H as H0. destruct H0 as (y & H0).
      apply incl_sort_aux in H0 as H1. specialize (number_inj _ _ _ H H1). subst y. assumption.
    Qed.

    Corollary StronglySorted_sort_aux : forall l,
      StronglySorted (fun '(_, n1) '(_, n2) => X.lt n1 n2) (sort_aux l).
Proof.
      intros. apply Sorted_StronglySorted.
      - intros (x1, n1) (x2, n2) (x3, n3) H1 H2.
        rewrite H1. assumption.
      - apply LocallySorted_sort_aux.
    Qed.

    Theorem Sorted_sort : forall l,
      LocallySorted (fun a b => X.lt (index a) (index b)) (sort l).
Proof.
      intros. unfold sort.
      pose proof (Sorted_sort_aux l).
      pose proof (incl_sort_aux l).
      induction H.
      constructor.
      constructor.
      simpl. constructor. apply IHLocallySorted.
      intros (x, n) H2. apply H0. right; assumption.
      destruct a as (x1, n1), b as (x2, n2). simpl.
      pose proof (H0 (x1, n1) (or_introl eq_refl)).
      pose proof (H0 (x2, n2) (or_intror (or_introl eq_refl))).
      rewrite in_map_iff in H2, H3.
      destruct H2 as (x1' & H2 & _).
      destruct H3 as (x2' & H3 & _). congruence.
    Qed.

    Corollary LocallySorted_sort : forall l,
      Sorted.Sorted (fun a b => X.lt (index a) (index b)) (sort l).
Proof.
intro; eapply Sorted_LocallySorted_iff, Sorted_sort; auto. Qed.

    Theorem incl_sort : forall l,
      incl (sort l) l.
Proof.
      intros. intros a H. unfold sort in H.
      apply in_map_iff in H. destruct H as ((x, n) & H1 & H2).
      simpl in H1. subst x.
      apply incl_sort_aux in H2.
      apply in_map_iff in H2. destruct H2 as (a' & H21 & H22).
      invert H21. assumption.
    Qed.

    Theorem incl_sort_2 : forall l x,
      In x l ->
      exists y, In y (sort l) /\ index y = index x.
Proof.
      intros. unfold sort.
      assert (In (x, index x) (map (fun a : A => (a, index a)) l)) as Ha.
      { apply in_map_iff. eexists. split; [reflexivity|eassumption]. }
      apply incl_sort_aux_2 in Ha. destruct Ha as (y & Ha).
      exists y. split.
      - apply in_map_iff. eexists (_, _). split; [reflexivity|eassumption].
      - apply incl_sort_aux in Ha. apply in_map_iff in Ha.
        destruct Ha as (y' & Ha & _).
        inv Ha. reflexivity.
    Qed.

    Theorem incl_sort_2_nearly_inj : forall l wrong x
      (number_nearly_inj : forall a b,
        In a l -> In b l -> index a = index b -> a = b \/ In (index a) wrong),
      In x l -> ~ In (index x) wrong -> In x (sort l).
Proof.
      intros. apply incl_sort_2 in H as H1. destruct H1 as (y & H11 & H12).
      apply incl_sort in H11 as H21. specialize (number_nearly_inj _ _ H21 H H12).
      destruct number_nearly_inj; congruence.
    Qed.

    Theorem incl_sort_2_inj : forall l
      (number_inj : forall a b,
        In a l -> In b l -> index a = index b -> a = b),
      incl l (sort l).
Proof.
      intros. intros x H. apply incl_sort_2 in H as H0. destruct H0 as (y & H01 & H02).
      apply incl_sort in H01 as H11. specialize (number_inj _ _ H11 H H02). subst y. assumption.
    Qed.

    Corollary StronglySorted_sort : forall l,
      StronglySorted (fun a b => X.lt (index a) (index b)) (sort l).
Proof.
      intros. apply Sorted_StronglySorted.
      - intros a b c H1 H2.
        rewrite H1. assumption.
      - apply LocallySorted_sort.
    Qed.

  End withIndex.

End SortWithIndex.

Module PosSortWithIndex := SortWithIndex OrdersEx.Positive_as_OT.
Module ZSortWithIndex := SortWithIndex Z.



Definition itv_strict_incl (i1 i2:itv) : bool :=
  if Z_lt_dec i2.(pre) i1.(pre) then
    if Z_le_dec i1.(post) i2.(post) then true
    else false
  else false.

Definition itv_strict_Incl i1 i2 :=
  i2.(pre) < i1.(pre) /\ i1.(post) <= i2.(post).

Lemma itv_strict_incl_spec : forall i1 i2,
  itv_strict_incl i1 i2 = true <-> itv_strict_Incl i1 i2.
Proof.
  unfold itv_strict_incl, itv_strict_Incl.
  split; intros.
  - destruct (Z_lt_dec i2.(pre) i1.(pre)); [|discriminate].
    destruct (Z_le_dec i1.(post) i2.(post)); [|discriminate].
    split; assumption.
  - destruct H.
    destruct (Z_lt_dec i2.(pre) i1.(pre)); [|contradiction].
    destruct (Z_le_dec i1.(post) i2.(post)); [|contradiction].
    reflexivity.
Qed.

Definition is_strict_ancestor (dom : PTree.t itv) (n1 n2:node) : bool :=
  match dom ! n1, dom ! n2 with
    | Some itv1, Some itv2 => itv_strict_incl itv2 itv1
    | _, _ => false
  end.

Definition sdom_test f := is_strict_ancestor f.(fn_dom).

Lemma sdom_test_correct : forall f (wf : wf_ssa_function f) n d,
  reached f n -> sdom_test f d n = true <-> sdom f d n.
Proof.
  intros. split; intros.
  - constructor.
    + apply wf.(fn_dom_wf _).(dom_test_correct _). assumption.
      unfold sdom_test, is_strict_ancestor in H0.
      unfold dom_test, is_ancestor.
      destruct (f.(fn_dom) ! d); [|discriminate].
      destruct (f.(fn_dom) ! n); [|discriminate].
      apply itv_strict_incl_spec in H0. unfold itv_strict_Incl in H0.
      apply itv_incl_spec. unfold itv_Incl.
      xomega.
    + intros contra. subst.
      unfold sdom_test, is_strict_ancestor in H0.
      destruct (f.(fn_dom) ! n); [|discriminate].
      apply itv_strict_incl_spec in H0.
      destruct H0 as (H0 & _). xomega.
  - inv H0.
    apply wf.(fn_dom_wf _).(dom_test_correct _) in DOM as H0; try assumption.
    unfold dom_test, is_ancestor in H0.
    unfold sdom_test, is_strict_ancestor.
    destruct (f.(fn_dom) ! d) as [n_d|] eqn:Hdom_d; [|discriminate].
    destruct (f.(fn_dom) ! n) as [n_n|] eqn:Hdom_n; [|discriminate].
    apply itv_incl_spec in H0. unfold itv_Incl in H0.
    apply itv_strict_incl_spec. unfold itv_strict_Incl.
    assert (pre n_d <> pre n_n) as Ha.
    { intros contra. apply NEQ.
      eapply wf.(fn_dom_wf _).(dom_pre_inj _); eauto.
    }
    xomega.
Qed.




Definition du_chain := P2Map.t (list node).

Construction

Definition regs_used_by (i : instruction) : list reg :=
 match i with
    | Iop _ l _ _ => l
    | Iload _ _ l _ _ => l
    | Istore _ _ l src _ => src::l
    | Icall _ ros l _ _
    | Itailcall _ ros l =>
      match ros with
        | inl r => r :: l
        | inr _ => l
      end
    | Ibuiltin _ l _ _ => (params_of_builtin_args l)
    | Icond _ l _ _ => l
    | Ijumptable r _ => r :: nil
    | Ireturn (Some r) => r :: nil
    | _ => nil
  end.

Lemma regs_used_by_correct : forall f pc i r,
  f.(fn_code) ! pc = Some i ->
  use_code f r pc ->
  In r (regs_used_by i).
Proof.
  intros.
  inv H0; rewrite H1 in H; inv H; simpl; auto.
Qed.

Lemma regs_used_by_complete : forall f pc i r,
  f.(fn_code) ! pc = Some i ->
  In r (regs_used_by i) ->
  use_code f r pc.
Proof.
  intros. destruct i; simpl in H0; try (econstructor; eassumption).
  - contradiction.
  - destruct s0.
    + econstructor; eassumption.
    + econstructor; eassumption.
  - destruct s0.
    + econstructor; eassumption.
    + econstructor; eassumption.
  - destruct H0 as [|[]]. subst.
    econstructor; eassumption.
  - destruct o.
    + destruct H0 as [|[]]. subst.
      econstructor; eassumption.
    + contradiction.
Qed.

Definition handle_reg_list (duc: du_chain) (pc : node) (rs: list reg) :=
  List.fold_left (fun u r => P2Map.set r (pc :: u #2 r) u) rs duc.

Lemma handle_reg_list_correct_1 : forall duc pc rs r pc',
  In pc' (duc #2 r) ->
  In pc' ((handle_reg_list duc pc rs) #2 r).
Proof.
  intros. unfold handle_reg_list. apply fold_invariant; intros.
  - assumption.
  - rewrite P2Map.gsspec.
    destruct (p2eq r x).
    + right. congruence.
    + assumption.
Qed.

Lemma handle_reg_list_correct_2 : forall duc pc rs r,
  In r rs ->
  In pc ((handle_reg_list duc pc rs) #2 r).
Proof.
  intros. unfold handle_reg_list. apply fold_becomes_true_growing; intros.
  - apply Exists_exists. exists r. split; [assumption|].
    intros.
    rewrite P2Map.gsspec.
    destruct (p2eq r r); [|congruence].
    left; reflexivity.
  - rewrite P2Map.gsspec.
    destruct (p2eq r x).
    + right. congruence.
    + assumption.
Qed.

Lemma handle_reg_list_complete_1 : forall duc pc rs r,
  In pc ((handle_reg_list duc pc rs) #2 r) ->
  ~ In pc (duc #2 r) ->
  In r rs.
Proof.
  intros. unfold handle_reg_list in H. revert H. apply fold_invariant; intros.
  - contradiction.
  - rewrite P2Map.gsspec in H2.
    destruct (p2eq r x).
    + congruence.
    + auto.
Qed.

Lemma handle_reg_list_complete_2 : forall duc pc rs r pc',
  In pc' ((handle_reg_list duc pc rs) #2 r) ->
  pc' <> pc ->
  In pc' (duc #2 r).
Proof.
  intros. unfold handle_reg_list in H. revert H. apply fold_invariant; intros.
  - assumption.
  - rewrite P2Map.gsspec in H2.
    destruct (p2eq r x).
    + subst. destruct H2; [congruence|]. auto.
    + auto.
Qed.

Definition def_use_1 duc c :=
  PTree.fold (fun u pc i => handle_reg_list u pc (regs_used_by i)) c duc.

Lemma def_use_1_correct_1 : forall duc c r pc,
  In pc (duc #2 r) ->
  In pc ((def_use_1 duc c) #2 r).
Proof.
  intros. unfold def_use_1. rewrite PTree.fold_spec. apply fold_invariant.
  - assumption.
  - intros (pc', i) duc'. simpl. intros. apply PTree.elements_complete in H0.
    apply handle_reg_list_correct_1. assumption.
Qed.

Lemma def_use_1_correct_2 : forall f duc r pc,
  use_code f r pc ->
  In pc ((def_use_1 duc f.(fn_code)) #2 r).
Proof.
  intros. unfold def_use_1. rewrite PTree.fold_spec.
  apply fold_becomes_true_growing.
  - apply Exists_exists.
    assert (exists i, f.(fn_code) ! pc = Some i) as (i & Ha).
    { inv H; eauto. }
    exists (pc, i). split.
    + apply PTree.elements_correct. assumption.
    + simpl. intros. apply handle_reg_list_correct_2.
      eapply regs_used_by_correct; eassumption.
  - intros (pc', i) duc'. simpl. intros. apply PTree.elements_complete in H0.
    apply handle_reg_list_correct_1. assumption.
Qed.

Lemma def_use_1_complete : forall f duc r pc,
  In pc ((def_use_1 duc f.(fn_code)) #2 r) ->
  ~ In pc (duc #2 r) ->
  use_code f r pc.
Proof.
  intros. revert H. unfold def_use_1.
  rewrite PTree.fold_spec. apply fold_invariant.
  - contradiction.
  - intros (pc', i) duc'. simpl. intros. apply PTree.elements_complete in H.
    destruct (peq pc' pc).
    + subst. destruct (in_dec peq pc (duc' #2 r)).
      * auto.
      * eapply regs_used_by_complete; try eassumption.
        eapply handle_reg_list_complete_1; eassumption.
    + apply handle_reg_list_complete_2 in H2.
      * auto.
      * congruence.
Qed.

Definition handle_phi_block_k (duc:du_chain) k pc pb :=
  List.fold_left (fun u pi =>
    match pi with
    | Iphi args _ =>
      match nth_error args k with
      | None => u
      | Some r => P2Map.set r (pc :: u #2 r) u
      end
    end) pb duc.

Lemma handle_phi_block_k_correct_1 : forall duc k pc pb r pc',
  In pc' (duc #2 r) ->
  In pc' ((handle_phi_block_k duc k pc pb) #2 r).
Proof.
  intros. unfold handle_phi_block_k. apply fold_invariant.
  - assumption.
  - intros pi duc'. intros. destruct pi as [args dst].
    destruct (nth_error args k).
    + rewrite P2Map.gsspec.
      destruct (p2eq r r0); [right|]; congruence.
    + assumption.
Qed.

Lemma handle_phi_block_k_correct_2 : forall duc k pc pb args dst r,
  In (Iphi args dst) pb ->
  nth_error args k = Some r ->
  In pc ((handle_phi_block_k duc k pc pb) #2 r).
Proof.
  intros. unfold handle_phi_block_k. apply fold_becomes_true_growing.
  - apply Exists_exists. eexists. split; [eassumption|].
    simpl.
    intros duc'. rewrite H0.
    rewrite P2Map.gsspec. destruct (p2eq r r); [|congruence].
    left; reflexivity.
  - intros pi duc'. intros. destruct pi as [args' dst'].
    destruct (nth_error args' k).
    + rewrite P2Map.gsspec.
      destruct (p2eq r r0); [right|]; congruence.
    + assumption.
Qed.

Lemma handle_phi_block_k_complete_1 : forall duc k pc pb r,
  In pc ((handle_phi_block_k duc k pc pb) #2 r) ->
  ~ In pc (duc #2 r) ->
  exists args dst,
    In (Iphi args dst) pb /\ nth_error args k = Some r.
Proof.
  intros. revert H. unfold handle_phi_block_k.
  apply fold_invariant.
  - contradiction.
  - intros pi duc'. intros.
    destruct pi as [args dst].
    destruct (nth_error args k) eqn:Hnth.
    + rewrite P2Map.gsspec in H2. destruct (p2eq r r0).
      * subst. eexists _, _. split; eassumption.
      * auto.
    + auto.
Qed.

Section fold_left2.

Context {A B C : Type}.
Context (f : A -> B -> C -> A).

Fixpoint fold_left2 (l1 : list B) (l2 : list C) a :=
  match l1, l2 with
  | [], _ => a
  | _, [] => a
  | b :: l1, c :: l2 => fold_left2 l1 l2 (f a b c)
  end.

Lemma fold_left2_invariant_nth : forall l1 l2 a (P:A->Prop),
  P a ->
  (forall k b c t,
    nth_error l1 k = Some b ->
    nth_error l2 k = Some c ->
    P t -> P (f t b c)) ->
  P (fold_left2 l1 l2 a).
Proof.
  intros l1. induction l1; intros.
  - simpl. assumption.
  - destruct l2.
    + simpl. assumption.
    + simpl. apply IHl1.
      * apply H0 with (k:=0%nat).
        -- reflexivity.
        -- reflexivity.
        -- assumption.
      * intros. apply H0 with (k:=S k); assumption.
Qed.

Lemma fold_left2_invariant : forall l1 l2 a (P:A->Prop),
  P a ->
  (forall b c t, In b l1 -> In c l2 -> P t -> P (f t b c)) ->
  P (fold_left2 l1 l2 a).
Proof.
  intros. apply fold_left2_invariant_nth.
  - assumption.
  - intros. apply nth_error_In in H1. apply nth_error_In in H2. auto.
Qed.

Lemma fold_left2_app : forall l11 l12 l21 l22 a,
  List.length l11 = List.length l21 ->
  fold_left2 (l11 ++ l12) (l21 ++ l22) a
  = fold_left2 l12 l22 (fold_left2 l11 l21 a).
Proof.
  induction l11; intros.
  - simpl in H. symmetry in H. apply length_zero_iff_nil in H. subst.
    reflexivity.
  - destruct l21 as [|b l21].
    + simpl in H. discriminate.
    + simpl. apply IHl11. simpl in H. congruence.
Qed.

Lemma fold_left2_becomes_true_growing :
  forall l1 l2 a (P : A -> Prop) k b c,
  nth_error l1 k = Some b ->
  nth_error l2 k = Some c ->
  (forall a, P (f a b c)) ->
  forall (f_growing : forall b c t, In b l1 -> In c l2 -> P t -> P (f t b c)),
  P (fold_left2 l1 l2 a).
Proof.
  intros.
  apply nth_error_split in H.
  destruct H as (l11 & l12 & HH1 & HH2).
  apply nth_error_split in H0.
  destruct H0 as (l21 & l22 & H01 & H02).
  rewrite HH1, H01.
  rewrite fold_left2_app by congruence.
  simpl. apply fold_left2_invariant.
  - apply H1.
  - intros. apply f_growing.
    + rewrite HH1. apply in_app_iff. right; right; assumption.
    + rewrite H01. apply in_app_iff. right; right; assumption.
    + assumption.
Qed.

End fold_left2.

Definition handle_phi_instr (duc : du_chain) preds pi :=
  match pi with
  | Iphi args _ =>
    fold_left2 (fun c pc arg => P2Map.set arg (pc :: c #2 arg) c) preds args duc
  end.

Lemma handle_phi_instr_correct_1 : forall duc preds pi r pc,
  In pc (duc #2 r) ->
  In pc ((handle_phi_instr duc preds pi) #2 r).
Proof.
  intros. destruct pi as [args dst]. simpl. apply fold_left2_invariant.
  - assumption.
  - intros pc' r' duc'. intros.
    rewrite P2Map.gsspec. destruct (p2eq r r').
    + right; congruence.
    + assumption.
Qed.

Lemma handle_phi_instr_correct_2 : forall duc preds args dst k r pc,
  nth_error preds k = Some pc ->
  nth_error args k = Some r ->
  In pc ((handle_phi_instr duc preds (Iphi args dst)) #2 r).
Proof.
  intros. simpl. eapply fold_left2_becomes_true_growing; try eassumption.
  - intros. rewrite P2Map.gsspec. destruct (p2eq r r); [|congruence].
    left; reflexivity.
  - intros pc' r' duc'. intros.
    rewrite P2Map.gsspec. destruct (p2eq r r').
    + right; congruence.
    + assumption.
Qed.

Lemma handle_phi_instr_complete : forall duc preds args dst r pc,
  In pc ((handle_phi_instr duc preds (Iphi args dst)) #2 r) ->
  ~ In pc (duc #2 r) ->
  exists k, nth_error preds k = Some pc /\ nth_error args k = Some r.
Proof.
  intros. revert H. unfold handle_phi_instr.
  apply fold_left2_invariant_nth.
  - contradiction.
  - intros k pc' r' duc'. intros.
    rewrite P2Map.gsspec in H3. destruct (p2eq r r').
    + subst. destruct H3; subst; eauto.
    + auto.
Qed.

Definition handle_phi_block (duc : du_chain) preds (pb : phiblock) :=
    fold_left (fun u pi => handle_phi_instr u preds pi) pb duc.

Lemma handle_phi_block_correct_1 : forall duc preds pb r pc,
  In pc (duc #2 r) ->
  In pc ((handle_phi_block duc preds pb) #2 r).
Proof.
  intros. unfold handle_phi_block.
  apply fold_invariant.
  - assumption.
  - intros pi duc'. intros.
    apply handle_phi_instr_correct_1. assumption.
Qed.

Lemma handle_phi_block_correct_2 : forall duc preds pb args dst k r pc,
  In (Iphi args dst) pb ->
  nth_error args k = Some r ->
  nth_error preds k = Some pc ->
  In pc ((handle_phi_block duc preds pb) #2 r).
Proof.
  intros. unfold handle_phi_block.
  apply fold_becomes_true_growing.
  - apply Exists_exists. eexists. split; [eassumption|].
    intros duc'.
    eapply handle_phi_instr_correct_2; eassumption.
  - intros pi duc'. intros.
    eapply handle_phi_instr_correct_1. assumption.
Qed.

Lemma handle_phi_block_complete : forall duc preds pb r pc,
  In pc ((handle_phi_block duc preds pb) #2 r) ->
  ~ In pc (duc #2 r) ->
  exists args dst k,
    In (Iphi args dst) pb /\
    nth_error preds k = Some pc /\
    nth_error args k = Some r.
Proof.
  intros. revert H. unfold handle_phi_block.
  apply fold_invariant.
  - contradiction.
  - intros pi duc'. intros.
    destruct (in_dec peq pc (duc' #2 r)).
    + auto.
    + destruct pi as [args dst]. exists args, dst.
      apply handle_phi_instr_complete in H2; try assumption.
      destruct H2 as (k & H21 & H22).
      exists k. repeat (split; eauto).
Qed.

Definition def_use_2 duc c phic :=
  let preds := make_predecessors c successors_instr in
  PTree.fold (fun u pc pb =>
    match preds ! pc with
    | None => u
    | Some preds => handle_phi_block u preds pb
    end) phic duc.

Lemma def_use_2_correct_1 : forall duc c phic r pc,
  In pc (duc #2 r) ->
  In pc ((def_use_2 duc c phic) #2 r).
Proof.
  intros. unfold def_use_2. rewrite PTree.fold_spec. apply fold_invariant.
  - assumption.
  - intros (pc', pb) duc'. simpl. intros. apply PTree.elements_complete in H0.
    destruct ((make_predecessors c successors_instr) ! pc'); [|assumption].
    apply handle_phi_block_correct_1. assumption.
Qed.

Lemma def_use_2_correct_2 : forall f (wf : wf_ssa_function f) duc r pc,
  use_phicode f r pc ->
  In pc ((def_use_2 duc f.(fn_code) f.(fn_phicode)) #2 r).
Proof.
  intros. unfold def_use_2. rewrite PTree.fold_spec.
  apply fold_becomes_true_growing.
  - apply Exists_exists. inv H.
    exists (pc0, phib). split.
    + apply PTree.elements_correct. assumption.
    + simpl. intros duc'.
      unfold index_pred, successors_list in KPRED.
      destruct ((make_predecessors f.(fn_code) successors_instr) ! pc0)
        as [l|]; [|discriminate].
      destruct l as [|pc' l]; [discriminate|].
      apply get_index_nth_error in KPRED.
      eapply handle_phi_block_correct_2; eassumption.
  - intros (pc', pb) duc'. simpl. intros. apply PTree.elements_complete in H0.
    destruct ((make_predecessors f.(fn_code) successors_instr) ! pc'); [|assumption].
    apply handle_phi_block_correct_1. assumption.
Qed.

Lemma make_predecessors_NoDup : forall f (wf : wf_ssa_function f) pc,
  NoDup ((make_predecessors f.(fn_code) successors_instr) !!! pc).
Proof.
  intros.
  destruct ((make_predecessors f.(fn_code) successors_instr) !!! pc)
    as [|p1 [|p2 l]] eqn:Hpreds; try now repeat (constructor || intros []).
  rewrite <- Hpreds. apply make_preds_NoDup.
  intros.
  assert (join_point pc f) as Ha.
  {
    unfold successors_list in Hpreds.
    destruct ((make_predecessors f.(fn_code) successors_instr) ! pc) eqn:Hpreds';
      [|discriminate].
    econstructor.
    - eassumption.
    - rewrite Hpreds. simpl. xomega.
  }
  apply wf.(fn_normalized _) with (pc:=n) in Ha.
  - rewrite H in Ha. inv Ha. unfold once. simpl.
    rewrite peq_true. reflexivity.
  - unfold successors, successors_list. rewrite PTree.gmap1.
    unfold option_map. rewrite H. assumption.
Qed.

Lemma def_use_2_complete : forall f (wf : wf_ssa_function f) duc r pc,
  In pc ((def_use_2 duc f.(fn_code) f.(fn_phicode)) #2 r) ->
  ~ In pc (duc #2 r) ->
  use_phicode f r pc.
Proof.
  intros. revert H. unfold def_use_2.
  rewrite PTree.fold_spec. apply fold_invariant.
  - contradiction.
  - intros (pc', pb) duc'. simpl. intros. apply PTree.elements_complete in H.
    destruct ((make_predecessors f.(fn_code) successors_instr) ! pc') eqn:Hpreds.
    + destruct (in_dec peq pc (duc' #2 r)).
      * auto.
      * apply handle_phi_block_complete in H2; try assumption.
        destruct H2 as (args & dst & k & H21 & H22 & H23).
        econstructor; try eassumption.
        unfold index_pred, successors_list.
        rewrite Hpreds. destruct l.
        -- destruct k; discriminate H22.
        -- apply nth_error_In in H22 as H22'.
           apply in_get_index_some in H22'. destruct H22' as (k' & H22').
           apply get_index_nth_error in H22' as H22''.
 We use the fact that all the elements of (p :: l) are distinct
              to conclude.
              This follows from make_predecessors_NoDup.
           *)           rewrite H22'. apply f_equal.
           apply NoDup_nth_error with (l:=p::l).
           ++ pose proof (make_predecessors_NoDup _ wf pc').
              unfold successors_list in H2. rewrite Hpreds in H2.
              assumption.
           ++ apply nth_error_Some. unfold node in H22''. (* HACK !!! *)
              congruence.
           ++ unfold node in *. (* HACK !!! *) congruence.
    + auto.
Qed.

Definition make_du_chain f : du_chain :=
  let u := def_use_1 (P2Map.init nil) (fn_code f) in
  def_use_2 u f.(fn_code) f.(fn_phicode).

Lemma make_du_chain_correct : forall f (wf : wf_ssa_function f) r pc,
  use f r pc ->
  In pc ((make_du_chain f) #2 r).
Proof.
  intros. unfold make_du_chain. inv H.
  - apply def_use_2_correct_1. apply def_use_1_correct_2. assumption.
  - apply def_use_2_correct_2; assumption.
Qed.

Lemma make_du_chain_complete : forall f (wf : wf_ssa_function f) r pc,
  In pc ((make_du_chain f) #2 r) ->
  use f r pc.
Proof.
  intros. unfold make_du_chain in H.
  destruct (in_dec peq pc ((def_use_1 (P2Map.init []) f.(fn_code)) #2 r)).
  - apply u_code. apply def_use_1_complete in i; try assumption.
    rewrite P2Map.gi. intros [].
  - apply u_phicode. eapply def_use_2_complete; eauto.
Qed.




Definition defined_var ins :=
  match ins with
  | Iop _ _ r _ => Some r
  | Iload _ _ _ r _ => Some r
  | Icall _ _ _ r _ => Some r
  | Ibuiltin _ _ (BR r) _ => Some r
  | _ => None
  end.

Definition compute_code_def (f : function) : P2Tree.t node -> P2Tree.t node :=
  PTree.fold
    (fun acc pc ins =>
      match defined_var ins with
      | Some r => P2Tree.set r pc acc
      | None => acc
      end)
    (fn_code f).

Definition compute_phi_def (f : function) : P2Tree.t node -> P2Tree.t node :=
  PTree.fold
   (fun acc pc phib =>
     fold_left
       (fun acc phi =>
       match phi with
       | Iphi args dst =>
           P2Tree.set dst pc acc
       end)
       phib acc)
   (fn_phicode f).


Definition get_all_def f :=
  let defs := P2Tree.empty node in
  let defs := compute_code_def f defs in
  let defs := compute_phi_def f defs in
  defs.

Definition compute_def (f : function) all_defs :=
  fun r =>
    match all_defs !2 r with
    | Some d => d
    | None => fn_entrypoint f
    end.

Lemma assigned_code_spec_defined_var : forall f d r,
  assigned_code_spec f.(fn_code) d r ->
  exists i, f.(fn_code) ! d = Some i /\ defined_var i = Some r.
Proof.
  intros. inv H; (eexists; split; [eassumption|reflexivity]).
Qed.

Lemma defined_var_assigned_code_spec : forall f d i r,
  f.(fn_code) ! d = Some i -> defined_var i = Some r ->
  assigned_code_spec f.(fn_code) d r.
Proof.
  intros. destruct i; inv H0; try (econstructor; eassumption).
  destruct b; inv H2; econstructor; eassumption.
Qed.

Lemma compute_code_def_correct_1 : forall f (wf : wf_ssa_function f) defs r,
  (forall pc, ~ assigned_code_spec f.(fn_code) pc r) ->
  (compute_code_def f defs) !2 r = defs !2 r.
Proof.
  intros. unfold compute_code_def. rewrite PTree.fold_spec.
  apply fold_invariant.
  - reflexivity.
  - intros (pc, i) defs'. simpl. intros. apply PTree.elements_complete in H0.
    destruct (defined_var i) as [dst|] eqn:Hdefined.
    + rewrite P2Tree.gsspec. destruct (P2Tree.elt_eq r dst).
      * subst. contradiction (H pc).
        eapply defined_var_assigned_code_spec; eassumption.
      * assumption.
    + assumption.
Qed.

Lemma compute_code_def_correct_2 : forall f (wf : wf_ssa_function f) defs r d,
  assigned_code_spec f.(fn_code) d r ->
  (compute_code_def f defs) !2 r = Some d.
Proof.
  intros. unfold compute_code_def. rewrite PTree.fold_spec.
  apply fold_becomes_true_growing.
  - apply Exists_exists. apply assigned_code_spec_defined_var in H.
    destruct H as (i & H1 & H2).
    exists (d, i). split.
    + apply PTree.elements_correct. assumption.
    + simpl. intros defs'. rewrite H2. rewrite P2Tree.gss. reflexivity.
  - intros (pc, i) defs'. simpl. intros. apply PTree.elements_complete in H0.
    destruct (defined_var i) as [dst|] eqn:Hdefined.
    + rewrite P2Tree.gsspec. destruct (P2Tree.elt_eq r dst).
      * subst. apply f_equal.
        eapply (assigned_code_unique _ wf); try eassumption.
        eapply defined_var_assigned_code_spec; eassumption.
      * assumption.
    + assumption.
Qed.

Lemma compute_code_def_complete : forall f (wf : wf_ssa_function f) defs r d,
  (compute_code_def f defs) !2 r = Some d ->
  defs !2 r = None ->
  assigned_code_spec f.(fn_code) d r.
Proof.
  intros. revert H. unfold compute_code_def. rewrite PTree.fold_spec.
  apply fold_invariant.
  - congruence.
  - intros (pc, i) defs'. simpl. intros. apply PTree.elements_complete in H.
    destruct (defined_var i) as [dst|] eqn:Hdefined.
    + rewrite P2Tree.gsspec in H2. destruct (P2Tree.elt_eq r dst).
      * inv H2. eapply defined_var_assigned_code_spec; eassumption.
      * auto.
    + auto.
Qed.

Lemma compute_phi_def_correct_1 : forall f (wf : wf_ssa_function f) defs r,
  (forall pc, ~ assigned_phi_spec f.(fn_phicode) pc r) ->
  (compute_phi_def f defs) !2 r = defs !2 r.
Proof.
  intros. unfold compute_phi_def. rewrite PTree.fold_spec.
  apply fold_invariant.
  - reflexivity.
  - intros (pc, pb) defs'. simpl. intros. apply PTree.elements_complete in H0.
    apply fold_invariant.
    + assumption.
    + intros pi defs''. intros. destruct pi as [args dst].
      rewrite P2Tree.gsspec. destruct (P2Tree.elt_eq r dst).
      * subst. contradiction (H pc).
        econstructor; [eassumption|eexists;eassumption].
      * assumption.
Qed.

Lemma compute_phi_def_correct_2 : forall f (wf : wf_ssa_function f) defs r d,
  assigned_phi_spec f.(fn_phicode) d r ->
  (compute_phi_def f defs) !2 r = Some d.
Proof.
  intros. unfold compute_phi_def. rewrite PTree.fold_spec.
  apply fold_becomes_true_growing.
  - apply Exists_exists. inv H.
    exists (d, phiinstr). split.
    + apply PTree.elements_correct. assumption.
    + simpl. intros defs'. apply fold_becomes_true_growing.
      * destruct H1 as (args & H1). apply Exists_exists.
        eexists. split; [eassumption|].
        simpl. intros defs''. rewrite P2Tree.gss. reflexivity.
      * intros pi defs''. intros. destruct pi as [args dst].
        rewrite P2Tree.gsspec. destruct (P2Tree.elt_eq r dst).
        -- reflexivity.
        -- assumption.
  - intros (pc, i) defs'. simpl. intros. apply PTree.elements_complete in H0.
    apply fold_invariant.
    + assumption.
    + intros pi defs''. intros. destruct pi as [args dst].
      rewrite P2Tree.gsspec. destruct (P2Tree.elt_eq r dst).
      * subst. apply f_equal.
        eapply (assigned_phi_unique _ wf); try eassumption.
        econstructor; [eassumption|eexists;eassumption].
      * assumption.
Qed.

Lemma compute_phi_def_complete : forall f (wf : wf_ssa_function f) defs r d,
  (compute_phi_def f defs) !2 r = Some d ->
  defs !2 r = None ->
  assigned_phi_spec f.(fn_phicode) d r.
Proof.
  intros. revert H. unfold compute_phi_def. rewrite PTree.fold_spec.
  apply fold_invariant.
  - congruence.
  - intros (pc, pb) defs'. simpl. intros. apply PTree.elements_complete in H.
    revert H2. apply fold_invariant.
    + assumption.
    + intros pi defs''. intros. destruct pi as [args dst].
      rewrite P2Tree.gsspec in H4. destruct (P2Tree.elt_eq r dst).
      * inv H4. econstructor; [eassumption|eexists;eassumption].
      * auto.
Qed.

Lemma compute_def_correct :
  forall f (wf : wf_ssa_function f) r d,
  def f r d ->
  compute_def f (get_all_def f) r = d.
Proof.
  intros. unfold compute_def, get_all_def.
  inv H.
  - rewrite (compute_phi_def_correct_1 _ wf), (compute_code_def_correct_1 _ wf).
    + rewrite P2Tree.gempty. reflexivity.
    + inv H0.
      * apply wf.(fn_ssa_params _). assumption.
      * assumption.
    + inv H0.
      * apply wf.(fn_ssa_params _). assumption.
      * assumption.
  - rewrite (compute_phi_def_correct_1 _ wf),
      (compute_code_def_correct_2 _ wf) with (d:=d).
    + reflexivity.
    + assumption.
    + intros. intros contra. eapply assigned_code_and_phi; eassumption.
  - rewrite (compute_phi_def_correct_2 _ wf) with (d:=d).
    + reflexivity.
    + assumption.
Qed.

Lemma compute_def_complete : forall f (wf : wf_ssa_function f) r d
  (USE: exists pc, use f r pc),
  compute_def f (get_all_def f) r = d ->
  def f r d.
Proof.
  intros. unfold compute_def, get_all_def in H.
  destruct ((compute_code_def f (P2Tree.empty _)) !2 r) as [d'|] eqn:Hcode.
  - apply def_code.
    apply (compute_code_def_complete _ wf) in Hcode as Hcode';
      try (rewrite P2Tree.gempty; reflexivity).
    rewrite (compute_phi_def_correct_1 _ wf) in H.
    + rewrite Hcode in H.
      subst. assumption.
    + intros. intros contra. eapply assigned_code_and_phi; eassumption.
  - destruct ((compute_phi_def f (compute_code_def f (P2Tree.empty node))) !2 r)
      as [d'|] eqn:Hphicode.
    + apply def_phicode. subst.
      apply (compute_phi_def_complete _ wf) in Hphicode; assumption.
    + subst. apply def_params. apply ext_params_undef.
      * assumption.
      * intros. intros contra.
        apply (compute_phi_def_correct_2 _ wf)
          with (defs:=compute_code_def f (P2Tree.empty _))
          in contra.
        congruence.
      * intros. intros contra.
        apply (compute_code_def_correct_2 _ wf) with (defs:=P2Tree.empty _)
          in contra.
        congruence.
Qed.



Definition collect_variables f :=
  let defs := all_def f.(fn_code) f.(fn_phicode) in
  let uses := all_uses f.(fn_code) f.(fn_phicode) in
  let params :=
    List.fold_left (fun acc x => SSARegSet.add x acc) f.(fn_params) SSARegSet.empty
  in
  SSARegSet.union (SSARegSet.union defs uses) params.

Definition ignore2 {A B} (x : A) (y : B) := x.



Lemma clos_refl_trans_clos_trans : forall {A} (R : A -> A -> Prop) x y,
  clos_refl_trans _ R x y <-> x = y \/ clos_trans _ R x y.
Proof.
  split; intros.
  - induction H.
    + right; apply t_step; assumption.
    + left; reflexivity.
    + destruct IHclos_refl_trans1, IHclos_refl_trans2.
      * left; congruence.
      * right; congruence.
      * right; congruence.
      * right; eauto using t_trans.
  - destruct H. subst y. apply rt_refl.
    induction H.
    + apply rt_step; assumption.
    + eauto using rt_trans.
Qed.