Module SSAlive


Require Import SetoidList.

Require Import Coqlib.
Require Import Time.
Require Import Errors.
Require Import Maps.
Require Import Lattice.
Require Import AST.
Require Import Integers.
Require Import Values.
Require Import Globalenvs.
Require Import Op.
Require Import Registers.
Require Import Utils.
Require Import SSA.
Require Import SSAutils.
Require Import Kildall.
Require Import KildallComp.
Require Import Kildall_v2.
Require Import DLib.
Require Import Classical.

Local Open Scope string_scope.

Liveness analysis over SSA


Notation reg_live := SSARegSet.add.
Notation reg_dead := SSARegSet.remove.

Definition reg_option_live (or: option reg)
    (lv: SSARegSet.t) :=
  match or with
  | None => lv
  | Some r => reg_live r lv
  end.

Definition reg_sum_live (ros: reg + ident)
    (lv: SSARegSet.t) :=
  match ros with
  | inl r => reg_live r lv
  | inr s => lv
  end.

Fixpoint reg_list_live
    (rl: list reg) (lv: SSARegSet.t) {struct rl}
      : SSARegSet.t :=
  match rl with
  | nil => lv
  | r1 :: rs => reg_list_live rs (reg_live r1 lv)
  end.

Fixpoint reg_list_dead
   (rl: list reg) (lv: SSARegSet.t) {struct rl}
     : SSARegSet.t :=
  match rl with
  | nil => lv
  | r1 :: rs => reg_list_dead rs (reg_dead r1 lv)
  end.

Definition transfer_phib k pb after :=
  List.fold_left (fun lv pi =>
    match pi with
    | Iphi args _ =>
      match nth_error args k with
      | None => lv
      | Some r => reg_live r lv
      end
    end) pb after.

Definition transfer_instruction preds (phicode:phicode)
    (i : instruction) pc
    (after: SSARegSet.t) : SSARegSet.t :=
  match i with
  | Inop s =>
    match phicode ! s with
    | None => after
    | Some pb =>
      match index_pred preds pc s with
      | None => after
      | Some k => transfer_phib k pb after
      end
    end
  | Iop op args res s =>
      reg_list_live args (reg_dead res after)
  | Iload chunk addr args dst s =>
      reg_list_live args (reg_dead dst after)
  | Istore chunk addr args src s =>
      reg_list_live args (reg_live src after)
  | Icall sig ros args res s =>
      reg_list_live args
       (reg_sum_live ros (reg_dead res after))
  | Itailcall sig ros args =>
      reg_list_live args
        (reg_sum_live ros after)
  | Ibuiltin ef args (BR res) s =>
      reg_list_live (params_of_builtin_args args) (reg_dead res after)
  | Ibuiltin ef args res s =>
      reg_list_live (params_of_builtin_args args) after
  | Icond cond args ifso ifnot =>
      reg_list_live args after
  | Ijumptable arg tbl =>
      reg_live arg after
  | Ireturn optarg =>
      reg_option_live optarg after
  end.

Definition transfer_phib' (phib : phiblock)
    (after: SSARegSet.t) : SSARegSet.t :=
  List.fold_left (fun lv pi =>
    match pi with
    | Iphi _ res => reg_dead res lv
    end) phib after.

Definition transfer
    (f: function) preds (pc: node)
    (after: SSARegSet.t) : SSARegSet.t :=
  match f.(fn_code)!pc with
  | None =>
      SSARegSet.empty
  | Some i =>
      let lv := transfer_instruction preds f.(fn_phicode) i pc after in
      if peq pc f.(fn_entrypoint) then reg_list_dead f.(fn_params) lv
      else
        match (fn_phicode f) ! pc with
        | None => lv
        | Some phib =>
          transfer_phib' phib lv
        end
  end.

The liveness analysis is then obtained by instantiating the general framework for backward dataflow analysis provided by module Kildall.

Module RegsetLat := LFSet(SSARegSet).
Module DS := Backward_Dataflow_Solver(RegsetLat)(NodeSetBackward).

Definition analyze (f: function): option (PMap.t SSARegSet.t) :=
  let preds :=
    time (Some "analyze ") "predecessors " (fun _ =>
      (make_predecessors (fn_code f) successors_instr)) tt
  in
  time (Some "analyze ") "fixpoint " (fun _ =>
    DS.fixpoint f.(fn_code) successors_instr (transfer f preds)) tt.


Lemma use_phicode_is_inop : forall f x pc
  (wf : wf_ssa_function f),
  use_phicode f x pc -> exists s, f.(fn_code) ! pc = Some (Inop s).
