Module BTL_SEimpl_SI


Require Import Coqlib Maps.
Require Import Classes.Equivalence.
Require Import Registers.
Require Import RTL BTL.
Require Import BTL_SEsimuref BTL_SEtheory OptionMonad.
Require Import BTL_SEimpl_prelude.

Import Notations.
Import HConsing.
Import SvalNotations.

Import ListNotations.
Local Open Scope list_scope.
Local Open Scope lazy_bool_scope.
Local Open Scope option_monad_scope.
Local Open Scope impure.


Section CSASV_apply.

Variable sis0 : sistate.

Program Definition tr_sis_ext (si : fpasv) : sistate := {|
  sis_hyp := sis0.(sis_hyp);
  sis_pre := fun ctx => sis_ok ctx sis0 /\ proj1_sig (si_ok_subst sis0 si) ctx;
  sis_sreg := fun r => match si r with
                       | Some sv => Some (sis_sv_subst sis0 sv)
                       | None => sis0 r
                       end;
  sis_smem := sis0.(sis_smem);
|}.
Solve All Obligations with
  (cbn beta; intros; rewrite sis_ok_preserved, okpred_preserved; reflexivity).

Lemma ok_tr_sis_ext ctx si:
  sis_ok ctx (tr_sis_ext si) <-> sis_ok ctx sis0 /\ proj1_sig (si_ok_subst sis0 si) ctx.
Proof.
  split; intro OK; try apply OK; split; try apply OK.
  case OK as (OK_SIS & OK_SI).
  intros r sv; simpl; autodestruct; intro SI.
  - injection 1 as <-; intro. eapply si_ok_subst_eval in SI; eauto.
  - apply OK_SIS.
Qed.

Lemma tr_sis_ext_rel_si si0 si1
  (SI_LE : incl si0.(fpa_ok) si1.(fpa_ok)):
  sis_rel (tr_sis_ext si0) (tr_sis_ext si1).
Proof.
split; auto.
  intro; rewrite !ok_tr_sis_ext; simpl; unfold incl in SI_LE.
  intuition eauto.
Qed.

Lemma exec_seq_strict si l:
  incl (fpa_ok si) (fpa_ok (exec_seq l si)).
Proof.
  revert si; induction l as [|(?,?)]; simpl; intro.
  - apply incl_refl.
  - eapply incl_tran. 2:apply IHl.
    unfold incl; simpl; tauto.
Qed.

Definition tr_sis_ext_empty ctx:
  sistate_eq ctx sis0 (tr_sis_ext si_empty).
Proof.
  split; try reflexivity.
  rewrite ok_tr_sis_ext; intuition.
  inversion 1.
Qed.

Lemma tr_sis_ext_get_reg si r sv:
  tr_sis_ext si r = Some sv ->
  sv = sis_sv_subst sis0 (ext si r).
Proof.
  unfold ext; simpl; autodestruct.
  unfold sis_sv_subst; simpl.
  intros _ ->; reflexivity.
Qed.

Lemma tr_sis_ext_get_ireg si ir sv:
  sis_get_ireg sis0 (tr_sis_ext si) ir = Some sv ->
  sv = sis_sv_subst sis0 (ir_subst (ext si) ir).
Proof.
  unfold sis_get_ireg, ir_subst; case force_input.
  - unfold sis_sv_subst; simpl.
    intros ->; reflexivity.
  - apply tr_sis_ext_get_reg.
Qed.

Lemma tr_sis_ext_get_ival si iv sv:
  sis_get_ival sis0 (tr_sis_ext si) iv = Some sv ->
  sv = sis_sv_subst sis0 (iv_subst (ext si) iv).
Proof.
  case iv as [ir|rop rargs]; simpl.
  - apply tr_sis_ext_get_ireg.
  - autodestruct; intros LMAP; injection 1 as <-.
    unfold rop_subst, sis_sv_subst.
    rewrite sv_subst_root_apply.
    f_equal.
    revert l LMAP. induction rargs; intro l; simpl.
    + congruence.
    + do 2 autodestruct; intros _ GET; injection 1 as <-; f_equal.
      * eapply tr_sis_ext_get_ireg; eauto.
      * eapply IHrargs; reflexivity.
