Module CounterMeasures


From Coq Require Import Program.Equality.
Require Import Coqlib Maps List Integers Errors.
Import ListNotations.
Require Import AST.
Require Import Op Registers.
Require Import RTL RTLtyping RTLpast.

Open Scope positive.

Toolkit for writing and specifying counter-measure transformations


Some tactics


Ltac destruct_conjs :=
  repeat
    match goal with
    | H: _ /\ _ |- _ => destruct H
    | H: exists _, _ |- _ => destruct H
    end.

Ltac cases :=
  repeat
    match goal with
    | H: context [ match ?x with _ => _ end ] |- _ => destruct x
    end.

Ltac cases_eq :=
  repeat
    match goal with
    | H: context [ match ?x with _ => _ end ] |- _ => destruct x eqn:?EQ
    end.

State monad


Record state : Type := mkstate {
  st_nextnode: positive; (* last used CFG node *)
  st_code: code; (* current CFG *)
}.

Monotone evolution of the state.

Inductive sincr (s1 s2: state) : Prop :=
  Sincr (NEXTNODE: Ple s1.(st_nextnode) s2.(st_nextnode)).

Remark sincr_refl: forall s, sincr s s.
Proof.
  intros; constructor; extlia.
Qed.

Lemma sincr_trans: forall s1 s2 s3, sincr s1 s2 -> sincr s2 s3 -> sincr s1 s3.
Proof.
  intros. inv H; inv H0. constructor; extlia.
Qed.

Dependently-typed state monad, ensuring that the final state is greater or equal (in the sense of predicate sincr above) than the initial state.

Inductive res {A: Type} {s: state}: Type := R (x: A) (s': state) (I: sincr s s').

Definition proj_val {A: Type} {st: state} (r: @res A st) :=
  let '(R v _ _) := r in v.

Definition proj_st {A: Type} {st: state} (r: @res A st) :=
  let '(R _ st' _) := r in st'.

Definition mon (A: Type) : Type := forall (s: state), @res A s.

Operations on this monad.

Definition ret {A: Type} (x: A): mon A :=
  fun s => R x s (sincr_refl s).

Definition bind {A B: Type} (x: mon A) (f: A -> mon B): mon B :=
  fun s1 => match x s1 with R vx s2 I1 =>
              match f vx s2 with R vy s3 I2 =>
                R vy s3 (sincr_trans s1 s2 s3 I1 I2)
              end
            end.

Notation "'doM' X <- A ; B" := (bind A (fun X => B))
   (at level 200, X ident, A at level 100, B at level 200).

Lemma bind_inversion {A B: Type} : forall (x: mon A) (f: A -> mon B) v st st' sincr,
    bind x f st = R v st' sincr ->
    exists vx st1 sincr1 sincr2, x st = R vx st1 sincr1 /\ f vx st1 = R v st' sincr2.
Proof.
  intros * BIND. unfold bind in BIND.
  destruct (x _) eqn:X, (f _ _) eqn:F; inv BIND.
  eauto 8.
Qed.

Program Definition fresh_pc : mon node :=
  fun s =>
    let pc := s.(st_nextnode) in
    R pc (mkstate (Pos.succ pc) s.(st_code)) _.
Next Obligation.
  constructor. simpl. extlia.
Qed.

Program Definition set_instr (pc: node) (i: instruction): mon unit :=
  fun s =>
    R tt (mkstate s.(st_nextnode) (PTree.set pc i s.(st_code))) _.
Next Obligation.
  intros; constructor; simpl; extlia.
Qed.

Ltac monadInv :=
  match goal with
  | H: bind _ _ _ = R _ _ _ |- _ =>
      apply bind_inversion in H as (?&?&?INCR&?INCR&?MON&?MON)
  | H: ret _ _ = R _ _ _ |- _ => inv H
  | H:fresh_pc _ = R _ _ _ |- _ => inv H
  | H:set_instr _ _ _ = R _ _ _ |- _ => inv H
  | x: res |- _ => destruct x
  | H:sincr _ _ |- _ => inv H
  end; simpl in *.

Partial state monad


Definition pmon (A: Type) : Type := forall (s: state), Errors.res (@res A s).

Definition pret {A: Type} (x: A): pmon A :=
  fun s => OK (R x s (sincr_refl s)).

Definition pbind {A B: Type} (x: pmon A) (f: A -> pmon B): pmon B :=
  fun s1 => match x s1 with
         | OK (R vx s2 I1) =>
             match f vx s2 with
             | OK (R vy s3 I2) => OK (R vy s3 (sincr_trans s1 s2 s3 I1 I2))
             | Error e => Error e
             end
         | Error e => Error e
         end.

Lemma pbind_inversion {A B: Type} : forall (x: pmon A) (f: A -> pmon B) v st st' sincr,
    pbind x f st = OK (R v st' sincr) ->
    exists vx st1 sincr1 sincr2, x st = OK (R vx st1 sincr1) /\ f vx st1 = OK (R v st' sincr2).
Proof.
  intros * BIND. unfold pbind in BIND.
  cases_eq; inv BIND; eauto 8.
Qed.

Ltac pMonadInv :=
  match goal with
  | H: pbind _ _ _ = OK (R _ _ _) |- _ =>
      apply pbind_inversion in H as (?&?&?INCR&?INCR&?MON&?MON)
  | H: OK _ = OK _ |- _ => inv H
  | H: Error _ = OK _ |- _ => inv H
  | H: Errors.bind _ _ = OK _ |- _ =>
      apply Errors.bind_inversion in H as (?&?MON&?MON)
  | x: unit |- _ => destruct x
  | H: _ |- _ => monadInv
  end; simpl in *.

Definition ptree_pmfold {A: Type} (f: positive -> A -> pmon unit) (t: PTree.t A): pmon unit :=
  PTree.fold (fun s1 k v => pbind s1 (fun _ => f k v)) t (pret tt).

Lemma ptree_pmfold_rec {A} f m_final st st' (P : PTree.t A -> state -> Prop) :
  (forall m m' a, (forall x, m ! x = m' ! x) -> P m a -> P m' a) ->
  P (PTree.empty _) st ->
  (forall m pc i st1 st2,
      sincr st st1 ->
      m!pc = None ->
      m_final!pc = Some i ->
      P m st1 ->
      f pc i st1 = Errors.OK st2 ->
      P (PTree.set pc i m) (proj_st st2)) ->
  @ptree_pmfold A f m_final st = Errors.OK st' ->
  P m_final (proj_st st').
Proof.
  intros * COMPAT P0 FP. revert st'.
  unfold ptree_pmfold; simpl.
  apply PTree_Properties.fold_rec; auto using sincr_refl.
  - intros * EQ IND * F. eauto.
  - intros * F. Errors.monadInv F. auto using sincr_refl.
  - intros * NONE FIND1 IND * BIND.
    unfold pbind in *. cases_eq; inv BIND. simpl.
    specialize (IND _ eq_refl); simpl in *.
    eapply FP in IND; eauto; simpl in *; auto.
Qed.

Ltac extlia :=
  repeat match goal with H: _ = R _ _ _ |- _ => clear H end;
  repeat match goal with H: _ = OK (R _ _ _) |- _ => clear H end;
  repeat monadInv;
  Coqlib.extlia.

Sequence builder. This applies to passes that translate each instruction individually into a sequence of instructions.


Inductive exits : Type :=
| OneExit
| NoExit
| TwoExits
| NExits
.

Fact exits_eq_dec : forall e1 e2 : exits, {e1 = e2} + {e1 <> e2}.
Proof.
intros. decide equality. Qed.

Global Ltac inv_existT_eq :=
  match goal with
  | H: existT _ _ _ = existT _ _ _ |- _ =>
      apply Eqdep_dec.inj_pair2_eq_dec in H; subst; auto using exits_eq_dec
  end.

Inductive seqinstr : exits -> Type :=
| Seinstr : (node -> instruction) -> seqinstr OneExit
| Sereturn : instruction -> seqinstr NoExit
| Second : condition -> list reg -> seqinstr OneExit -> seqinstr OneExit -> option bool -> seqinstr TwoExits
| Sejumptable : reg -> seqinstr NExits
| Secatch : seqinstr NoExit
| Ssinstr {e} : (node -> instruction) -> seqinstr e -> seqinstr e
| Ssmerge2 {e} : condition -> list reg -> seqinstr OneExit -> seqinstr OneExit -> option bool -> seqinstr e -> seqinstr e
| Ssmerge1 {e} : condition -> list reg -> seqinstr NoExit -> option bool -> seqinstr e -> seqinstr e
.

Good formation of seqinstr wrt the successors_instr fun


Inductive wf_seq : forall {e}, seqinstr e -> Prop :=
| wfs_einstr : forall i,
    (forall pc, successors_instr (i pc) = [pc]) ->
    wf_seq (Seinstr i)
| wfs_ereturn : forall i,
    successors_instr i = [] ->
    wf_seq (Sereturn i)
| wfs_econd : forall c rs seq1 seq2 pred,
    wf_seq seq1 -> wf_seq seq2 ->
    wf_seq (Second c rs seq1 seq2 pred)
| wfs_ejumptable : forall r, wf_seq (Sejumptable r)
| wfs_ecatch : wf_seq Secatch
| wfs_sinstr {e} : forall i (seq: seqinstr e),
    (forall pc, successors_instr (i pc) = [pc]) ->
    wf_seq seq ->
    wf_seq (Ssinstr i seq)
| wfs_smerge2 {e} : forall c rs seq1 seq2 pred (seq: seqinstr e),
    wf_seq seq1 -> wf_seq seq2 ->
    wf_seq seq ->
    wf_seq (Ssmerge2 c rs seq1 seq2 pred seq)
| wfs_smerge1 {e} : forall c rs seq1 pred (seq: seqinstr e),
    wf_seq seq1 ->
    wf_seq seq ->
    wf_seq (Ssmerge1 c rs seq1 pred seq).
Global Hint Constructors wf_seq : rtlcm.

Ltac inv_wf_seq :=
  match goal with
  | H: wf_seq (Seinstr _) |- _ => inv H
  | H: wf_seq (Sereturn _) |- _ => inv H
  | H: wf_seq (Second _ _ _ _ _) |- _ => inv H
  | H: wf_seq (Sejumptable _) |- _ => inv H
  | H: wf_seq Secatch |- _ => inv H
  | H: wf_seq (Ssinstr _ _) |- _ => inv H
  | H: wf_seq (Ssmerge2 _ _ _ _ _ _) |- _ => inv H
  | H: wf_seq (Ssmerge1 _ _ _ _ _) |- _ => inv H
  end.

Inductive cont : exits -> Type :=
| COneExit : node -> cont OneExit
| CNoExit : cont NoExit
| CTwoExits : node -> node -> cont TwoExits
| CNExits : list node -> cont NExits.

Inductive is_cont : forall {e}, cont e -> node -> Prop :=
| isContOne : forall pc, is_cont (COneExit pc) pc
| isContTwo : forall pc1 pc2 pc,
    pc = pc1 \/ pc = pc2 ->
    is_cont (CTwoExits pc1 pc2) pc
| isContN : forall pcs pc,
    In pc pcs ->
    is_cont (CNExits pcs) pc.
Global Hint Constructors is_cont : rtlcm.

Ltac inv_is_cont :=
  match goal with
  | H:is_cont CNoExit _ |- _ => inv H
  | H:is_cont (COneExit _) _ |- _ => inv H
  | H:is_cont (CTwoExits _ _) _ |- _ => inv H
  | H:is_cont (CNExits _) _ |- _ => inv H
  end.

Ltac is_cont_specialize :=
  match goal with
  | I: (forall _, is_cont _ _ -> _), H:is_cont _ _ |- _ => specialize (I _ H)
  | H: forall _, is_cont (COneExit ?pc) _ -> _ |- _ =>
      specialize (H pc (isContOne _)) as ?PC; clear H
  | H: forall _, is_cont (CTwoExits ?pc1 ?pc2) _ -> _ |- _ =>
      specialize (H pc1 (isContTwo _ _ _ (or_introl eq_refl))) as ?PC;
      specialize (H pc2 (isContTwo _ _ _ (or_intror eq_refl))) as ?PC; clear H
  end.

Section cseq.

  Inductive cseqinstr : exits -> Type :=
  | Ceinstr : instruction -> cseqinstr OneExit
  | Cereturn : instruction -> cseqinstr NoExit
  | Cecond : condition -> list reg -> cseq OneExit -> cseq OneExit -> option bool -> cseqinstr TwoExits
  | Cejumptable : reg -> list node -> cseqinstr NExits
  | Cecatch : cseqinstr NoExit
  | Csinstr {e} : instruction -> cseq e -> cseqinstr e
  | Csmerge2 {e} : condition -> list reg -> cseq OneExit -> cseq OneExit -> option bool -> cseq e -> cseqinstr e
  | Csmerge1 {e} : condition -> list reg -> cseq NoExit -> option bool -> cseq e -> cseqinstr e

  with cseq : exits -> Type :=
  | Cseq {e} : node -> cseqinstr e -> node -> cseq e.

  Section cseq_ind2.
    Variable P : forall {e}, cseq e -> Prop.

    Hypothesis Heinstr : forall pc i pcl, P (Cseq pc (Ceinstr i) pcl).
    Hypothesis Hereturn : forall pc i pcl, P (Cseq pc (Cereturn i) pcl).
    Hypothesis Hecond : forall pc c rs s1 s2 pred pcl,
        P s1 -> P s2 -> P (Cseq pc (Cecond c rs s1 s2 pred) pcl).
    Hypothesis Hejumptable : forall pc r pcs pcl, P (Cseq pc (Cejumptable r pcs) pcl).
    Hypothesis Hecatch : forall pc pcl, P (Cseq pc Cecatch pcl).
    Hypothesis Hsinstr : forall {e} pc i (s: cseq e) pcl,
        P s -> P (Cseq pc (Csinstr i s) pcl).
    Hypothesis Hsmerge2 : forall {e} pc c rs s1 s2 pred (s3: cseq e) pcl,
        P s1 -> P s2 -> P s3 -> P (Cseq pc (Csmerge2 c rs s1 s2 pred s3) pcl).
    Hypothesis Hsmerge1 : forall {e} pc c rs s1 pred (s3: cseq e) pcl,
        P s1 -> P s3 -> P (Cseq pc (Csmerge1 c rs s1 pred s3) pcl).

    Lemma cseq_ind2 : forall {e} (s: cseq e), P s.
    Proof.
      fix Fix 2.
      intros ? s. destruct s, c.
      - apply Heinstr.
      - apply Hereturn.
      - apply Hecond; auto.
      - apply Hejumptable.
      - apply Hecatch.
      - apply Hsinstr; auto.
      - apply Hsmerge2; auto.
      - apply Hsmerge1; auto.
    Qed.
  End cseq_ind2.

  Definition cseq_pc {e} (cs : cseq e) :=
    let '(Cseq pc _ _) := cs in pc.

  Definition cseq_last {e} (cs : cseq e) :=
    let '(Cseq _ _ pc) := cs in pc.

  Variable abort : node.

Backward specification

  Inductive in_cseq : forall {e}, node -> instruction -> cseq e -> Prop :=
  | in_einstr : forall pc i pcl, in_cseq pc i (Cseq pc (Ceinstr i) pcl)
  | in_ereturn : forall pc i pcl, in_cseq pc i (Cseq pc (Cereturn i) pcl)
  | in_econd_1 : forall pc c rs pred s1 s2 pcl,
      in_cseq pc (Icond c rs (cseq_pc s1) (cseq_pc s2) pred) (Cseq pc (Cecond c rs s1 s2 pred) pcl)
  | in_econd_2 : forall pc i c rs pred s1 s2 pcf pcl,
      in_cseq pc i s1 -> in_cseq pc i (Cseq pcf (Cecond c rs s1 s2 pred) pcl)
  | in_econd_3 : forall pc i c rs pred s1 s2 pcf pcl,
      in_cseq pc i s2 -> in_cseq pc i (Cseq pcf (Cecond c rs s1 s2 pred) pcl)
  | in_ejumptable : forall pc r pcs pcl, in_cseq pc (Ijumptable r pcs) (Cseq pc (Cejumptable r pcs) pcl)
  | in_ecatch : forall pc pcl, in_cseq pc (Inop abort) (Cseq pc Cecatch pcl)
  | in_sinstr_1 {e} : forall pc i (s: cseq e) pcl,
      in_cseq pc i (Cseq pc (Csinstr i s) pcl)
  | in_sinstr_2 {e} : forall pc i i' (s: cseq e) pcf pcl,
      in_cseq pc i s -> in_cseq pc i (Cseq pcf (Csinstr i' s) pcl)
  | in_smerge2_1 {e} : forall pc c rs pred s1 s2 (s: cseq e) pcl,
      in_cseq pc (Icond c rs (cseq_pc s1) (cseq_pc s2) pred) (Cseq pc (Csmerge2 c rs s1 s2 pred s) pcl)
  | in_smerge2_2 {e} : forall pc i c rs pred s1 s2 (s: cseq e) pcf pcl,
      in_cseq pc i s1 -> in_cseq pc i (Cseq pcf (Csmerge2 c rs s1 s2 pred s) pcl)
  | in_smerge2_3 {e} : forall pc i c rs pred s1 s2 (s: cseq e) pcf pcl,
      in_cseq pc i s2 -> in_cseq pc i (Cseq pcf (Csmerge2 c rs s1 s2 pred s) pcl)
  | in_smerge2_4 {e} : forall pc i c rs pred s1 s2 (s: cseq e) pcf pcl,
      in_cseq pc i s -> in_cseq pc i (Cseq pcf (Csmerge2 c rs s1 s2 pred s) pcl)
  | in_smerge1_1 {e} : forall pc c rs pred s1 (s: cseq e) pcl,
      in_cseq pc (Icond c rs (cseq_pc s1) (cseq_pc s) pred) (Cseq pc (Csmerge1 c rs s1 pred s) pcl)
  | in_smerge1_2 {e} : forall pc i c rs pred s1 (s: cseq e) pcf pcl,
      in_cseq pc i s1 -> in_cseq pc i (Cseq pcf (Csmerge1 c rs s1 pred s) pcl)
  | in_smerge1_4 {e} : forall pc i c rs pred s1 (s: cseq e) pcf pcl,
      in_cseq pc i s -> in_cseq pc i (Cseq pcf (Csmerge1 c rs s1 pred s) pcl).

  Local Hint Constructors in_cseq : rtlcm.

  Lemma cseq_pc_in : forall {e} (s : cseq e),
      exists i, in_cseq (cseq_pc s) i s.
  Proof.
    induction s using cseq_ind2; simpl; esplit; econstructor.
    Unshelve. all:exact xH.
  Qed.

  Inductive spec_mode :=
  | NoPred
  | Pred.

  Inductive wf_cseq m : forall {e}, cseq e -> Prop :=
  | wf_einstr : forall pcf i pcl, wf_cseq m (Cseq pcf (Ceinstr i) pcl)
  | wf_ereturn : forall pcf i pcl, wf_cseq m (Cseq pcf (Cereturn i) pcl)
  | wf_econd : forall pcf c rs seq1 seq2 pred pcl,
      (forall i, ~in_cseq pcf i seq1) ->
      (forall i, ~in_cseq pcf i seq2) ->
      (forall pc i1 i2, in_cseq pc i1 seq1 -> in_cseq pc i2 seq2 -> False) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> ~In (cseq_pc seq1) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> ~In (cseq_pc seq2) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq2) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq1) ->
      wf_cseq m seq1 -> wf_cseq m seq2 ->
      wf_cseq m (Cseq pcf (Cecond c rs seq1 seq2 pred) pcl)
  | wf_ejumptable : forall pcf r pcs pcl,
      wf_cseq m (Cseq pcf (Cejumptable r pcs) pcl)
  | wf_ecatch : forall pcf pcl,
      wf_cseq m (Cseq pcf Cecatch pcl)
  | wf_sinstr {e} : forall pcf i (seq: cseq e) pcl,
      (m = Pred -> successors_instr i = [cseq_pc seq]) ->
      (forall i, ~in_cseq pcf i seq) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> ~In (cseq_pc seq) (successors_instr i1)) ->
      wf_cseq m seq ->
      wf_cseq m (Cseq pcf (Csinstr i seq) pcl)
  | wf_smerge2 {e} : forall pcf c rs seq1 seq2 pred (seq: cseq e) pcl,
      (forall i, ~in_cseq pcf i seq1) ->
      (forall i, ~in_cseq pcf i seq2) ->
      (forall i, ~in_cseq pcf i seq) ->
      (forall pc i1 i2, in_cseq pc i1 seq1 -> in_cseq pc i2 seq2 -> False) ->
      (forall pc i1 i2, in_cseq pc i1 seq1 -> in_cseq pc i2 seq -> False) ->
      (forall pc i1 i2, in_cseq pc i1 seq2 -> in_cseq pc i2 seq -> False) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> ~In (cseq_pc seq1) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> ~In (cseq_pc seq2) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> ~In (cseq_pc seq) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq2) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq1) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq1) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq2) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> forall pc2 i2, In pc2 (successors_instr i1) -> in_cseq pc2 i2 seq -> pc2 = cseq_pc seq) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> forall pc2 i2, In pc2 (successors_instr i1) -> in_cseq pc2 i2 seq -> pc2 = cseq_pc seq) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> In (cseq_pc seq) (successors_instr i1) -> pc1 = cseq_last seq1) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq2 -> In (cseq_pc seq) (successors_instr i1) -> pc1 = cseq_last seq2) ->
      wf_cseq m seq1 -> wf_cseq m seq2 -> wf_cseq m seq ->
      wf_cseq m (Cseq pcf (Csmerge2 c rs seq1 seq2 pred seq) pcl)
  | wf_smerge1 {e} : forall pcf c rs seq1 pred (seq: cseq e) pcl,
      (forall i, ~in_cseq pcf i seq1) ->
      (forall i, ~in_cseq pcf i seq) ->
      (forall pc i1 i2, in_cseq pc i1 seq1 -> in_cseq pc i2 seq -> False) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> ~In (cseq_pc seq1) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> ~In (cseq_pc seq) (successors_instr i1)) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq -> forall pc2 i2, In pc2 (successors_instr i1) -> ~in_cseq pc2 i2 seq1) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> forall pc2 i2, In pc2 (successors_instr i1) -> in_cseq pc2 i2 seq -> pc2 = cseq_pc seq) ->
      (m = Pred -> forall pc1 i1, in_cseq pc1 i1 seq1 -> In (cseq_pc seq) (successors_instr i1) -> False) ->
      wf_cseq m seq1 -> wf_cseq m seq ->
      wf_cseq m (Cseq pcf (Csmerge1 c rs seq1 pred seq) pcl).