Proof.
  intros. destruct H. exists pc.
  apply wf.(fn_normalized _).
  - apply wf.(fn_phicode_inv _). congruence.
  - apply pred_is_edge in KPRED. inv KPRED.
    unfold successors_list, successors.
    rewrite PTree.gmap1.
    unfold option_map. rewrite H. assumption.
Qed.

Lemma wf_ssa_use_and_def_same_pc_precise : forall f x pc,
  wf_ssa_function f ->
  use f x pc ->
  def f x pc ->
  assigned_phi_spec f.(fn_phicode) pc x \/ ext_params f x /\ use_phicode f x pc.
Proof.
  intros. inversion H1; subst.
  - right. split; [assumption|]. inversion H0; subst; auto.
    apply fn_entry in H. destruct H as (s & H).
    inversion H3; subst; congruence.
  - inversion H0; subst; auto.
    + eapply fn_use_def_code in H; eauto. contradiction.
    + apply (use_phicode_is_inop _ _ _ H) in H3.
      destruct H3 as (s & H3). inversion H2; subst; congruence.
  - left. assumption.
Qed.

Lemma prop_dec_exists_dec : forall (c : code) (P : node -> Prop)
  (P_finite : forall pc, P pc -> c ! pc <> None)
  (P_dec : forall pc, P pc \/ ~ P pc),
  (exists pc, P pc) \/ ~ exists pc, P pc.
Proof.
  intros.
   induction c.
  - right. intros (pc & contra). apply P_finite in contra.
    contradiction contra. apply PTree.gempty.
  - assert ((exists pc : node, P (xO pc)) \/ ~ (exists pc : node, P (xO pc))) as Ha.
    { apply IHc1.
      - intros. intros contra. apply (P_finite (xO pc)); assumption.
      - intros. apply P_dec.
    }
    assert ((exists pc : node, P (xI pc)) \/ ~ (exists pc : node, P (xI pc))) as Hb.
    { apply IHc2.
      - intros. intros contra. apply (P_finite (xI pc)); assumption.
      - intros. apply P_dec.
    }
    destruct Ha as [Ha|Ha].
    + left. destruct Ha as (pc & Ha).
      exists (xO pc). assumption.
    + destruct (P_dec xH).
      * left. exists xH; assumption.
      * destruct Hb as [Hb|Hb].
        -- left. destruct Hb as (pc & Hb).
           exists (xI pc). assumption.
        -- right. intros contra. destruct contra as (pc & contra).
           destruct pc.
           ++ apply Hb. exists pc. assumption.
           ++ apply Ha. exists pc. assumption.
           ++ contradiction.
Qed.

Lemma assigned_code_spec_finite : forall c pc x,
  assigned_code_spec c pc x ->
  c ! pc <> None.
Proof.
  intros. inv H; congruence.
Qed.

Lemma assigned_code_spec_dec : forall c pc x,
  assigned_code_spec c pc x \/ ~ assigned_code_spec c pc x.
Proof.
  intros.
  destruct (c ! pc) eqn:Hpc; try (right; intros contra; inv contra; congruence).
  destruct i; try (right; intros contra; inv contra; congruence).
  - destruct (p2eq r x); try (right; intros contra; inv contra; congruence).
    left. subst. econstructor; eassumption.
  - destruct (p2eq r x); try (right; intros contra; inv contra; congruence).
    left. subst. econstructor; eassumption.
  - destruct (p2eq r x); try (right; intros contra; inv contra; congruence).
    left. subst. econstructor; eassumption.
  - destruct b; try (right; intros contra; inv contra; congruence).
    destruct (p2eq x0 x); try (right; intros contra; inv contra; congruence).
    left. subst. econstructor; eassumption.
Qed.

Lemma assigned_phi_spec_finite : forall c pc x,
  assigned_phi_spec c pc x -> c ! pc <> None.
Proof.
  intros. inv H; congruence.
Qed.

Definition phi_eq (phi1 phi2 : phiinstruction) :=
  match phi1, phi2 with
  | Iphi _ r1, Iphi _ r2 => r1 = r2
  end.

Lemma phi_eq_dec : forall (phi1 phi2 : phiinstruction),
  { phi_eq phi1 phi2 } + { ~ phi_eq phi1 phi2 }.
Proof.
  intros. destruct phi1, phi2. simpl.
  apply p2eq.
Qed.

Lemma assigned_phi_spec_dec : forall c pc x,
  assigned_phi_spec c pc x \/ ~ assigned_phi_spec c pc x.