Qed.

Lemma tr_sis_ext_set_ival ctx sis1 si r iv:
  sis_set_ival r iv sis0 (tr_sis_ext si) = Some sis1 ->
  sistate_eq ctx sis1 (tr_sis_ext (si_set r (iv_subst (ext si) iv) si)).
Proof.
  unfold sis_set_ival; autodestruct; intro GET; injection 1 as <-.
  eapply tr_sis_ext_get_ival in GET as ->; eauto.
  set (sv := iv_subst _ _).
  split; try reflexivity.
  - rewrite ok_set_sreg, !ok_tr_sis_ext; simpl; intuition (subst; eauto).
  - intro; simpl; unfold fset; case Reg.eq as [<-|]; reflexivity.
Qed.

Program Definition sis_restrict_alive (out : reg -> bool) (sis : sistate) : sistate :=
  {|
    sis_hyp := sis.(sis_hyp);
    sis_pre := fun ctx => sis_ok ctx sis;
    sis_sreg := restrict_alive out (ext sis.(sis_sreg));
    sis_smem := sis.(sis_smem);
  |}.
Next Obligation.
  apply sis_ok_preserved.
Qed.

Lemma ok_sis_restrict_alive ctx out sis:
  sis_ok ctx (sis_restrict_alive out sis) <-> sis_ok ctx sis.
Proof.
  split; intros OK; try apply OK; constructor; try apply OK.
  intros r sv R.
  apply restrict_alive_Some in R as (R & _).
  apply ext_inv in R as [R | ->]; try discriminate.
  eapply OK in R; eauto.
Qed.

Lemma tr_sis_ext_restrict_alive ctx si out:
  sistate_eq ctx (sis_restrict_alive out (tr_sis_ext si))
                 (tr_sis sis0 (si_restrict_alive out si) None).
Proof.
  split; try reflexivity.
  - rewrite ok_sis_restrict_alive, ok_tr_sis_ext, ok_tr_sis.
    reflexivity.
  - intro; simpl; unfold restrict_alive, ext.
    repeat (autodestruct; try reflexivity).
Qed.

Definition alive_list (out : list reg) (r : reg) : bool :=
  in_dec Reg.eq r out.

End CSASV_apply.