End cseq.

Ltac inv_in_cseq :=
  match goal with
  | H: in_cseq _ _ _ (Cseq _ (Ceinstr _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Cereturn _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Cecond _ _ _ _ _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Cejumptable _ _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ Cecatch _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Csinstr _ _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Csmerge2 _ _ _ _ _ _) _) |- _ => inv H
  | H: in_cseq _ _ _ (Cseq _ (Csmerge1 _ _ _ _ _) _) |- _ => inv H
  end; try inv_existT_eq.

Concretize a sequence of instructions

Section conc_seq.
  Variable abort : node.

Concretization function

  Fixpoint concretize_seq {e} (pc : node) (s : seqinstr e) : cont e -> mon (cseq e) :=
    match s in seqinstr e return cont e -> mon (cseq e) with
    | Seinstr i =>
        fun k => match k with
              | COneExit next => ret (Cseq pc (Ceinstr (i next)) pc)
              end
    | Sereturn i => fun k => ret (Cseq pc (Cereturn i) pc)
    | Second c rs seq1 seq2 pred =>
        fun k => match k with
              | CTwoExits next1 next2 =>
                  doM pc1 <- fresh_pc;
                  doM pc2 <- fresh_pc;
                  doM cseq1 <- concretize_seq pc1 seq1 (COneExit next1);
                  doM cseq2 <- concretize_seq pc2 seq2 (COneExit next2);
                  ret (Cseq pc (Cecond c rs cseq1 cseq2 pred) pc)
              end
    | Sejumptable r =>
        fun k => match k with CNExits pcs => ret (Cseq pc (Cejumptable r pcs) pc) end
    | Secatch => fun k => ret (Cseq pc Cecatch pc)
    | Ssinstr i seq1 =>
        fun k =>
          doM pc1 <- fresh_pc;
          doM cseq1 <- concretize_seq pc1 seq1 k;
          ret (Cseq pc (Csinstr (i pc1) cseq1) (cseq_last cseq1))
    | Ssmerge2 c rs seq1 seq2 pred seq3 =>
        fun k =>
          doM pc1 <- fresh_pc;
          doM pc2 <- fresh_pc;
          doM pc3 <- fresh_pc;
          doM cseq1 <- concretize_seq pc1 seq1 (COneExit pc3);
          doM cseq2 <- concretize_seq pc2 seq2 (COneExit pc3);
          doM cseq3 <- concretize_seq pc3 seq3 k;
          ret (Cseq pc (Csmerge2 c rs cseq1 cseq2 pred cseq3) (cseq_last cseq3))
    | Ssmerge1 c rs seq1 pred seq3 =>
        fun k =>
          doM pc1 <- fresh_pc;
          doM pc2 <- fresh_pc;
          doM pc3 <- fresh_pc;
          doM cseq1 <- concretize_seq pc1 seq1 CNoExit;
          doM cseq3 <- concretize_seq pc3 seq3 k;
          ret (Cseq pc (Csmerge1 c rs cseq1 pred cseq3) (cseq_last cseq3))
    end.

Concretization relation


  Inductive concretized : forall {e}, node -> seqinstr e -> cont e -> cseq e -> Prop :=
  | conc_einstr : forall pc i next,
      concretized pc (Seinstr i) (COneExit next) (Cseq pc (Ceinstr (i next)) pc)
  | conc_ereturn : forall pc i,
      concretized pc (Sereturn i) CNoExit (Cseq pc (Cereturn i) pc)
  | conc_econd : forall pc c rs seq1 seq2 pred pc1 pc2 next1 next2 cseq1 cseq2,
      concretized pc1 seq1 (COneExit next1) cseq1 ->
      concretized pc2 seq2 (COneExit next2) cseq2 ->
      concretized pc (Second c rs seq1 seq2 pred) (CTwoExits next1 next2)
        (Cseq pc (Cecond c rs cseq1 cseq2 pred) pc)
  | conc_ejumptable : forall pc r pcs,
      concretized pc (Sejumptable r) (CNExits pcs) (Cseq pc (Cejumptable r pcs) pc)
  | conc_ecatch : forall pc,
      concretized pc Secatch CNoExit (Cseq pc Cecatch pc)
  | conc_sinstr {e} : forall pc i seq (k: cont e) pc1 cseq,
      concretized pc1 seq k cseq ->
      concretized pc (Ssinstr i seq) k (Cseq pc (Csinstr (i pc1) cseq) (cseq_last cseq))
  | conc_smerge2 {e} : forall pc c rs seq1 seq2 pred seq3 (k: cont e) pc1 pc2 next cseq1 cseq2 cseq3,
      concretized pc1 seq1 (COneExit next) cseq1 ->
      concretized pc2 seq2 (COneExit next) cseq2 ->
      concretized next seq3 k cseq3 ->
      concretized pc (Ssmerge2 c rs seq1 seq2 pred seq3) k
        (Cseq pc (Csmerge2 c rs cseq1 cseq2 pred cseq3) (cseq_last cseq3))
  | conc_smerge1 {e} : forall pc c rs seq1 pred seq3 (k: cont e) pc1 next cseq1 cseq3,
      concretized pc1 seq1 CNoExit cseq1 ->
      concretized next seq3 k cseq3 ->
      concretized pc (Ssmerge1 c rs seq1 pred seq3) k
        (Cseq pc (Csmerge1 c rs cseq1 pred cseq3) (cseq_last cseq3)).