Proof.
  intros.
  destruct (c ! pc) eqn:Hpc; try (right; intros contra; inv contra; congruence).
  destruct (InA_dec phi_eq_dec (Iphi nil x) p).
  - left. econstructor. eassumption. apply InA_alt in i.
    destruct i as (phi & i1 & i2).
    destruct phi as [args r].
    simpl in i1. subst.
    exists args. assumption.
  - right. intros contra. apply n. inv contra.
    rewrite Hpc in H. inv H. destruct H0 as (args & H0).
    apply InA_alt. eexists. split; [|eassumption].
    reflexivity.
Qed.

Lemma make_predecessors_some : forall {A} (code:PTree.t A) successors s l,
  (make_predecessors code successors) ! s = Some l ->
  forall p, In p l -> exists i, code ! p = Some i.
Proof.
  unfold make_predecessors.
  intros A code successors.
  apply PTree_Properties.fold_rec; intros.
  - rewrite <- H. eapply H0; eauto.
  - rewrite PTree.gempty in H. inv H.
  - destruct (peq k p).
    * subst. rewrite PTree.gss. eauto.
    * rewrite PTree.gso; auto.
      destruct (in_dec peq p (a !!! s)).
      -- unfold successors_list in i.
         destruct (a ! s) eqn:Has. eauto. contradiction i.
      -- eelim add_successors_correct2 with (tolist := (successors v)); eauto.
         unfold successors_list. rewrite H2. assumption.
Qed.

Lemma fn_phicode_code : forall f,
  wf_ssa_function f ->
  forall pc block,
    (fn_phicode f) ! pc = Some block ->
    exists ins, (fn_code f) ! pc = Some ins.
Proof.
  intros.
  generalize (fn_phicode_inv f H pc) ; eauto.
  intros [HH1 HH2].
  exploit HH2; eauto. congruence.
  intros. inv H1.

  assert ((make_predecessors (fn_code f) successors_instr) !!! pc = l).
  { unfold successors_list. rewrite Hpreds. auto. }
  assert (exists pred, In pred l). destruct l. simpl in *. omega.
  exists p ; eauto. destruct H2.
  exploit @make_predecessors_some; eauto. intros [i Hi].
  assert (In pc (successors_instr i)).
  { eapply make_predecessors_correct2 ; eauto.
    unfold successors_list.
    unfold make_preds. rewrite Hpreds. auto.
  }

  exploit fn_code_closed ; eauto.
Qed.

Lemma ssa_use_exists_def : forall f,
  wf_ssa_function f ->
  forall x u,
  use f x u -> exists d, def f x d.
Proof.
  intros.
  destruct (prop_dec_exists_dec f.(fn_code) (fun pc => assigned_code_spec f.(fn_code) pc x));
    simpl.
  - intros. eapply assigned_code_spec_finite; eassumption.
  - intros. apply assigned_code_spec_dec.
  - destruct H1; eauto.
  - destruct (prop_dec_exists_dec f.(fn_code) (fun pc => assigned_phi_spec f.(fn_phicode) pc x));
      simpl.
    + intros. apply assigned_phi_spec_finite in H2.
      destruct (f.(fn_phicode) ! pc) eqn:Hphi; [|congruence].
      eapply fn_phicode_code in Hphi; try assumption.
      destruct Hphi as (i & Hphi). congruence.
    + intros. apply assigned_phi_spec_dec.
    + destruct H2; eauto.
    + exists (f.(fn_entrypoint)). econstructor. econstructor 2; eauto.
Qed.



Inductive live_spec (f : function) (r : reg)
   (pc : node) : Prop :=
| live_spec_use :
    ~ def f r pc ->
    use f r pc ->
    live_spec f r pc
| live_spec_pred :
    forall pc',
    cfg f pc pc' ->
    ~ def f r pc ->
    live_spec f r pc' ->
    live_spec f r pc.

Inductive liveout_spec (f : function) (r : reg)
   (pc : node) : Prop :=
| liveout_spec_use :
    forall pc',
    cfg f pc pc' ->
    ~ def f r pc' ->
    use f r pc' ->
    liveout_spec f r pc
| liveout_spec_trans :
    forall pc',
    cfg f pc pc' ->
    ~ def f r pc' ->
    liveout_spec f r pc' ->
    liveout_spec f r pc.

Record wf_live1 f (live : reg -> node -> Prop) := MK_WF_LIVE1 {
    wf_live_incl1 : forall pc pc' x,
      cfg f pc pc' ->
      live x pc' ->
      live x pc \/ def f x pc;

    wf_live_use1 : forall pc x, use f x pc -> ~ def f x pc -> live x pc
  }.