Section SymbolicInvariants.

  Context `{HCF : HashConsingFcts}.
  Context `{HC : HashConsingHyps HCF}.
  Context `{RRULES: rrules_set}.

Evaluation of memory invariant

Section IMemSubst.
  Variable (se_mode : se_mode) (hrs0 : ristate).

Definition meminv_annot_get (an : meminv_annot_t) : ?? option store_num_t * meminv_annot_t :=
  match an with
  | Some (an0 :: an) => RET (Some an0, Some an)
  | None => RET (None, None)
  | Some nil => FAILWITH "meminv_annot_get"
  end.
Global Opaque meminv_annot_get.

Definition apply_meminv_annot (an : option store_num_t) (si : store_info) : store_info :=
  match an with
  | None => si
  | Some num => set_store_num num si
  end.

Fixpoint hrs_imem_subst_aux (an : meminv_annot_t) (im : imem) (hrs : ristate) {struct im} : ?? ristate :=
  match im with
  | nil =>
      RET hrs
  | mk_istore chk addr iargs sinfo isrc :: im =>
      DO an <~ @meminv_annot_get an;;
      let (an0, an) := an in
      DO args <~ @hrs_lr_get HCF hrs0 iargs;;
      DO src <~ @hrs_sreg_get HCF hrs0 isrc;;
      DO hrs1 <~ @hrs_set_store HCF HC RRULES se_mode (ris_smem hrs) chk addr args src
                      (apply_meminv_annot an0 sinfo) hrs;;
      hrs_imem_subst_aux an im hrs1
  end.

Lemma hrs_imem_subst_aux_rel an im hrs:
  WHEN hrs_imem_subst_aux an im hrs ~> hrs' THEN
  ris_rel se_mode hrs hrs'.
Proof.
  revert an hrs; induction im as [|[]]; simpl; intros; wlp_seq.
  - reflexivity.
  - intros (?,?) _; wlp_seq; repeat intro.
    erewrite hrs_set_store_rel, IHim by eauto.
    reflexivity.
Qed.

Lemma hrs_imem_subst_aux_spec ctx an im sis0 hrs sis m0
  (RR0 : ris_refines_ok ctx hrs0 sis0)
  (RR : ris_refines se_mode ctx hrs (set_smem (sis_sm_subst_trg sis0 m0) sis)):
  WHEN hrs_imem_subst_aux an im hrs ~> hrs1 THEN
  let m1 := sis_sm_subst_trg sis0 (fold_left istore_subst im m0) in
  ris_refines se_mode ctx hrs1 (set_smem m1 sis).
Proof.
  revert an hrs m0 RR.
  induction im as [|[chk addr iargs sinfo isrc] im]; simpl; intros; wlp_seq.
  - apply RR.
  - intros (an0, an1) _; wlp_seq; intros args ARGS src SRC hrs1 STORE hrs2 REC.
    eapply IHim, REC.
    set (m1 := fSstore m0 _ _ _ _ _).
    set (sis1 := set_smem (sis_sm_subst_trg sis0 m1) sis).
    apply (ris_refines_step RR).
      { eapply hrs_set_store_rel, STORE. }
      { unfold sis1; constructor; simpl; try tauto.
        intro; rewrite !ok_set_smem; intuition; apply H1; revert H.
        unfold m1, sis_sm_subst_trg; simpl; intros ->; repeat autodestruct. }
    clear RR; intro RR.
    eapply hrs_lr_get_spec_alive in ARGS as (sargs & GARGS & EQA & AARGS); eauto.
    eapply hrs_sreg_get_spec_alive in SRC as (ssrc & GSRC & EQS & ASRC ); eauto.
    eapply hrs_set_store_spec in STORE as RR1_0; eauto using ris_refines_ok_rmem.
    set (hm1 := fSstore hrs chk addr args _ src) in RR1_0.
    refine (ris_refines_morph _ RR1_0).
    transitivity (set_smem hm1 sis); cycle 1.
    + eapply set_smem_morph. reflexivity.
      simpl; rewrite EQA, EQS, GSRC.
      apply MEM_EQ in RR as ->; simpl.
      eenough (list_sval_equiv ctx sargs _) as ->. case an0; reflexivity.
      rewrite !eval_list_sval_eq, lsv_subst_eq, l_lsv_l, map_map, map_opt_map; simpl.
      revert GARGS; rewrite lmap_sv_eq; autodestruct.
      intro MAP; injection 1 as <-; rewrite l_lsv_l.
      revert MAP; clear; revert l; induction iargs; simpl; intro;
        try do 2 autodestruct; try_simplify_someHyps.
      rewrite IHiargs; reflexivity.
    + constructor; try reflexivity.
      rewrite !ok_set_smem.
      apply MEM_EQ in RR; simpl in RR; rewrite <- RR.
      enough (alive (eval_smem ctx hm1) -> alive (eval_smem ctx hrs)). tauto.
      simpl; repeat autodestruct.
Qed.
Global Opaque hrs_imem_subst_aux.

Definition hrs_imem_subst (an : meminv_annot_t) (im : imem) : ?? (smem * OKset.t) :=
  DO hrs <~ hrs_imem_subst_aux an im hrs0;;
  RET (ris_smem hrs, ris_pre hrs).

Lemma hrs_imem_subst_strict ctx an im:
  WHEN hrs_imem_subst an im ~> res THEN
  let (_, ok) := res in
  OKset.eval ctx ok -> ris_ok ctx hrs0.
Proof.
  unfold hrs_imem_subst; wlp_seq; intros hrs AUX.
  eapply hrs_imem_subst_aux_rel, AUX.
Qed.

Lemma hrs_imem_subst_spec ctx sis0 an im
  (RR : ris_refines se_mode ctx hrs0 sis0):
  let sm := sis_sm_subst_trg sis0 (imem_subst im) in
  WHEN hrs_imem_subst an im ~> res THEN
  let (m, ok) := res in
  ok_equivp True (sis_ok ctx sis0 /\ alive (eval_smem ctx sm)) (OKset.eval ctx ok) (se_ok_equiv se_mode) /\
  (OKset.eval ctx ok -> smem_equiv ctx m sm).
Proof.
  intro sm; unfold hrs_imem_subst; wlp_seq.
  intros hrs AUX.
  assert (RR1 : ris_refines se_mode ctx hrs (set_smem sm sis0)). {
    apply (ris_refines_step RR).
      { eapply hrs_imem_subst_aux_rel, AUX. }
      { apply set_smem_rel. }
    clear RR; intro RR.
    eapply hrs_imem_subst_aux_spec; eauto.
    apply wref. eapply ris_refines_ok_morph, RR.
    unfold sis_sm_subst_trg; simpl.
    constructor; try reflexivity.
    rewrite ok_set_smem; intuition eapply OK_SMEM; eauto.
  }
  split.
  - apply OK_EQUIV in RR1.
    eapply ok_equivp_morph in RR1; rewrite ?ok_set_smem; try reflexivity.
    inversion RR1; constructor; tauto.
  - intros ROK. apply (ok_ref ROK RR1).
Qed.
Global Opaque hrs_imem_subst.

Definition hrs_imem_subst_isol an (im : imem) : ?? (smem * OKset.t) :=
  let hrs_mem := {|
    ris_smem := ris_smem hrs0;
    ris_input_init := Some false; ris_sreg := PTree.empty _;
    ris_pre := OKset.empty;
  |} in
  DO hrs <~ hrs_imem_subst_aux an im hrs_mem;;
  RET (ris_smem hrs, ris_pre hrs).

Lemma hrs_imem_subst_isol_spec ctx sis0 an im
  (RR : ris_refines_ok ctx hrs0 sis0):
  let sm := sis_sm_subst_trg sis0 (imem_subst im) in
  WHEN hrs_imem_subst_isol an im ~> res THEN
  let (m, ok) := res in
  OKset.eval ctx ok ->
  alive (eval_smem ctx sm) /\ smem_equiv ctx m sm.
Proof.
  intro sm; unfold hrs_imem_subst_isol; wlp_seq.
  set (hrs_mem := {|ris_smem:=_|}).
  intros hrs AUX.
  assert (ris_refines_ok ctx hrs_mem (set_smem (sis_sm_subst_trg sis0 fSinit) sis_empty)). {
    constructor; try reflexivity.
    - apply OKset.eval_empty.
    - apply ok_set_smem; split.
      + apply sis_ok_empty.
      + apply RR.
    - apply RR.
  }
  eapply hrs_imem_subst_aux_spec in AUX as RR1; eauto.
  intros ROK; apply (ok_ref ROK) in RR1.
  split; apply RR1.
Qed.
Global Opaque hrs_imem_subst_isol.

End IMemSubst.

Implementation of tr_sis

Section TrSingle.
  Variable (m : se_mode).

Definition hrs_sv_set r sv (hrs: ristate): ?? ristate :=
  RET (ris_sreg_set hrs (red_PTree_set r sv hrs)).

Definition hrs_ival_set (hrs hrs_old: ristate) (r: reg) (iv: ival): ?? ristate :=
  match iv with
  | Ireg ir =>
      DO sv <~ @hrs_ir_get HCF hrs hrs_old ir;;
      hrs_sv_set r sv hrs
  | Iop rop args =>
      DO lsv <~ @hrs_lir_get HCF hrs hrs_old args;;
      @hrs_rsv_set HCF HC RRULES m r rop lsv None hrs_old.(ris_smem) hrs
  end.

Lemma hrs_ival_set_spec ctx sis0 sis1 hrs hrs_old r iv
  (RR0: ris_refines_ok ctx hrs_old sis0)
  (RR1: ris_refines_ok ctx hrs sis1):
  WHEN hrs_ival_set hrs hrs_old r iv ~> hrs' THEN
  exists sis2,
    sis_set_ival r iv sis0 sis1 = Some sis2 /\
    ris_refines m ctx hrs' sis2.
Proof.
  unfold hrs_ival_set, hrs_sv_set, sis_set_ival.
  case iv as [ir|rop args]; try wlp_seq; simpl.
  - intros sv GET.
    eapply hrs_ir_get_spec in GET as [?[GET]]; eauto; rewrite GET.
    eexists; split. reflexivity.
    apply ris_set_reg_refines; auto. apply ok_equivp_intro_eqv; split; intros _.
    + apply RR1.
    + intro; eapply sis_get_ireg_nofail in GET; eauto using SOK.
  - intros lsv LSV hrs' H.
    eapply hrs_lir_get_spec in LSV as (lsv' & LSV_GET & LSV_EQ); eauto.
      rewrite LSV_GET.
    do 2 esplit. reflexivity.
    eapply hrs_rsv_set_spec in H as RR2; eauto; cycle 1.
      { rewrite LSV_EQ; intro; eapply sis_get_ireg_lmap_nofail in LSV_GET; eauto using SOK. }
      { eapply ris_refines_ok_rmem, RR0. }
    eapply ris_refines_morph. 2:exact RR2.
    apply set_sreg_morph. reflexivity.
    apply root_apply_morph_equiv.
    + apply LSV_EQ.
    + apply RR0.
Qed.

Lemma hrs_ival_set_rel hrs hrs_old r iv:
  WHEN hrs_ival_set hrs hrs_old r iv ~> hrs' THEN
  ris_rel m hrs hrs'.
Proof.
  unfold hrs_ival_set, hrs_sv_set; case iv as [|]; try wlp_seq; simpl.
  - intros ? _. constructor; simpl; try reflexivity.
    all:intros until ctx; rewrite <- ris_ok_set_sreg; auto.
  - intros hrs' ?; eapply hrs_rsv_set_rel; eauto.
Qed.

Global Opaque hrs_ival_set hrs_sv_set.

Applying a sequence of invariant operations


Fixpoint exec_seq_imp (hrs hrs_old: ristate) (l: list (reg*ival)): ?? ristate :=
  match l with
  | nil => RET hrs
  | (r,iv)::l =>
      DO hrs' <~ hrs_ival_set hrs hrs_old r iv;;
      exec_seq_imp hrs' hrs_old l
  end.

Lemma exec_seq_imp_rel hrs hrs_old l:
  WHEN exec_seq_imp hrs hrs_old l ~> hrs' THEN
  ris_rel m hrs hrs'.
Proof.
  revert hrs. induction l as [|(r, iv)]; intros; simpl; wlp_seq.
  - reflexivity.
  - intros hrs2 H2 hrs3 H3.
    apply IHl in H3; eapply hrs_ival_set_rel in H2.
    etransitivity; eassumption.
Qed.

Lemma exec_seq_imp_spec ctx sis0 si hrs hrs_old l
  (RR0: ris_refines_ok ctx hrs_old sis0)
  (RR1: ris_refines_ok ctx hrs (tr_sis_ext sis0 si)):
  WHEN exec_seq_imp hrs hrs_old l ~> hrs' THEN
  ris_refines m ctx hrs' (tr_sis_ext sis0 (exec_seq l si)).
Proof.
  revert si hrs RR1; induction l as [|(r, iv) l]; intros; simpl; wlp_seq.
  - eapply wref, RR1.
  - intros hrs2 SET hrs3 SEQ.
    eapply hrs_ival_set_spec in SET as (sis2 & SET & RR2); eauto.
    eapply tr_sis_ext_set_ival in SET.
    eapply (ris_refines_morph SET) in RR2.
    eapply (ris_refines_step RR2).
      { apply exec_seq_imp_rel in SEQ; apply SEQ. }
      { apply tr_sis_ext_rel_si. apply exec_seq_strict. }
    clear RR2; intro RR2.
    eapply IHl; eauto.
Qed.
Global Opaque exec_seq_imp.

Building a PTree from the compact invariants representation, only on live variables


Fixpoint build_alive_imp (loutputs: list reg) (hrs: ristate): ?? (PTree.t sval) :=
  match loutputs with
  | nil => RET (PTree.empty sval)
  | r :: tl =>
      DO sreg <~ build_alive_imp tl hrs;;
      DO hsv <~ @hrs_sreg_get HCF hrs r;;
      RET (PTree.set r hsv sreg)
  end.

Lemma build_alive_imp_spec ctx loutputs hrs sis r
  (RR: ris_refines_ok ctx hrs sis):
  WHEN build_alive_imp loutputs hrs ~> sreg THEN
  optsv_simu ctx (sreg ! r) (restrict_alive (alive_list loutputs) (ext sis) r).
Proof.
  induction loutputs as [|r0 tl]; simpl; wlp_seq.
  - reflexivity.
  - intros sreg REC hsv GET.
    apply IHtl in REC.
    eapply hrs_sreg_get_spec in GET as [sv [EQ EV]]; eauto.
    unfold restrict_alive.
    rewrite !PTree.gsspec; case peq as [->|].
    + unfold alive_list; rewrite proj_sumbool_is_true by (simpl; tauto).
      unfold ext; rewrite EQ. constructor; exact EV.
    + rewrite REC.
      replace (alive_list (r0 :: tl) r) with (alive_list tl r)
        by (unfold alive_list, proj_sumbool; simpl; repeat autodestruct).
      reflexivity.
Qed.

Global Opaque build_alive_imp.

Applying a csasv


Definition tr_hrs_single (hrs: ristate) (csi: csasv): ?? ristate :=
  DO hrs' <~ exec_seq_imp hrs hrs csi.(aseq);;
  DO sreg <~ build_alive_imp (Regset.elements csi.(outputs)) hrs';;
  RET (ris_sreg_set hrs' sreg).

Lemma tr_hrs_single_rel hrs csi:
  WHEN tr_hrs_single hrs csi ~> hrs' THEN
  ris_rel m hrs hrs'.
Proof.
  unfold tr_hrs_single; wlp_seq.
  intros hrs' SEQ ? _; apply exec_seq_imp_rel in SEQ as [?].
  constructor; simpl; try assumption; setoid_rewrite <- ris_ok_set_sreg; auto.
Qed.

Section TrSingleSpec.
  Variable (ctx : iblock_common_context).

Theorem tr_hrs_single_spec hrs sis csi
  (RR: ris_refines_ok ctx hrs sis):
  WHEN tr_hrs_single hrs csi ~> hrs' THEN
  ris_refines m ctx (ris_input_init_set hrs' None) (tr_sis sis (siof csi) None).
Proof.
  unfold tr_hrs_single; wlp_seq.
  intros hrs' SEQ sreg OUT.
  eapply ris_refines_ok_morph in RR as RR0.
    2:symmetry; apply tr_sis_ext_empty.
  eapply exec_seq_imp_spec with (sis0:=sis) in SEQ; eauto.
  apply (ris_refines_step SEQ).
    { rewrite <- ris_ok_set_input_init, <- ris_ok_set_sreg; auto. }
    { constructor; simpl; auto.
      intro; rewrite ok_tr_sis, ok_tr_sis_ext; auto. }
  clear SEQ; intro RR1.
  eapply wref.
  eapply ris_refines_ok_morph.
    { symmetry. apply tr_sis_ext_restrict_alive. }
  set (sis1 := tr_sis_ext _ _) in RR1; fold sis1; clearbody sis1.
  constructor; try apply RR1.
  - rewrite ok_sis_restrict_alive; apply RR1.
  - intro. unfold ris_sreg_get; simpl.
    replace (SOME sv <- sreg ! r IN Some sv) with (sreg ! r)
      by autodestruct.
    eapply build_alive_imp_spec in OUT as ->; eauto.
    unfold restrict_alive, alive_list, regset_in_dec, proj_sumbool.
    repeat (autodestruct; try reflexivity).
Qed.

Global Opaque tr_hrs_single.

Corollary tr_hrs_single_spec_input_init hrs sis csi ini
  (RR: ris_refines_ok ctx hrs sis):
  WHEN tr_hrs_single hrs csi ~> hrs' THEN
  ris_refines m ctx (ris_input_init_set hrs' (Some ini)) (tr_sis sis (siof csi) (Some ini)).
Proof.
  intros hrs' TR.
  eapply tr_hrs_single_spec in TR; eauto.
  inversion TR; apply ris_refines_intro;
    [|intros ROK _; apply ok_ref in TR; [|rewrite <-!ris_ok_set_input_init in *; exact ROK];
      inversion TR; constructor].
  - apply (ok_equivp_morph OK_EQUIV); symmetry.
      reflexivity.
      2:rewrite <-!ris_ok_set_input_init; reflexivity.
    split; intros []; constructor; simpl in *; auto.
    + intros r sv; specialize (OK_SREG r sv); revert OK_SREG; simpl; autodestruct.
    + intros r sv; autodestruct; intro csi_eq.
      * specialize (OK_SREG r sv); simpl in OK_SREG; rewrite csi_eq in OK_SREG. exact OK_SREG.
      * injection 1 as <-; simpl. congruence.
  - apply MEM_EQ.
  - intros r; generalize (REG_EQ r).
    unfold ris_sreg_get.
    simpl; repeat (autodestruct; intro); simpl;
    solve [ inversion 1 | reflexivity ].
Qed.

Corollary tr_hrs_single_correct hrs sis csi
  (SIM: se_ok_check m = true)
  (RR: ris_refines_ok ctx hrs sis):
  WHEN tr_hrs_single hrs csi ~> hrs' THEN
  ris_refines_ok ctx (ris_input_init_set hrs' None) (tr_sis sis (siof csi) None).
Proof.
  intros hrs' TR; intros.
  eapply tr_hrs_single_spec in TR as RR1; eauto.
  apply tr_hrs_single_rel in TR as [?]; rewrite SIM in RRL_OK_SI.
  eapply ok_ref; eauto.
  rewrite <- ris_ok_set_input_init; apply RRL_OK_SI; eauto using ROK.
Qed.



Definition hrs_ini_states (csix : invariants) : ?? ristate * ristate :=
  DO hrsH <~ tr_hrs_single ris_empty (history csix);;
  DO hrsHG <~ tr_hrs_single hrsH (glue csix);;
  let hrsT := ris_input_init_set hrsHG (Some true) in
  DO memS <~ hrs_imem_subst m hrsT None (meminv csix);;
  let (memS, okS) := memS in
  let hrsS := {|
    ris_smem := memS;
    ris_input_init := Some false;
    ris_sreg := ris_sreg hrsH;
    ris_pre := okS;
  |} in
  RET (hrsS, hrsT).

Lemma hrs_ini_states_spec csix:
  WHEN hrs_ini_states csix ~> r THEN
  let (hrsS, hrsT) := r in
  ris_refines m ctx hrsS (sis_source csix) /\
  ris_refines m ctx hrsT (sis_target csix).
Proof.
  unfold hrs_ini_states; wlp_seq.
  intros hrsH TR_H hrsHG TR_G (memS, okS) IMEM; wlp_seq.
  apply tr_hrs_single_rel in TR_H as INI_H; apply RRL_INPUT in INI_H; simpl in INI_H.
  assert (RR_H: ris_refines m ctx hrsH (sis_history csix)). {
    eapply tr_hrs_single_spec_input_init in TR_H; eauto using ris_refines_ok_empty.
    replace (ris_input_init_set hrsH (Some false)) with hrsH in TR_H. exact TR_H.
    rewrite INI_H; destruct hrsH; reflexivity.
  }
  set (hrsT := ris_input_init_set hrsHG (Some true)) in *.
  apply tr_hrs_single_rel in TR_G as REL_G.
  assert (RR_T: ris_refines m ctx hrsT (sis_target csix)). {
    unfold hrsT.
    apply (ris_refines_step RR_H).
      { rewrite <- ris_ok_set_input_init. apply REL_G. }
      { apply tr_sis_rel. }
    clear RR_H; intro RR_H.
    eapply tr_hrs_single_spec_input_init in TR_G; eauto.
  }
  set (hrsS := {|ris_smem := memS;|}).
  split. 2:apply RR_T.
  assert (REL_S : ris_ok ctx hrsS -> ris_ok ctx hrsHG)
    by (eapply hrs_imem_subst_strict in IMEM; exact IMEM).
  apply (ris_refines_step RR_H).
    { intro. apply REL_G, REL_S; auto. }
    { rewrite <- sis_source_rel. apply tr_sis_rel. }
  clear RR_H; intro RR_H.
  apply (ris_refines_step RR_T).
    { unfold hrsT; rewrite <- ris_ok_set_input_init; apply REL_S. }
    { apply sis_source_rel. }
  clear RR_T; intro RR_T.
  eapply hrs_imem_subst_spec in IMEM as (IMEM_OK & IMEM_EQ); eauto.
  apply ris_refines_intro.
  - apply (ok_equivp_morph IMEM_OK).
    + intuition apply RR_H.
    + rewrite sis_source_ok_sis_target; reflexivity.
    + reflexivity.
  - intros ROK _; constructor.
    + apply IMEM_EQ, ROK.
    + intro; etransitivity. 2:eapply REG_EQ in RR_H; apply RR_H.
      unfold ris_sreg_get.
      replace (ris_input_init hrsS) with (ris_input_init hrsH). reflexivity.
Qed.

End TrSingleSpec.
End TrSingle.

Application of the invariants at the end of the blocks

Memory invariant meminv_sfv_set


Definition meminv_rfv (im : node -> imem) (rfv : rfval) : ?? list imem :=
  match rfv with
  | Sgoto pc => RET [im pc]
  | Scall _ _ _ _ pc | Sbuiltin _ _ _ pc =>
      if is_nil (im pc)
      then RET [nil]
      else FAILWITH "meminv_rfv: memory invariant at Call / Builtin"
  | Stailcall _ _ _ | Sreturn _ => RET [nil]
  | Sjumptable _ lpc => RET (map im lpc)
  end.

Lemma meminv_rfv_spec im rfv:
  WHEN meminv_rfv im rfv ~> res THEN
  let (imP, imS) := meminv_sfv_set im (sfval_of_rsval rfv) in
  imP /\ forall m : imem, imS m <-> In m res.
Proof.
  unfold meminv_rfv, meminv_sfv_set.
  repeat (autodestruct; simpl); intros;
  try solve [wlp_simplify]; wlp_seq.
  - (* Sjumptable *)
    setoid_rewrite in_map_iff.
    intuition decompose record H; eauto.
Qed.
Global Opaque meminv_rfv.

Register invariants si_sfv_set


Definition si_rfv (gm : node -> csasv) rfv: ?? list csasv :=
  match rfv with
  | Sgoto pc => RET [gm pc]
  | Scall sig svid args res pc =>
      if test_clobberable (gm pc) res
      then RET [csi_remove res (gm pc)]
      else FAILWITH "ri_sfv: Scall res not clobberable"
  | Sbuiltin ef args bres pc =>
      match reg_builtin_res bres with
      | Some r =>
          if test_clobberable (gm pc) r
          then RET [csi_remove r (gm pc)]
          else FAILWITH "ri_sfv: Sbuiltin res not clobberable"
      | None =>
          if test_csifreem (gm pc)
          then RET [gm pc]
          else FAILWITH "ri_sfv: Sbuiltin memory dependent"
      end
  | Stailcall _ _ _ | Sreturn _ => RET []
  | Sjumptable sv lpc => RET (map gm lpc)
  end.

Lemma si_rfv_spec gm rfv:
  WHEN si_rfv gm rfv ~> res THEN
  let (siP, siS) := si_sfv_set gm (sfval_of_rsval rfv) in
  siP /\ forall si : csasv, siS si <-> In si res.
Proof.
  unfold si_rfv, si_sfv_set.
  repeat (autodestruct; simpl); intros;
  try solve [wlp_simplify]; wlp_seq.
  - (* Sjumptable *)
    setoid_rewrite in_map_iff.
    intuition decompose record H; eauto.
Qed.
Global Opaque si_rfv.
End SymbolicInvariants.