Correctness


  Lemma concretize_seq_correct : forall {e} (s: seqinstr e) pc k cs st st' sincr,
      concretize_seq pc s k st = R cs st' sincr ->
      concretized pc s k cs.
  Proof.
    dependent induction s; intros * CONC; simpl in *.
    - (* Seinstr *)
      dependent destruction k. repeat monadInv. constructor.
    - (* Sereturn *)
      dependent destruction k. repeat monadInv. constructor.
    - (* Second *)
      dependent destruction k. repeat monadInv.
      econstructor; eauto.
    - (* Sejumptable *)
      dependent destruction k. repeat monadInv. constructor.
    - (* Secatch *)
      dependent destruction k. repeat monadInv. constructor.
    - (* Ssinstr *)
      repeat monadInv. constructor; eauto.
    - (* Ssmerge2 *)
      repeat monadInv. econstructor; eauto.
    - (* Ssmerge1 *)
      repeat monadInv. econstructor; eauto.
  Qed.

  Ltac concretize_seq_specialize :=
    match goal with
    | IHs: (forall _, _ -> _ -> _) |- _ =>
        specialize (IHs _ eq_refl JMeq_refl)
    | IHs: (forall _ _ _ _ _ _, concretize_seq _ _ _ _ = R _ _ _ -> _),
        H:concretize_seq _ _ _ _ = R _ _ _ |- _ => specialize (IHs _ _ _ _ _ _ H)
    end.

  Lemma concretize_seq_preserves : forall {e} (s: seqinstr e) pc k cs st st' sincr,
      concretize_seq pc s k st = R cs st' sincr ->
      forall pc i, (st_code st)!pc = Some i <-> (st_code st')!pc = Some i.
  Proof.
    Ltac concretize_seq_preserves :=
      repeat
        match goal with
        | _ => concretize_seq_specialize
        | H: forall _ _, _ <-> (st_code ?st)!_ = Some _ |- _ <-> (st_code ?st)!_ = Some _ =>
            rewrite <-H
        | _ => reflexivity
        | _ => monadInv
        | k: cont _ |- _ => dependent destruction k
        end.
    induction s; intros * CONC *; simpl in *; concretize_seq_preserves.
  Qed.

  Lemma concretize_seq_in_inv : forall {e} (s: seqinstr e) pcf k cs st st' sincr,
      concretize_seq pcf s k st = R cs st' sincr ->
      forall pc i, in_cseq abort pc i cs -> pc = pcf \/ st_nextnode st <= pc < st_nextnode st'.
  Proof.
    Ltac concretize_seq_in_inv :=
      repeat
        match goal with
        | _ => concretize_seq_specialize
        | IHs: (forall _ _, in_cseq _ _ _ ?cs -> _ \/ _), H: in_cseq _ _ _ ?cs |- _ =>
            specialize (IHs _ _ H) as [|]; extlia
        | _ => monadInv
        | _ => inv_in_cseq
        | _ => now auto
        | k: cont _ |- _ => dependent destruction k; [idtac]
        end.
    induction s; intros * CONC * IN; simpl in *; concretize_seq_in_inv.
  Qed.

  Fact concretize_seq_cseq_pc : forall {e} (s: seqinstr e) pcf k cs st st' sincr,
      concretize_seq pcf s k st = R cs st' sincr ->
      cseq_pc cs = pcf.
  Proof.
    destruct s; intros * CONC; try (dependent destruction k; [idtac]);
      simpl in *; repeat monadInv; auto.
  Qed.

  Ltac concretize_seq_successors_inv :=
    repeat
      match goal with
      | _ => concretize_seq_specialize
      | k: cont _ |- _ => dependent destruction k; [idtac]
      | _ => monadInv
      | H: successors_instr _ = _ |- _ => rewrite H in *
      | H: forall _, successors_instr _ = _ |- _ => rewrite H in *
      | H: _ \/ _ |- _ => destruct H; subst
      | H: _ /\ _ |- _ => destruct H; subst
      | H: False |- _ => inv H
      | I: wf_seq _ -> _, H:wf_seq _ |- _ => specialize (I H)
      | I: (forall _ _, in_cseq _ _ _ _ -> _), H: in_cseq _ _ _ _ |- _ => specialize (I _ _ H)
      | I: (forall _, In _ _ -> _), H: In _ _ |- _ => specialize (I _ H)
      | _ => extlia
      | _ => inv_wf_seq
      | _ => inv_in_cseq
      | _ => inv_existT_eq
      | _ => inv_is_cont
      | _ => erewrite concretize_seq_cseq_pc; [|eauto]
      | _ => simpl in *; eauto with rtlcm
      end.

  Lemma concretize_seq_successors_inv : forall {e} (s: seqinstr e) pcf k cs st st' sincr,
      concretize_seq pcf s k st = R cs st' sincr ->
      wf_seq s ->
      forall pc i, in_cseq abort pc i cs ->
              forall pc1,
                In pc1 (successors_instr i) ->
                ((e = OneExit -> pc = cseq_last cs) /\ is_cont k pc1) \/ st_nextnode st <= pc1 < st_nextnode st' \/ pc1 = abort.
  Proof.
    induction s; intros * CONC WF * IN * SUCC; simpl in *.
    all:concretize_seq_successors_inv.
    1,2:left; split; eauto with rtlcm; intros; congruence.
  Qed.

  Lemma concretize_seq_wf {m} : forall {e} (s: seqinstr e) pcf k cs st st' sincr,
      concretize_seq pcf s k st = R cs st' sincr ->
      pcf < st_nextnode st ->
      abort < st_nextnode st ->
      (m = Pred -> forall pc, is_cont k pc -> pc < st_nextnode st) ->
      (m = Pred -> wf_seq s) ->
      wf_cseq abort m cs.
  Proof.
    Ltac concretize_seq_wf :=
      repeat
        match goal with
        | _ => concretize_seq_specialize
        | _ => is_cont_specialize
        (* | H: forall _ _, _ <-> (st_code ?st)!_ = Some _ |- _ <-> (st_code ?st)!_ = Some _ => *)
        (*     rewrite <-H *)
        | k:cont _ |- _ => dependent destruction k; [idtac]
        | H:Pred = Pred -> _ |- _ => specialize (H eq_refl)
        | _ => monadInv
        | _ => extlia
        | _ => inv_wf_seq
        | _ => inv_existT_eq
        | _ => inv_is_cont
        | H: _ < _ -> _ |- wf_cseq _ _ _ => apply H; auto; try extlia
        | |- ~_ => intros ?
        | IN1: in_cseq _ _ _ _, IN2: in_cseq _ _ _ _ |- _ =>
            eapply concretize_seq_in_inv in IN1 as [|]; [| |now eauto];
            (eapply concretize_seq_in_inv in IN2 as [|]; [| |now eauto]); subst; extlia
        | IN: in_cseq _ _ _ _ |- _ =>
            eapply concretize_seq_in_inv in IN as [|]; [| |now eauto]; subst; extlia
        | IN: In (cseq_pc _) (successors_instr _) |- _ =>
            erewrite concretize_seq_cseq_pc in IN; eauto
        | IN1: In _ (successors_instr ?i), IN2: in_cseq _ _ ?i ?s |- _ =>
            eapply concretize_seq_successors_inv with (cs:=s) in IN1 as [(?&?)|[|]]; eauto
        | _ => assumption
        | _ => constructor
        | _ => intros; subst
        end.
    induction s; intros * CONC LT ABORT CONT WF; simpl in *; concretize_seq_wf.
    - erewrite H2, concretize_seq_cseq_pc; eauto.
    - erewrite concretize_seq_cseq_pc; eauto.
    - erewrite concretize_seq_cseq_pc; eauto.
  Qed.

End conc_seq.

Global Hint Constructors in_cseq : rtlcm.
Global Hint Resolve cseq_pc_in : rtlcm.

Add a sequence of instructions into the CFG

Section add_seq.

  Variable abort : node.

  Fixpoint add_seq {e} (s : cseq e) : mon unit :=
    match s with
    | Cseq pc (Ceinstr i) _ => set_instr pc i
    | Cseq pc (Cereturn i) _ => set_instr pc i
    | Cseq pc (Cecond c rs seq1 seq2 pred) _ =>
        doM tt <- set_instr pc (Icond c rs (cseq_pc seq1) (cseq_pc seq2) pred);
        doM tt <- add_seq seq1;
        doM tt <- add_seq seq2;
        ret tt
    | Cseq pc (Cejumptable r pcs) _ =>
        set_instr pc (Ijumptable r pcs)
    | Cseq pc Cecatch _ => set_instr pc (Inop abort)
    | Cseq pc (Csinstr i seq) _ =>
        doM tt <- set_instr pc i;
        add_seq seq
    | Cseq pc (Csmerge2 c rs seq1 seq2 pred seq) _ =>
        doM tt <- set_instr pc (Icond c rs (cseq_pc seq1) (cseq_pc seq2) pred);
        doM tt <- add_seq seq1;
        doM tt <- add_seq seq2;
        add_seq seq
    | Cseq pc (Csmerge1 c rs seq1 pred seq) _ =>
        doM tt <- set_instr pc (Icond c rs (cseq_pc seq1) (cseq_pc seq) pred);
        doM tt <- add_seq seq1;
        add_seq seq
    end.

Specification generator


  Fixpoint spec_cseq {e} (Pnode : node -> Prop) (m : spec_mode) (code : code) (s : cseq e) : Prop :=
    match s with
    | Cseq pc (Ceinstr i) _ =>
        code!pc = Some i
    | Cseq pc (Cereturn i) _ => code!pc = Some i
    | Cseq pc (Cecond c rs seq1 seq2 pred) _ =>
        code!pc = Some (Icond c rs (cseq_pc seq1) (cseq_pc seq2) pred)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq1) pc)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq2) pc)
        /\ spec_cseq Pnode m code seq1
        /\ spec_cseq Pnode m code seq2
        /\ Pnode (cseq_pc seq1) /\ Pnode (cseq_pc seq2)
    | Cseq pc (Cejumptable r pcs) _ =>
        code!pc = Some (Ijumptable r pcs)
    | Cseq pc Cecatch _ => code!pc = Some (Inop abort)
    | Cseq pc (Csinstr i seq) _ =>
        code!pc = Some i
        /\ (m = Pred -> one_predecessor code (cseq_pc seq) pc)
        /\ spec_cseq Pnode m code seq
        /\ Pnode (cseq_pc seq)
    | Cseq pc (Csmerge2 c rs seq1 seq2 pred seq) _ =>
        code!pc = Some (Icond c rs (cseq_pc seq1) (cseq_pc seq2) pred)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq1) pc)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq2) pc)
        /\ spec_cseq Pnode m code seq1
        /\ spec_cseq Pnode m code seq2
        /\ (m = Pred -> two_predecessors code (cseq_pc seq) (cseq_last seq1) (cseq_last seq2))
        /\ spec_cseq Pnode m code seq
        /\ Pnode (cseq_pc seq1) /\ Pnode (cseq_pc seq2) /\ Pnode (cseq_pc seq)
    | Cseq pc (Csmerge1 c rs seq1 pred seq) _ =>
        code!pc = Some (Icond c rs (cseq_pc seq1) (cseq_pc seq) pred)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq1) pc)
        /\ (m = Pred -> one_predecessor code (cseq_pc seq) pc)
        /\ spec_cseq Pnode m code seq1
        /\ spec_cseq Pnode m code seq
        /\ Pnode (cseq_pc seq1) /\ Pnode (cseq_pc seq)
    end.

  Definition spec_seq {e} code pc (seq: seqinstr e) cont :=
    exists cseq, concretized pc seq cont cseq /\ spec_cseq (fun _ => True) NoPred code cseq.

Properties



  Inductive nodup_cont : forall {e}, cont e -> Prop :=
  | ndContNo : nodup_cont CNoExit
  | ndContOne : forall pc, nodup_cont (COneExit pc)
  | ndContTwo : forall pc1 pc2, pc1 <> pc2 -> nodup_cont (CTwoExits pc1 pc2)
  | ndContN : forall pcs, list_norepet pcs -> nodup_cont (CNExits pcs).

  Ltac inv_nodup_cont :=
    match goal with
    | H:nodup_cont CNoExit |- _ => inv H
    | H:nodup_cont (COneExit _) |- _ => inv H
    | H:nodup_cont (CTwoExits _ _) |- _ => inv H
    | H:nodup_cont (CNExits _) |- _ => inv H
    end.

  Lemma is_predecessor_code : forall code1 code2 pc1 pc2,
      is_predecessor code1 pc1 pc2 ->
      (forall i, In pc2 (successors_instr i) -> code1!pc1 = Some i -> code2!pc1 = Some i) ->
      is_predecessor code2 pc1 pc2.
  Proof.
    intros * IN IMP.
    eapply Kildall.make_predecessors_correct_3 in IN as (?&INSTR&PRED).
    eapply Kildall.make_predecessors_correct_1; eauto.
  Qed.

  Corollary one_predecessor_code : forall code1 code2 pc1 pc2,
      one_predecessor code1 pc1 pc2 ->
      (forall i, In pc1 (successors_instr i) -> forall pc, code2!pc = Some i -> code1!pc = Some i) ->
      one_predecessor code2 pc1 pc2.
  Proof.
    intros * ONE IMP ? IN. eapply ONE.
    eapply is_predecessor_code; eauto.
  Qed.

  Corollary two_predecessors_code : forall code1 code2 pc1 pc2 pc3,
      two_predecessors code1 pc1 pc2 pc3 ->
      (forall i, In pc1 (successors_instr i) -> forall pc, code2!pc = Some i -> code1!pc = Some i) ->
      two_predecessors code2 pc1 pc2 pc3.
  Proof.
    intros * ONE IMP ? IN. eapply ONE.
    eapply is_predecessor_code; eauto.
  Qed.

  Lemma spec_cseq_code {e} : forall (P: node -> Prop) m code1 code2 (s: cseq e),
      spec_cseq P m code1 s ->
      (forall pc i, in_cseq abort pc i s -> code1!pc = Some i -> code2!pc = Some i) ->
      (m = Pred -> forall pc1 i, In pc1 (successors_instr i) -> P pc1 -> (exists i, in_cseq abort pc1 i s) -> forall pc, code2!pc = Some i -> code1!pc = Some i) ->
      spec_cseq P m code2 s.
  Proof.
    induction s using cseq_ind2; intros * SPEC CODE1 CODE2;
      simpl in *; destruct_conjs; eauto with rtlcm.
    - (* Second *)
      repeat esplit; eauto with rtlcm.
      1,2:(intros; eapply one_predecessor_code; eauto;
           intros; eapply CODE2; eauto;
           edestruct (@cseq_pc_in abort OneExit); eauto with rtlcm).
      + eapply IHs1; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
      + eapply IHs2; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
    - (* Ssinstr *)
      repeat esplit; eauto with rtlcm.
      + intros. eapply one_predecessor_code; eauto.
        intros. eapply CODE2; eauto.
        edestruct (@cseq_pc_in abort e); eauto with rtlcm.
      + eapply IHs; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
    - (* Ssmerge2 *)
      repeat esplit; eauto with rtlcm.
      1,2:(intros; eapply one_predecessor_code; eauto;
           intros; eapply CODE2; eauto;
           edestruct (@cseq_pc_in abort OneExit); eauto with rtlcm).
      + eapply IHs1; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
      + eapply IHs2; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
      + intros. eapply two_predecessors_code; eauto.
        intros. eapply CODE2; eauto.
        edestruct (@cseq_pc_in abort e); eauto with rtlcm.
      + eapply IHs3; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
    - (* Ssmerge1 *)
      repeat esplit; eauto with rtlcm.
      + intros; eapply one_predecessor_code; eauto.
        intros; eapply CODE2; eauto.
        edestruct (@cseq_pc_in abort NoExit); eauto with rtlcm.
      + intros; eapply one_predecessor_code; eauto.
        intros; eapply CODE2; eauto.
        edestruct (@cseq_pc_in abort e); eauto with rtlcm.
      + eapply IHs1; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
      + eapply IHs2; eauto. 1,2:intros; destruct_conjs; eauto with rtlcm.
  Qed.

  Lemma spec_cseq_implies {e} : forall (P1 P2: node -> Prop) m code (s: cseq e),
      spec_cseq P1 m code s ->
      (forall x, P1 x -> P2 x) ->
      spec_cseq P2 m code s.
  Proof.
    induction s using cseq_ind2; intros * SPEC IMP; simpl in *;
      destruct_conjs; repeat esplit; eauto.
  Qed.