Record wf_live2 f (live_in live_out : reg -> node -> Prop):= MK_WF_LIVE2 {
    wf_liveout_incl2 : forall pc pc' x,
      cfg f pc pc' ->
      live_in x pc' ->
      live_out x pc ;

    wf_livein_incl2 : forall pc x,
      ~ def f x pc ->
      live_out x pc ->
      live_in x pc ;

    wf_live_use2 : forall pc x,
      use f x pc ->
      ~ def f x pc ->
      live_in x pc
  }.

Definition wf_live3 f (live : reg -> node -> Prop) :=
  forall r pc,
  liveout_spec f r pc ->
  live r pc.

Definition wf_live4 f (live : reg -> node -> Prop) :=
  forall r pc,
  live_spec f r pc ->
  live r pc.

Lemma liveout_live : forall f (wf : wf_ssa_function f),
  forall r pc pc',
  cfg f pc pc' ->
  live_spec f r pc' ->
  liveout_spec f r pc.
Proof.
  intros. revert dependent pc. induction H0; intros; subst.
  - eapply liveout_spec_use; eauto.
  - eapply liveout_spec_trans; eauto.
Qed.

Lemma live_liveout :
  forall f pc x,
  ~ def f x pc ->
  liveout_spec f x pc ->
  live_spec f x pc.
Proof.
  intros. induction H0.
  - eapply live_spec_pred; eauto. eapply live_spec_use; eauto.
  - eapply live_spec_pred; eauto.
Qed.

Lemma live_spec_exists_def : forall f (wf : wf_ssa_function f) r pc,
  live_spec f r pc ->
  exists pc', def f r pc'.
Proof.
  intros. induction H. eapply ssa_use_exists_def; eassumption.
  eauto.
Qed.

Lemma liveout_spec_exists_def : forall f (wf : wf_ssa_function f) r pc,
  liveout_spec f r pc ->
  exists pc', def f r pc'.
Proof.
  intros. induction H; eauto.
  eapply ssa_use_exists_def; eassumption.
Qed.

Lemma live_wf_live1 : forall f (wf : wf_ssa_function f),
  wf_live1 f (live_spec f).
Proof.
  split; intros.
  - destruct (live_spec_exists_def _ wf _ _ H0) as (pc'' & H1).
    destruct (peq pc pc'').
    + subst. right; assumption.
    + left. eapply live_spec_pred; eauto using ssa_def_unique.
  - eapply live_spec_use; eassumption.
Qed.

Lemma live_wf_live2 : forall f (wf : wf_ssa_function f),
  wf_live2 f (live_spec f) (liveout_spec f).
Proof.
  split; intros.
  - eapply liveout_live; eauto.
  - eapply live_liveout; eauto.
  - econstructor; eassumption.
Qed.

Lemma wf_live2_wf_live1 : forall f (wf : wf_ssa_function f) live_in live_out,
  wf_live2 f live_in live_out ->
  wf_live1 f live_in.
Proof.
  split; intros.
  - destruct (classic (def f x pc)); [right; assumption|].
    left. eapply wf_livein_incl2; eauto. eapply wf_liveout_incl2; eauto.
  - eapply wf_live_use2; eauto.
Qed.

Lemma wf_live2_wf_live3 : forall f (wf : wf_ssa_function f) live_in live_out,
  wf_live2 f live_in live_out ->
  wf_live3 f live_out.
Proof.
  unfold wf_live3; intros.
  induction H0.
  - eapply wf_liveout_incl2; try eassumption.
    eapply wf_live_use2; eassumption.
  - eapply wf_liveout_incl2; try eassumption.
    eapply wf_livein_incl2; eassumption.
Qed.

Lemma wf_live2_wf_live4 : forall f (wf : wf_ssa_function f) live_in live_out,
  wf_live2 f live_in live_out ->
  wf_live4 f live_in.
Proof.
  unfold wf_live4; intros.
  induction H0.
  - eapply wf_live_use2; eassumption.
  - eapply wf_livein_incl2; try eassumption.
    eapply wf_liveout_incl2; eassumption.
Qed.

Lemma wf_live1_wf_live4 : forall f (wf : wf_ssa_function f) live,
  wf_live1 f live ->
  wf_live4 f live.
Proof.
  unfold wf_live4; intros.
  induction H0.
  - eapply wf_live_use1; eassumption.
  - exploit wf_live_incl1; eauto.
    intros H3. destruct H3; [assumption|congruence].
Qed.

Lemma live_spec_exists_path : forall f (wf : wf_ssa_function f) r pc,
  live_spec f r pc ->
  exists d u l,
  def f r d /\
  use f r u /\
  SSApath f (Dom.PState pc) l (Dom.PState u) /\
  ~ In d (l ++ cons u nil).
Proof.
  intros.
  destruct (live_spec_exists_def _ wf _ _ H) as (d & Hdef).
  exists d.
  induction H.
  - exists pc, nil. split; [assumption|]. split; [assumption|].
    split; [constructor|].
    intros [contra|[]]. congruence.
  - apply IHlive_spec in Hdef as H2. destruct H2 as (u & l & _ & H21 & H22 & H23).
    exists u, (pc::l). split; [assumption|]. split; [assumption|].
    split.
    + econstructor; try eassumption.
      econstructor; try eassumption.
      inv H. eapply fn_code_reached; eassumption.
    + intros [contra|contra]; congruence.
Qed.

Lemma liveout_spec_exists_path : forall f (wf : wf_ssa_function f) r pc,
  liveout_spec f r pc ->
  exists d u l,
  def f r d /\
  use f r u /\
  SSApath f (Dom.PState pc) (pc::l) (Dom.PState u) /\
  ~ In d (l ++ u :: nil).
Proof.
  intros.
  destruct (liveout_spec_exists_def _ wf _ _ H) as (d & Hdef).
  exists d.
  induction H.
  - exists pc', nil. split; [assumption|]. split; [assumption|].
    split.
    + econstructor; [|constructor]. constructor; [|assumption].
      inv H. eapply fn_code_reached; eassumption.
    + intros [contra|[]]. congruence.
  - apply IHliveout_spec in Hdef as H2. destruct H2 as (u & l & _ & H21 & H22 & H23).
    exists u, (pc'::l). split; [assumption|]. split; [assumption|].
    split.
    + econstructor; try eassumption.
      econstructor; try eassumption.
      inv H. eapply fn_code_reached; eassumption.
    + intros [contra|contra]; congruence.
Qed.

Lemma exists_path_live_spec : forall f (wf : wf_ssa_function f) r d u pc l,
  def f r d ->
  use f r u ->
  SSApath f (Dom.PState pc) l (Dom.PState u) ->
  ~ In d (l ++ u :: nil) ->
  live_spec f r pc.
Proof.
  intros. induction H1. (* je comprends pas trop pourquoi Ã§a marche sans remember *)
  - constructor; [|assumption].
    intros contra. apply H2. left. eapply ssa_def_unique; eassumption.
  - inv STEP; [|inv H1; inv STEP].
    eapply live_spec_pred.
    eassumption.
    intros contra. apply H2. left. eapply ssa_def_unique; eassumption.
    eapply IHpath; eauto. intros contra. apply H2. right; assumption.
Qed.

Lemma exists_path_liveout_spec : forall f (wf : wf_ssa_function f) r d u pc l,
  def f r d ->
  use f r u ->
  SSApath f (Dom.PState pc) (pc::l) (Dom.PState u) ->
  ~ In d (l ++ u :: nil) ->
  liveout_spec f r pc.
Proof.
  intros. induction H1. (* là non plus *)
  inv STEP; [|inv H1; inv STEP].
  inv H1.
  - eapply liveout_spec_use.
    eassumption.
    intros contra. apply H2. left. eapply ssa_def_unique; eassumption.
    assumption.
  - inv STEP; [|inv PATH; inv STEP].
    eapply liveout_spec_trans.
    eassumption.
    intros contra. apply H2. left. eapply ssa_def_unique; eassumption.
    eapply IHpath; eauto. intros contra. apply H2. right; assumption.
Qed.

Lemma live_spec_not_def : forall f r d pc,
  def f r d ->
  live_spec f r pc ->
  d <> pc.
Proof.
  intros. inversion H0; congruence.
Qed.

Lemma live_spec_reached : forall f (wf : wf_ssa_function f) r pc,
  live_spec f r pc ->
  reached f pc.
Proof.
  intros. inversion H.
  - eapply use_reached; eassumption.
  - inv H0. eapply fn_code_reached; eassumption.
Qed.

Lemma liveout_spec_reached : forall f (wf : wf_ssa_function f) r pc,
  liveout_spec f r pc ->
  reached f pc.
Proof.
  intros. inversion H.
  - inv H0. eapply fn_code_reached; eassumption.
  - inv H0. eapply fn_code_reached; eassumption.
Qed.

Lemma live_spec_strict : forall f (wf : wf_ssa_function f) r d pc,
  def f r d ->
  live_spec f r pc ->
  sdom f d pc.
Proof.
  intros. constructor.
  - right.
    eapply live_spec_reached; eassumption.
    intros.
    destruct (live_spec_exists_path _ wf _ _ H0)
      as (d' & u & l & H21 & H22 & H23 & H24).
    assert (d' = d) by (eapply ssa_def_unique; eauto); subst.
    pose proof (fn_strict _ wf _ _ _ H22 H21).
    inv H2.
    + contradiction H24. rewrite in_app_iff. right; left; reflexivity.
    + assert (In d (p ++ l)).
      { apply PATH. eapply Dom.path_app; eassumption. }
      apply in_app_iff in H2.
      destruct H2; [assumption|].
      contradiction H24. rewrite in_app_iff. left; assumption.
  - eapply live_spec_not_def; eassumption.
Qed.

Lemma liveout_spec_strict : forall f (wf : wf_ssa_function f) r d pc,
  def f r d ->
  liveout_spec f r pc ->
  dom f d pc.
Proof.
  intros.
  destruct (peq d pc).
  - subst. left.
  - right.
    eapply liveout_spec_reached; eassumption.
    intros.
    destruct (liveout_spec_exists_path _ wf _ _ H0)
      as (d' & u & l & H21 & H22 & H23 & H24).
    assert (d' = d) by (eapply ssa_def_unique; eauto); subst.
    pose proof (fn_strict _ wf _ _ _ H22 H21).
    inv H2.
    + contradiction H24. rewrite in_app_iff. right; left; reflexivity.
    + assert (In d (p ++ pc :: l)).
      { apply PATH. eapply Dom.path_app; eassumption. }
      apply in_app_iff in H2.
      destruct H2; [assumption|].
      destruct H2; [congruence|].
      contradiction H24. rewrite in_app_iff. left; assumption.
Qed.

Corollary live_spec_no_entry : forall f (wf : wf_ssa_function f) r,
  ~ live_spec f r f.(fn_entrypoint).
Proof.
  intros. intros contra.
  destruct (live_spec_exists_def _ wf _ _ contra) as (d & H).
  eapply live_spec_strict in contra; try eassumption.
  inv contra.
  apply (Dom.dom_entry peq) in DOM. congruence.
Qed.

Section WF_LIVE.

Variable f : function.
Variable wf : wf_ssa_function f.

Notation preds := (make_predecessors (fn_code f) successors_instr).

Definition Lin := (fun pc live_out => (transfer f preds pc (live_out pc))).

Definition Lout {A: Type} := (fun (live: PMap.t A) pc => live # pc).

Lemma reg_list_live_incl : forall x l s,
    SSARegSet.In x s ->
    SSARegSet.In x (reg_list_live l s).
Proof.
  induction l ; intros ; simpl ; auto.
  eapply IHl ; eauto.
  eapply SSARegSet.add_2 ; eauto.
Qed.

Lemma reg_list_live_incl_2 : forall x l s,
    In x l ->
    SSARegSet.In x (reg_list_live l s).
Proof.
  induction l ; intros ; inv H.
  simpl.
  eapply reg_list_live_incl ; eauto.
  eapply SSARegSet.add_1 ; eauto.
  eapply IHl ; eauto.
Qed.

Lemma reg_list_dead_not_in : forall l r s,
  SSARegSet.In r s ->
  ~ In r l ->
  SSARegSet.In r (reg_list_dead l s).
Proof.
  induction l ; intros; go.
  simpl.
  eapply IHl; eauto.
  eapply SSARegSet.remove_2 ; eauto.
  destruct a, r ; intro Hcont ; invh and ; subst ; go.
Qed.

Lemma reg_sum_live_incl : forall x ros s,
  SSARegSet.In x s ->
  SSARegSet.In x (reg_sum_live ros s).
Proof.
  intros. destruct ros; simpl.
  - apply SSARegSet.add_2. assumption.
  - assumption.
Qed.

Lemma reg_option_live_incl : forall x or s,
  SSARegSet.In x s ->
  SSARegSet.In x (reg_option_live or s).
Proof.
  intros. destruct or; simpl.
  - apply SSARegSet.add_2. assumption.
  - assumption.
Qed.

Lemma OP2_eq : forall r1 r2, OP2.eq r1 r2 <-> r1 = r2.
Proof.
  split; intros.
  - destruct r1, r2, H; simpl in *; congruence.
  - split; congruence.
Qed.

Lemma liveout_incl : forall live, analyze f = Some live ->
    forall pc pc',
    cfg f pc pc' ->
    SSARegSet.Subset (Lin pc' (Lout live)) (Lout live pc).
Proof.
  intros. intros x H1.
  generalize H ; intros AN.
  unfold analyze in H.
  inv H0.
  (exploit (@DS.fixpoint_solution _ (fn_code f)
                                 successors_instr
                                 (transfer f preds)
                                 live pc ins pc' H HCFG_ins HCFG_in)); eauto.
  intros.
  unfold transfer. rewrite H0.
  unfold DS.L.bot, DS.L.eq.
  unfold Regset.Equal; reflexivity.
Qed.

Lemma transfer_phib_livein_incl : forall live k phib,
    SSARegSet.Subset live (transfer_phib k phib live).
Proof.
  intros. revert live. induction phib; intros.
  - reflexivity.
  - simpl. destruct a. destruct (nth_error l k); [|apply IHphib].
    unfold SSARegSet.Subset; intros.
    apply IHphib. apply SSARegSet.add_2; assumption.
Qed.

Lemma transfer_phib'_livein_incl : forall live pc phib x,
    f.(fn_phicode) ! pc = Some phib ->
    SSARegSet.In x live ->
    ~ def f x pc ->
    SSARegSet.In x (transfer_phib' phib live).
Proof.
  intros. remember phib as phib' in H.
  assert (incl phib phib') as Ha by (subst; apply incl_refl).
  clear Heqphib'. revert dependent live.
  induction phib; intros.
  - assumption.
  - simpl. destruct a.
    eapply IHphib; try eassumption.
    + eapply incl_cons_inv; eassumption.
    + apply SSARegSet.remove_2; [|eassumption].
      intros contra. apply OP2_eq in contra. subst.
      apply H1. apply def_phicode. econstructor; [eassumption|].
      eexists. apply Ha. left; reflexivity.
Qed.

Lemma transfer_instruction_livein_incl : forall live i pc x,
    f.(fn_code) ! pc = Some i ->
    SSARegSet.In x live ->
    ~ def f x pc ->
    SSARegSet.In x (transfer_instruction preds (fn_phicode f) i pc live).
Proof.
  intros. unfold transfer_instruction. destruct i.
  - destruct (f.(fn_phicode) ! n); [|assumption].
    destruct (index_pred preds pc n); [|assumption].
    apply transfer_phib_livein_incl; assumption.
  - apply reg_list_live_incl. apply SSARegSet.remove_2; [|assumption].
    intros contra. apply OP2_eq in contra. subst.
    apply H1. apply def_code. econstructor; eassumption.
  - apply reg_list_live_incl. apply SSARegSet.remove_2; [|assumption].
    intros contra. apply OP2_eq in contra. subst.
    apply H1. apply def_code. econstructor; eassumption.
  - apply reg_list_live_incl. apply SSARegSet.add_2. assumption.
  - apply reg_list_live_incl.
    destruct s0.
    + apply SSARegSet.add_2. apply SSARegSet.remove_2; [|assumption].
      intros contra. apply OP2_eq in contra. subst.
      apply H1. apply def_code. econstructor; eassumption.
    + apply SSARegSet.remove_2; [|assumption].
      intros contra. apply OP2_eq in contra. subst.
      apply H1. apply def_code. econstructor; eassumption.
  - apply reg_list_live_incl.
    destruct s0.
    + apply SSARegSet.add_2. assumption.
    + assumption.
  - destruct b.
    + apply reg_list_live_incl. apply SSARegSet.remove_2; [|assumption].
      intros contra. apply OP2_eq in contra. subst.
      apply H1. apply def_code. econstructor; eassumption.
    + apply reg_list_live_incl. assumption.
    + apply reg_list_live_incl. assumption.
  - apply reg_list_live_incl. assumption.
  - apply SSARegSet.add_2. assumption.
  - destruct o; [apply SSARegSet.add_2|]; assumption.
Qed.

Lemma livein_incl : forall live pc x
    (Hincl : forall x, SSARegSet.In x (Lout live pc) -> f.(fn_code) ! pc <> None),
    SSARegSet.In x (Lout live pc) ->
    ~ def f x pc ->
    SSARegSet.In x (Lin pc (Lout live)).
Proof.
  intros.
  (unfold Lout, Lin in * ; unfold transfer).
  { case_eq (fn_code f) ! pc ; intros.
    - destruct (peq pc f.(fn_entrypoint)).
      + subst. apply reg_list_dead_not_in.
        apply transfer_instruction_livein_incl; assumption.
        intros contra. apply H0. constructor.
        constructor. assumption.
      + case_eq (f.(fn_phicode) ! pc); intros.
        * eapply transfer_phib'_livein_incl; try eassumption.
          apply transfer_instruction_livein_incl; assumption.
        * apply transfer_instruction_livein_incl; assumption.
    - apply Hincl in H. congruence.
  }
Qed.

Lemma analyze_some : forall live, analyze f = Some live ->
  forall pc x,
  SSARegSet.In x (Lout live pc) ->
  f.(fn_code) ! pc <> None.
Proof.
  intros.
  unfold analyze, time in H.
  intros contra.
  eapply DS.fixpoint_some in contra; eauto.
  apply contra in H0.
  inversion H0.
Qed.

Lemma transfer_phib_live_use : forall live k phib args dst x,
  In (Iphi args dst) phib ->
  nth_error args k = Some x ->
  SSARegSet.In x (transfer_phib k phib live).
Proof.
  intros. revert dependent live. induction phib; intros.
  - contradiction H.
  - simpl. destruct H.
    + subst. rewrite H0. apply transfer_phib_livein_incl.
      apply SSARegSet.add_1. apply OP2_eq; reflexivity.
    + destruct a.
      destruct (nth_error l k); eapply IHphib; eassumption.
Qed.

Lemma transfer_instruction_live_use : forall live i pc x,
    f.(fn_code) ! pc = Some i ->
    use f x pc ->
    SSARegSet.In x (transfer_instruction preds (fn_phicode f) i pc live).
Proof.
  intros. inv H0.
  - inv H1; rewrite H0 in H; inv H; simpl.
    + apply reg_list_live_incl_2; assumption.
    + apply reg_list_live_incl_2; assumption.
    + apply reg_list_live_incl_2; assumption.
    + destruct dst; apply reg_list_live_incl_2; assumption.
    + destruct H2.
      * apply reg_list_live_incl. apply SSARegSet.add_1.
        apply OP2_eq; assumption.
      * apply reg_list_live_incl_2; assumption.
    + destruct H2.
      * apply reg_list_live_incl. apply SSARegSet.add_1.
        apply OP2_eq; assumption.
      * apply reg_list_live_incl_2; assumption.
    + destruct H2.
      * apply reg_list_live_incl. apply SSARegSet.add_1.
        apply OP2_eq; assumption.
      * apply reg_list_live_incl_2; assumption.
    + apply reg_list_live_incl_2; assumption.
    + apply reg_list_live_incl_2; assumption.
    + apply SSARegSet.add_1. apply OP2_eq; reflexivity.
    + apply SSARegSet.add_1. apply OP2_eq; reflexivity.
  - destruct (use_phicode_is_inop _ _ _ wf H1) as (s & H2).
    rewrite H2 in H; inv H; simpl.
    inv H1.
    assert (pc0 = s).
    { apply pred_is_edge in KPRED. inv KPRED.
      rewrite H in H2; inv H2.
      simpl in H0. destruct H0 as [|[]].
      congruence.
    }
    subst.
    rewrite PHIB. rewrite KPRED.
    eapply transfer_phib_live_use; eassumption.
Qed.

Lemma live_use : forall live pc x,
    ~ def f x pc ->
    use f x pc ->
    SSARegSet.In x (Lin pc (Lout live)).
Proof.
  intros.
  (unfold Lout, Lin in * ; unfold transfer).
  { case_eq (fn_code f) ! pc ; intros.
    - destruct (peq pc f.(fn_entrypoint)).
      + subst. apply reg_list_dead_not_in.
        apply transfer_instruction_live_use; assumption.
        intros contra. apply H. constructor.
        constructor. assumption.
      + case_eq (f.(fn_phicode) ! pc); intros.
        * eapply transfer_phib'_livein_incl; try eassumption.
          apply transfer_instruction_live_use; assumption.
        * apply transfer_instruction_live_use; assumption.
    - inv H0.
      + inv H2; congruence.
      + inv H2.
        apply pred_is_edge in KPRED. inv KPRED.
        congruence.
  }
Qed.

Lemma live_wf_live : forall live, analyze f = Some live ->
  wf_live2 f
    (fun x pc => SSARegSet.In x (Lin pc (Lout live)))
    (fun x pc => SSARegSet.In x (Lout live pc)).
Proof.
  intros. split; intros.
  - eapply liveout_incl; eauto.
  - eapply livein_incl; eauto.
    intros. eapply analyze_some; eauto.
  - eapply live_use; eauto.
Qed.

End WF_LIVE.