Correctness


  Lemma add_seq_preserves {e} : forall (s : cseq e) tt st st' sincr,
      add_seq s st = R tt st' sincr ->
      forall pc i,
        (forall pc' i', in_cseq abort pc' i' s -> pc <> pc') ->
        (st_code st)!pc = Some i <->
        (st_code st')!pc = Some i.
  Proof.
    induction s using cseq_ind2; intros * ADD * NIN; simpl in *.
    - (* Sinstr *)
      inv ADD; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Sereturn *)
      inv ADD; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Second *)
      repeat monadInv.
      rewrite <-IHs2 with (st':=st'), <-IHs1 with (st':=x2); eauto with rtlcm; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Sejumptable *)
      inv ADD; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Secatch *)
      inv ADD; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Ssinstr *)
      repeat monadInv.
      erewrite <-IHs with (st':=st'); eauto with rtlcm; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Ssmerge2 *)
      repeat monadInv.
      rewrite <-IHs3 with (st':=st'), <-IHs2 with (st':=x4), <-IHs1 with (st':=x2); eauto with rtlcm; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
    - (* Ssmerge1 *)
      repeat monadInv.
      rewrite <-IHs2 with (st':=st'), <-IHs1 with (st':=x2); eauto with rtlcm; simpl.
      rewrite PTree.gso; eauto with rtlcm. reflexivity.
  Qed.

  Local Ltac my_split :=
    repeat
      match goal with
      | |- exists _, _ => esplit
      | |- _ <= _ < _ => idtac
      | |- _ /\ _ => split
      end.

  Lemma add_seq_inv {e} : forall (s: cseq e) tt st st' sincr,
      add_seq s st = R tt st' sincr ->
      forall pc i, (st_code st')!pc = Some i ->
              in_cseq abort pc i s
              \/ ((st_code st)!pc = Some i /\ (forall i, ~in_cseq abort pc i s)).
  Proof.
    Local Ltac add_seq_inv :=
      repeat
        match goal with
        | H: (PTree.set ?x _ _)!?y = Some _ |- _ =>
            rewrite PTree.gsspec in H; destruct (peq _ _); [inv H|]
        | _ => monadInv
        | _ => right; split; [assumption|]
        | _ => intros ? IN; inv IN
        | _ => congruence
        | _ => inv_existT_eq
        | H:forall _, ~_ |- _ => now (eapply H; eauto)
        end; auto with rtlcm.

    induction s using cseq_ind2; intros * ADD * FIND; simpl in *.
    - (* Seinstr *) add_seq_inv.
    - (* Sereturn *) add_seq_inv.
    - (* Second *)
      add_seq_inv.
      eapply IHs2 in MON1 as [|(?&?)]; eauto with rtlcm.
      eapply IHs1 in MON0 as [|(?&?)]; eauto with rtlcm.
      add_seq_inv.
    - (* Sejumptable *) add_seq_inv.
    - (* Seabort *) add_seq_inv.
    - (* Ssinstr *)
      add_seq_inv.
      eapply IHs in MON0 as [|(?&?)]; eauto with rtlcm.
      add_seq_inv.
    - (* Ssmerge2 *)
      add_seq_inv.
      eapply IHs3 in MON2 as [|(?&?)]; eauto with rtlcm.
      eapply IHs2 in MON1 as [|(?&?)]; eauto with rtlcm.
      eapply IHs1 in MON0 as [|(?&?)]; eauto with rtlcm.
      add_seq_inv.
    - (* Ssmerge1 *)
      add_seq_inv.
      eapply IHs2 in MON1 as [|(?&?)]; eauto with rtlcm.
      eapply IHs1 in MON0 as [|(?&?)]; eauto with rtlcm.
      add_seq_inv.
  Qed.

  Lemma add_seq_correct Pnode {e} : forall (s : cseq e) m tt st st' sincr,
      add_seq s st = R tt st' sincr ->
      wf_cseq abort m s ->
      (m = Pred -> forall pc i, (st_code st)!pc = Some i -> forall pc2 i2, In pc2 (successors_instr i) -> in_cseq abort pc2 i2 s -> pc2 = cseq_pc s) ->
      (forall pc i, in_cseq abort pc i s -> pc = cseq_pc s \/ Pnode pc) ->
      spec_cseq Pnode m (st_code st') s.
  Proof.
    (* adhoc tactic to solve repetitive goals *)
    Local Ltac add_seq_correct :=
      repeat
        (match goal with
         (* Solving the goal *)
         | |- (PTree.set ?pc _ _)!?pc = Some _ => rewrite PTree.gss; eauto
         | _ => extlia
         | _ => congruence
         | _ => monadInv
         | H: in_cseq _ _ _ _ |- _ => now (contradict H; eauto)
         | H: In _ (successors_instr _) |- _ => now (contradict H; eauto)
         | H1: (forall _, ~in_cseq _ _ _ ?s), H2:in_cseq _ _ _ ?s |- False => eapply H1, H2
         (* Simplify context *)
         | H: set_instr _ _ _ = R _ _ _ |- _ => inv H
         | H: successors_instr _ = _ |- _ => rewrite H in *; simpl in *
         | |- _ = _ -> _ => intros ?; subst
         | |- forall _ _, in_cseq _ _ _ _ -> _ <> _ => intros * ?IN ?; subst
         | _ => inv_wf_seq; try inv_existT_eq
         | _ => inv_is_cont
         | _ => inv_nodup_cont
         | _ => inv_existT_eq
         (* Specialize hypotheses *)
         | I: ?x = ?x -> _ |- _ => specialize (I eq_refl)
         | I: is_predecessor _ _ _ |- _ =>
             apply Kildall.make_predecessors_correct_3 in I as (?&?FIND&?)
         | |- one_predecessor _ _ _ => intros ? ?ISP
         | |- two_predecessors _ _ _ _ => intros ? ?ISP
         | H: (PTree.set ?x _ _)!?y = Some _ |- _ =>
             rewrite PTree.gsspec in H; destruct (peq _ _); [inv H|]
         | _ => my_split; eauto
         end; simpl in *; auto).

    induction s using cseq_ind2; intros * ADD ND NSUCC PNODE; inv ND; simpl in *; auto.
    - (* Seinstr *) add_seq_correct.
    - (* Sereturn *) add_seq_correct.
    - (* Second *)
      add_seq_correct.
      + do 2 (erewrite <- add_seq_preserves; eauto; add_seq_correct).
      + eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct.
        1:{ add_seq_correct. edestruct (cseq_pc_in abort s1) as (?&?). add_seq_correct. }
        eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct.
        edestruct (cseq_pc_in abort s1) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct.
        eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
        1:{ edestruct (cseq_pc_in abort s2) as (?&?). add_seq_correct. }
        add_seq_correct.
        edestruct (cseq_pc_in abort s2) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + eapply IHs1, spec_cseq_code in MON0; eauto; add_seq_correct.
        * intros * ??. erewrite <-add_seq_preserves; eauto.
          intros * ??; subst. eauto.
        * intros * SUCC ? (?&?) * FIND.
          eapply add_seq_inv in FIND as [|(FIND&_)]; eauto; add_seq_correct.
        * intros * FIND * SUCC IN. add_seq_correct.
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s2); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + eapply IHs2, spec_cseq_implies in MON1; eauto; add_seq_correct.
        * intros * FIND * SUCC IN.
          eapply add_seq_inv in FIND as [?IN|(FIND&_)]; eauto; add_seq_correct.
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s1); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + edestruct (cseq_pc_in abort s1).
        edestruct (PNODE (cseq_pc s1)); eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s2).
        edestruct (PNODE (cseq_pc s2)); eauto with rtlcm; subst.
        add_seq_correct.
    - (* Sejumptable *) add_seq_correct.
    - (* Secatch *) add_seq_correct.
    - (* Ssinstr *)
      inv_existT_eq. add_seq_correct.
      + erewrite <- add_seq_preserves; eauto; add_seq_correct.
      + eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct.
        edestruct (cseq_pc_in abort s) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + eapply IHs; eauto.
        * intros ? * FIND * SUCC IN. add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm.
             add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + edestruct (cseq_pc_in abort s).
        edestruct (PNODE (cseq_pc s)); eauto with rtlcm; subst.
        add_seq_correct.
    - (* Ssmerge2 *)
      add_seq_correct.
      + do 3 (erewrite <- add_seq_preserves; eauto; add_seq_correct).
      + edestruct (cseq_pc_in abort s1) as (?&?).
        do 3 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct).
        edestruct (cseq_pc_in abort s1) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s2) as (?&?).
        do 3 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct).
        edestruct (cseq_pc_in abort s2) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + eapply IHs1, spec_cseq_code in MON0; eauto; add_seq_correct.
        * intros * ??. erewrite <- 2 add_seq_preserves; eauto.
          1,2:(intros * ??; subst; eauto).
        * intros * SUCC ? (?&?) * FIND.
          do 2 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; [| |eauto]; add_seq_correct).
        * intros * FIND * SUCC IN. add_seq_correct.
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s2); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + eapply IHs2, spec_cseq_code in MON1; eauto; add_seq_correct.
        * intros * ??. erewrite <- add_seq_preserves; eauto.
          intros * ??; subst; eauto.
        * intros * SUCC ? (?&?) * FIND.
          do 2 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; [| |eauto]; add_seq_correct).
        * intros * FIND * SUCC IN.
          do 1 (eapply add_seq_inv in FIND as [?IN|(FIND&_)]; [| |eauto]; add_seq_correct).
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s1); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + edestruct (cseq_pc_in abort s1) as (?&IN1).
        edestruct (cseq_pc_in abort s2) as (?&IN2).
        edestruct (cseq_pc_in abort s3) as (?&IN3).
        repeat (eapply add_seq_inv in FIND as [IN|(FIND&_)]; [| |now eauto]); eauto; add_seq_correct.
        * exfalso.
          destruct H as [EQ|[EQ|]]; auto; rewrite EQ in *; eauto.
        * eapply NSUCC in FIND; eauto with rtlcm. subst.
          contradict IN3; eauto.
      + eapply IHs3 in MON2; eauto; add_seq_correct.
        * intros * FIND * SUCC IN.
          repeat (eapply add_seq_inv in FIND as [?IN|(FIND&_)]; [| |now eauto]; add_seq_correct).
          -- destruct SUCC as [|[|[]]]; subst; auto.
             ++ edestruct (cseq_pc_in abort s1); add_seq_correct.
             ++ edestruct (cseq_pc_in abort s2); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + edestruct (cseq_pc_in abort s1).
        edestruct (PNODE (cseq_pc s1)); eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s2).
        edestruct (PNODE (cseq_pc s2)); eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s3).
        edestruct (PNODE (cseq_pc s3)); eauto with rtlcm; subst.
        add_seq_correct.
    - (* Ssmerge1 *)
      add_seq_correct.
      + do 3 (erewrite <- add_seq_preserves; eauto; add_seq_correct).
      + edestruct (cseq_pc_in abort s1) as (?&?).
        do 2 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct).
        edestruct (cseq_pc_in abort s1) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s2) as (?&?).
        do 2 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto; add_seq_correct).
        edestruct (cseq_pc_in abort s2) as (?&?). eapply NSUCC in H; eauto with rtlcm; subst.
        add_seq_correct.
      + eapply IHs1, spec_cseq_code in MON0; eauto; add_seq_correct.
        * intros * ??. erewrite <- add_seq_preserves; eauto.
          intros * ??; subst; eauto.
        * intros * SUCC ? (?&?) * FIND.
          do 2 (eapply add_seq_inv in FIND as [IN|(FIND&_)]; [| |eauto]; add_seq_correct).
        * intros * FIND * SUCC IN. add_seq_correct.
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s2); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + eapply IHs2, spec_cseq_code in MON1; eauto; add_seq_correct.
        * intros * FIND * SUCC IN.
          do 1 (eapply add_seq_inv in FIND as [?IN|(FIND&_)]; [| |eauto]; add_seq_correct).
          -- destruct SUCC as [|[|[]]]; subst; auto.
             edestruct (cseq_pc_in abort s1); add_seq_correct.
          -- eapply NSUCC in SUCC; subst; eauto with rtlcm; add_seq_correct.
        * intros * INCS. edestruct (PNODE pc0); eauto with rtlcm; subst.
          add_seq_correct.
      + edestruct (cseq_pc_in abort s1).
        edestruct (PNODE (cseq_pc s1)); eauto with rtlcm; subst.
        add_seq_correct.
      + edestruct (cseq_pc_in abort s2).
        edestruct (PNODE (cseq_pc s2)); eauto with rtlcm; subst.
        add_seq_correct.
  Qed.

End add_seq.

Backward spec


Definition spec_seq_inv {e} Pnode abort code pc i pcf (seq : seqinstr e) (k : cont e) :=
  exists cseq,
    concretized pcf seq k cseq
    /\ in_cseq abort pc i cseq
    /\ spec_cseq abort Pnode Pred code cseq.

Full node transformation


Definition add_abort_blk :=
  doM pc <- fresh_pc;
  doM di <- set_instr pc (Ibuiltin EF_cm_catch [] BR_none pc);
  ret pc.

Definition add_prologue abort (s : seqinstr OneExit) entry :=
  doM pc <- fresh_pc;
  doM cs <- concretize_seq pc s (COneExit entry);
  doM tt <- add_seq abort cs;
  ret pc.

Total transformation

Section total.
  Variable prologue : seqinstr OneExit.

  Variable transf_nop : node -> node -> seqinstr OneExit.
  Variable transf_op : node -> operation -> list reg -> reg -> node -> seqinstr OneExit.
  Variable transf_load : node -> trapping_mode -> memory_chunk -> addressing -> list reg -> reg -> node -> seqinstr OneExit.
  Variable transf_store : node -> memory_chunk -> addressing -> list reg -> reg -> node -> seqinstr OneExit.
  Variable transf_call : node -> signature -> reg + qualident -> list reg -> reg -> node -> seqinstr OneExit.
  Variable transf_tailcall : node -> signature -> reg + qualident -> list reg -> seqinstr NoExit.
  Variable transf_builtin : node -> external_function -> list (builtin_arg reg) -> builtin_res reg -> node -> seqinstr OneExit.
  Variable transf_cond : node -> condition -> list reg -> node -> node -> option bool -> seqinstr TwoExits.
  Variable transf_jumptable : node -> reg -> list node -> seqinstr NExits.
  Variable transf_return : node -> option reg -> seqinstr NoExit.

Rewrite a single instruction


  Definition transf_instr pc i : (sigT (fun e => (seqinstr e * cont e)%type)) :=
    match i with
    | Inop pc1 | Iassert _ _ pc1 => (existT _ _ (transf_nop pc pc1, COneExit pc1))
    | Iop op rs r pc1 => (existT _ _ (transf_op pc op rs r pc1, COneExit pc1))
    | Iload trap chk addr rs r pc1 => (existT _ _ (transf_load pc trap chk addr rs r pc1, COneExit pc1))
    | Istore chk addr rs r pc1 => (existT _ _ (transf_store pc chk addr rs r pc1, COneExit pc1))
    | Icall sig ros rs r pc1 => (existT _ _ (transf_call pc sig ros rs r pc1, COneExit pc1))
    | Itailcall sig ros rs => (existT _ _ (transf_tailcall pc sig ros rs, CNoExit))
    | Ibuiltin ef rs r pc1 => (existT _ _ (transf_builtin pc ef rs r pc1, COneExit pc1))
    | Icond c rs pc1 pc2 o => (existT _ _ (transf_cond pc c rs pc1 pc2 o, CTwoExits pc1 pc2))
    | Ijumptable r pcs => (existT _ _ (transf_jumptable pc r pcs, CNExits pcs))
    | Ireturn r => (existT _ _ (transf_return pc r, CNoExit))
    end.

Rewrite the whole code


  Definition ptree_mfold {A: Type} (f: positive -> A -> mon unit) (t: PTree.t A): mon unit :=
    PTree.fold (fun s1 k v => bind s1 (fun _ => f k v)) t (ret tt).

  Lemma ptree_mfold_rec {A} f m_final st (P : PTree.t A -> state -> Prop) :
    (forall m m' a, (forall x, m ! x = m' ! x) -> P m a -> P m' a) ->
    P (PTree.empty _) st ->
    (forall m pc i st1,
        sincr st st1 ->
        m!pc = None ->
        m_final!pc = Some i ->
        P m st1 ->
        P (PTree.set pc i m) (proj_st (f pc i st1))) ->
    P m_final (proj_st (@ptree_mfold A f m_final st)).
  Proof.
    intros * COMPAT P0 FP.
    assert ((fun m st' => P m st' /\ sincr st st') m_final (proj_st (ptree_mfold f m_final st))) as (?&?); auto.
    unfold ptree_mfold; simpl.
    apply PTree_Properties.fold_rec; auto using sincr_refl.
    - intros * EQ (P1&INCR1). split; eauto.
    - intros * NONE FIND1 (P1&INCR1).
      destruct (bind _ _) eqn:TR; repeat monadInv.
      split; eauto using sincr_trans.
      eapply FP in P1; eauto; simpl in *.
      all:rewrite MON in *; simpl in *; auto.
      rewrite MON0 in P1; auto.
  Qed.

  Definition transf_code abort :=
    ptree_mfold
      (fun pc i =>
         let '(existT _ _ (seq, cont)) := transf_instr pc i in
         doM cs <- concretize_seq pc seq cont;
         add_seq abort cs).

  Definition transf_function (f : function) :=
    let initstate := mkstate (Pos.succ (max_pc_function f)) (PTree.empty _) in
    let res := (doM abort <- add_abort_blk;
                doM dummy <- transf_code abort (fn_code f);
                add_prologue abort prologue (fn_entrypoint f)) initstate in
    ((proj_st res).(st_code), proj_val res).

Correctness

  Lemma transf_code_preserves abort : forall code tt st st' sincr,
      transf_code abort code st = R tt st' sincr ->
      forall pcl i,
        (forall pc i, code!pc = Some i -> pc < pcl) ->
        pcl < (st_nextnode st) ->
        (st_code st)!pcl = Some i ->
        (st_code st')!pcl = Some i.
  Proof.
    intros * TR. unfold transf_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto. rewrite <-TR.
    eapply ptree_mfold_rec; auto.
    - intros * EQ. now setoid_rewrite EQ.
    - intros * INCR NONE SOME IND * LT1 LT2 FIND.
      destruct (transf_instr pc i) as (?&?&?) eqn:TR1.
      destruct (bind _ _) eqn:ADD. repeat monadInv.
      erewrite <- add_seq_preserves with (st:=x2); simpl; eauto.
      2:{ intros * IN.
          assert (pc < pcl) as LT by (eapply LT1; now rewrite PTree.gss).
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; extlia.
      }
      erewrite <- concretize_seq_preserves; [|eauto].
      eapply IND; eauto.
      + intros * FIND'. specialize (LT1 pc0 i1).
        rewrite PTree.gso in LT1; [|congruence]; auto.
  Qed.

  Lemma transf_code_correct abort : forall code tt st st' sincr,
      transf_code abort code st = R tt st' sincr ->
      (forall pc i, code!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> pc < st_nextnode st) ->
      (abort < st_nextnode st) ->
      (forall pc i,
        code!pc = Some i ->
        forall x seq cont,
          transf_instr pc i = existT _ x (seq, cont) ->
          exists cseq,
            concretized pc seq cont cseq
            /\ (forall pc' i, in_cseq abort pc' i cseq -> pc' = pc \/ st_nextnode st <= pc')
            /\ spec_cseq abort (fun _ : node => True) NoPred (st_code st') cseq)
      /\ (forall pc i, (st_code st')!pc = Some i -> pc < st_nextnode st').
  Proof.
    intros * TR LT1 LT2 ABORT. unfold transf_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto. rewrite <-TR.
    eapply ptree_mfold_rec.
    - intros * EQ. now setoid_rewrite EQ.
    - split; eauto.
      intros * FIND. rewrite PTree.gempty in FIND. inv FIND.
    - intros * INCR NONE SOME (IND1&IND2). split.
      2:{ intros * FIND.
          destruct (transf_instr pc i) as (?&?&?) eqn:TR1.
          destruct (bind _ _) eqn:ADD; repeat monadInv.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_in_inv in IN as [|]; eauto; try extlia.
            subst. specialize (LT1 _ _ SOME). extlia.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND2 _ _ FIND). extlia.
      }
      intros * FIND * TR1.
      rewrite PTree.gsspec in FIND. destruct (peq pc0 pc); [inv FIND|].
      + (* this one *)
        rewrite TR1.
        destruct (bind _ _) eqn:ADD; repeat monadInv.
        eapply add_seq_correct, spec_cseq_implies in MON0; eauto.
        do 3 (esplit; eauto using concretize_seq_correct).
        all:intros; repeat monadInv; try congruence; try extlia.
        * eapply concretize_seq_in_inv in H as [|]; eauto; extlia.
        * eapply concretize_seq_wf; eauto; try congruence.
          -- specialize (LT1 _ _ SOME). extlia.
          -- extlia.
      + (* another one *)
        destruct (transf_instr pc i) as (?&?&?) eqn:TR2.
        specialize (IND1 _ _ FIND). rewrite TR1 in IND1. edestruct IND1 as (?&CONC1&CLT1&SPEC1); eauto.
        destruct (bind _ _) eqn:ADD; repeat monadInv.
        do 3 (esplit; eauto).
        eapply spec_cseq_code in SPEC1; eauto.
        * intros * IN ?FIND.
          erewrite <- add_seq_preserves, <- concretize_seq_preserves; eauto.
          intros * ?IN ?; subst.
          specialize (IND2 _ _ FIND0).
          eapply concretize_seq_in_inv in IN0 as [|]; eauto; subst; try extlia.
          specialize (LT1 _ _ SOME). specialize (CLT1 _ _ IN) as [|]; extlia.
        * intros. congruence.
  Qed.

  Lemma transf_function_entry : forall f code entry,
      transf_function f = (code, entry) ->
      exists abort, spec_seq abort code entry prologue (COneExit (fn_entrypoint f)).
  Proof.
    intros * TR. inv TR. unfold add_prologue in *.
    destruct (bind _ _) eqn:TR. repeat monadInv.
    do 3 esplit; eauto using concretize_seq_correct.
    eapply add_seq_correct, spec_cseq_implies in MON3; eauto.
    - eapply concretize_seq_wf; eauto; try congruence.
      + extlia.
      + simpl. unfold add_abort_blk in *. repeat monadInv. extlia.
    - congruence.
  Qed.

  Lemma transf_function_correct : forall f code entry,
      transf_function f = (code, entry) ->
      forall pc i,
        (fn_code f)!pc = Some i ->
        let '(existT _ _ (seq, cont)) := transf_instr pc i in
        exists abort,
          spec_seq abort code pc seq cont
          /\ code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort).
  Proof.
    intros * TR. inv TR. unfold add_prologue, add_abort_blk in *.
    intros * FIND.
    destruct transf_instr as (?&?&?) eqn:TRI.
    destruct (bind _ _) eqn:TR. repeat monadInv.
    eapply transf_code_correct in MON0 as SPEC; eauto; try congruence; simpl; try extlia.
    2:{ intros * FIND'. apply max_pc_function_sound in FIND'.
        extlia. }
    2:{ intros * FIND'. rewrite PTree.gsspec in FIND'.
        destruct (peq _ _); try extlia.
        rewrite PTree.gempty in FIND'. inv FIND'. }
    edestruct SPEC as ((?&SPEC1&SPEC2&SPEC3)&SPEC4); eauto.
    repeat (esplit; eauto).
    - eapply spec_cseq_code; eauto; try congruence.
      intros * IN1 FIND'.
      erewrite <-add_seq_preserves, <-concretize_seq_preserves. 2,3:eauto. simpl; eauto.
      intros * IN2 ?; subst.
      specialize (SPEC4 _ _ FIND').
      eapply SPEC2 in IN1 as [|]; subst.
      1,2:eapply concretize_seq_in_inv in IN2 as [|]; eauto; subst.
      all:extlia.
    - erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
      erewrite <-transf_code_preserves; eauto; simpl.
      3:now rewrite PTree.gss.
      + intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
      + extlia.
      + intros * IN. eapply concretize_seq_in_inv in IN; eauto. simpl in *.
        destruct IN; extlia.
  Qed.

Backward spec


  Hypothesis WF_PROLOGUE : wf_seq prologue.
  Hypothesis WF_NOP : forall pc pc1, wf_seq (transf_nop pc pc1).
  Hypothesis WF_OP : forall pc op rs r pc1, wf_seq (transf_op pc op rs r pc1).
  Hypothesis WF_LOAD : forall pc trp chk addr rs r pc1, wf_seq (transf_load pc trp chk addr rs r pc1).
  Hypothesis WF_STORE : forall pc chk addr rs r pc1, wf_seq (transf_store pc chk addr rs r pc1).
  Hypothesis WF_CALL : forall pc sig ros rs r pc1, wf_seq (transf_call pc sig ros rs r pc1).
  Hypothesis WF_TAILCALL : forall pc sig ros rs, wf_seq (transf_tailcall pc sig ros rs).
  Hypothesis WF_BUILTIN : forall pc ef rs r pc1, wf_seq (transf_builtin pc ef rs r pc1).
  Hypothesis WF_COND : forall pc c rs pc1 pc2 pred, wf_seq (transf_cond pc c rs pc1 pc2 pred).
  Hypothesis WF_JUMPTABLE : forall pc r pcs, wf_seq (transf_jumptable pc r pcs).
  Hypothesis WF_RETURN : forall pc r, wf_seq (transf_return pc r).

  Lemma transf_instr_wf_seq : forall pc i e s c,
      transf_instr pc i = existT _ e (s, c) ->
      wf_seq s.
  Proof.
    intros * TR.
    destruct i; simpl in *;
      apply EqdepFacts.eq_sigT_fst in TR as ?; subst;
      apply Eqdep_dec.inj_pair2_eq_dec in TR; auto using exits_eq_dec; inv TR;
      eauto.
  Qed.

  Lemma transf_instr_cont_succ : forall pc i e s c,
      transf_instr pc i = existT _ e (s, c) ->
      forall pc, is_cont c pc -> In pc (successors_instr i).
  Proof.
    intros * TR * CONT.
    destruct i; simpl in *;
      apply EqdepFacts.eq_sigT_fst in TR as ?; subst;
      apply Eqdep_dec.inj_pair2_eq_dec in TR; auto using exits_eq_dec; inv TR;
      inv CONT; eauto.
    destruct H1; auto.
  Qed.

  Fact transf_code_successors_ge : forall abort f env tt st st' sincr,
      transf_code abort (fn_code f) st = R tt st' sincr ->
      wt_function f env ->
      max_pc_function f < st_nextnode st ->
      abort < st_nextnode st ->
      (forall pc i, (st_code st)!pc = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st) ->
      (forall pc1 i, (st_code st')!pc1 = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st').
  Proof.
    intros * TR WT LT1 LT2 LT3. unfold transf_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto. rewrite <-TR.
    eapply ptree_mfold_rec; auto.
    - intros * INCR NONE SOME * IND * FIND * SUCC.
      destruct transf_instr as (?&?&?) eqn:TR1.
      destruct (bind _ _) eqn:ADD; repeat monadInv.
      eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
      + eapply concretize_seq_successors_inv in IN as [(?&?)|[|]]; eauto; try extlia.
        * eapply transf_instr_cont_succ in TR1; eauto.
          eapply max_pc_function_sound_successors in TR1; eauto.
          extlia.
        * eapply transf_instr_wf_seq; eauto.
      + eapply concretize_seq_preserves in FIND; eauto.
        specialize (IND _ _ FIND _ SUCC). extlia.
  Qed.

  Lemma transf_code_inv : forall abort f env tt st st' sincr,
      transf_code abort (fn_code f) st = R tt st' sincr ->
      wt_function f env ->
      (max_pc_function f < st_nextnode st) ->
      (forall pc i, (fn_code f)!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st) ->
      (max_pc_function f < abort < st_nextnode st) ->
      (forall pc i,
          (st_code st')!pc = Some i ->
          (exists pcf i' e (s: seqinstr e) k cseq,
              (fn_code f)!pcf = Some i'
              /\ transf_instr pcf i' = existT _ e (s, k)
              /\ pcf <= max_pc_function f
              /\ concretized pcf s k cseq
              /\ (forall pc' i, in_cseq abort pc' i cseq -> pc' = pcf \/ st_nextnode st <= pc' < st_nextnode st')
              /\ in_cseq abort pc i cseq
              /\ spec_cseq abort (fun pc => st_nextnode st <= pc < st_nextnode st') Pred (st_code st') cseq)
          \/ (st_code st)!pc = Some i)
      /\ (forall pc i, (st_code st')!pc = Some i -> pc < st_nextnode st')
      /\ (forall pc1 i, (st_code st')!pc1 = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st').
  Proof.
    intros * TR WT MAX LT1 LT2 LT3 ABORT. unfold transf_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto. rewrite <-TR.
    eapply ptree_mfold_rec; auto.
    - intros * EQ. setoid_rewrite EQ.
      intros * (IND1&IND2&IND3); eauto.
    - intros * INCR NONE SOME * (IND1&IND2&IND3).
      destruct transf_instr as (?&?&?) eqn:TR1.
      destruct (bind _ _) eqn:ADD; repeat monadInv.
      split; [|split].
      2:{ intros * FIND.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_in_inv in IN as [|]; eauto; try extlia.
            subst. specialize (LT1 _ _ SOME). extlia.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND2 _ _ FIND). extlia.
      }
      2:{ intros * FIND * SUCC.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_successors_inv in IN as [(?&?)|]; eauto; try extlia.
            + eapply transf_instr_cont_succ in TR1; eauto.
              eapply max_pc_function_sound_successors in TR1; eauto.
              extlia.
            + eapply transf_instr_wf_seq; eauto.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND3 _ _ FIND _ SUCC). extlia.
      }
      intros * FIND.
      eapply add_seq_inv in MON0 as FIND'; eauto; destruct FIND' as [|(FIND'&NIN)]; try congruence.
      + left. exists pc. repeat esplit; eauto using concretize_seq_correct. now rewrite PTree.gss.
        3:eapply add_seq_correct in MON0; eauto.
        * eapply max_pc_function_sound; eauto.
        * intros * IN.
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; try extlia.
        * eapply concretize_seq_wf; eauto.
          -- specialize (LT1 _ _ SOME). extlia.
          -- extlia.
          -- intros _ * CONT.
             eapply transf_instr_cont_succ in TR1; eauto.
             eapply max_pc_function_sound_successors in TR1; eauto.
             extlia.
          -- intros _. eapply transf_instr_wf_seq; eauto.
        * intros _ * FIND' * SUCC IN.
          erewrite <-concretize_seq_preserves in FIND'; eauto.
          specialize (IND3 _ _ FIND' _ SUCC).
          erewrite concretize_seq_cseq_pc; [|eauto].
          eapply concretize_seq_in_inv in MON as [|]; eauto.
          extlia.
        * intros * IN.
          erewrite concretize_seq_cseq_pc; [|eauto].
          eapply concretize_seq_in_inv in MON as [|]; eauto. extlia.
      + erewrite <-concretize_seq_preserves in FIND'; [|eauto].
        eapply IND1 in FIND' as [(pcf&?&?&?&?&?&?&?&?&?&SLT&?&SPEC)|]; auto.
        left. repeat (esplit; eauto).
        * rewrite PTree.gso; eauto. congruence.
        * intros. edestruct SLT; eauto; extlia.
        * eapply spec_cseq_implies, spec_cseq_code in SPEC; eauto.
          -- intros * IN FIND1.
             erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto.
             intros * ?IN ?; subst.
             specialize (IND2 _ _ FIND1).
             eapply concretize_seq_in_inv in IN0 as [|]; eauto; subst; try extlia.
             specialize (LT1 _ _ SOME). specialize (SLT _ _ IN) as [|]; subst; try extlia.
             congruence.
          -- intros _ * SUCC PROP (?&IN) * FIND1.
             erewrite concretize_seq_preserves; [|eauto].
             eapply add_seq_inv in FIND1 as [|(FIND1&_)]; eauto. exfalso.
             specialize (SLT _ _ IN) as SLT1.
             eapply concretize_seq_successors_inv in H4; eauto using transf_instr_wf_seq.
             destruct SLT1, H4 as [(?&ISC)|[|]]; subst; try extlia.
             eapply transf_instr_cont_succ, max_pc_function_sound_successors in ISC; eauto.
             extlia.
          -- intros *. extlia.
  Qed.

  Lemma transf_function_inv env : forall f code entry,
      transf_function f = (code, entry) ->
      wt_function f env ->
      forall pc i,
        code!pc = Some i ->
        exists abort,
          code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort)
          /\ let spec_seq_inv {e} := @spec_seq_inv e (fun pc => pc <> entry) abort code in
            spec_seq_inv pc i entry prologue (COneExit (fn_entrypoint f))
            \/ (exists pcf pcl, ((fn_code f)!pcf = Some (Inop pcl) \/ exists cond args, (fn_code f)!pcf = Some (Iassert cond args pcl)) /\ spec_seq_inv pc i pcf (transf_nop pcf pcl) (COneExit pcl))
            \/ (exists op rs r pcf pcl, (fn_code f)!pcf = Some (Iop op rs r pcl) /\ spec_seq_inv pc i pcf (transf_op pcf op rs r pcl) (COneExit pcl))
            \/ (exists trp chk addr rs r pcf pcl, (fn_code f)!pcf = Some (Iload trp chk addr rs r pcl) /\ spec_seq_inv pc i pcf (transf_load pcf trp chk addr rs r pcl) (COneExit pcl))
            \/ (exists chk addr rs r pcf pcl, (fn_code f)!pcf = Some (Istore chk addr rs r pcl) /\ spec_seq_inv pc i pcf (transf_store pcf chk addr rs r pcl) (COneExit pcl))
            \/ (exists sig ros rs r pcf pcl, (fn_code f)!pcf = Some (Icall sig ros rs r pcl) /\ spec_seq_inv pc i pcf (transf_call pcf sig ros rs r pcl) (COneExit pcl))
            \/ (exists sig ros rs pcf, (fn_code f)!pcf = Some (Itailcall sig ros rs) /\ spec_seq_inv pc i pcf (transf_tailcall pcf sig ros rs) CNoExit)
            \/ (exists ef rs r pcf pcl, (fn_code f)!pcf = Some (Ibuiltin ef rs r pcl) /\ spec_seq_inv pc i pcf (transf_builtin pcf ef rs r pcl) (COneExit pcl))
            \/ (exists c rs pc1 pc2 pred pcf, (fn_code f)!pcf = Some (Icond c rs pc1 pc2 pred) /\ spec_seq_inv pc i pcf (transf_cond pcf c rs pc1 pc2 pred) (CTwoExits pc1 pc2))
            \/ (exists r pcs pcf, (fn_code f)!pcf = Some (Ijumptable r pcs) /\ spec_seq_inv pc i pcf (transf_jumptable pcf r pcs) (CNExits pcs))
            \/ (exists r pcf, (fn_code f)!pcf = Some (Ireturn r) /\ spec_seq_inv pc i pcf (transf_return pcf r) CNoExit)
            \/ (i = Ibuiltin EF_cm_catch [] BR_none abort).
  Proof.
    intros * TR WT. unfold transf_function, add_prologue, add_abort_blk in TR.
    destruct (bind _ _) eqn:BIND; repeat monadInv. inv TR.
    intros * FIND. do 2 esplit.
    1:{ erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
        erewrite <-transf_code_preserves; eauto; simpl.
        3:now rewrite PTree.gss.
        - intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
        - extlia.
        - intros * IN ?; subst.
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; extlia.
    }
    eapply add_seq_inv with (s:=x6) in FIND as [|(FIND&_)]; eauto; simpl in *.
    - left. repeat esplit; eauto using concretize_seq_correct.
      eapply add_seq_correct in MON3; eauto.
      + eapply concretize_seq_wf; eauto. 1,2:extlia.
        * intros _ * CONT. inv CONT.
          eapply wt_entrypoint in WT as (?&ENTRY). eapply max_pc_function_sound in ENTRY. extlia.
      + intros _ * FIND * SUCC IN. exfalso.
        erewrite <-concretize_seq_preserves in FIND; eauto. simpl in *.
        assert (st_nextnode x3 <= pc2) as LE.
        { eapply concretize_seq_in_inv in IN; [|eauto]; extlia. }
        eapply transf_code_successors_ge in FIND; eauto; try extlia.
        intros * FIND' * SUCC'; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; simpl in *.
        destruct SUCC' as [|]; extlia.
      + intros * IN. erewrite concretize_seq_cseq_pc; [|eauto]. extlia.
    - right.
      erewrite <-concretize_seq_preserves in FIND; eauto; simpl in *.
      eapply transf_code_inv in MON0 as ([(?&i'&?&?&?&?&FIND'&TR&?&CONC&SLT&IN&SPEC)|]&IND2&IND3); eauto.
      + assert (spec_seq_inv (fun pc => pc <> st_nextnode x3) (Pos.succ (max_pc_function f)) (st_code s') pc i x x1 x4) as SPEC'.
        { repeat (esplit; eauto).
          eapply spec_cseq_code, spec_cseq_implies in SPEC; eauto; try extlia.
          - intros * ?IN ?FIND.
            erewrite <-add_seq_preserves, <-concretize_seq_preserves. 2,3:eauto. auto.
            intros * ?IN ?; subst. specialize (IND2 _ _ FIND0).
            eapply concretize_seq_in_inv in IN1 as [|]; eauto; extlia.
          - intros _ * SUCC ? (?&?IN) * ?FIND.
            eapply add_seq_inv in FIND0 as [|(FIND0&_)]; [| |eauto].
            2:{ erewrite <-concretize_seq_preserves in FIND0; [|eauto]. auto. }
            exfalso.
            specialize (SLT _ _ IN0) as SLT1. simpl in *.
            eapply concretize_seq_successors_inv in H1; eauto using transf_instr_wf_seq.
            destruct SLT1, H1 as [(?&ISC)|[|]]; subst; try extlia.
            inv ISC.
            eapply wt_entrypoint in WT as (?&ENTRY); eapply max_pc_function_sound in ENTRY; extlia.
        }
        destruct i'; simpl in *;
          apply EqdepFacts.eq_sigT_fst in TR as ?; subst;
          apply Eqdep_dec.inj_pair2_eq_dec in TR; auto using exits_eq_dec; inv TR; eauto 16.
      + simpl in *. rewrite PTree.gsspec in H. cases; [inv H; eauto 14|].
        rewrite PTree.gempty in H. inv H.
      + simpl. extlia.
      + intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
      + intros * FIND'; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; extlia.
      + intros * FIND' * SUCC; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; simpl in *.
        destruct SUCC as [|[]]; extlia.
      + simpl. extlia.
  Qed.
End total.

Global Hint Unfold spec_seq in_seq spec_seq_inv : rtlcm.

Default total transfer functions


Definition transf_nop_default (_ : node) (_ : node) := Seinstr Inop.
Definition transf_op_default (_ : node) op rs r (_ : node) := Seinstr (Iop op rs r).
Definition transf_load_default (_ : node) trp chk addr rs r (_ : node) := Seinstr (Iload trp chk addr rs r).
Definition transf_store_default (_ : node) chk addr rs r (_ : node) := Seinstr (Istore chk addr rs r).
Definition transf_call_default (_ : node) sig ros rs r (_ : node) := Seinstr (Icall sig ros rs r).
Definition transf_tailcall_default (_ : node) sig ros rs := Sereturn (Itailcall sig ros rs).
Definition transf_builtin_default (_ : node) ef rs r (_ : node) := Seinstr (Ibuiltin ef rs r).
Definition transf_cond_default (_ : node) c rs (_ _ : node) pred := Second c rs (Seinstr Inop) (Seinstr Inop) pred.
Definition transf_jumptable_default (_ : node) r (_ : list node) := Sejumptable r.
Definition transf_return_default (_ : node) r := Sereturn (Ireturn r).

Global Hint Unfold
  transf_nop_default transf_op_default transf_load_default transf_store_default
  transf_call_default transf_tailcall_default transf_builtin_default
  transf_cond_default transf_jumptable_default transf_return_default : rtlcm.

Transformation that completely replace the body of the function

Section replace.
  Variable body : seqinstr NoExit.

  Definition transf_replace_function (f : function) :=
    let initstate := mkstate (Pos.succ (fn_entrypoint f)) (PTree.empty _) in
    let res := (doM abort <- add_abort_blk;
                doM cs <- concretize_seq (fn_entrypoint f) body CNoExit;
                add_seq abort cs) initstate in
    (proj_st res).(st_code).

  Lemma transf_replace_function_entry : forall f,
      exists abort, spec_seq abort (transf_replace_function f) (fn_entrypoint f) body CNoExit.
  Proof.
    intros *. unfold transf_replace_function.
    destruct (bind _ _) eqn:TR. repeat monadInv.
    do 3 esplit; eauto using concretize_seq_correct.
    eapply add_seq_correct, spec_cseq_implies in MON1; eauto.
    - eapply concretize_seq_wf; eauto; try congruence.
      + extlia.
      + simpl. unfold add_abort_blk in *. repeat monadInv. extlia.
    - congruence.
  Qed.

Backward spec


  Hypothesis WF_BODY : wf_seq body.

  Lemma transf_replace_function_inv env : forall f code,
      transf_replace_function f = code ->
      wt_function f env ->
      forall pc i,
        code!pc = Some i ->
        exists abort,
          code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort)
          /\ (spec_seq_inv (fun pc => pc <> fn_entrypoint f) abort code pc i (fn_entrypoint f) body CNoExit
             \/ (i = Ibuiltin EF_cm_catch [] BR_none abort)).
  Proof.
    intros * TR WT. unfold transf_replace_function, add_abort_blk in TR; subst.
    destruct (bind _ _) eqn:BIND; repeat monadInv.
    intros * FIND. do 2 esplit.
    1:{ erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
        - now rewrite PTree.gss.
        - intros * IN ?; subst.
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; extlia.
    }
    eapply add_seq_inv with (s:=x2) in FIND as [|(FIND&_)]; eauto; simpl in *.
    - left. repeat esplit; eauto using concretize_seq_correct.
      eapply add_seq_correct in MON1; eauto.
      + eapply concretize_seq_wf; eauto. 1,2:extlia.
        * intros _ * CONT. inv CONT.
      + intros _ * FIND * SUCC IN. exfalso.
        erewrite <-concretize_seq_preserves in FIND; eauto. simpl in *.
        assert (Pos.succ (fn_entrypoint f) <> pc2) as NEQ.
        { eapply concretize_seq_in_inv in IN; [|eauto]. extlia. }
        rewrite PTree.gsspec, PTree.gempty in FIND. destruct (peq _ _); inv FIND; simpl in *.
        destruct SUCC as [|]; subst; extlia.
      + intros * IN. erewrite concretize_seq_cseq_pc; [|eauto]. extlia.
    - right.
      erewrite <-concretize_seq_preserves in FIND; eauto; simpl in *.
      rewrite PTree.gsspec, PTree.gempty in FIND. destruct (peq _ _); inv FIND; auto.
  Qed.
End replace.

Partial transformation

Section partial.
  Variable prologue : seqinstr OneExit.

  Variable transf_nop : node -> Errors.res (seqinstr OneExit).
  Variable transf_op : operation -> list reg -> reg -> node -> Errors.res (seqinstr OneExit).
  Variable transf_load : trapping_mode -> memory_chunk -> addressing -> list reg -> reg -> node -> Errors.res (seqinstr OneExit).
  Variable transf_store : memory_chunk -> addressing -> list reg -> reg -> node -> Errors.res (seqinstr OneExit).
  Variable transf_call : signature -> reg + qualident -> list reg -> reg -> node -> Errors.res (seqinstr OneExit).
  Variable transf_tailcall : signature -> reg + qualident -> list reg -> Errors.res (seqinstr NoExit).
  Variable transf_builtin : external_function -> list (builtin_arg reg) -> builtin_res reg -> node -> Errors.res (seqinstr OneExit).
  Variable transf_cond : condition -> list reg -> node -> node -> option bool -> Errors.res (seqinstr TwoExits).
  Variable transf_jumptable : reg -> list node -> Errors.res (seqinstr NExits).
  Variable transf_return : option reg -> Errors.res (seqinstr NoExit).

  Open Scope error_monad_scope.

Rewrite a single instruction

  Definition transf_partial_instr i : Errors.res (sigT (fun e => (seqinstr e * cont e)%type)) :=
    match i with
    | Inop pc | Iassert _ _ pc =>
        do seq <- transf_nop pc; OK (existT _ _ (seq, COneExit pc))
    | Iop op rs r pc =>
        do seq <- transf_op op rs r pc; OK (existT _ _ (seq, COneExit pc))
    | Iload trap chk addr rs r pc =>
        do seq <- transf_load trap chk addr rs r pc; OK (existT _ _ (seq, COneExit pc))
    | Istore chk addr rs r pc =>
        do seq <- transf_store chk addr rs r pc; OK (existT _ _ (seq, COneExit pc))
    | Icall sig ros rs r pc =>
        do seq <- transf_call sig ros rs r pc; OK (existT _ _ (seq, COneExit pc))
    | Itailcall sig ros rs =>
        do seq <- transf_tailcall sig ros rs; OK (existT _ _ (seq, CNoExit))
    | Ibuiltin ef rs r pc =>
        do seq <- transf_builtin ef rs r pc; OK (existT _ _ (seq, COneExit pc))
    | Icond c rs pc1 pc2 o =>
        do seq <- transf_cond c rs pc1 pc2 o; OK (existT _ _ (seq, CTwoExits pc1 pc2))
    | Ijumptable r pcs =>
        do seq <- transf_jumptable r pcs; OK (existT _ _ (seq, CNExits pcs))
    | Ireturn r =>
        do seq <- transf_return r; OK (existT _ _ (seq, CNoExit))
    end.

Rewrite the whole code


  Definition transf_partial_code abort :=
    ptree_pmfold
      (fun pc i st =>
         match transf_partial_instr i with
         | OK (existT _ _ (seq, cont)) =>
             OK (bind (concretize_seq pc seq cont) (add_seq abort) st)
         | Error e => Error e
         end).

  Close Scope error_monad_scope.

  Definition transf_partial_function (f : function) :=
    let initstate := mkstate (Pos.succ (max_pc_function f)) (PTree.empty _) in
    Errors.bind
      (pbind
         (fun st => Errors.OK (add_abort_blk st))
         (fun abort =>
            pbind (transf_partial_code abort (fn_code f))
              (fun _ st => Errors.OK (add_prologue abort prologue (fn_entrypoint f) st))
         ) initstate)
      (fun '(R val st _) => OK (st.(st_code), val)).

Correctness

  Lemma transf_partial_code_preserves abort : forall code tt st st' sincr,
      transf_partial_code abort code st = OK (R tt st' sincr) ->
      forall pcl i,
        (forall pc i, code!pc = Some i -> pc < pcl) ->
        pcl < (st_nextnode st) ->
        (st_code st)!pcl = Some i ->
        (st_code st')!pcl = Some i.
  Proof.
    intros * TR. unfold transf_partial_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto.
    eapply ptree_pmfold_rec with (m_final:=code); eauto.
    - intros * EQ. now setoid_rewrite EQ.
    - intros * INCR NONE SOME IND TR' * LT1 LT2 FIND; simpl in *.
      destruct (transf_partial_instr i) as [[? (?&?)]|] eqn:TR1; inv TR'.
      destruct (bind _ _) eqn:ADD; repeat monadInv.
      erewrite <-add_seq_preserves, <-concretize_seq_preserves, IND. 5,6:eauto. all:eauto.
      + intros * FIND'. specialize (LT1 pc0 i1).
        rewrite PTree.gso in LT1; [|congruence]; auto.
      + intros * IN.
        assert (pc < pcl) as LT by (eapply LT1; now rewrite PTree.gss).
        eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; extlia.
  Qed.

  Lemma transf_partial_code_correct abort : forall code tt st st' sincr,
      (forall pc i, code!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> pc < st_nextnode st) ->
      (abort < st_nextnode st) ->
      transf_partial_code abort code st = OK (R tt st' sincr) ->
      (forall pc i,
        code!pc = Some i ->
        exists e (seq: seqinstr e) cont cseq,
          transf_partial_instr i = OK (existT _ e (seq, cont))
          /\ concretized pc seq cont cseq
          /\ (forall pc' i, in_cseq abort pc' i cseq -> pc' = pc \/ st_nextnode st <= pc')
          /\ spec_cseq abort (fun _ : node => True) NoPred (st_code st') cseq)
      /\ (forall pc i, (st_code st')!pc = Some i -> pc < st_nextnode st').
  Proof.
    intros * LT1 LT2 ABORT TR. unfold transf_partial_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto.
    eapply ptree_pmfold_rec with (m_final:=code); eauto.
    - intros * EQ. now setoid_rewrite EQ.
    - split; eauto.
      intros * FIND. rewrite PTree.gempty in FIND. inv FIND.
    - intros * INCR NONE SOME (IND1&IND2) TR'.
      destruct (transf_partial_instr i) as [(?&?&?)|] eqn:TR1; inv TR'.
      split.
      2:{ intros * FIND.
          destruct (bind _ _) eqn:ADD; repeat monadInv.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_in_inv in IN as [|]; eauto; try extlia.
            subst. specialize (LT1 _ _ SOME). extlia.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND2 _ _ FIND). extlia.
      }
      intros * FIND. rewrite PTree.gsspec in FIND. destruct (peq pc0 pc); [inv FIND|].
      + (* this one *)
        destruct (bind _ _) eqn:ADD; repeat monadInv.
        eapply add_seq_correct, spec_cseq_implies in MON0; eauto.
        repeat (esplit; eauto using concretize_seq_correct).
        all:intros; repeat monadInv; try congruence; try extlia.
        * eapply concretize_seq_in_inv in H as [|]; eauto; extlia.
        * eapply concretize_seq_wf; eauto; try congruence.
          -- specialize (LT1 _ _ SOME). extlia.
          -- extlia.
      + (* another one *)
        clear TR1.
        edestruct IND1 as (?&?&?&?&TR1&CONC1&CLT1&SPEC1); eauto.
        destruct (bind _ _) eqn:ADD; repeat monadInv.
        repeat (esplit; eauto).
        (* assert (st_nextnode st <= st_nextnode st1) as LE1 by extlia. *)
        (* assert (st_nextnode st1 <= st_nextnode s') as LE2 by extlia. *)
        eapply spec_cseq_code in SPEC1; eauto.
        * intros * IN ?FIND.
          erewrite <- add_seq_preserves, <- concretize_seq_preserves; eauto.
          intros * ?IN ?; subst.
          specialize (IND2 _ _ FIND0).
          eapply concretize_seq_in_inv in IN0 as [|]; eauto; subst; try extlia.
          specialize (LT1 _ _ SOME). specialize (CLT1 _ _ IN) as [|]; extlia.
        * intros. congruence.
  Qed.

  Lemma transf_partial_function_entry : forall f code entry,
      transf_partial_function f = OK (code, entry) ->
      exists abort,
        spec_seq abort code entry prologue (COneExit (fn_entrypoint f))
        /\ code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort).
  Proof.
    intros * TR. unfold transf_partial_function, add_prologue in *.
    repeat pMonadInv.
    repeat (esplit; eauto using concretize_seq_correct).
    - eapply add_seq_correct, spec_cseq_implies in MON2; eauto.
      + eapply concretize_seq_wf; eauto; try congruence; extlia.
      + congruence.
    - erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
      erewrite <-transf_partial_code_preserves; eauto; simpl.
      3:now rewrite PTree.gss.
      + intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
      + extlia.
      + intros * IN. eapply concretize_seq_in_inv in IN; eauto. simpl in *.
        destruct IN; extlia.
  Qed.

  Lemma transf_partial_function_correct : forall f code entry,
      transf_partial_function f = OK (code, entry) ->
      forall pc i,
        (fn_code f)!pc = Some i ->
        exists e seq cont abort,
          transf_partial_instr i = OK (existT _ e (seq, cont))
          /\ spec_seq abort code pc seq cont
          /\ code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort).
  Proof.
    intros * TR. inv TR. unfold transf_partial_function, add_prologue, add_abort_blk in *.
    intros * FIND.
    repeat pMonadInv.
    eapply transf_partial_code_correct in MON1 as SPEC; eauto; try congruence; simpl; try extlia.
    2:{ intros * FIND'. apply max_pc_function_sound in FIND'. extlia. }
    2:{ intros * FIND'. rewrite PTree.gsspec in FIND'.
        destruct (peq _ _); try extlia.
        rewrite PTree.gempty in FIND'. inv FIND'. }
    edestruct SPEC as ((?&?&?&?&TR&SPEC1&SPEC2&SPEC3)&SPEC4); eauto.
    repeat (esplit; eauto).
    - eapply spec_cseq_code; eauto; try congruence.
      intros * IN1 FIND'.
      erewrite <-add_seq_preserves, <-concretize_seq_preserves. 2,3:eauto. simpl; eauto.
      intros * IN2 ?; subst.
      specialize (SPEC4 _ _ FIND').
      eapply SPEC2 in IN1 as [|]; subst.
      1,2:eapply concretize_seq_in_inv in IN2 as [|]; eauto; subst.
      all:extlia.
    - erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
      erewrite <-transf_partial_code_preserves; eauto; simpl.
      3:now rewrite PTree.gss.
      + intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
      + extlia.
      + intros * IN. eapply concretize_seq_in_inv in IN; eauto. simpl in *.
        destruct IN; extlia.
  Qed.

Inversion spec

  Hypothesis WF_PROLOGUE : wf_seq prologue.
  Hypothesis WF_NOP : forall pc s, transf_nop pc = OK s -> wf_seq s.
  Hypothesis WF_OP : forall op rs r pc s, transf_op op rs r pc = OK s -> wf_seq s.
  Hypothesis WF_LOAD : forall trp chk addr rs r pc s, transf_load trp chk addr rs r pc = OK s -> wf_seq s.
  Hypothesis WF_STORE : forall chk addr rs r pc s, transf_store chk addr rs r pc = OK s -> wf_seq s.
  Hypothesis WF_CALL : forall sig ros rs r pc s, transf_call sig ros rs r pc = OK s -> wf_seq s.
  Hypothesis WF_TAILCALL : forall sig ros rs s, transf_tailcall sig ros rs = OK s -> wf_seq s.
  Hypothesis WF_BUILTIN : forall ef rs r pc s, transf_builtin ef rs r pc = OK s -> wf_seq s.
  Hypothesis WF_COND : forall c rs pc1 pc2 pred s, transf_cond c rs pc1 pc2 pred = OK s -> wf_seq s.
  Hypothesis WF_JUMPTABLE : forall r pcs s, transf_jumptable r pcs = OK s -> wf_seq s.
  Hypothesis WF_RETURN : forall r s, transf_return r = OK s -> wf_seq s.

  Lemma transf_partial_instr_wf_seq : forall i e s c,
      transf_partial_instr i = OK (existT _ e (s, c)) ->
      wf_seq s.
  Proof.
    intros * TR.
    destruct i; simpl in *; Errors.monadInv TR.
    all:repeat
          match goal with
          | H: existT _ ?e _ = existT _ ?e _ |- _ =>
              apply Eqdep_dec.inj_pair2_eq_dec in H; auto using exits_eq_dec; subst
          end; eauto.
  Qed.

  Lemma transf_partial_instr_cont_succ : forall i e s c,
      transf_partial_instr i = OK (existT _ e (s, c)) ->
      forall pc, is_cont c pc -> In pc (successors_instr i).
  Proof.
    intros * TR * CONT.
    destruct i; simpl in *; Errors.monadInv TR.
    all:repeat
          match goal with
          | H: existT _ ?e _ = existT _ ?e _ |- _ =>
              apply Eqdep_dec.inj_pair2_eq_dec in H; auto using exits_eq_dec; subst
          end; inv CONT; eauto.
    destruct H1; auto.
  Qed.

  Fact transf_partial_code_successors_ge : forall abort f env tt st st' sincr,
      transf_partial_code abort (fn_code f) st = OK (R tt st' sincr) ->
      wt_function f env ->
      max_pc_function f < st_nextnode st ->
      abort < st_nextnode st ->
      (forall pc i, (st_code st)!pc = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st) ->
      (forall pc1 i, (st_code st')!pc1 = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st').
  Proof.
    intros * TR WT LT1 LT2 LT3. unfold transf_partial_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto.
    eapply ptree_pmfold_rec with (P:=fun _ st => forall pc1 i, (st_code st)!pc1 = Some i -> forall pc2, _ -> pc2 < st_nextnode st); eauto.
    - intros * INCR NONE SOME * IND * TR' * FIND * SUCC; simpl in *.
      destruct (transf_partial_instr i) as [(?&?&?)|] eqn:TR1; inv TR'.
      destruct (bind _ _) eqn:ADD; repeat monadInv.
      eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
      + eapply concretize_seq_successors_inv in IN as [(?&?)|[|]]; eauto; try extlia.
        * eapply transf_partial_instr_cont_succ in TR1; eauto.
          eapply max_pc_function_sound_successors in TR1; eauto.
          extlia.
        * eapply transf_partial_instr_wf_seq; eauto.
      + eapply concretize_seq_preserves in FIND; eauto.
        specialize (IND _ _ FIND _ SUCC). extlia.
  Qed.

  Lemma transf_partial_code_inv : forall abort f env tt st st' sincr,
      transf_partial_code abort (fn_code f) st = OK (R tt st' sincr) ->
      wt_function f env ->
      (max_pc_function f < st_nextnode st) ->
      (forall pc i, (fn_code f)!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> pc < st_nextnode st) ->
      (forall pc i, (st_code st)!pc = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st) ->
      (max_pc_function f < abort < st_nextnode st) ->
      (forall pc i,
          (st_code st')!pc = Some i ->
          (exists pcf i' e (s: seqinstr e) k cseq,
              (fn_code f)!pcf = Some i'
              /\ transf_partial_instr i' = OK (existT _ e (s, k))
              /\ pcf <= max_pc_function f
              /\ concretized pcf s k cseq
              /\ (forall pc' i, in_cseq abort pc' i cseq -> pc' = pcf \/ st_nextnode st <= pc' < st_nextnode st')
              /\ in_cseq abort pc i cseq
              /\ spec_cseq abort (fun pc => st_nextnode st <= pc < st_nextnode st') Pred (st_code st') cseq)
          \/ (st_code st)!pc = Some i)
      /\ (forall pc i, (st_code st')!pc = Some i -> pc < st_nextnode st')
      /\ (forall pc1 i, (st_code st')!pc1 = Some i -> forall pc2, In pc2 (successors_instr i) -> pc2 < st_nextnode st').
  Proof.
    intros * TR WT MAX LT1 LT2 LT3 ABORT. unfold transf_partial_code in *.
    replace st' with (proj_st (R tt st' sincr0)) by auto.
    eapply ptree_pmfold_rec with
      (P:=fun m st => (forall pc i, (st_code st)!pc = Some i ->
                            (exists _ _ _ _ _ _,
                                m!_= Some _ /\ _ /\ _ /\ _
                                /\ (forall pc i, _ -> _ \/ _ <= _ < st_nextnode st) /\ _
                                /\ spec_cseq _ (fun _ => _ <= _ < st_nextnode st) _ (st_code st) _) \/ _)
                   /\ (forall pc1 i, (st_code st)!_ = Some _ -> _ < st_nextnode st)
                   /\ (forall pc1 i, (st_code st)!_ = Some _ -> forall pc2, _ -> _ < st_nextnode st)); eauto.
    - intros * EQ. setoid_rewrite EQ.
      intros (IND1&IND2&IND3); eauto.
    - intros * INCR NONE SOME * (IND1&IND2&IND3) ?TR.
      destruct transf_partial_instr as [(?&?&?)|] eqn:TR1; inv TR0.
      destruct (bind _ _) eqn:ADD. repeat monadInv.
      split; [|split].
      2:{ intros * FIND.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_in_inv in IN as [|]; eauto; try extlia.
            subst. specialize (LT1 _ _ SOME). extlia.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND2 _ _ FIND). extlia.
      }
      2:{ intros * FIND * SUCC.
          eapply add_seq_inv in FIND as [IN|(FIND&_)]; eauto.
          - eapply concretize_seq_successors_inv in IN as [(?&?)|]; eauto; try extlia.
            + eapply transf_partial_instr_cont_succ in TR1; eauto.
              eapply max_pc_function_sound_successors in TR1; eauto.
              extlia.
            + eapply transf_partial_instr_wf_seq; eauto.
          - eapply concretize_seq_preserves in FIND; eauto.
            specialize (IND3 _ _ FIND _ SUCC). extlia.
      }
      intros * FIND.
      eapply add_seq_inv in MON0 as FIND'; eauto; destruct FIND' as [|(FIND'&NIN)]; try congruence.
      + left. exists pc. repeat esplit; eauto using concretize_seq_correct. now rewrite PTree.gss.
        3:eapply add_seq_correct in MON0; eauto.
        * eapply max_pc_function_sound; eauto.
        * intros * IN.
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; try extlia.
        * eapply concretize_seq_wf; eauto.
          -- specialize (LT1 _ _ SOME). extlia.
          -- extlia.
          -- intros _ * CONT.
             eapply transf_partial_instr_cont_succ in TR1; eauto.
             eapply max_pc_function_sound_successors in TR1; eauto.
             extlia.
          -- intros _. eapply transf_partial_instr_wf_seq; eauto.
        * intros _ * FIND' * SUCC IN.
          erewrite <-concretize_seq_preserves in FIND'; eauto.
          specialize (IND3 _ _ FIND' _ SUCC).
          erewrite concretize_seq_cseq_pc; [|eauto].
          eapply concretize_seq_in_inv in MON as [|]; eauto.
          extlia.
        * intros * IN.
          erewrite concretize_seq_cseq_pc; [|eauto].
          eapply concretize_seq_in_inv in MON as [|]; eauto. extlia.
      + erewrite <-concretize_seq_preserves in FIND'; [|eauto].
        eapply IND1 in FIND' as [(pcf&?&?&?&?&?&?&?&?&?&SLT&?&SPEC)|]; auto.
        left. repeat (esplit; eauto).
        * rewrite PTree.gso; eauto. congruence.
        * intros. edestruct SLT; eauto; extlia.
        * eapply spec_cseq_implies, spec_cseq_code in SPEC; eauto.
          -- intros * IN FIND1.
             erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto.
             intros * ?IN ?; subst.
             specialize (IND2 _ _ FIND1).
             eapply concretize_seq_in_inv in IN0 as [|]; eauto; subst; try extlia.
             specialize (LT1 _ _ SOME). specialize (SLT _ _ IN) as [|]; subst; try extlia.
             congruence.
          -- intros _ * SUCC PROP (?&IN) * FIND1.
             erewrite concretize_seq_preserves; [|eauto].
             eapply add_seq_inv in FIND1 as [|(FIND1&_)]; eauto. exfalso.
             specialize (SLT _ _ IN) as SLT1.
             eapply concretize_seq_successors_inv in H4; eauto using transf_partial_instr_wf_seq.
             destruct SLT1, H4 as [(?&ISC)|[|]]; subst; try extlia.
             eapply transf_partial_instr_cont_succ, max_pc_function_sound_successors in TR1; eauto.
             extlia.
          -- intros *. extlia.
  Qed.

  Lemma transf_partial_function_inv env : forall f code entry,
      transf_partial_function f = OK (code, entry) ->
      wt_function f env ->
      forall pc i,
        code!pc = Some i ->
        exists abort,
          code!abort = Some (Ibuiltin EF_cm_catch [] BR_none abort)
          /\ let spec_seq_inv {e} := @spec_seq_inv e (fun pc => pc <> entry) abort code in
            spec_seq_inv pc i entry prologue (COneExit (fn_entrypoint f))
            \/ (exists pcf pcl s, ((fn_code f)!pcf = Some (Inop pcl) \/ exists cond args, (fn_code f)!pcf = Some (Iassert cond args pcl)) /\ transf_nop pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists op rs r pcf pcl s, (fn_code f)!pcf = Some (Iop op rs r pcl) /\ transf_op op rs r pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists trp chk addr rs r pcf pcl s, (fn_code f)!pcf = Some (Iload trp chk addr rs r pcl) /\ transf_load trp chk addr rs r pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists chk addr rs r pcf pcl s, (fn_code f)!pcf = Some (Istore chk addr rs r pcl) /\ transf_store chk addr rs r pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists sig ros rs r pcf pcl s, (fn_code f)!pcf = Some (Icall sig ros rs r pcl) /\ transf_call sig ros rs r pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists sig ros rs pcf s, (fn_code f)!pcf = Some (Itailcall sig ros rs) /\ transf_tailcall sig ros rs = OK s /\ spec_seq_inv pc i pcf s CNoExit)
            \/ (exists ef rs r pcf pcl s, (fn_code f)!pcf = Some (Ibuiltin ef rs r pcl) /\ transf_builtin ef rs r pcl = OK s /\ spec_seq_inv pc i pcf s (COneExit pcl))
            \/ (exists c rs pc1 pc2 pred pcf s, (fn_code f)!pcf = Some (Icond c rs pc1 pc2 pred) /\ transf_cond c rs pc1 pc2 pred = OK s /\ spec_seq_inv pc i pcf s (CTwoExits pc1 pc2))
            \/ (exists r pcs pcf s, (fn_code f)!pcf = Some (Ijumptable r pcs) /\ transf_jumptable r pcs = OK s /\ spec_seq_inv pc i pcf s (CNExits pcs))
            \/ (exists r pcf s, (fn_code f)!pcf = Some (Ireturn r) /\ transf_return r = OK s /\ spec_seq_inv pc i pcf s CNoExit)
            \/ (i = Ibuiltin EF_cm_catch [] BR_none abort).
  Proof.
    intros * TR WT. unfold transf_partial_function, add_prologue, add_abort_blk in TR.
    repeat pMonadInv.
    intros * FIND. do 2 esplit.
    1:{ erewrite <-add_seq_preserves, <-concretize_seq_preserves; eauto; simpl.
        erewrite <-transf_partial_code_preserves; eauto; simpl.
        3:now rewrite PTree.gss.
        - intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
        - extlia.
        - intros * IN ?; subst.
          eapply concretize_seq_in_inv in IN as [|]; [| |eauto]; extlia.
    }
    eapply add_seq_inv with (s:=x1) in FIND as [|(FIND&_)]; eauto; simpl in *.
    - left. repeat esplit; eauto using concretize_seq_correct.
      eapply add_seq_correct in MON2; eauto.
      + eapply concretize_seq_wf; eauto. 1,2:try extlia.
        * intros _ * CONT. inv CONT.
          eapply wt_entrypoint in WT as (?&ENTRY). eapply max_pc_function_sound in ENTRY. extlia.
      + intros _ * FIND * SUCC IN. exfalso.
        erewrite <-concretize_seq_preserves in FIND; eauto. simpl in *.
        assert (st_nextnode x3 <= pc2) as LE.
        { eapply concretize_seq_in_inv in IN; [|eauto]; extlia. }
        eapply transf_partial_code_successors_ge in FIND; eauto; try extlia.
        intros * FIND' * SUCC'; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; simpl in *.
        destruct SUCC' as [|]; extlia.
      + intros * IN. erewrite concretize_seq_cseq_pc; [|eauto]. extlia.
    - right.
      erewrite <-concretize_seq_preserves in FIND; eauto; simpl in *.
      eapply transf_partial_code_inv in MON1 as ([(?&i'&?&?&?&?&FIND'&TR&?&CONC&SLT&IN&SPEC)|]&IND2&IND3); eauto.
      + assert (spec_seq_inv (fun pc => pc <> st_nextnode x3) (Pos.succ (max_pc_function f)) (st_code s') pc i x x4 x5) as SPEC'.
        { repeat (esplit; eauto).
          eapply spec_cseq_code, spec_cseq_implies in SPEC; eauto; try extlia.
          - intros * ?IN ?FIND.
            erewrite <-add_seq_preserves, <-concretize_seq_preserves. 2,3:eauto. auto.
            intros * ?IN ?; subst. specialize (IND2 _ _ FIND0).
            eapply concretize_seq_in_inv in IN1 as [|]; eauto; extlia.
          - intros _ * SUCC ? (?&?IN) * ?FIND.
            eapply add_seq_inv in FIND0 as [|(FIND0&_)]; [| |eauto].
            2:{ erewrite <-concretize_seq_preserves in FIND0; [|eauto]. auto. }
            exfalso.
            specialize (SLT _ _ IN0) as SLT1. simpl in *.
            eapply concretize_seq_successors_inv in H1; eauto using transf_instr_wf_seq.
            destruct SLT1, H1 as [(?&ISC)|[|]]; subst; try extlia.
            inv ISC.
            eapply wt_entrypoint in WT as (?&ENTRY); eapply max_pc_function_sound in ENTRY; extlia.
        }
        destruct i'; simpl in *; Errors.monadInv TR.
        all:repeat
          match goal with
          | H: existT _ ?e _ = existT _ ?e _ |- _ =>
              apply Eqdep_dec.inj_pair2_eq_dec in H; auto using exits_eq_dec; subst
          end; eauto 18.
      + simpl in *. rewrite PTree.gsspec in H. cases; [inv H; eauto 14|].
        rewrite PTree.gempty in H. inv H.
      + simpl. extlia.
      + intros * FIND'. eapply max_pc_function_sound in FIND'. extlia.
      + intros * FIND'; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; extlia.
      + intros * FIND' * SUCC; simpl in *.
        rewrite PTree.gsspec, PTree.gempty in FIND'. destruct (peq _ _); inv FIND'; simpl in *.
        destruct SUCC as [|[]]; extlia.
      + simpl. extlia.
  Qed.
End partial.

Default partial transfer functions


Definition transf_partial_nop_default (_ : node) := Errors.OK (Seinstr Inop).
Definition transf_partial_op_default op rs r (_ : node) := Errors.OK (Seinstr (Iop op rs r)).
Definition transf_partial_load_default trp chk addr rs r (_ : node) := Errors.OK (Seinstr (Iload trp chk addr rs r)).
Definition transf_partial_store_default chk addr rs r (_ : node) := Errors.OK (Seinstr (Istore chk addr rs r)).
Definition transf_partial_call_default sig ros rs r (_ : node) := Errors.OK (Seinstr (Icall sig ros rs r)).
Definition transf_partial_tailcall_default sig ros rs := Errors.OK (Sereturn (Itailcall sig ros rs)).
Definition transf_partial_builtin_default ef rs r (_ : node) := Errors.OK (Seinstr (Ibuiltin ef rs r)).
Definition transf_partial_cond_default c rs (_ _ : node) pred := Errors.OK (Second c rs (Seinstr Inop) (Seinstr Inop) pred).
Definition transf_partial_jumptable_default r (_ : list node) := Errors.OK (Sejumptable r).
Definition transf_partial_return_default r := Errors.OK (Sereturn (Ireturn r)).

Global Hint Unfold
  transf_partial_nop_default transf_partial_op_default transf_partial_load_default transf_partial_store_default
  transf_partial_call_default transf_partial_tailcall_default transf_partial_builtin_default
  transf_partial_cond_default transf_partial_jumptable_default transf_partial_return_default : rtlcm.

Tactics and notations


Opaque transf_function.

Ltac inv_seq :=
  repeat
    match goal with
    | H: exists (pc : node), _ |- _ => destruct H as (?pc&?)
    | H: exists _, _ |- _ => destruct H as (?&?)
    | H: _ /\ _ |- _ => destruct H
    | H: (_, _) = (_, _) |- _ => inv H
    | H: existT _ ?e _ = existT _ ?e _ |- _ =>
        apply Eqdep.EqdepTheory.inj_pair2 in H; subst; simpl in *
    | H: existT _ _ _ = existT _ _ _ |- _ =>
        apply EqdepFacts.eq_sigT_fst in H as ?; subst
    | H: concretized _ _ _ _ |- _ => inv H; [idtac]
    | _ => autounfold with rtlcm in *
    end.

Ltac inv_transf_function :=
  repeat
    match goal with
    | H: _ \/ _ |- _ => destruct H
    | H: ?p = ?p -> _ |- _ => specialize (H eq_refl)
    | _ => congruence
    | _ => now (eauto 12)
    | _ => pMonadInv
    | _ => inv_in_cseq
    | _ => inv_seq
    end.

Tactic Notation "plus_step" int_or_var(n) :=
  do n (try (eapply Smallstep.plus_left'; [| |eauto using Events.E0_left])).

Notation "i ;; seq" := ((i : seqinstr _ -> seqinstr _) seq) (at level 15, right associativity).

Helpers for proof of security


Require Import Globalenvs Events.
Require Import Smallstep RTL RTLfault.

Section protected_multi_step.

Variable tprog : program.
Let tge := Genv.globalenv tprog.
Variable f : fault.

Hypothesis protected_fault_step : forall st0 t0 st1 st2,
  RTL.initial_state tprog st0 ->
  star step tge st0 t0 st1 ->
  fstep tge (Valid st1) [Event_fault f] st2 ->
  gets_caught tprog st2
  \/ (exists (st' : RTL.state) (t' : trace),
        star fstep tge st2 t' (Valid st') /\ nofault t' /\ star step tge st1 t' st').

Lemma protected_multi_step: forall st0 st1 st2 t1 t2,
    RTL.initial_state tprog st0 ->
    star step tge st0 t1 st1 ->
    star fstep tge (Valid st1) t2 st2 ->
    last_event_is_fault f t2 ->
    gets_caught tprog st2
    \/ (exists st' t',
          star fstep tge st2 t' (Valid st')
          /\ nofault t'
          /\ star step tge st1 (ignore_faults t2 ** t') st').
Proof.
  intros *.
  remember (Valid st1) as st1'.
  intros INIT PSTEP FSTEP FAULT. revert st1 t1 Heqst1' PSTEP.
  induction FSTEP; subst; intros.
  - (* no step, impossible *)
    inv FAULT.
  - (* one step, fault now or later? *)
    apply last_event_is_fault_app_inv in FAULT as [(F1&NIL2)|(NF1&F2)]; subst.
    + (* now *)
      apply fstep_trace_length in H as LEN.
      repeat
        match goal with
        | H: last_event_is_fault _ _ |- _ => inv H; simpl in *; try lia
        end.
      eapply protected_fault_step in H as [CAUGHT|(st'&?&FSTEP'&NFAULT&STEPS)]; eauto.
      * left. unfold gets_caught in *.
        eapply star_determinacy in FSTEP as [DET|(DET&NIL)]; eauto. 2:constructor.
        apply Caught_stuck in DET as Stuck. destruct Stuck; subst; auto.
      * destruct (Caught_or_not s3); subst; [left; constructor|right].
        destruct s3; try congruence.
        eapply star_determinacy in FSTEP' as [DET|(DET&NIL)]; eauto; subst.
        apply star_fstep_step in DET.
        exists s, E0.
        split; [|split]; auto. 1,3:constructor.
        eapply star_trans; eauto.
    + (* later *)
      destruct s2.
      2:{ apply Caught_stuck in FSTEP as (?&_); subst.
          inv F2. }
      apply fstep_step in H as STEP1; auto.
      assert (star step tge st0 (t0 ** t1) s) as SSTEP by eauto using star_right.
      specialize (IHFSTEP F2 _ _ eq_refl SSTEP).
      destruct IHFSTEP as [CAUGHT|(st'&?&FSTEP'&NFAULT&STEPS)]; auto.
      right.
      do 2 esplit. split; [|split]; eauto.
      rewrite ignore_faults_app, Eapp_assoc.
      eapply star_left; eauto.
      rewrite nofault_ignore_faults; auto.
Qed.

End protected_multi_step.