Module BTL_SEtheory

A theory of symbolic simulation (i.e. simulation of symbolic executions) on BTL blocks.


Require Import Coqlib Maps RelationClasses.
Require Import AST Integers Values Events Memory Globalenvs.
Require Import Op Registers.
Require Import RTL BTL OptionMonad.
Require Export BTL_Invariants.
Require Import ValueDomain.
Import HConsing.

Semantics of Symbolic Values


The semantics of symbolic execution is parametrized by the context of the execution of a block
Record iblock_common_context := Bcctx {
  cge: BTL.genv;
  csp: val;
  crs0: regset;
  cmbc: mem * option block_classification;
initial memory state and optional block classification. If the classification is not None, the symbolic evaluation will fail if an address does not match its annotation.
}.

Definition cm0 ctx : mem := fst (cmbc ctx).
Definition ctx_bc ctx : option block_classification := snd (cmbc ctx).

Inductive ctx_strict_reflect : option block_classification -> bool -> Prop :=
  | mode_strict bc : ctx_strict_reflect (Some bc) true
  | mode_lax : ctx_strict_reflect None false.


Local Open Scope option_monad_scope.

Import ListNotations.

Semantics
Fixpoint eval_sval ctx (sv: sval): option val :=
  match sv with
  | Sinput r _ => Some ((crs0 ctx)#r)
  | Sop op l _ =>
     SOME args <- eval_list_sval ctx l IN
     eval_operation (cge ctx) (csp ctx) op args (cm0 ctx)
  | Sfoldr op l sv0 _ =>
     SOME args <- eval_list_sval ctx l IN
     SOME v0 <- eval_sval ctx sv0 IN
     fold_right (fun a ob => SOME b <- ob IN
       eval_operation (cge ctx) (csp ctx) op [a;b] (cm0 ctx)) (Some v0) args
  | Sload sm trap chunk addr lsv aaddr _ =>
      SOME args <- eval_list_sval ctx lsv IN
      SOME m <- eval_smem ctx sm IN
      match trap with
      | TRAP =>
          SOME a <- eval_addressing (cge ctx) (csp ctx) addr args IN
          ASSERT (vmatch_opt_dec (ctx_bc ctx) a aaddr) IN
          Mem.loadv chunk m a
      | NOTRAP =>
          match eval_addressing (cge ctx) (csp ctx) addr args with
          | None => Some Vundef
          | Some a =>
              match vmatch_opt_dec (ctx_bc ctx) a aaddr, Mem.loadv chunk m a with
              | left _, Some val => Some val
              | _, _ => Some Vundef
              end
          end
      end
  end
with eval_list_sval ctx (lsv: list_sval): option (list val) :=
  match lsv with
  | Snil _ => Some nil
  | Scons sv lsv' _ =>
    SOME v <- eval_sval ctx sv IN
    SOME lv <- eval_list_sval ctx lsv' IN
    Some (v::lv)
  end
with eval_smem ctx (sm: smem): option mem :=
  match sm with
  | Sinit _ => Some (cm0 ctx)
  | Sstore sm chunk addr lsv sinfo srce _ =>
     SOME args <- eval_list_sval ctx lsv IN
     SOME a <- eval_addressing (cge ctx) (csp ctx) addr args IN
     ASSERT (vmatch_opt_dec (ctx_bc ctx) a (store_aaddr sinfo)) IN
     SOME m <- eval_smem ctx sm IN
     SOME sv <- eval_sval ctx srce IN
     Mem.storev chunk m a sv
  end.



Wrapping the evaluation in the option monad
Definition eval_osv ctx (osv: option sval): option val :=
  SOME sv' <- osv IN eval_sval ctx sv'.

Notations to make lemmas more readable
Module SvalNotations.
Notation "'sval_equiv' ctx sv1 sv2" := (eval_sval ctx sv1 = eval_sval ctx sv2)
  (only parsing, at level 0, ctx at next level, sv1 at next level, sv2 at next level).

Notation "'osval_equiv' ctx osv1 osv2" := (eval_osv ctx osv1 = eval_osv ctx osv2)
  (only parsing, at level 0, ctx at next level, osv1 at next level, osv2 at next level).

Notation "'list_sval_equiv' ctx lsv1 lsv2" := (eval_list_sval ctx lsv1 = eval_list_sval ctx lsv2)
  (only parsing, at level 0, ctx at next level, lsv1 at next level, lsv2 at next level).

Notation "'smem_equiv' ctx sm1 sm2" := (eval_smem ctx sm1 = eval_smem ctx sm2)
  (only parsing, at level 0, ctx at next level, sm1 at next level, sm2 at next level).

Notation "'alive' o" := (o = None -> False) (at level 0).

End SvalNotations.
Import SvalNotations.

Semantics of partial affine forms with Sfoldr

Scaling affine forms

Section Scale.

Variable scalev: val -> val.
Variable select_op: operation -> bool.

Hypothesis scalev_distr: forall ctx op v1 scv1 v2,
  select_op op = true ->
  scalev v1 = scv1 ->
  eval_operation (cge ctx) (csp ctx) op [scv1; scalev v2] (cm0 ctx) =
  SOME r <- eval_operation (cge ctx) (csp ctx) op [v1; v2] (cm0 ctx) IN Some (scalev r).

Lemma scalev_distr_list ctx op v0 scv0
  (SCALE_v0: scalev v0 = scv0)
  (OK_op: select_op op = true)
  :forall l scl
  (SCALE_l: fold_right (fun a l0 => scalev a::l0) nil l = scl),
  fold_right (fun a ob => SOME b <- ob IN
    eval_operation (cge ctx) (csp ctx) op [a;b] (cm0 ctx)) (Some scv0) scl =
  SOME r <- fold_right (fun a ob => SOME b <- ob IN
      eval_operation (cge ctx) (csp ctx) op [a;b] (cm0 ctx)) (Some v0) l IN
  Some (scalev r).
Proof.
  induction l; simpl.
  - intros; subst; simpl; auto.
  - intros lsc. clear SCALE_v0.
    intros ACC0; inv ACC0; simpl.
    rewrite IHl; clear IHl; auto.
    autodestruct. auto.
Qed.

Variable scale: sval -> sval.

Hypothesis scale_correct: forall ctx sv,
  eval_sval ctx (scale sv) = SOME v <- eval_sval ctx sv IN Some (scalev v).

Fixpoint scale_l (lsv: list_sval): list_sval :=
  match lsv with
  | Snil _ => fSnil
  | Scons sv lsv' _ => fScons (scale sv) (scale_l lsv')
  end.

Lemma scale_l_correct: forall ctx lsv,
  eval_list_sval ctx (scale_l lsv) = SOME lv <- eval_list_sval ctx lsv IN
  Some (fold_right (fun a l0 => scalev a::l0) nil lv).
Proof.
  induction lsv; simpl; auto.
  rewrite scale_correct with (sv:=sv); auto.
  autodestruct. rewrite IHlsv; auto.
  simplify_option.
Qed.

Definition rescale (sv: sval): sval :=
  match sv with
  | Sfoldr op l sv0 _ =>
      if select_op op then fSfoldr op (scale_l l) (scale sv0)
      else scale sv
  | _ => scale sv
  end.

Theorem rescale_correct ctx sv v:
  eval_sval ctx (scale sv) = Some v ->
  eval_sval ctx (rescale sv) = Some v.
Proof.
  unfold rescale. do 2 autodestruct.
  intros OP _ ; simpl.
  rewrite !scale_correct; auto.
  rewrite scale_l_correct with (lsv:=lsv); auto.
  simpl; do 3 autodestruct; intros FOLD ESV ELSV HI; inv HI.
  erewrite scalev_distr_list; auto.
  rewrite FOLD; reflexivity.
Qed.

End Scale.

Import Datatypes.

Accumulating and merging affine forms

Section Accumulate.

Variable accv: val -> val -> val.
Variable select_op: operation -> bool.

Hypothesis accv_eval: forall ctx v1 v2 op,
    select_op op = true ->
    eval_operation (cge ctx) (csp ctx) op [v1; v2] (cm0 ctx) = Some (accv v1 v2).

Hypothesis accv_commut: forall v1 v2, accv v1 v2 = accv v2 v1.

Hypothesis accv_assoc: forall v1 v2 v3, accv v1 (accv v2 v3) = accv (accv v1 v2) v3.

Lemma accv_assoc_list ctx op vl v0 accv0
  (ACC_vr: accv vl v0 = accv0)
  (OK_op: select_op op = true)
  :forall l vr
  (ACC_l: fold_right (fun a ob => SOME b <- ob IN
    eval_operation (cge ctx) (csp ctx) op [a;b] (cm0 ctx)) (Some v0) l = Some vr),
  fold_right (fun a ob => SOME b <- ob IN
    eval_operation (cge ctx) (csp ctx) op [a;b] (cm0 ctx)) (Some accv0) l =
  Some (accv vl vr).
Proof.
  induction l; simpl.
  - try_simplify_someHyps.
  - intros vr. autodestruct.
    intros FOLD. erewrite IHl; eauto.
    rewrite !accv_eval; auto.
    intros HI; inv HI.
    rewrite !accv_assoc, (accv_commut a vl); reflexivity.
Qed.

Variable merge1: sval -> sval -> option sval.
Variable acc0: sval -> sval -> sval.
Variable compare: sval -> sval -> comparison.

Hypothesis compare_correct: forall ctx sv1 sv2 sv3,
  compare sv1 sv2 = Eq ->
  merge1 sv1 sv2 = Some sv3 ->
  eval_sval ctx sv3 =
  SOME v1 <- eval_sval ctx sv1 IN
  SOME v2 <- eval_sval ctx sv2 IN Some (accv v1 v2).

Hypothesis acc0_correct: forall ctx (sv1 sv2: sval),
  eval_sval ctx (acc0 sv1 sv2) =
  SOME v1 <- eval_sval ctx sv1 IN
  SOME v2 <- eval_sval ctx sv2 IN Some (accv v1 v2).

Fixpoint merge (l1 l2: list_sval) {struct l1}: list_sval :=
  match l1 with
  | Snil _ => l2
  | Scons sv1 l1' _ =>
     (fix merge_in l2 { struct l2 } :=
     match l2 with
     | Snil _ => l1
     | Scons sv2 l2' _ =>
       match compare sv1 sv2 with
       | Lt => fScons sv1 (merge l1' l2)
       | Eq => match merge1 sv1 sv2 with
               | Some sv3 => fScons sv3 (merge l1' l2')
               | None => fScons sv1 (fScons sv2 (merge l1' l2'))
               end
       | Gt => fScons sv2 (merge_in l2')
       end
    end) l2
  end.

Theorem merge_correct ctx l1 op
  (OK_op: select_op op = true): forall l2 v1 v2 sv0_1 sv0_2 h1 h2,
  eval_sval ctx (Sfoldr op l1 sv0_1 h1) = Some v1 ->
  eval_sval ctx (Sfoldr op l2 sv0_2 h2) = Some v2 ->
  eval_sval ctx (fSfoldr op (merge l1 l2) (acc0 sv0_1 sv0_2)) = Some (accv v1 v2).
Proof.
  induction l1; simpl.
  - intros until sv0_2; intros _ _.
    autodestruct; intros EV01 HI; inv HI.
    do 2 autodestruct. rewrite acc0_correct.
    intros EV02 ELV2 FOLD. rewrite EV01, EV02.
    intros; eapply accv_assoc_list; eauto.
  - do 2 autodestruct. intros ELV1 ESV l2.
    induction l2; simpl; intros until sv0_2; intros _ _.
    + do 3 autodestruct; intros EV02 FOLD EV01 ACC0 HI; inv HI.
      rewrite ESV, ELV1, acc0_correct, EV01, EV02; trivial.
      simpl. set (vacc:=accv v0 v2).
      erewrite accv_assoc_list with (accv0:=vacc) (v0:=v0); eauto.
      rewrite accv_eval in *; auto. inv ACC0.
      rewrite <- !accv_assoc, (accv_commut v2 v3); reflexivity.
    + do 5 autodestruct; intros EV02 ELV2 ESV0 FOLD0 EV01 ACC0 FOLD1.
      revert FOLD1; simpl; autodestruct; intros FOLD1 ACC1.
      autodestruct; intros COMP.
      * generalize (IHl1 l2 v3 v6 sv0_1 sv0_2).
        simpl. rewrite ELV1, ELV2, EV01, EV02.
        intros IHl1S; exploit IHl1S; eauto; clear IHl1S.
        do 2 autodestruct.
        intros ACC2 ELVM IHl1S. autodestruct.
        -- intros MERGE1; simpl. erewrite compare_correct; eauto.
           rewrite ESV, ESV0, ELVM; simpl. rewrite IHl1S.
           erewrite accv_eval in *; eauto. inv ACC0; inv ACC1.
           rewrite !accv_assoc; do 2 f_equal.
           rewrite <- !accv_assoc, (accv_commut v3 v4); reflexivity.
        -- intros; simpl. rewrite ESV, ESV0, ELVM; simpl. rewrite IHl1S.
           erewrite !accv_eval in *; eauto. inv ACC0; inv ACC1.
           rewrite !accv_assoc; do 2 f_equal.
           rewrite <- !accv_assoc, (accv_commut v3 v4); reflexivity.
      * generalize (IHl1 (Scons sv0 l2 hid1) v3 v2 sv0_1 sv0_2).
        simpl. rewrite ELV1, ELV2, !acc0_correct, ESV, ESV0, EV01, EV02; trivial.
        intros IHl1S; exploit IHl1S; eauto; clear IHl1S.
        { simpl; rewrite FOLD1; auto. } autodestruct.
        simpl. intros ELVM IHl1S. rewrite IHl1S.
        erewrite accv_eval in *; eauto. inv ACC0; inv ACC1.
        rewrite !accv_assoc; reflexivity.
      * generalize (IHl2 v1 v6 sv0_1 sv0_2).
        simpl. rewrite !acc0_correct, ESV0, EV01, EV02; trivial.
        intros IHl2S; exploit IHl2S; eauto; clear IHl2S.
        { rewrite FOLD0; auto. }
        autodestruct.
        simpl. intros ELVM IHl2S. rewrite IHl2S.
        erewrite accv_eval in *; eauto. inv ACC0; inv ACC1.
        rewrite !accv_assoc; do 2 f_equal.
        rewrite <- accv_assoc, accv_commut; reflexivity.
Qed.

End Accumulate.

End of semantics of partial affine forms

Lemma root_apply_morph_equiv o args0 args1 sm0 sm1 ctx
  (EARGS: list_sval_equiv ctx args0 args1)
  (EMEM: smem_equiv ctx sm0 sm1):
  sval_equiv ctx (root_apply o args0 sm0) (root_apply o args1 sm1).
Proof.
  case o; simpl; rewrite ?EARGS, ?EMEM; reflexivity.
Qed.

Inductive optsv_simu ctx: option sval -> option sval -> Prop :=
  | Ssome_simu sv1 sv2
      (SIMU: sval_equiv ctx sv1 sv2)
     :optsv_simu ctx (Some sv1) (Some sv2)
  | Snone_simu: optsv_simu ctx None None
.

Global Instance optsv_simu_Equivalence {ctx} : Equivalence (optsv_simu ctx).
Proof.
  constructor.
  - intros []; constructor; reflexivity.
  - intros ? ? []; constructor; auto.
  - intros ? ? ?; do 2 inversion 1; try constructor; auto.
    rewrite SIMU. apply SIMU0.
Qed.

Lemma eval_osv_morph [ctx osv0 osv1]
  (EQ : optsv_simu ctx osv0 osv1):
  eval_osv ctx osv0 = eval_osv ctx osv1.
Proof.
  case EQ; simpl; auto.
Qed.

Definition eval_scondition ctx (cond: condition) (lsv: list_sval): option bool :=
  SOME args <- eval_list_sval ctx lsv IN
  eval_condition cond args (cm0 ctx).

The symbolic memory preserves predicate Mem.valid_pointer with respect to initial memory. Hence, arithmetic operations and Boolean conditions do not depend on the current memory of the block (their semantics only depends on the initial memory of the block). The correctness of this idea is proved on lemmas sexec_op_correct and eval_scondition_eq.
Lemma valid_pointer_preserv ctx sm:
  forall m b ofs, eval_smem ctx sm = Some m ->
  Mem.valid_pointer (cm0 ctx) b ofs = Mem.valid_pointer m b ofs.
Proof.
  induction sm; simpl; intros; try_simplify_someHyps; auto.
  repeat autodestruct; intros; erewrite IHsm by reflexivity.
  eapply Mem.storev_preserv_valid; eauto.
Qed.
Local Hint Resolve valid_pointer_preserv: core.

The semantics of SIs.

Local Hint Resolve si_wf: core.

Definition si_ok ctx (si: fpasv): Prop :=
  forall sv, List.In sv (fpa_ok si) -> eval_sval ctx sv <> None.

Lemma si_ok_eval si r sv ctx:
  si_ok ctx si -> si r=Some sv -> eval_sval ctx sv <> None.
Proof.
   intros OK H H0; destruct (is_input_dec sv) as [H1|H1]. { inv H1; simpl in *; congruence. }
   intros; destruct (OK sv); eauto.
Qed.

We build the regset obtained by evaluating in ctx the result of map on sreg
Definition eval_map_sreg (ctx:iblock_common_context) (map: sval -> sval) (sreg : PTree.tree sval): regset :=
  let default := fst (crs0 ctx) in
  let rsmap := PTree.map (fun r sv =>
       match eval_sval ctx (map sv) with
       | Some v => v
       | None => (crs0 ctx)#r end) sreg
  in (default,
    PTree.combine (fun oa ob =>
      match ob with
      | Some x => Some x
      | None => oa
      end) (snd (crs0 ctx)) rsmap).

Lemma eval_map_sreg_correct ctx map sreg r:
  (eval_map_sreg ctx map sreg)#r
  = match (SOME sv <- sreg!r IN eval_sval ctx (map sv)) with
    | Some v => v
    | None => (crs0 ctx)#r
    end.
Proof.
  unfold eval_map_sreg, Regmap.get, eval_osv; simpl.
  rewrite PTree.gcombine; auto.
  rewrite PTree.gmap.
  case (sreg ! r); reflexivity.
Qed.

Lemma eval_map_sreg_correct_some ctx map (sreg: PTree.t sval) sv r:
  sreg!r = Some sv ->
  (eval_map_sreg ctx map sreg)#r =
       match eval_sval ctx (map sv) with
       | Some v => v
       | None => (crs0 ctx)#r
       end.
Proof.
  rewrite eval_map_sreg_correct. intros ->. reflexivity.
Qed.

Lemma eval_map_sreg_correct_none ctx map (sreg: PTree.t sval) r:
  sreg!r = None ->
  (eval_map_sreg ctx map sreg)#r = (crs0 ctx)#r.
Proof.
  rewrite eval_map_sreg_correct. intros ->. reflexivity.
Qed.

REM: not really useful. Only for information...
Lemma si_ok_eval_map si r sv ctx:
  si_ok ctx si -> si r=Some sv -> eval_sval ctx sv = Some ((eval_map_sreg ctx (fun sv => sv) si)#r).
Proof.
  intros; erewrite eval_map_sreg_correct_some; eauto.
  autodestruct.
  intros; exploit si_ok_eval; eauto.
  intuition.
Qed.

Definition sreg_ok ctx (sreg: reg -> option sval): Prop :=
  forall r sv, sreg r = Some sv -> eval_sval ctx sv <> None.

Lemma si_ok_sreg ctx (si: fpasv): si_ok ctx si -> sreg_ok ctx si.
Proof.
  unfold si_ok, not; intros H r sv H0 H1.
  destruct (is_input_dec sv) as [X|X]; eauto.
  inv X; simpl in *; congruence.
Qed.

Definition match_sreg ctx (sreg: reg -> option sval) (rs: regset): Prop :=
  forall r sv, sreg r = Some sv -> eval_sval ctx sv = Some (rs#r).

Lemma match_sreg_ok ctx sreg rs:
  match_sreg ctx sreg rs -> sreg_ok ctx sreg.
Proof.
  intros MATCH r sv H; erewrite MATCH; eauto. congruence.
Qed.
Local Hint Resolve match_sreg_ok: core.

Definition match_si ctx (si: fpasv) (rs: regset): Prop :=
  si_ok ctx si /\ match_sreg ctx si rs.

Lemma match_si_ok ctx si rs:
  match_si ctx si rs -> si_ok ctx si.
Proof.
  destruct 1; auto.
Qed.
Local Hint Resolve match_si_ok si_ok_sreg: core.

Definition match_invs ctx (csix: invariants) (rs: regset): Prop :=
  match_si ctx (history csix) (crs0 ctx)
  /\ match_si ctx (glue csix) rs.

Lemma only_liveness_match_si ctx si:
  only_liveness si -> match_si ctx si (crs0 ctx).
Proof.
  intros (ONLYok & ONLY); split.
  - intros sv. rewrite ONLYok; simpl. intuition.
  - intros r sv EQ. exploit ONLY; eauto.
    intros; subst; simpl; auto.
Qed.
Local Hint Resolve only_liveness_match_si: core.

Lemma only_liveness_match_invs ctx csix:
  only_liveness (history csix) ->
  only_liveness (glue csix) ->
  match_invs ctx csix (crs0 ctx).
Proof.
  unfold match_invs; intuition eauto.
Qed.

Lemma svfree_preserv ge sp rs1 rs2 m r sv
  (EQREGS: forall r0, r0 <> r -> rs1#r0 = rs2#r0):
  svfree sv r ->
  eval_sval (Bcctx ge sp rs1 m) sv = eval_sval (Bcctx ge sp rs2 m) sv.
Proof.
  induction sv using sval_mut with
    (P0 := fun lsv => lsvfree lsv r ->
      eval_list_sval (Bcctx ge sp rs1 m) lsv = eval_list_sval (Bcctx ge sp rs2 m) lsv)
    (P1 := fun sm => smfree sm r ->
      eval_smem (Bcctx ge sp rs1 m) sm = eval_smem (Bcctx ge sp rs2 m) sm); simpl in *; auto.
  + intros. rewrite EQREGS; auto.
  + intros. rewrite IHsv; auto.
  + intros (SVF & LSVF). rewrite IHsv, IHsv0; auto.
  + intros (SMF & LSVF). rewrite IHsv, IHsv0; auto.
  + intros (SVF & LSVF). rewrite IHsv, IHsv0; auto.
  + intros (SMF & LSVF & SVF). rewrite IHsv, IHsv0, IHsv1; auto.
Qed.

Lemma fold_right_ext {A B} (f1 f2: A -> B -> B) b l:
  (forall x y, f1 x y = f2 x y) ->
  fold_right f1 b l = fold_right f2 b l.
Proof.
  intros EQ; induction l; simpl; auto.
  rewrite IHl, EQ; reflexivity.
Qed.

Lemma svfreem_preserv ge sp rs m1 m2 sv:
  svfreem sv ->
  eval_sval (Bcctx ge sp rs m1) sv = eval_sval (Bcctx ge sp rs m2) sv.
Proof.
  induction sv using sval_mut with
    (P0 := fun lsv => lsvfreem lsv ->
      eval_list_sval (Bcctx ge sp rs m1) lsv = eval_list_sval (Bcctx ge sp rs m2) lsv)
    (P1 := fun sm => True); simpl in *; try tauto.
  + intros (SMF & LSVF). rewrite IHsv; auto.
    autodestruct.
    intros; eapply op_depends_on_memory_correct; eauto.
  + intros (SMF & LSVF & SVF). rewrite IHsv, IHsv0; auto.
    do 2 autodestruct.
    intros; eapply fold_right_ext.
    intros; destruct y; auto.
    eapply op_depends_on_memory_correct; eauto.
  + intros (SVF & LSVF). rewrite IHsv, IHsv0; auto.
Qed.

Lemma matchsi_update_r ge sp rs1 rs2 m res (csi: csasv)
  (FREE: sifree csi res)
  (ONLYLIVEr: forall sv, csi res = Some sv -> sv = fSinput res)
  (MATCH: match_si (Bcctx ge sp rs1 m) (csi_remove res csi) rs2)
  :forall v, match_si (Bcctx ge sp (rs1#res <- v) m) csi (rs2#res <- v).
Proof.
  destruct FREE as (FREE_In & FREE).
  unfold match_si, sifree.
  intros v; split.
  * intros sv H H0.
    exploit match_si_ok; eauto.
    erewrite svfree_preserv; eauto.
    intros; rewrite PMap.gso; eauto.
  * intros r; destruct (Pos.eq_dec res r) as [EQ|DIFF].
    + subst.
      rewrite PMap.gss.
      intros sv EQ; exploit ONLYLIVEr; simpl; eauto.
      intros X; inv X; simpl in *.
      rewrite PMap.gss; eauto.
    + rewrite PMap.gso; eauto.
      intros sv EQ.
      generalize EQ. erewrite <- csi_gro; eauto.
      intros EQr.
      destruct MATCH as (MATCH_In & MATCH).
      erewrite <- MATCH; eauto.
      destruct (only_live_input_dec r sv) as [H|H].
      { inv H; simpl. rewrite PMap.gso; eauto. }
      intros; erewrite svfree_preserv; eauto.
      intros; rewrite PMap.gso; eauto.
Qed.

Lemma matchsi_update_m ge sp rs1 rs2 m1 m2 (si: fpasv)
  (FREEm: sifreem si)
  (MATCH: match_si (Bcctx ge sp rs1 m1) si rs2)
  :match_si (Bcctx ge sp rs1 m2) si rs2.
Proof.
  unfold sifreem in *.
  destruct MATCH as (MATCH_In & MATCH); split.
  * intros sv H H0; eapply MATCH_In; eauto.
    erewrite svfreem_preserv; eauto.
  * intros r sv H.
    erewrite svfreem_preserv; eauto.
    destruct (is_input_dec sv) as [H1|H1]; eauto.
    inv H1; simpl; auto.
Qed.
Local Hint Resolve matchsi_update_m: core.

Lemma matchinvs_update_m ge sp rs1 rs2 m1 m2 csix:
  sifreem (history csix) ->
  sifreem (glue csix) ->
  match_invs (Bcctx ge sp rs1 m1) csix rs2 ->
  match_invs (Bcctx ge sp rs1 m2) csix rs2.
Proof.
  unfold match_invs; simpl; intuition eauto.
Qed.

Lemma clobbered_compat_sifreem csi res: clobbered_compat csi res -> sifreem csi.
Proof.
  intros [_ FREEm _]; auto.
Qed.

Lemma clobbered_compat_sifreem_remove csi res:
  clobbered_compat csi res -> sifreem (csi_remove res csi).
Proof.
  intros [_ FREEm _]; auto.
Qed.

Local Hint Resolve clobbered_compat_sifreem clobbered_compat_sifreem_remove: core.

Lemma clobbered_compat_matchsi_preserv ge sp rs1 rs2 m1 csi res:
  clobbered_compat csi res ->
  match_si (Bcctx ge sp rs1 m1) (csi_remove res csi) rs2 ->
  forall v m2, match_si (Bcctx ge sp (rs1#res <- v) m2) csi (rs2#res <- v).
Proof.
  intros COMPAT MATCHSI v m2; eapply matchsi_update_m; eauto.
  destruct COMPAT. eapply matchsi_update_r; eauto.
Qed.

Local Hint Resolve clobbered_compat_matchsi_preserv: core.

Lemma clobbered_compat_matchinvs_preserv ge sp rs1 rs2 m1 (csix: invariants) res:
  clobbered_compat (history csix) res ->
  clobbered_compat (glue csix) res ->
  match_invs (Bcctx ge sp rs1 m1) (csix_remove res csix) rs2 ->
  forall v m2, match_invs (Bcctx ge sp (rs1#res <- v) m2) csix (rs2#res <- v).
Proof.
  unfold csix_remove, match_invs; simpl; intuition eauto.
Qed.

A theory of build_frame and eqlive_reg


Definition build_frame {A} (si: reg -> option A) (r: reg): Prop := alive (si r).

Lemma build_frame_intro {A} (si: reg -> option A) r sv: si r = Some sv -> build_frame si r.
Proof.
   unfold build_frame; congruence.
Qed.

Local Hint Unfold build_frame: core.
Local Hint Resolve build_frame_intro: core.

Definition eqlive_reg (frame: reg -> Prop) (rs1 rs2: regset): Prop :=
 forall r, (frame r) -> rs1#r = rs2#r.

Lemma match_si_eqlive ctx si rs1 rs2:
  match_si ctx si rs1 ->
  eqlive_reg (build_frame si) rs1 rs2 ->
  match_si ctx si rs2.
Proof.
  unfold match_si, eqlive_reg, build_frame.
  intros (MATCH_In & MATCH) EQLIVE. split; auto.
  intros r sv EQ; erewrite <- EQLIVE, <- MATCH; eauto.
  try_simplify_someHyps.
Qed.
Local Hint Resolve match_si_eqlive: core.

Lemma match_invs_eqlive ctx csix rs1 rs2:
  match_invs ctx csix rs1 ->
  eqlive_reg (build_frame csix) rs1 rs2 ->
  match_invs ctx csix rs2.
Proof.
  unfold match_invs; intuition eauto.
Qed.

Lemma eqlive_reg_update (frame: reg -> Prop) rs1 rs2 r v:
  eqlive_reg (fun r1 => r1 <> r /\ frame r1) rs1 rs2 ->
  eqlive_reg frame (rs1 # r <- v) (rs2 # r <- v).
Proof.
  unfold eqlive_reg; intros EQLIVE r0 ALIVE.
  destruct (Pos.eq_dec r r0) as [H|H].
  - subst. rewrite! Regmap.gss. auto.
  - rewrite! Regmap.gso; auto.
Qed.

Lemma eqlive_reg_update_gso frame rs1 rs2 res r: forall v : val,
  eqlive_reg frame rs1 # res <- v rs2 # res <- v ->
  res <> r -> frame r ->
  rs1 # r = rs2 # r.
Proof.
  intros v REGS NRES INR. unfold eqlive_reg in REGS.
  specialize REGS with r. apply REGS in INR.
  rewrite !Regmap.gso in INR; auto.
Qed.

Lemma eqlive_reg_monotonic (frame1 frame2: reg -> Prop) rs1 rs2:
  eqlive_reg frame2 rs1 rs2 -> (forall r, frame1 r -> frame2 r) -> eqlive_reg frame1 rs1 rs2.
Proof.
  unfold eqlive_reg; intuition.
Qed.

Lemma eqlive_reg_list (frame: reg -> Prop) args rs1 rs2:
  eqlive_reg frame rs1 rs2 -> (forall r, List.In r args -> (frame r)) -> rs1##args = rs2##args.
Proof.
  induction args; simpl; auto.
  intros EQLIVE ALIVE; rewrite IHargs; auto.
  unfold eqlive_reg in EQLIVE.
  rewrite EQLIVE; auto.
Qed.

Lemma eqlive_reg_refl frame rs: eqlive_reg frame rs rs.
Proof.
  intros r; auto.
Qed.
Global Hint Resolve eqlive_reg_refl: core.

Lemma eqlive_reg_trans frame rs1 rs2 rs3:
  eqlive_reg frame rs1 rs2 -> eqlive_reg frame rs2 rs3 -> eqlive_reg frame rs1 rs3.
Proof.
  unfold eqlive_reg; intros H1 H2 r ALIVE. rewrite H1; eauto.
Qed.

Lemma find_function_eqlive ge frame ros rs1 rs2:
  eqlive_reg frame rs1 rs2 ->
  (forall r, ros = inl r -> frame r) ->
  find_function ge ros rs1 = find_function ge ros rs2.
Proof.
  intros EQLIVE.
  destruct ros; simpl; auto.
  intros H; erewrite (EQLIVE r); eauto.
Qed.

Inductive eqlive_stackframes: stackframe -> stackframe -> Prop :=
  | eqlive_stackframes_intro res f sp pc rs1 rs2
      (EQUIV: forall v, eqlive_reg (build_frame (f.(fn_gm) pc)) (rs1 # res <- v) (rs2 # res <- v)):
       eqlive_stackframes (Stackframe res f sp pc rs1) (Stackframe res f sp pc rs2).

Inductive eqlive_states: state -> state -> Prop :=
  | eqlive_states_intro
      st1 st2 f sp pc rs1 rs2 m bc
      (STACKS: list_forall2 eqlive_stackframes st1 st2)
      (EQUIV: eqlive_reg (build_frame (f.(fn_gm) pc)) rs1 rs2):
      eqlive_states (State st1 f sp pc rs1 m bc) (State st2 f sp pc rs2 m bc)
  | eqlive_states_call st1 st2 f args m bc
      (STACKS: list_forall2 eqlive_stackframes st1 st2):
      eqlive_states (Callstate st1 f args m bc) (Callstate st2 f args m bc)
  | eqlive_states_return st1 st2 v m bc
      (STACKS: list_forall2 eqlive_stackframes st1 st2):
      eqlive_states (Returnstate st1 v m bc) (Returnstate st2 v m bc).

Local Hint Constructors eqlive_stackframes eqlive_states: core.

Lemma eqlive_stackframes_refl stf: eqlive_stackframes stf stf.
Proof.
  destruct stf; econstructor; eauto.
Qed.
Global Hint Resolve eqlive_stackframes_refl: core.

Lemma eqlive_stack_refl stk: list_forall2 eqlive_stackframes stk stk.
Proof.
  induction stk; simpl; constructor; auto.
Qed.
Global Hint Resolve eqlive_stack_refl: core.

Lemma eqlive_states_refl s: eqlive_states s s.
Proof.
  induction s; simpl; econstructor; auto.
Qed.

Lemma eqlive_stackframes_trans stf1 stf2:
  eqlive_stackframes stf1 stf2 ->
  forall stf3, eqlive_stackframes stf2 stf3 ->
  eqlive_stackframes stf1 stf3.
Proof.
  destruct 1; intros stf3 EQLIVE; inv EQLIVE; intuition subst.
  simpl in *; econstructor; eauto.
  intros; eapply eqlive_reg_trans; eauto.
Qed.

Lemma eqlive_stacks_trans stk1 stk2:
  list_forall2 eqlive_stackframes stk1 stk2 ->
  forall stk3, list_forall2 eqlive_stackframes stk2 stk3 ->
  list_forall2 eqlive_stackframes stk1 stk3.
Proof.
  induction 1; intros stk3 EQLIVE; inv EQLIVE; econstructor; eauto.
  intros; eapply eqlive_stackframes_trans; eauto.
Qed.

Local Hint Resolve eqlive_stacks_trans: core.

Lemma eqlive_states_trans s1 s2:
  eqlive_states s1 s2 ->
  forall s3, eqlive_states s2 s3 ->
  eqlive_states s1 s3.
Proof.
  destruct 1; intros s3 EQLIVE; inv EQLIVE; intuition subst;
  econstructor; eauto.
  intros; eapply eqlive_reg_trans; eauto.
Qed.

Auxiliary definitions on Builtins


Section EVAL_BUILTIN_SARG.

Variable ctx: iblock_common_context.
Variable m: mem.

Inductive eval_builtin_sarg: builtin_arg sval -> val -> Prop :=
  | seval_BA: forall x v,
      eval_sval ctx x = Some v ->
      eval_builtin_sarg (BA x) v
  | seval_BA_int: forall n,
      eval_builtin_sarg (BA_int n) (Vint n)
  | seval_BA_long: forall n,
      eval_builtin_sarg (BA_long n) (Vlong n)
  | seval_BA_float: forall n,
      eval_builtin_sarg (BA_float n) (Vfloat n)
  | seval_BA_single: forall n,
      eval_builtin_sarg (BA_single n) (Vsingle n)
  | seval_BA_loadstack: forall chunk ofs v,
      Mem.loadv chunk m (Val.offset_ptr (csp ctx) ofs) = Some v ->
      eval_builtin_sarg (BA_loadstack chunk ofs) v
  | seval_BA_addrstack: forall ofs,
      eval_builtin_sarg (BA_addrstack ofs) (Val.offset_ptr (csp ctx) ofs)
  | seval_BA_loadglobal: forall chunk id ofs v,
      Mem.loadv chunk m (Senv.symbol_address (cge ctx) id ofs) = Some v ->
      eval_builtin_sarg (BA_loadglobal chunk id ofs) v
  | seval_BA_addrglobal: forall id ofs,
      eval_builtin_sarg (BA_addrglobal id ofs) (Senv.symbol_address (cge ctx) id ofs)
  | seval_BA_splitlong: forall hi lo vhi vlo,
      eval_builtin_sarg hi vhi -> eval_builtin_sarg lo vlo ->
      eval_builtin_sarg (BA_splitlong hi lo) (Val.longofwords vhi vlo)
  | seval_BA_addptr: forall a1 a2 v1 v2,
      eval_builtin_sarg a1 v1 -> eval_builtin_sarg a2 v2 ->
      eval_builtin_sarg (BA_addptr a1 a2)
                       (if Archi.ptr64 then Val.addl v1 v2 else Val.add v1 v2)
.

Definition eval_builtin_sargs (al: list (builtin_arg sval)) (vl: list val) : Prop :=
  list_forall2 eval_builtin_sarg al vl.

End EVAL_BUILTIN_SARG.


Lemma eval_builtin_sarg_iff [A B : Type] ctx (fs : A -> option sval) (fb : A -> option B) (fv : B -> val)
  (arg : builtin_arg A) (sarg : builtin_arg sval) (barg : builtin_arg B) (m : mem) (v : val)
  (MATCH: forall (x : A) (y : B) (sv : sval),
            fs x = Some sv -> fb x = Some y ->
            eval_sval ctx sv = Some (fv y))
  (MAP0: map_builtin_arg_opt fs arg = Some sarg)
  (MAP1: map_builtin_arg_opt fb arg = Some barg):
  eval_builtin_sarg ctx m sarg v <-> eval_builtin_arg (cge ctx) fv (csp ctx) m barg v.
Proof.
  split; intro EVAL.
  - revert arg barg MAP0 MAP1. induction EVAL; intros arg barg;
      set (g := eval_builtin_arg _ _ _ _ _ _); (* avoid the destruction of the if-then-else in [eval_BA_addptr] *)
      case arg as []; simpl; repeat autodestruct; intros; simplify_someHyps;
      subst g.
    all:try solve [econstructor; eauto].
    + intros EVAL FB FS. rewrite (MATCH _ _ _ FS FB) in EVAL. injection EVAL as <-.
      apply eval_BA.
  - revert arg sarg MAP0 MAP1. induction EVAL; intros arg sarg; simpl;
      set (g := eval_builtin_sarg _ _ _ _);
      case arg as []; simpl; repeat autodestruct; intros; simplify_someHyps;
      subst g.
    all:solve [econstructor; eauto].
Qed.


Lemma map_builtin_arg_opt_id A (f : A -> option A) b
  (FID: forall x, f x = Some x):
  map_builtin_arg_opt f b = Some b.
Proof.
  induction b; simpl; rewrite ?FID, ?IHb1, ?IHb2; reflexivity.
Qed.

Lemma builtin_arg_opt_map_correct ctx rs m arg varg:
  eval_builtin_arg (cge ctx) (fun r => rs # r) (csp ctx) m arg varg ->
  forall (sreg : reg -> option sval) (b: builtin_arg sval), map_builtin_arg_opt sreg arg = Some b ->
  match_sreg ctx sreg rs ->
  eval_builtin_sarg ctx m b varg.
Proof.
  intros EVAL sreg b MAP MATCH.
  eapply eval_builtin_sarg_iff with (fb := fun r => Some r) in EVAL; try eassumption.
  - injection 2 as ->. eauto.
  - apply map_builtin_arg_opt_id. reflexivity.
Qed.

Lemma builtin_arg_opt_map_exact arg:
  forall (sreg: reg -> option sval) b,
  map_builtin_arg_opt sreg arg = Some b ->
  forall ctx rs m varg, eval_builtin_sarg ctx m b varg ->
  match_sreg ctx sreg rs ->
  eval_builtin_arg (cge ctx) (fun r => rs # r) (csp ctx) m arg varg.
Proof.
  intros sreg b MAP ctx rs m varg EVAL MATCH.
  eapply eval_builtin_sarg_iff with (fb := fun r => Some r) in EVAL; try eassumption.
  - injection 2 as ->. eauto.
  - apply map_builtin_arg_opt_id. reflexivity.
Qed.


Lemma map_builtin_arg_opt_all_Some A B (f : A -> option B) (g : A -> B) b
  (FSOME: forall x, f x = Some (g x)):
  map_builtin_arg_opt f b = Some (map_builtin_arg g b).
Proof.
  induction b; simpl; rewrite ?FSOME, ?IHb1, ?IHb2; reflexivity.
Qed.

Lemma eval_builtin_sarg_correct ctx rs m sreg: forall arg varg,
  (forall r, eval_sval ctx (sreg r) = Some rs # r) ->
  eval_builtin_arg (cge ctx) (fun r => rs # r) (csp ctx) m arg varg ->
  eval_builtin_sarg ctx m (map_builtin_arg sreg arg) varg.
Proof.
  intros. eapply builtin_arg_opt_map_correct.
  - eassumption.
  - apply map_builtin_arg_opt_all_Some. reflexivity.
  - injection 1 as <-. auto.
Qed.

Lemma eval_builtin_sarg_exact ctx rs m sreg: forall arg varg,
  (forall r, eval_sval ctx (sreg r) = Some rs # r) ->
  eval_builtin_sarg ctx m (map_builtin_arg sreg arg) varg ->
  eval_builtin_arg (cge ctx) (fun r => rs # r) (csp ctx) m arg varg.
Proof.
  intros. eapply builtin_arg_opt_map_exact.
  - apply map_builtin_arg_opt_all_Some. reflexivity.
  - eassumption.
  - injection 1 as <-. auto.
Qed.

Lemma eval_builtin_sargs_exact ctx rs m sreg: forall args vargs,
  (forall r, eval_sval ctx (sreg r) = Some rs # r) ->
  eval_builtin_sargs ctx m (map (map_builtin_arg sreg) args) vargs ->
  eval_builtin_args (cge ctx) (fun r => rs # r) (csp ctx) m args vargs.
Proof.
  induction args.
  - simpl. intros. inv H0. constructor.
  - intros vargs SEVAL BARG. simpl in BARG. inv BARG.
    constructor; [| eapply IHargs; eauto].
    eapply eval_builtin_sarg_exact; eauto.
Qed.
Local Hint Resolve eval_builtin_sargs_exact: core.


Definition eval_builtin_sval ctx : builtin_arg sval -> option (builtin_arg val) :=
  map_builtin_arg_opt (eval_sval ctx).

Definition eval_list_builtin_sval ctx : list (builtin_arg sval) -> option (list (builtin_arg val)) :=
  map_opt (eval_builtin_sval ctx).


Lemma eval_builtin_sval_arg ctx bs:
  forall ba m v,
  eval_builtin_sval ctx bs = Some ba ->
  eval_builtin_arg (cge ctx) (fun id => id) (csp ctx) m ba v ->
  eval_builtin_sarg ctx m bs v.
Proof.
  unfold eval_builtin_sval;
  intros ba m v MAP EVAL.
  eapply eval_builtin_sarg_iff with (fs := fun id => Some id) (fb := eval_sval ctx) in EVAL. exact EVAL.
  - injection 1 as ->. auto.
  - apply map_builtin_arg_opt_id. reflexivity.
  - assumption.
Qed.

Lemma eval_builtin_sarg_sval ctx m v: forall bs,
  eval_builtin_sarg ctx m bs v ->
  exists ba,
    eval_builtin_sval ctx bs = Some ba
    /\ eval_builtin_arg (cge ctx) (fun id => id) (csp ctx) m ba v.
Proof.
  intros bs EVAL.
  assert (E: eval_builtin_sval ctx bs <> None). {
    unfold eval_builtin_sval; induction EVAL; simpl; repeat autodestruct; congruence.
  }
  case (eval_builtin_sval ctx bs) as [ba|] eqn:BA. 2:congruence.
  exists ba. split. reflexivity.
  eapply eval_builtin_sarg_iff with (fs := fun id => Some id) (fb := eval_sval ctx) in EVAL. exact EVAL.
  - injection 1 as ->. auto.
  - apply map_builtin_arg_opt_id. reflexivity.
  - assumption.
Qed.

Lemma eval_builtin_sval_args ctx lbs:
  forall lba m v,
  eval_list_builtin_sval ctx lbs = Some lba ->
  list_forall2 (eval_builtin_arg (cge ctx) (fun id => id) (csp ctx) m) lba v ->
  eval_builtin_sargs ctx m lbs v.
Proof.
  unfold eval_builtin_sargs, eval_list_builtin_sval; induction lbs; simpl; intros lba m v.
  - injection 1 as <-. inversion 1. econstructor.
  - repeat autodestruct. injection 3 as <-. inversion 1.
    econstructor.
    + eapply eval_builtin_sval_arg; eauto.
    + eapply IHlbs; eauto.
Qed.

Lemma eval_builtin_sargs_sval ctx m lv: forall lbs,
  eval_builtin_sargs ctx m lbs lv ->
  exists lba,
    eval_list_builtin_sval ctx lbs = Some lba
    /\ list_forall2 (eval_builtin_arg (cge ctx) (fun id => id) (csp ctx) m) lba lv.
Proof.
  unfold eval_list_builtin_sval. induction 1.
  - eexists. constructor.
    + simpl. reflexivity.
    + constructor.
  - destruct IHlist_forall2 as (lba & A & B).
    apply eval_builtin_sarg_sval in H. destruct H as (ba & A' & B').
    eexists. constructor.
    + simpl. rewrite A'. rewrite A. reflexivity.
    + constructor; assumption.
Qed.

Lemma eval_builtin_sval_correct ctx m: forall bs1 v bs2,
  eval_builtin_sarg ctx m bs1 v ->
  (eval_builtin_sval ctx bs1) = (eval_builtin_sval ctx bs2) ->
  eval_builtin_sarg ctx m bs2 v.
Proof.
  intros. exploit eval_builtin_sarg_sval; eauto.
  intros (ba & X1 & X2).
  eapply eval_builtin_sval_arg; eauto.
  congruence.
Qed.

Lemma eval_list_builtin_sval_correct ctx m vargs: forall lbs1,
  eval_builtin_sargs ctx m lbs1 vargs ->
  forall lbs2, (eval_list_builtin_sval ctx lbs1) = (eval_list_builtin_sval ctx lbs2) ->
  eval_builtin_sargs ctx m lbs2 vargs.
Proof.
  intros. exploit eval_builtin_sargs_sval; eauto.
  intros (ba & X1 & X2).
  eapply eval_builtin_sval_args; eauto.
  congruence.
Qed.

Symbolic (final) value of a block


Inductive sfval :=
  | Sgoto (pc: exit)
  | Scall (sig:signature) (svos: sval + ident) (lsv:list_sval) (res:reg) (pc:exit)
  | Stailcall: signature -> sval + ident -> list_sval -> sfval
  | Sbuiltin (ef:external_function) (sargs: list (builtin_arg sval)) (res: builtin_res reg) (pc:exit)
  | Sjumptable (sv: sval) (tbl: list exit)
  | Sreturn: option sval -> sfval
.

Definition sfind_function ctx (svos : sval + ident): option fundef :=
  match svos with
  | inl sv => SOME v <- eval_sval ctx sv IN Genv.find_funct (cge ctx) v
  | inr symb => SOME b <- Genv.find_symbol (cge ctx) symb IN Genv.find_funct_ptr (cge ctx) b
  end.

We encapsulate the context of execution of a block in order to specify it with the associated function
Record iblock_function_context := Bfctx {
  cf: function;
  cc:> iblock_common_context
}.

Import ListNotations.
Local Open Scope list_scope.

Inductive sem_sfval ctx stk bc: sfval -> regset -> mem -> trace -> state -> Prop :=
  | exec_Sgoto pc rs m:
      sem_sfval ctx stk bc (Sgoto pc) rs m E0 (State stk (cf ctx) (csp ctx) pc rs m bc)
  | exec_Sreturn pstk osv rs m m' bc' v:
      (csp ctx) = (Vptr pstk Ptrofs.zero) ->
      Mem.free m pstk 0 (cf ctx).(fn_stacksize) = Some m' ->
      match osv with Some sv => eval_sval ctx sv | None => Some Vundef end = Some v ->
      sem_sfval ctx stk bc (Sreturn osv) rs m
         E0 (Returnstate stk v m' bc')
  | exec_Scall rs m sig svos lsv args res pc fd bc':
      sfind_function ctx svos = Some fd ->
      funsig fd = sig ->
      eval_list_sval ctx lsv = Some args ->
      sem_sfval ctx stk bc (Scall sig svos lsv res pc) rs m
        E0 (Callstate (Stackframe res (cf ctx) (csp ctx) pc rs::stk) fd args m bc')
  | exec_Stailcall pstk rs m sig svos args fd m' bc' lsv:
      sfind_function ctx svos = Some fd ->
      funsig fd = sig ->
      (csp ctx) = Vptr pstk Ptrofs.zero ->
      Mem.free m pstk 0 (cf ctx).(fn_stacksize) = Some m' ->
      eval_list_sval ctx lsv = Some args ->
      sem_sfval ctx stk bc (Stailcall sig svos lsv) rs m
        E0 (Callstate stk fd args m' bc')
  | exec_Sbuiltin m' bc' rs m vres res pc t sargs ef vargs:
      eval_builtin_sargs ctx m sargs vargs ->
      external_call ef (cge ctx) vargs m t vres m' ->
      sem_sfval ctx stk bc (Sbuiltin ef sargs res pc) rs m
        t (State stk (cf ctx) (csp ctx) pc (regmap_setres res vres rs) m' bc')
  | exec_Sjumptable sv tbl pc' n rs m:
      eval_sval ctx sv = Some (Vint n) ->
      list_nth_z tbl (Int.unsigned n) = Some pc' ->
      sem_sfval ctx stk bc (Sjumptable sv tbl) rs m
        E0 (State stk (cf ctx) (csp ctx) pc' rs m bc)
.

Preservation properties under a simu_proof_context


Record simu_proof_context := Sctx {
  sge1: BTL.genv;
  sge2: BTL.genv;
  sge_match: forall s, Genv.find_symbol sge1 s = Genv.find_symbol sge2 s;
  ssp: val;
  srs0: regset;
  smbc: mem * option block_classification
}.

Definition sm0 ctx : mem := fst (smbc ctx).

Definition bcctx1 (ctx: simu_proof_context) := Bcctx ctx.(sge1) ctx.(ssp) ctx.(srs0) ctx.(smbc).
Definition bcctx2 (ctx: simu_proof_context) := Bcctx ctx.(sge2) ctx.(ssp) ctx.(srs0) ctx.(smbc).

Section SymbValPreserved.

Hypothesis ctx: simu_proof_context.
Local Hint Resolve sge_match: core.

Lemma eval_sval_preserved sv:
  eval_sval (bcctx1 ctx) sv = eval_sval (bcctx2 ctx) sv.
Proof.
  induction sv using sval_mut with
    (P0 := fun lsv => eval_list_sval (bcctx1 ctx) lsv = eval_list_sval (bcctx2 ctx) lsv)
    (P1 := fun sm => eval_smem (bcctx1 ctx) sm = eval_smem (bcctx2 ctx) sm); simpl in *; auto.
  + rewrite IHsv; clear IHsv. autodestruct.
    erewrite eval_operation_preserved; eauto.
  + rewrite IHsv, IHsv0; clear IHsv IHsv0; autodestruct.
  + rewrite IHsv0; clear IHsv0.
    autodestruct; intros.
    erewrite IHsv; do 2 autodestruct;
    erewrite eval_addressing_preserved; eauto.
  + rewrite IHsv; clear IHsv. destruct (eval_sval _ _); auto.
    rewrite IHsv0; auto.
  + rewrite IHsv0; clear IHsv0. destruct (eval_list_sval _ _); auto.
    erewrite eval_addressing_preserved; eauto.
    destruct (eval_addressing _ _ _ _); auto.
    rewrite IHsv; clear IHsv. destruct (eval_smem _ _); auto.
    rewrite IHsv1; auto.
Qed.

Lemma list_sval_eval_preserved lsv:
  eval_list_sval (bcctx1 ctx) lsv = eval_list_sval (bcctx2 ctx) lsv.
Proof.
  induction lsv; simpl in *; auto.
  rewrite eval_sval_preserved. destruct (eval_sval _ _); auto.
  rewrite IHlsv; auto.
Qed.

Lemma smem_eval_preserved sm:
  eval_smem (bcctx1 ctx) sm = eval_smem (bcctx2 ctx) sm.
Proof.
  induction sm; simpl in *; auto.
  rewrite list_sval_eval_preserved. destruct (eval_list_sval _ _); auto.
  erewrite eval_addressing_preserved; eauto.
  destruct (eval_addressing _ _ _ _); auto.
  rewrite IHsm; clear IHsm. destruct (eval_smem _ _); auto.
  rewrite eval_sval_preserved; auto.
Qed.

Lemma eval_builtin_sval_preserved sv:
  eval_builtin_sval (bcctx1 ctx) sv = eval_builtin_sval (bcctx2 ctx) sv.
Proof.
  unfold eval_builtin_sval; induction sv; simpl in *; auto.
  all: try (erewrite eval_sval_preserved by eauto); trivial.
  all: erewrite IHsv1 by eauto; erewrite IHsv2 by eauto; reflexivity.
Qed.

Lemma eval_list_builtin_sval_preserved lsv:
  eval_list_builtin_sval (bcctx1 ctx) lsv = eval_list_builtin_sval (bcctx2 ctx) lsv.
Proof.
  unfold eval_list_builtin_sval; induction lsv; simpl in *; auto.
  erewrite eval_builtin_sval_preserved, IHlsv.
  reflexivity.
Qed.

Lemma eval_scondition_preserved cond lsv:
  eval_scondition (bcctx1 ctx) cond lsv = eval_scondition (bcctx2 ctx) cond lsv.
Proof.
  unfold eval_scondition.
  rewrite list_sval_eval_preserved. destruct (eval_list_sval _ _); auto.
Qed.

Lemma match_sreg_preserved sreg rs:
  match_sreg (bcctx1 ctx) sreg rs -> match_sreg (bcctx2 ctx) sreg rs.
Proof.
  intros MATCH r sv. rewrite <- eval_sval_preserved; auto.
Qed.
Local Hint Resolve match_sreg_preserved: core.

Lemma match_si_preserved si rs:
  match_si (bcctx1 ctx) si rs -> match_si (bcctx2 ctx) si rs.
Proof.
  intros (MATCH_In & MATCH); split; eauto.
  intros sv H. rewrite <- eval_sval_preserved; auto.
Qed.
Local Hint Resolve match_si_preserved: core.

Lemma match_invs_preserved csix rs:
  match_invs (bcctx1 ctx) csix rs -> match_invs (bcctx2 ctx) csix rs.
Proof.
  unfold match_invs; intuition eauto.
Qed.

Hypothesis senv_preserved_BTL: Senv.equiv (sge1 ctx) (sge2 ctx).

Lemma senv_find_symbol_preserved id:
  Senv.find_symbol (sge1 ctx) id = Senv.find_symbol (sge2 ctx) id.
Proof.
  destruct senv_preserved_BTL as (A & B & C). congruence.
Qed.

Lemma senv_symbol_address_preserved id ofs:
  Senv.symbol_address (sge1 ctx) id ofs = Senv.symbol_address (sge2 ctx) id ofs.
Proof.
  unfold Senv.symbol_address. rewrite senv_find_symbol_preserved.
  reflexivity.
Qed.

Lemma eval_builtin_sarg_preserved m: forall bs varg,
  eval_builtin_sarg (bcctx1 ctx) m bs varg ->
  eval_builtin_sarg (bcctx2 ctx) m bs varg.
Proof.
  induction 1; simpl.
  all: try (constructor; auto).
  - rewrite <- eval_sval_preserved. assumption.
  - rewrite <- senv_symbol_address_preserved. assumption.
  - rewrite senv_symbol_address_preserved. eapply seval_BA_addrglobal.
Qed.

Lemma eval_builtin_sargs_preserved m lbs vargs:
  eval_builtin_sargs (bcctx1 ctx) m lbs vargs ->
  eval_builtin_sargs (bcctx2 ctx) m lbs vargs.
Proof.
  induction 1; constructor; eauto.
  eapply eval_builtin_sarg_preserved; auto.
Qed.

End SymbValPreserved.

Syntax and Semantics of symbolic internal states


sis_pre is a precondition on initial context
Record sistate :=
  { sis_pre: iblock_common_context -> Prop;
    sis_sreg:> reg -> option sval;
    sis_smem: smem;
    sis_pre_preserved: forall pctx, sis_pre (bcctx1 pctx) <-> sis_pre (bcctx2 pctx)
  }.

Record sistate_eq ctx (si0 si1 : sistate) : Prop := {
    EQ_SIS_PRE: si0.(sis_pre) ctx <-> si1.(sis_pre) ctx;
    EQ_SIS_SREG: forall (r : reg), optsv_simu ctx (si0.(sis_sreg) r) (si1.(sis_sreg) r);
    EQ_SIS_SMEM: smem_equiv ctx (si0.(sis_smem)) (si1.(sis_smem))
  }.

Global Instance sistate_Equivalence {ctx : iblock_common_context} : Equivalence (sistate_eq ctx).
Proof.
  constructor.
  - intros ?; constructor; reflexivity.
  - intros ? ? []; constructor; symmetry; auto.
  - intros ? ? ? [] []; constructor; etransitivity; eauto.
Qed.

Lemma sreg_ok_morph [ctx si0 si1]:
  sistate_eq ctx si0 si1 ->
  sreg_ok ctx si0 <-> sreg_ok ctx si1.
Proof.
  revert si0 si1; eassert (Sym : _). 2:{
    intros si0 si1 EQ; split.
    + apply (Sym si0 si1 EQ).
    + apply Sym; symmetry; apply EQ.
  }
  unfold sreg_ok; intros si0 si1 [] H r ?.
  specialize (EQ_SIS_SREG0 r); inversion EQ_SIS_SREG0; [|congruence].
  injection 1 as <-; rewrite <- SIMU; eapply H; eauto.
Qed.


Predicate on which (rs, m) is a possible final state after evaluating ss on ((crs0 ctx), (cm0 ctx))
Definition sem_sistate ctx (sis: sistate) (rs: regset) (m: mem): Prop :=
  sis.(sis_pre) ctx
  /\ eval_smem ctx sis.(sis_smem) = Some m
  /\ match_sreg ctx sis rs
.

Local Hint Resolve match_sreg_preserved: core.

Lemma sem_sistate_preserved ctx sis rs m:
  sem_sistate (bcctx1 ctx) sis rs m -> sem_sistate (bcctx2 ctx) sis rs m.
Proof.
   intros (PRE & MEM & REG); repeat split; eauto.
   + apply sis_pre_preserved; auto.
   + erewrite <- smem_eval_preserved; eauto.
Qed.

Function to transform a list of reg into a list of symbolic values (i.e. the list_sval type). It may fail if the register does not evaluate correctly.
Fixpoint lmap_sv {A} (sreg: A -> option sval) (l: list A): option list_sval :=
  match l with
  | nil => Some (fSnil)
  | r::l' =>
     SOME sv <- sreg r IN
     SOME lsv <- lmap_sv sreg l' IN
     Some (fScons sv lsv)
  end.

Function to transform a list of builtin args reg in a list of symbolic values builtin args, it may fail. We use it to traduce the BTL list (builtin_arg reg) into a list (builtin_arg sval)
Definition bamap_opt {A B}:
  (builtin_arg A -> option (builtin_arg B)) -> list (builtin_arg A) -> option (list (builtin_arg B)) :=
  map_opt.

Lemma eval_lmap_sv ctx sreg l rs:
  match_sreg ctx sreg rs ->
  forall lsv, lmap_sv sreg l = Some lsv ->
  eval_list_sval ctx lsv = Some (rs ## l).
Proof.
  intros H; induction l as [|r l]; simpl.
  - intros; try_simplify_someHyps.
  - intros lsv.
    repeat (autodestruct; simpl; intro).
    try_simplify_someHyps; simpl; auto.
    intros; erewrite H, IHl; eauto.
Qed.

Lemma lmap_sv_TRIV_alive args sis
  (TRIV: forall r, build_frame sis r)
  :alive (lmap_sv sis args).
Proof.
  unfold build_frame in *; induction args; simpl.
  - congruence.
  - repeat autodestruct; simpl.
Qed.
Local Hint Resolve lmap_sv_TRIV_alive: core.

Lemma eval_bamap_opt_correct ctx rs m args vargs:
  eval_builtin_args (cge ctx) (fun r : reg => rs # r)
    (csp ctx) m args vargs ->
  forall sis l,
    bamap_opt (map_builtin_arg_opt sis) args = Some l ->
    match_sreg ctx sis rs ->
  eval_builtin_sargs ctx m l vargs.
Proof.
  unfold bamap_opt, map_opt. induction 1.
  - intros. inv H. constructor.
  - simpl. intros sis l. repeat autodestruct; intros. inv H1.
    econstructor.
    + eapply builtin_arg_opt_map_correct; eauto.
    + eapply IHlist_forall2; eauto.
Qed.

Lemma eval_bamap_opt_exact ctx m l vargs:
  eval_builtin_sargs ctx m l vargs ->
  forall sis rs args,
    bamap_opt (map_builtin_arg_opt sis) args = Some l ->
    match_sreg ctx sis rs ->
  eval_builtin_args (cge ctx) (fun r : positive => rs # r)
    (csp ctx) m args vargs.
Proof.
  unfold bamap_opt, map_opt. induction 1; intros sis rs args LBMAP MSI.
  - destruct args; inv LBMAP.
    + constructor.
    + generalize H0; repeat autodestruct.
  - destruct args; inv LBMAP.
    generalize H2; repeat autodestruct.
    inv H2. intros LBMAP BMAP _.
    constructor.
    + eapply builtin_arg_opt_map_exact; eauto.
    + eapply IHlist_forall2; eauto.
Qed.

Lemma builtin_arg_opt_map_TRIV_alive (A: Type) (sis: reg -> option A) args
  (TRIVFRAME : forall r : reg, build_frame sis r)
  (LBMAP: bamap_opt (map_builtin_arg_opt sis) args = None)
  :False.
Proof.
  unfold bamap_opt, map_opt in LBMAP.
  induction args.
  - inv LBMAP.
  - generalize LBMAP. simpl; repeat autodestruct.
    generalize dependent a.
    induction a; try (simpl; repeat autodestruct; congruence; fail).
    all: simpl; repeat autodestruct;
         [ intros BMAP2 _ _ _; apply IHa2; auto; simpl;
           rewrite BMAP2; reflexivity
         | intros BMAP1 _ _; apply IHa1; auto; simpl;
           rewrite BMAP1; reflexivity ].
Qed.

Symbolic execution of final step


Definition sum_left_optmap {A B C} (f : A -> option B) (x : A + C): option (B + C) :=
  match x with
  | inl y => SOME r <- f y IN Some (inl r)
  | inr z => Some (inr z)
  end.

Definition sexec_final_sfv (i: final) (sreg: reg -> option sval): option sfval :=
  match i with
  | Bgoto pc => Some (Sgoto pc)
  | Bcall sig ros args res pc =>
    SOME svos <- sum_left_optmap sreg ros IN
    SOME sargs <- lmap_sv sreg args IN
    Some (Scall sig svos sargs res pc)
  | Btailcall sig ros args =>
    SOME svos <- sum_left_optmap sreg ros IN
    SOME sargs <- lmap_sv sreg args IN
    Some (Stailcall sig svos sargs)
  | Bbuiltin ef args res pc =>
    SOME sargs <- bamap_opt (map_builtin_arg_opt sreg) args IN
    Some (Sbuiltin ef sargs res pc)
  | Breturn None => Some (Sreturn None)
  | Breturn (Some r) =>
    SOME sv <- sreg r IN
    Some (Sreturn (Some sv))
  | Bjumptable reg tbl =>
    SOME sv <- sreg reg IN
    Some (Sjumptable sv tbl)
  end.

Local Hint Constructors sem_sfval: core.

Lemma sexec_final_sfv_correct (ctx: iblock_function_context) stk bc i sis t rs m s sfv:
  sem_sistate ctx sis rs m ->
  final_step (cge ctx) stk (cf ctx) (csp ctx) rs m bc i t s ->
  sexec_final_sfv i sis = Some sfv ->
  sem_sfval ctx stk bc sfv rs m t s.
Proof.
  intros (PRE&MEM&REG).
  destruct 1; subst; simpl; intros; intuition eauto;
  try_simplify_someHyps; repeat autodestruct; try_simplify_someHyps.
  + (* Bcall *)
     intros; eapply exec_Scall; auto.
    - unfold sum_left_optmap in *; try_simplify_someHyps; repeat autodestruct; try_simplify_someHyps.
      intros; erewrite REG; eauto.
    - erewrite eval_lmap_sv; simpl; intuition eauto.
  + (* Btailcall *) intros. eapply exec_Stailcall; eauto.
    - unfold sum_left_optmap in *; try_simplify_someHyps; repeat autodestruct; try_simplify_someHyps.
      intros; erewrite REG; eauto.
    - erewrite eval_lmap_sv; simpl; intuition eauto.
  + (* Bbuiltin *) intros. eapply exec_Sbuiltin; eauto.
    eapply eval_bamap_opt_correct; eauto.
  + (* Bjumptable *) intros. eapply exec_Sjumptable; eauto.
    erewrite REG; eauto. congruence.
Qed.

Lemma sexec_final_sfv_TRIV_alive i sis
  (TRIVFRAME: forall r, build_frame sis r)
  :alive (sexec_final_sfv i sis).
Proof.
  destruct i; simpl; try unfold sum_left_optmap.
  all: repeat autodestruct; intuition (congruence || eauto).
  eapply builtin_arg_opt_map_TRIV_alive; eauto.
Qed.
Local Hint Resolve sexec_final_sfv_TRIV_alive: core.

Local Hint Constructors final_step: core.

Lemma sexec_final_sfv_exact (ctx: iblock_function_context) stk bc i sis t rs m s sfv:
  sem_sistate ctx sis rs m ->
  sexec_final_sfv i sis = Some sfv ->
  sem_sfval ctx stk bc sfv rs m t s
  -> final_step (cge ctx) stk (cf ctx) (csp ctx) rs m bc i t s.
Proof.
  intros (PRE&MEM&REG) SEXEC LAST.
  destruct i; simpl in *; try_simplify_someHyps;
  repeat autodestruct; try_simplify_someHyps; intros; inv LAST; eauto.
  + (* Breturn Some *)
     erewrite REG in *; eauto. try_simplify_someHyps.
     intros; fold (regmap_optget (Some r) Vundef rs).
     eapply exec_Breturn; eauto.
  + (* Breturn None *)
    try_simplify_someHyps.
    intros; fold (regmap_optget None Vundef rs).
    eapply exec_Breturn; eauto.
  + (* Bcall *)
    erewrite eval_lmap_sv in *; simpl; eauto.
    try_simplify_someHyps.
    intros; eapply exec_Bcall; eauto.
    destruct fn; simpl in * |- *; try_simplify_someHyps.
    autodestruct. try_simplify_someHyps.
    intros; erewrite REG in *; eauto.
  + (* Btailcall *)
    erewrite eval_lmap_sv in *; simpl; eauto.
    try_simplify_someHyps.
    intros; eapply exec_Btailcall; eauto.
    destruct fn; simpl in * |- *; try_simplify_someHyps.
    autodestruct. try_simplify_someHyps.
    intros; erewrite REG in *; eauto.
  + (* Bbuiltin *)
    eapply exec_Bbuiltin; eauto.
    eapply eval_bamap_opt_exact; eauto.
  + (* Bjumptable *)
    eapply exec_Bjumptable; eauto.
    erewrite REG in *; try_simplify_someHyps.
Qed.

OK property for internal states
Record sis_ok ctx (sis: sistate): Prop := {
  OK_PRE: sis.(sis_pre) ctx;
  OK_SMEM: eval_smem ctx sis.(sis_smem) <> None;
  OK_SREG: sreg_ok ctx sis
}.

Lemma sis_ok_morph [ctx si0 si1]:
  sistate_eq ctx si0 si1 ->
  sis_ok ctx si0 <-> sis_ok ctx si1.
Proof.
  revert si0 si1; eassert (Sym : _). 2:{
    intros si0 si1 EQ; split.
    + apply (Sym si0 si1 EQ).
    + apply Sym; symmetry; apply EQ.
  }
  intros si0 si1 EQ []; inversion EQ; constructor.
  + apply EQ_SIS_PRE0; auto.
  + rewrite <- EQ_SIS_SMEM0; auto.
  + apply (sreg_ok_morph EQ); auto.
Qed.

Lemma sis_ok_preserved ctx sis:
  sis_ok (bcctx1 ctx) sis <-> sis_ok (bcctx2 ctx) sis.
Proof.
  split; intros H; inv H; econstructor;
  try (apply sis_pre_preserved; auto);
  try (rewrite smem_eval_preserved in * || rewrite <- smem_eval_preserved; auto);
  unfold sreg_ok in *; intros; rewrite eval_sval_preserved || rewrite <- eval_sval_preserved; eauto.
Qed.

Lemma sem_sis_ok ctx sis rs m:
  sem_sistate ctx sis rs m -> sis_ok ctx sis.
Proof.
  intros (PRE&MEM&REG).
  econstructor; eauto || (try congruence).
Qed.

Program Definition set_sreg (r:reg) (sv:sval) (sis:sistate): sistate :=
  {| sis_pre:=(fun ctx =>
      (forall rsv, sis r = Some rsv -> eval_sval ctx rsv <> None) /\
      (sis.(sis_pre) ctx));
     sis_sreg:=fun y => if Pos.eq_dec r y then Some sv else sis y;
     sis_smem:= sis.(sis_smem)|}.
Next Obligation.
  intuition eauto;
  try (rewrite eval_sval_preserved in H2 || rewrite <- eval_sval_preserved in H2);
  try apply sis_pre_preserved; eauto.
Qed.

Lemma set_sreg_correct ctx dst sv sis (rs rs': regset) m:
  sem_sistate ctx sis rs m ->
  (eval_sval ctx sv = Some rs' # dst) ->
  (forall r, build_frame sis r -> r <> dst -> rs'#r = rs#r) ->
  sem_sistate ctx (set_sreg dst sv sis) rs' m.
Proof.
  unfold sem_sistate, match_sreg, set_sreg; intros (PRE&MEM&REG) NEW OLD; simpl.
  intuition.
  - clear NEW; erewrite REG in *; eauto. congruence.
  - destruct (Pos.eq_dec dst r); try_simplify_someHyps; eauto.
    intros; clear NEW. erewrite REG; eauto. rewrite OLD; eauto.
Qed.

Lemma set_sreg_TRIV_alive ctx dst sv sis rs m
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  :forall r, build_frame (set_sreg dst sv sis) r.
Proof.
  unfold build_frame, match_si, set_sreg in *; simpl in *.
  intros r; autodestruct.
Qed.

Lemma ok_set_sreg ctx r sv sis:
  sis_ok ctx (set_sreg r sv sis)
  <-> (sis_ok ctx sis /\ eval_sval ctx sv <> None).
Proof.
  unfold set_sreg; split.
  + intros [(SVAL & PRE) SMEM SREG]; simpl in *; split.
    - econstructor; eauto.
      intros r0; generalize (SREG r0); destruct (Pos.eq_dec r r0);
      intuition subst. specialize (SVAL sv0); auto.
    - generalize (SREG r sv); destruct (Pos.eq_dec r r); auto.
  + intros ([PRE SMEM SREG] & SVAL).
    econstructor; simpl; eauto.
    intros r0; destruct (Pos.eq_dec r r0); try congruence.
    intros; specialize (SREG r0 sv0); auto.
Qed.


Program Definition set_smem (sm:smem) (sis:sistate): sistate :=
  {| sis_pre:=(fun ctx => eval_smem ctx sis.(sis_smem) <> None /\ (sis.(sis_pre) ctx));
     sis_sreg:= sis.(sis_sreg);
     sis_smem:= sm |}.
Next Obligation.
  intuition eauto;
  try (rewrite smem_eval_preserved in H || rewrite <- smem_eval_preserved in H; congruence);
  try apply sis_pre_preserved; eauto.
Qed.

Lemma set_smem_correct ctx sm sis rs m m':
  sem_sistate ctx sis rs m ->
  (eval_smem ctx sm = Some m') ->
  sem_sistate ctx (set_smem sm sis) rs m'.
Proof.
  intros (PRE&MEM&REG) NEW.
  unfold sem_sistate; simpl.
  intuition.
Qed.

symbolic execution of basic instructions


Definition sexec_op op args dst (sis: sistate): option sistate :=
   SOME args <- lmap_sv sis args IN
   Some (set_sreg dst (fSop op args) sis).

Lemma sexec_op_TRIV_alive ctx op args dst sis rs m
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  :exists sis', sexec_op op args dst sis = Some sis' /\ forall r, build_frame sis' r.
Proof.
  unfold sexec_op; autodestruct.
  intros; eexists; split; eauto.
  + intros; eapply set_sreg_TRIV_alive; eauto.
  + intros; exploit lmap_sv_TRIV_alive; intuition eauto.
Qed.

Local Hint Resolve valid_pointer_preserv: core.
Lemma sexec_op_correct ctx op args dst sis sis' rs m v
 (EVAL: eval_operation (cge ctx) (csp ctx) op rs ## args m = Some v)
 (SIS: sem_sistate ctx sis rs m)
 :sexec_op op args dst sis = Some sis' -> sem_sistate ctx sis' (rs#dst <- v) m.
Proof.
  unfold sexec_op; autodestruct; try_simplify_someHyps.
  intros; eapply set_sreg_correct; eauto.
  - simpl. destruct SIS as (PRE&MEM&REG).
    rewrite Regmap.gss; simpl; auto.
    erewrite eval_lmap_sv; simpl; eauto.
    try_simplify_someHyps.
    intros; erewrite op_valid_pointer_eq; eauto.
  - intros; rewrite Regmap.gso; auto.
Qed.

Definition sexec_load trap chunk addr args aaddr dst (sis: sistate): option sistate :=
   SOME args <- lmap_sv sis args IN
   Some (set_sreg dst (fSload sis.(sis_smem) trap chunk addr args aaddr) sis).

Lemma sexec_load_TRIV_alive ctx trap chunk addr args aaddr dst sis rs m
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  :exists sis', sexec_load trap chunk addr args aaddr dst sis = Some sis' /\ forall r, build_frame sis' r.
Proof.
  unfold sexec_load; autodestruct.
  intros; eexists; split; eauto.
  + intros; eapply set_sreg_TRIV_alive; eauto.
  + intros; exploit lmap_sv_TRIV_alive; intuition eauto.
Qed.

Lemma sexec_load_correct ctx chunk addr args aaddr dst sis sis' rs m v trap
 (HLOAD: has_loaded (cge ctx) (csp ctx) (ctx_bc ctx) rs m chunk addr args aaddr v trap)
 (SIS: sem_sistate ctx sis rs m)
 : (sexec_load trap chunk addr args aaddr dst sis = Some sis') ->
   (sem_sistate ctx sis' (rs#dst <- v) m).
Proof.
  unfold sexec_load; autodestruct; try_simplify_someHyps.
  intros; inv HLOAD; eapply set_sreg_correct; eauto.
  2,4: intros; rewrite Regmap.gso; auto.
  - simpl. destruct SIS as (PRE&MEM&REG).
    destruct trap; rewrite Regmap.gss; simpl; auto;
    erewrite eval_lmap_sv; simpl; eauto;
    try_simplify_someHyps; repeat autodestruct.
  - simpl. destruct SIS as (PRE&MEM&REG).
    rewrite Regmap.gss; simpl; auto.
    erewrite eval_lmap_sv; simpl; eauto.
    rewrite MEM; simpl. do 2 autodestruct. rewrite LOAD; auto.
Qed.

Definition sexec_store chunk addr args sinfo src (sis: sistate): option sistate :=
   SOME args <- lmap_sv sis args IN
   SOME src <- sis src IN
   Some (set_smem (fSstore sis.(sis_smem) chunk addr args sinfo src) sis).

Lemma sexec_store_TRIV_alive sis chunk addr args sinfo src ctx rs m
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  :exists sis', sexec_store chunk addr args sinfo src sis = Some sis'
   /\ forall r, build_frame sis' r.
Proof.
  unfold sexec_store.
  repeat autodestruct; simpl in *; intuition eauto.
  exploit lmap_sv_TRIV_alive; intuition eauto.
Qed.

Lemma sexec_store_correct ctx chunk addr args sinfo src sis sis' rs m m' a
  (EVAL: eval_addressing (cge ctx) (csp ctx) addr rs ## args = Some a)
  (VMATCH: vmatch_opt (ctx_bc ctx) a (store_aaddr sinfo))
  (STORE: Mem.storev chunk m a (rs # src) = Some m')
  (SIS: sem_sistate ctx sis rs m)
  :sexec_store chunk addr args sinfo src sis = Some sis' -> sem_sistate ctx sis' rs m'.
Proof.
  unfold sexec_store; repeat autodestruct; try_simplify_someHyps.
  intros; eapply set_smem_correct; eauto.
  destruct SIS as (PRE&MEM&REG).
  simpl. erewrite eval_lmap_sv; simpl; eauto.
  try_simplify_someHyps; autodestruct.
  intros; erewrite REG; eauto.
Qed.

Lemma eval_scondition_eq ctx cond args sis rs m lsv
  (SIS : sem_sistate ctx sis rs m)
  (ARGS: lmap_sv sis args = Some lsv)
  :eval_scondition ctx cond lsv = eval_condition cond rs ## args m.
Proof.
  destruct SIS as (PRE&MEM&REG); unfold eval_scondition; simpl.
  erewrite eval_lmap_sv; simpl; eauto.
  eapply cond_valid_pointer_eq; eauto.
Qed.

symbolic execution of blocks


symbolic state

Inductive pstate (S : Type) :=
  | Sfinal (sis: S) (sfv: sfval)
  | Scond (cond: condition) (args: list_sval) (ifso ifnot: pstate S)
  | Sabort
 .
Arguments Sfinal {_}.
Arguments Scond {_}.
Arguments Sabort {_}.

Definition sstate := pstate sistate.

outcome of a symbolic execution path

Record poutcome (istate : Type) := sout {
   _sis: istate;
   _sfv: sfval;
}.
Arguments sout {_}.
Arguments _sis [_].
Arguments _sfv [_].


Definition soutcome := poutcome sistate.

Fixpoint get_soutcome [S] ctx (ss:pstate S): option (poutcome S) :=
  match ss with
  | Sfinal sis sfv => Some (sout sis sfv)
  | Scond cond args ifso ifnot =>
     SOME b <- eval_scondition ctx cond args IN
     get_soutcome ctx (if b then ifso else ifnot)
  | Sabort => None
  end.

Frame for a symbolic final value
Definition sfv_frame f sfv: reg -> Prop :=
  match sfv with
  | Sgoto pc => build_frame (f.(fn_gm) pc)
  | Sreturn _ | Stailcall _ _ _ => fun r => False
  | Scall _ _ _ res pc => fun r => r <> res /\ build_frame (f.(fn_gm) pc) r
  | Sjumptable _ tbl => fun r => exists pc, List.In pc tbl /\ build_frame (f.(fn_gm) pc) r
  | Sbuiltin _ _ bres pc => fun r => (forall res, reg_builtin_res bres = Some res -> r <> res)
                                                  /\ build_frame (f.(fn_gm) pc) r
  end.

Definition triv_frame (f: function) (sfv: sfval) (r: reg):= True.

transition (t,cs) produced by a sstate in initial context ctx
Inductive sem_sstate (SVFF: function -> sfval -> reg -> Prop)
  (ctx: iblock_function_context) stk bc t cs: sstate -> Prop :=
  | sem_Sfinal sis sfv rs m
     (SIS: sem_sistate ctx sis rs m)
     (LIVEOK: forall r, SVFF (cf ctx) sfv r -> build_frame sis r)
     (SFV: sem_sfval ctx stk bc sfv rs m t cs)
     : sem_sstate SVFF ctx stk bc t cs (Sfinal sis sfv)
  | sem_Scond b cond args ifso ifnot
     (SEVAL: eval_scondition ctx cond args = Some b)
     (SELECT: sem_sstate SVFF ctx stk bc t cs (if b then ifso else ifnot))
     : sem_sstate SVFF ctx stk bc t cs (Scond cond args ifso ifnot)
  .

Lemma sem_sstate_run SVFF ctx stk bc ss t cs:
  sem_sstate SVFF ctx stk bc t cs ss ->
  exists sis sfv rs m,
    get_soutcome ctx ss = Some (sout sis sfv)
    /\ sem_sistate ctx sis rs m
    /\ (forall r, SVFF (cf ctx) sfv r -> build_frame sis r)
    /\ sem_sfval ctx stk bc sfv rs m t cs.
Proof.
  induction 1; simpl; try_simplify_someHyps; do 4 eexists; intuition eauto.
Qed.

Local Hint Resolve sem_Sfinal: core.

Lemma run_sem_sstate (SVFF: function -> sfval -> reg -> Prop)
  (ctx: iblock_function_context) ss sis sfv:
  get_soutcome ctx ss = Some (sout sis sfv) ->
  forall rs m stk bc cs t,
  sem_sistate ctx sis rs m ->
  (forall r, SVFF (cf ctx) sfv r -> build_frame sis r) ->
  sem_sfval ctx stk bc sfv rs m t cs ->
  sem_sstate SVFF ctx stk bc t cs ss.
Proof.
  induction ss; simpl; try_simplify_someHyps.
  autodestruct; intros; econstructor; eauto.
  autodestruct; eauto.
Qed.

Model of Symbolic Execution with Continuation Passing Style Parameter k is the continuation, i.e. the sstate construction that will be applied in each execution branch. Its input parameter is the symbolic internal state of the branch.
Notation "'STBIND' X <- A 'IN' B" := (match A with Some X => B | None => Sabort end)
         (at level 200, X name, A at level 100, B at level 200)
         : option_monad_scope.

Fixpoint sexec_rec ib (sis:sistate) (k: sistate -> sstate): sstate :=
  match ib with
  | BF fin _ =>
    STBIND sfv <- sexec_final_sfv fin sis IN
    Sfinal sis sfv
basic instructions
  | Bnop _ => k sis
  | Bop op args res _ =>
     STBIND sis' <- sexec_op op args res sis IN
     k sis'
  | Bload trap chunk addr args aaddr dst _ =>
     STBIND sis' <- sexec_load trap chunk addr args aaddr dst sis IN
     k sis'
  | Bstore chunk addr args sinfo src _ =>
     STBIND sis' <- sexec_store chunk addr args sinfo src sis IN
     k sis'
composed instructions
  | Bseq ib1 ib2 =>
      sexec_rec ib1 sis (fun sis2 => sexec_rec ib2 sis2 k)
  | Bcond cond args ifso ifnot _ =>
      let ifso := sexec_rec ifso sis k in
      let ifnot := sexec_rec ifnot sis k in
      STBIND args <- lmap_sv sis args IN
      Scond cond args ifso ifnot
  end
  .

Definition sexec ib sinit := sexec_rec ib sinit (fun _ => Sabort).

Local Hint Constructors sem_sstate: core.

Local Hint Resolve sexec_op_correct sexec_final_sfv_correct
  sexec_load_correct sexec_store_correct: core.

Lemma intro_False_sem_sstate SVFF ctx stk bc t cs ss: False -> sem_sstate SVFF ctx stk bc t cs ss.
Proof.
destruct 1. Qed.
Local Hint Resolve intro_False_sem_sstate: core.

Lemma sexec_rec_correct (ctx: iblock_function_context) stk bc t s ib rs m rs1 m1 ofin
  (ISTEP: iblock_istep (cge ctx) (csp ctx) (ctx_bc ctx) rs m ib rs1 m1 ofin): forall sis k
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  (CONT: match ofin with
         | None => forall sis', sem_sistate ctx sis' rs1 m1 ->
             (forall r, build_frame sis' r) -> sem_sstate triv_frame ctx stk bc t s (k sis')
         | Some fin => final_step (cge ctx) stk (cf ctx) (csp ctx) rs1 m1 bc fin t s
         end),
  sem_sstate triv_frame ctx stk bc t s (sexec_rec ib sis k).
Proof.
  induction ISTEP; simpl; eauto; intros.
  - (* final *) autodestruct; intuition eauto.
  - (* op *)
    exploit sexec_op_TRIV_alive; eauto.
    intros (sis' & EQ & TRIV); try_simplify_someHyps.
  - (* load *)
    exploit sexec_load_TRIV_alive; eauto.
    intros (sis' & EQ & TRIV); try_simplify_someHyps.
  - (* store *)
     exploit sexec_store_TRIV_alive; eauto.
     intros (sis' & EQ & TRIV); try_simplify_someHyps.
  - (* condition *)
    autodestruct; intros; eauto.
    eapply sem_Scond; eauto.
      * erewrite eval_scondition_eq; eauto.
      * replace (if b then sexec_rec ifso sis k else sexec_rec ifnot sis k)
           with (sexec_rec (if b then ifso else ifnot) sis k);
        try autodestruct; eauto.
Qed.

NB: each concrete execution can be executed on the symbolic state (produced from sexec) (sexec is a correct over-approximation)
Theorem sexec_correct (strict : bool) (ctx: iblock_function_context) sinit bc stk ib t s
  (CTX_BC: ctx_bc ctx = ASSERT strict IN Some bc):
  iblock_step strict (cge ctx) stk (cf ctx) (csp ctx) (crs0 ctx) (cm0 ctx) bc ib t s ->
NB: the two properties below gives the correctness property of an "history invariant"
  sem_sistate ctx sinit (crs0 ctx) (cm0 ctx) ->
  (forall r : reg, build_frame sinit r) ->
  sem_sstate triv_frame ctx stk bc t s (sexec ib sinit).
Proof.
  intros (rs' & m' & fin & ISTEP & FSTEP) SIS TRIVFRAME.
  eapply sexec_rec_correct; simpl; rewrite ?CTX_BC; eauto.
Qed.

Remark that we want to reason modulo "extensionality" wrt Regmap.get about regsets.
Lemma sem_sistate_determ ctx sis rs1 m1 rs2 m2:
  sem_sistate ctx sis rs1 m1 ->
  sem_sistate ctx sis rs2 m2 ->
     (eqlive_reg (build_frame sis) rs2 rs1)
  /\ m1 = m2.
Proof.
  intros H1 H2.
  destruct H1 as (_&MEM1&REG1).
  destruct H2 as (_&MEM2&REG2); simpl in *.
  intuition try congruence.
  intros r. unfold build_frame. destruct (sis r) eqn: EQ; try congruence.
  cut (Some rs2 # r = Some rs1#r).
  { congruence. }
  erewrite <- REG2; eauto.
Qed.


Local Hint Constructors eqlive_stackframes list_forall2: core.
Local Hint Resolve eqlive_reg_update eqlive_reg_monotonic list_nth_z_in: core.

Lemma sem_sfval_equiv rs1 rs2 ctx stk bc sfv m t s:
  sem_sfval ctx stk bc sfv rs1 m t s ->
  (eqlive_reg (sfv_frame (cf ctx) sfv) rs1 rs2) ->
  exists s', sem_sfval ctx stk bc sfv rs2 m t s' /\ eqlive_states s s'.
Proof.
  destruct 1; simpl in *; intros; subst; eexists; split; econstructor; eauto.
  + (* Builtin *)
    unfold regmap_setres; autodestruct;
    intros; try (apply eqlive_reg_update; intros); eapply eqlive_reg_monotonic; intuition subst; eauto.
    inv H2; contradiction.
    all: split; auto; intros r' RES EQ; inv RES.
  + (* Jumptable *)
     eapply eqlive_reg_monotonic; intuition eauto.
Qed.


Few properties on symbolic execution failure

Definition abort_sistate ctx (sis: sistate): Prop :=
  ~(sis.(sis_pre) ctx)
  \/ eval_smem ctx sis.(sis_smem) = None
  \/ exists r sv, sis r = Some sv /\ eval_sval ctx sv = None.

Lemma sem_sistate_exclude_abort ctx sis rs m:
  sem_sistate ctx sis rs m ->
  abort_sistate ctx sis ->
  False.
Proof.
  intros (PRE&MEM&REG); simpl in *.
  intros [ABORT1 | [ABORT2 | (r & sv & FRAME & REG2)]]; try congruence.
  erewrite REG in *; eauto. congruence.
Qed.

Lemma set_sreg_preserv_abort ctx sv dst sis:
  abort_sistate ctx sis ->
  abort_sistate ctx (set_sreg dst sv sis).
Proof.
  unfold abort_sistate; simpl; intros [PRE|[MEM|(r&svr&FRAMEr&REG)]]; auto; [ intuition |].
  destruct (Pos.eq_dec dst r) as [TEST|TEST] eqn: HTEST.
  - subst. erewrite FRAMEr. intuition eauto.
  - right. right. eexists; eexists; rewrite HTEST, FRAMEr. eauto.
Qed.

Lemma sexec_op_preserv_abort ctx op args dest sis sis':
  abort_sistate ctx sis ->
  sexec_op op args dest sis = Some sis' ->
  abort_sistate ctx sis'.
Proof.
  unfold sexec_op; autodestruct; try_simplify_someHyps.
  intros; eapply set_sreg_preserv_abort; eauto.
Qed.

Lemma sexec_load_preserv_abort ctx chunk addr args aaddr dest sis sis' trap:
  abort_sistate ctx sis ->
  sexec_load trap chunk addr args aaddr dest sis = Some sis' ->
  abort_sistate ctx sis'.
Proof.
  unfold sexec_load; autodestruct; try_simplify_someHyps.
  intros; eapply set_sreg_preserv_abort; eauto.
Qed.

Lemma set_smem_preserv_abort ctx sm sis:
  abort_sistate ctx sis ->
  abort_sistate ctx (set_smem sm sis).
Proof.
  unfold abort_sistate; simpl; try tauto.
Qed.

Lemma sexec_store_preserv_abort ctx chunk addr args sinfo src sis sis':
  abort_sistate ctx sis ->
  sexec_store chunk addr args sinfo src sis = Some sis' ->
  abort_sistate ctx sis'.
Proof.
  unfold sexec_store. intros SABORT.
  repeat autodestruct; try_simplify_someHyps.
  intros; eapply set_smem_preserv_abort; eauto.
Qed.

Lemma sem_sstate_Sabort SVFF ctx stk bc t s:
   sem_sstate SVFF ctx stk bc t s Sabort -> False.
Proof.
  intros X; inv X.
Qed.


Section REC_EXACT_PROOF.

Local Hint Resolve sexec_op_preserv_abort sexec_load_preserv_abort
  sexec_store_preserv_abort sem_sistate_exclude_abort sem_sstate_Sabort: core.

Lemma sexec_exclude_abort SVFF ctx stk bc ib t s1: forall sis k
  (SEXEC: sem_sstate SVFF ctx stk bc t s1 (sexec_rec ib sis k))
  (CONT: forall sis', sem_sstate SVFF ctx stk bc t s1 (k sis') -> (abort_sistate ctx sis') -> False)
  (ABORT: abort_sistate ctx sis),
  False.
Proof.
  induction ib; simpl; intros sis k.
  1-5,7: try autodestruct; simpl; eauto; intros _ SEXEC; inv SEXEC; intros; eauto.
  - (* cond *)
    destruct b; eauto.
  - (* seq *)
    intros; eapply IHib1; eauto.
    simpl. eauto.
Qed.

Lemma set_sreg_abort ctx dst sv sis rs m:
  sem_sistate ctx sis rs m ->
  eval_sval ctx sv = None ->
  abort_sistate ctx (set_sreg dst sv sis).
Proof.
  intros (PRE&MEM&REG).
  unfold abort_sistate, build_frame, set_sreg; simpl.
  right; right.
  exists dst; exists sv; destruct (Pos.eq_dec dst dst); simpl; try intuition congruence.
Qed.

Lemma sexec_op_abort ctx sis op args dest sis' rs m
  (EVAL: eval_operation (cge ctx) (csp ctx) op rs ## args m = None)
  (SIS: sem_sistate ctx sis rs m)
  :(sexec_op op args dest sis=Some sis') -> abort_sistate ctx sis'.
Proof.
  unfold sexec_op; autodestruct.
  simpl; try_simplify_someHyps.
  intros; eapply set_sreg_abort; try eassumption.
  simpl. destruct SIS as (PRE&MEM&REG).
  erewrite eval_lmap_sv; simpl; eauto.
  erewrite op_valid_pointer_eq; eauto.
Qed.

Lemma sexec_load_TRAP_abort ctx chunk addr args aaddr dst sis sis' rs m
  (EVAL: forall a,
           eval_addressing (cge ctx) (csp ctx) addr rs ## args = Some a ->
           vmatch_opt (ctx_bc ctx) a aaddr ->
         Mem.loadv chunk m a = None)
  (SIS: sem_sistate ctx sis rs m)
  :(sexec_load TRAP chunk addr args aaddr dst sis = Some sis') -> abort_sistate ctx sis'.
Proof.
  unfold sexec_load; autodestruct.
  simpl; try_simplify_someHyps.
  intros; eapply set_sreg_abort; eauto.
  simpl. destruct SIS as (PRE&MEM&REG).
  erewrite eval_lmap_sv; simpl; eauto.
  try_simplify_someHyps.
  intros; do 2 autodestruct; try_simplify_someHyps.
Qed.

Lemma set_smem_abort ctx sm sis rs m:
  sem_sistate ctx sis rs m ->
  eval_smem ctx sm = None ->
  abort_sistate ctx (set_smem sm sis).
Proof.
  intros (PRE&MEM&REG) NEW.
  unfold abort_sistate; simpl.
  tauto.
Qed.

Lemma sexec_store_abort ctx chunk addr args sinfo src sis sis' rs m
  (EVAL: forall a,
           eval_addressing (cge ctx) (csp ctx) addr rs ## args = Some a ->
           vmatch_opt (ctx_bc ctx) a (store_aaddr sinfo) ->
         Mem.storev chunk m a (rs # src) = None)
  (SIS: sem_sistate ctx sis rs m)
  :sexec_store chunk addr args sinfo src sis = Some sis' -> abort_sistate ctx sis'.
Proof.
  unfold sexec_store; repeat autodestruct.
  simpl in *; intuition; try_simplify_someHyps.
  intros; eapply set_smem_abort; eauto.
  simpl. destruct SIS as (PRE&MEM&REG).
  erewrite eval_lmap_sv; simpl; eauto.
  try_simplify_someHyps.
  intros; erewrite REG; eauto.
  try_simplify_someHyps.
  intros; do 2 autodestruct; eauto.
Qed.

Local Hint Resolve sexec_op_abort sexec_load_TRAP_abort sexec_store_abort sexec_final_sfv_exact: core.

Lemma sexec_rec_exact ctx stk bc ib t s1: forall sis k
  (SEXEC: sem_sstate sfv_frame ctx stk bc t s1 (sexec_rec ib sis k))
  rs m
  (SIS: sem_sistate ctx sis rs m)
  (CONT: forall sis', sem_sstate sfv_frame ctx stk bc t s1 (k sis') ->
  (abort_sistate ctx sis') -> False),
     match iblock_istep_run (cge ctx) (csp ctx) (ctx_bc ctx) ib rs m with
     | Some (out rs' m' (Some fin)) =>
        exists s2, final_step (cge ctx) stk (cf ctx) (csp ctx) rs' m' bc fin t s2
                   /\ eqlive_states s1 s2
     | Some (out rs' m' None) => exists sis', (sem_sstate sfv_frame ctx stk bc t s1 (k sis'))
                                              /\ (sem_sistate ctx sis' rs' m')
     | None => False
     end.
Proof.
  induction ib; simpl; intros sis k;
  try (autodestruct; simpl; [ try_simplify_someHyps | intros _ SEXEC; inv SEXEC ]);
  intros; eauto.
  - (* final *)
    inv SEXEC.
    intros; exploit (sem_sistate_determ ctx sis rs m); eauto.
    intros (REG&MEM); subst.
    exploit (sem_sfval_equiv rs0 rs); eauto.
    intros (s2 & EQUIV & SFV'); eauto.
  - (* Bop *)
     autodestruct; eauto.
  - (* Bload *)
    destruct trap.
    + repeat autodestruct.
      { eexists; split; eauto.
        eapply sexec_load_correct; eauto.
        econstructor; eauto. }
      all:
        intros; apply CONT with (sis':=s); auto;
        eapply sexec_load_TRAP_abort; eauto;
        intros; try_simplify_someHyps;
        exfalso; auto.
    + repeat autodestruct.
      all:eexists; split; eauto;
          eapply sexec_load_correct; eauto;
          solve [econstructor; eauto; intros; congruence
                |rewrite EQ0; discriminate 1
                |rewrite EQ2; injection 1 as ->; auto].
  - repeat autodestruct; eauto.
    all:
      intros; apply CONT with (sis':=s); auto;
      eapply sexec_store_abort; eauto;
      intros; try_simplify_someHyps; congruence.
  - (* Bseq *)
    exploit IHib1; eauto. clear sis SEXEC SIS.
    { simpl; intros; eapply sexec_exclude_abort; eauto. }
    destruct (iblock_istep_run _ _ _ _ _) eqn: ISTEP; auto.
    destruct o.
    destruct _fin eqn: OFIN; simpl; eauto.
    intros (sis1 & SEXEC1 & SIS1).
    exploit IHib2; eauto.
  - (* Bcond *)
    inv SEXEC.
    erewrite eval_scondition_eq in SEVAL; eauto.
    rewrite SEVAL.
    destruct b.
    + exploit IHib1; eauto.
    + exploit IHib2; eauto.
Qed.

NB: each execution of a symbolic state (produced from sexec) represents a concrete execution (sexec is exact).
Theorem sexec_exact (strict:bool) (ctx:iblock_function_context) stk bc ib t s1 sinit rs m
  (CTX_BC: ctx_bc ctx = ASSERT strict IN Some bc):
  sem_sistate ctx sinit rs m ->
  sem_sstate sfv_frame ctx stk bc t s1 (sexec ib sinit) ->
  exists s2, iblock_step strict (cge ctx) stk (cf ctx) (csp ctx) rs m bc ib t s2
             /\ eqlive_states s1 s2.
Proof.
  intros; exploit sexec_rec_exact; eauto.
  repeat autodestruct; simpl; try tauto.
  - intros D1 D2 ISTEP (s2 & FSTEP & EQSTEP); subst.
    rewrite CTX_BC in ISTEP.
    eexists; split; eauto.
    repeat eexists; eauto.
    erewrite iblock_istep_run_equiv; eauto.
  - intros D1 D2 ISTEP (sis & SEXEC & _); subst.
    inversion SEXEC.
Qed.

End REC_EXACT_PROOF.


Simulation properties on internal states


Inductive svident_simu ctx: (sval + ident) -> (sval + ident) -> Prop :=
  | Sleft_simu sv1 sv2
      (SIMU: sval_equiv ctx sv1 sv2)
      :svident_simu ctx (inl sv1) (inl sv2)
  | Sright_simu id1 id2
      (IDSIMU: id1 = id2)
      :svident_simu ctx (inr id1) (inr id2)
  .

Definition bargs_simu ctx (args1 args2: list (builtin_arg sval)): Prop :=
  eval_list_builtin_sval ctx args1 = eval_list_builtin_sval ctx args2.

Inductive sfv_simu ctx: sfval -> sfval -> Prop :=
  | Sgoto_simu pc: sfv_simu ctx (Sgoto pc) (Sgoto pc)
  | Scall_simu sig ros1 ros2 args1 args2 r pc
      (SVID: svident_simu ctx ros1 ros2)
      (ARGS: eval_list_sval ctx args1 = eval_list_sval ctx args2)
      :sfv_simu ctx (Scall sig ros1 args1 r pc) (Scall sig ros2 args2 r pc)
  | Stailcall_simu sig ros1 ros2 args1 args2
      (SVID: svident_simu ctx ros1 ros2)
      (ARGS: eval_list_sval ctx args1 = eval_list_sval ctx args2)
      :sfv_simu ctx (Stailcall sig ros1 args1) (Stailcall sig ros2 args2)
  | Sbuiltin_simu ef lba1 lba2 br pc
      (BARGS: bargs_simu ctx lba1 lba2)
      :sfv_simu ctx (Sbuiltin ef lba1 br pc) (Sbuiltin ef lba2 br pc)
  | Sjumptable_simu sv1 sv2 lpc
      (VAL: eval_sval ctx sv1 = eval_sval ctx sv2)
      :sfv_simu ctx (Sjumptable sv1 lpc) (Sjumptable sv2 lpc)
  | simu_Sreturn osv1 osv2
      (OPT:optsv_simu ctx osv1 osv2)
      :sfv_simu ctx (Sreturn osv1) (Sreturn osv2)
.

Definition sistate_simu ctx (sis1 sis2:sistate): Prop :=
  forall rs1 m, sem_sistate ctx sis1 rs1 m ->
  exists rs2, sem_sistate ctx sis2 rs2 m
  /\ eqlive_reg (build_frame sis1) rs1 rs2.


Builder for the target initial state (with invariant tree in sreg

Applying symbolic invariants


Substitutions in parallel symbolic invariants We apply it only on a source sistate: subst should never return None. Below, we enforce here a sval result (instead of an option sval) to simplify proofs.

Fixpoint sv_subst (substm: smem) (subst: reg -> option sval) (sv: sval): sval :=
  match sv with
  | Sinput r _ =>
      match subst r with
      | Some sv' => sv'
      | None => fSinput r
      end
  | Sop op lsv _ =>
     let lsv' := lsv_subst substm subst lsv in
     fSop op lsv'
  | Sfoldr op lsv sv0 _ =>
     let lsv' := lsv_subst substm subst lsv in
     let sv0' := sv_subst substm subst sv0 in
     fSfoldr op lsv' sv0'
  | Sload sm trap chunk addr lsv aaddr _ =>
     let sm' := sm_subst substm subst sm in
     let lsv' := lsv_subst substm subst lsv in
     fSload sm' trap chunk addr lsv' aaddr
  end
with lsv_subst (substm: smem) (subst: reg -> option sval) (lsv0: list_sval): list_sval :=
  match lsv0 with
  | Snil _ => fSnil
  | Scons sv lsv _ =>
    let sv' := sv_subst substm subst sv in
    let lsv' := lsv_subst substm subst lsv in
    fScons sv' lsv'
  end
with sm_subst substm (subst: reg -> option sval) (sm0: smem): smem :=
  match sm0 with
  | Sinit _ => substm
  | Sstore sm chunk addr lsv sinfo sv _ =>
     let sm' := sm_subst substm subst sm in
     let lsv' := lsv_subst substm subst lsv in
     let sv' := sv_subst substm subst sv in
     fSstore sm' chunk addr lsv' sinfo sv'
  end.

Lemma sv_subst_correct rs m ctx substm subst sv:
  (eval_smem ctx substm = Some m) ->
  (forall r, eval_osv ctx (subst r) = Some (rs#r)) ->
  eval_sval ctx (sv_subst substm subst sv) = eval_sval (Bcctx (cge ctx) (csp ctx) rs (m, ctx_bc ctx)) sv.
Proof.
  intros EVAL_MEM EVAL_REG.
  induction sv using sval_mut with
    (P0:=(fun lsv => eval_list_sval ctx (lsv_subst substm subst lsv) =
      eval_list_sval (Bcctx (cge ctx) (csp ctx) rs (m, ctx_bc ctx)) lsv))
    (P1:=(fun sm => eval_smem ctx (sm_subst substm subst sm) =
      eval_smem (Bcctx (cge ctx) (csp ctx) rs (m, ctx_bc ctx)) sm));
    simpl; try (rewrite IHsv); try (rewrite IHsv0); try (rewrite IHsv1); eauto.
  - generalize (EVAL_REG r); autodestruct; simpl; try_simplify_someHyps.
  - autodestruct. intros; eapply op_valid_pointer_eq; eauto.
  - do 2 autodestruct. intros; eapply fold_right_ext.
    intros; destruct y; auto.
    eapply op_valid_pointer_eq; eauto.
Qed.

Lemma sv_subst_ext_equiv sv: forall sm sis,
  sv_subst sm (fun r => Some (ext sis r)) sv = sv_subst sm sis sv.
Proof.
  induction sv using sval_mut
  with (P0 := fun lsv => forall sm sis, lsv_subst sm (fun r => Some (ext sis r)) lsv =
          lsv_subst sm sis lsv)
       (P1 := fun sm => forall sm0 sis, sm_subst sm0 (fun r => Some (ext sis r)) sm =
          sm_subst sm0 sis sm); simpl; auto.
  all: intros; rewrite IHsv; try rewrite IHsv0; try rewrite IHsv1; auto.
Qed.

we build the regset obtained by evaluating in ctx the result of sis on si
Definition eval_subst_si ctx (sis:sistate) := eval_map_sreg ctx (sv_subst sis.(sis_smem) sis).

Lemma eval_subst_si_correct ctx sis (si:fpasv) sv r:
  si r = Some sv ->
  (eval_subst_si ctx sis si)#r =
       match eval_sval ctx (sv_subst sis.(sis_smem) sis sv) with
       | Some v => v
       | None => (crs0 ctx)#r
       end.
Proof eval_map_sreg_correct_some ctx (sv_subst sis.(sis_smem) sis) si sv r.

Empty symbolic state and related properties
Program Definition sis_empty: sistate :=
  {| sis_pre := fun _ => True;
     sis_sreg := fun r => Some (fSinput r);
     sis_smem := fSinit
|}.
Next Obligation.
  split; auto.
Qed.

Lemma sis_ok_empty ctx:
  sis_ok ctx sis_empty.
Proof.
  unfold sis_empty; constructor; simpl; try congruence; auto.
  intros r sv HINV; inv HINV; simpl; congruence.
Qed.
Global Hint Resolve sis_ok_empty: sempty.

Lemma sv_subst_sis_empty ctx sv:
  eval_sval ctx (sv_subst fSinit sis_empty sv) = eval_sval ctx sv.
Proof.
  induction sv using sval_mut with
    (P0 := fun lsv => eval_list_sval ctx (lsv_subst fSinit sis_empty lsv) =
      eval_list_sval ctx lsv)
    (P1 := fun sm => eval_smem ctx (sm_subst fSinit sis_empty sm) = eval_smem ctx sm);
    intros; unfold ext; try rewrite H; simpl in *; eauto.
  all: erewrite IHsv; auto; erewrite IHsv0; auto; erewrite IHsv1; auto.
Qed.
Global Hint Resolve sv_subst_sis_empty: sempty.
Global Hint Rewrite sv_subst_sis_empty: sempty.

Variant of the si_ok definition with substitution
Definition si_ok_subst (sis: sistate) (si: fpasv) ctx: Prop :=
  forall sv, List.In sv (fpa_ok si) -> eval_sval ctx (sv_subst (sis_smem sis) sis sv) <> None.

Lemma si_ok_empty ctx:
  si_ok ctx si_empty.
Proof.
  unfold si_ok; simpl; contradiction.
Qed.

Lemma si_ok_subst_empty ctx sis:
  si_ok_subst sis si_empty ctx.
Proof.
  unfold si_ok_subst; simpl; contradiction.
Qed.

Lemma si_ok_subst_sis_empty_si_ok ctx si:
  si_ok_subst sis_empty si ctx ->
  si_ok ctx si.
Proof.
  unfold si_ok_subst, si_ok. intros H sv HIN.
  rewrite <- sv_subst_sis_empty; eauto.
Qed.
Global Hint Resolve si_ok_empty si_ok_subst_empty si_ok_subst_sis_empty_si_ok: sempty.

Lemma si_ok_subst_hd lseq: forall ctx r sv (si: fpasv) sis
  (OK_SUBST: si_ok_subst sis (exec_seq lseq (si_set r sv si)) ctx),
  si_ok_subst sis (si_set r sv si) ctx.
Proof.
  induction lseq; simpl; auto; intros until sis.
  autodestruct; intros; subst.
  apply IHlseq in OK_SUBST.
  intros sv' HIN; eapply OK_SUBST.
  unfold si_set, si_apply; simpl; auto.
Qed.

ir_subst with the actual substitution using si
Definition ir_subst_si (sis: sistate) (si: fpasv) (ir: ireg): option sval :=
  if ir.(force_input) then sis ir else Some (sv_subst (sis_smem sis) sis (ext si ir)).

More lemmas about lmap_sv and ir_subst_si

Lemma lsv_subst_sis_empty args: forall ctx si,
  list_sval_equiv ctx (lsv_subst (sis_smem sis_empty) sis_empty (lsvof (ext si) args))
    (lsvof (ext si) args).
Proof.
  intros ctx si. generalize (lsvof (ext si) args). intro sl. (* The lemma can be generalized ? *)
  induction sl; simpl. reflexivity.
  rewrite <- IHsl, sv_subst_sis_empty. reflexivity.
Qed.
Global Hint Resolve lsv_subst_sis_empty: sempty.
Global Hint Rewrite lsv_subst_sis_empty: sempty.

Lemma ir_subst_si_sv_subst sis si ir sv:
  ir_subst_si sis si ir = Some sv ->
  sv = sv_subst (sis_smem sis) sis (ir_subst (ext si) ir).
Proof.
  unfold ir_subst_si, ir_subst; autodestruct; simpl.
  intros _ ->. reflexivity.
Qed.

Lemma lmap_sv_lsv_subst args: forall ctx l (si: fpasv) sis,
  lmap_sv (ir_subst_si sis si) args = Some l ->
  list_sval_equiv ctx (lsv_subst (sis_smem sis) sis (lsvof (ext si) args)) l.
Proof.
  induction args; simpl.
  - intros; inv H; simpl; reflexivity.
  - intros until sis.
    autodestruct. intro IR. apply ir_subst_si_sv_subst in IR as ->.
    autodestruct. intros IS. eapply IHargs in IS as ->.
    injection 1 as <-. reflexivity.
Qed.

Lemma lmap_sv_input_subst args: forall (sis: sistate) (si: fpasv),
  lmap_sv sis args =
  lmap_sv (ir_subst_si sis si) (ir_input_of args).
Proof.
  induction args; intros; simpl; auto.
  unfold input, ir_subst_si in *; simpl.
  autodestruct. erewrite IHargs; auto.
Qed.

Lemma eval_ir_subst_si_ok_nofail sis si ctx ir sv
  (SIS_OK: sis_ok ctx sis)
  (SI_OK : si_ok_subst sis si ctx)
  (SUBST : ir_subst_si sis si ir = Some sv):
  alive (eval_sval ctx sv).
Proof.
  apply OK_SREG in SIS_OK.
  revert SUBST; unfold ir_subst_si; autodestruct; intro.
  - intros SIS. apply SIS_OK in SIS. exact SIS.
  - injection 1 as <-.
    assert (CASE: let C := is_input (ext si ir) in C \/ ~C)
      by (destruct ext; simpl; solve [left; constructor|right; inversion 1]).
    case CASE as [[?]|NIN].
    + simpl; autodestruct; simpl.
      * intros SIS. apply SIS_OK in SIS. exact SIS.
      * congruence.
    + apply SI_OK. apply fpa_wf with (r := regof ir). 2:exact NIN.
      revert NIN; unfold ext; autodestruct.
      intros _ C; exfalso; apply C; constructor.
Qed.

Lemma eval_lmap_sv_input_nofail (lr: list reg): forall ctx sis si lsv
  (SOK: sis_ok ctx sis)
  (LMAP: lmap_sv (ir_subst_si sis si) (ir_input_of lr) = Some lsv),
  eval_list_sval ctx lsv = None -> False.
Proof.
  induction lr.
  - simpl; intros; try_simplify_someHyps.
  - intros; simplify_option.
    inv SOK.
    intros. generalize EQ1; clear EQ1.
    unfold ir_subst_si; simpl.
    intros; eapply OK_SREG0 in EQ; eauto.
Qed.

Transfer an internal state by applying the invariant and updating the pre-condition. Here, sis_input_init indicates the default option sval.
Program Definition tr_sis (sis: sistate) (si: fpasv) (sis_input_init: bool): sistate :=
  {| sis_pre := fun ctx => sis.(sis_pre) ctx
                        /\ sreg_ok ctx sis
                        /\ si_ok_subst sis si ctx;
     sis_sreg := fun r => match si r with
                          | Some sv => Some (sv_subst sis.(sis_smem) sis sv)
                          | None => if sis_input_init then Some (fSinput r) else None
                          end;
     sis_smem := sis.(sis_smem) |}.
Next Obligation.
  unfold sreg_ok, si_ok_subst; intuition eauto;
  try (rewrite eval_sval_preserved in * || rewrite <- eval_sval_preserved in *; eauto);
  try (apply sis_pre_preserved; auto).
Qed.

Lemma tr_sis_morph [ctx sis fp0 fp1 sinit]:
  fpa_eq fp0 fp1 ->
  sistate_eq ctx (tr_sis sis fp0 sinit) (tr_sis sis fp1 sinit).
Proof.
  intros [FPA_OK_EQ FPA_REG_EQ]; constructor.
  - simpl; unfold si_ok_subst; setoid_rewrite FPA_OK_EQ; reflexivity.
  - intro r; simpl. unfold si_apply. rewrite FPA_REG_EQ. reflexivity.
  - reflexivity.
Qed.

Lemma tr_sis_okpreserv ctx sis si sinit:
  sis_ok ctx (tr_sis sis si sinit) -> sis_ok ctx sis.
Proof.
  intros [(PRE & REG & _) MEM _]; simpl in *; constructor; auto.
Qed.

Lemma tr_sis_ok_indep_sinit ctx sis si:
  sis_ok ctx (tr_sis sis si true) <-> sis_ok ctx (tr_sis sis si false).
Proof.
  split; intros []; constructor; simpl in *; try assumption.
  all:intros r sv; specialize (OK_SREG0 r); simpl in OK_SREG0;
      autodestruct; intro; try solve [apply OK_SREG0].
  - injection 1 as <-; discriminate 1.
Qed.

Definition tr_sis_ok ctx sis (si: fpasv): Prop :=
  sis_ok ctx sis -> si_ok_subst sis si ctx.

Lemma ok_tr_sis ctx sis (si: fpasv) sinit
  (SI_DONT_TRAP: tr_sis_ok ctx sis si)
  :sis_ok ctx (tr_sis sis si sinit) <-> sis_ok ctx sis.
Proof.
  split. apply tr_sis_okpreserv.
  intro OK; generalize OK; inv OK; econstructor; eauto;
  unfold tr_sis_ok, si_ok_subst in *; simpl in *; intuition.
  intros r sv GETR SEVAL; destruct (si r) as [sv0|] eqn:EQSI;
  [| destruct (sinit) eqn:SINIT ]; inv GETR; [| simpl in SEVAL; congruence ].
  destruct (is_input_dec sv0) as [X|X].
  { inv X; simpl in *.
    destruct (sis r0) eqn: SISr0; simpl in *; try congruence.
    exploit OK_SREG0; intuition eauto.
  }
  eapply H; eauto.
Qed.

Lemma tr_sis_correct sis (si: fpasv) ctx rs m
  (SIS: sem_sistate ctx sis rs m)
  (TRIVFRAME: forall r, build_frame sis r)
  (SI_DONT_TRAP: tr_sis_ok ctx sis si)
  :sem_sistate ctx (tr_sis sis si false) (eval_subst_si ctx sis si) m
    /\ match_si (Bcctx (cge ctx) (csp ctx) rs (m, ctx_bc ctx)) si (eval_subst_si ctx sis si).
Proof.
  exploit sem_sis_ok; eauto.
  rewrite <- ok_tr_sis with (sinit:=false); eauto.
  intros [(PRE&REGOK&trREGOK1) MEMOK trREGOK2]. simpl in *.
  unfold sem_sistate, build_frame, match_si in *; simpl in *.
  destruct SIS as (_&MEM&REG).
  intuition eauto.
  * intros r sv H; generalize (trREGOK2 r). clear trREGOK2; intros trREGOK2.
    destruct (si r) eqn: SIr; simpl in *; try_simplify_someHyps.
    intros; erewrite eval_subst_si_correct; eauto.
    try_simplify_someHyps.
    autodestruct.
    intros; exploit trREGOK2; intuition eauto.
  * intros sv H H0; exploit trREGOK1; eauto.
    exploit (sv_subst_correct rs m ctx sis.(sis_smem) sis); eauto.
    { intros r0. unfold eval_osv; autodestruct.
      intros; erewrite REG; eauto. }
    intros X; rewrite !X; intuition eauto.
  * intros r sv H; generalize (trREGOK2 r). rewrite H; clear trREGOK2; intros trREGOK2.
    erewrite eval_subst_si_correct; eauto.
    exploit (sv_subst_correct rs m ctx sis.(sis_smem) sis); eauto.
    { intros r0. unfold eval_osv; autodestruct.
      intros; erewrite REG; eauto. }
    intros X; rewrite !X; intuition eauto.
    autodestruct.
    intros; exploit trREGOK2; intuition eauto.
Qed.

Operations on two sistate

Section TwoStatesOp.

Definition sis_get_ireg (sis0 sis1 : sistate) (r : ireg) : option sval :=
  if r.(force_input) then sis0 r else sis1 r.

Lemma sis_get_ireg_nofail ctx sis0 sis1 r sv
  (SOK0 : sis_ok ctx sis0)
  (SOK1 : sis_ok ctx sis1)
  (GET: sis_get_ireg sis0 sis1 r = Some sv):
  alive (eval_sval ctx sv).
Proof.
  unfold sis_get_ireg in GET; case force_input in GET; [inversion SOK0|inversion SOK1];
  apply OK_SREG0 in GET; exact GET.
Qed.

Lemma sis_get_ireg_lmap_nofail ctx sis0 sis1 rs lsv
  (SOK0 : sis_ok ctx sis0)
  (SOK1 : sis_ok ctx sis1)
  (GET: lmap_sv (sis_get_ireg sis0 sis1) rs = Some lsv):
  alive (eval_list_sval ctx lsv).
Proof.
  revert lsv GET. induction rs as [|r]; simpl.
  - injection 1 as <-; simpl; congruence.
  - do 2 autodestruct; injection 3 as <-; simpl; repeat autodestruct; intros C; exfalso.
    + eapply IHrs; eauto.
    + eapply sis_get_ireg_nofail in EQ0; eauto.
Qed.

Definition sis_set_rop (r : reg) (rop : root_op) (lir : list ireg) (sis0 sis1 : sistate): option sistate :=
  SOME lsv <- lmap_sv (sis_get_ireg sis0 sis1) lir IN
  Some (set_sreg r (root_apply rop lsv sis0.(sis_smem)) sis1).

Definition sis_get_ival (sis0 sis1 : sistate) (iv : ival) : option sval :=
  match iv with
  | Ireg ir => sis_get_ireg sis0 sis1 ir
  | Iop rop args => SOME lsv <- lmap_sv (sis_get_ireg sis0 sis1) args IN
                    Some (root_apply rop lsv sis0.(sis_smem))
  end.

Definition sis_set_ival (r : reg) (iv : ival) (sis0 sis1 : sistate): option sistate :=
  SOME sv <- sis_get_ival sis0 sis1 iv IN
  Some (set_sreg r sv sis1).

Fixpoint sis_set_seq (l : list (reg*ival)) (sis0 sis1 : sistate): option sistate :=
  match l with
  | nil => Some sis1
  | (r, iv) :: l =>
      SOME sis2 <- sis_set_ival r iv sis0 sis1 IN
      sis_set_seq l sis0 sis2
  end.

Lemma sis_set_seq_okpreserv ctx l sis0 sis1 sis2:
  sis_set_seq l sis0 sis1 = Some sis2 ->
  sis_ok ctx sis2 -> sis_ok ctx sis1.
Proof.
  intros SEQ ?; revert sis1 SEQ; induction l as [|(r, iv)]; simpl; intro sis1.
  - injection 1 as ->; assumption.
  - unfold sis_set_ival; autodestruct; intros _ SEQ.
    apply IHl in SEQ.
    apply ok_set_sreg in SEQ.
    apply SEQ.
Qed.

End TwoStatesOp.

Source symbolic state builder and register/memory set functions


Section InvariantApplication.

  Variable csix : invariants.
  Variable ctx : iblock_common_context.

sis_history is obtained by applying the history invariants to the inital state

Definition sis_history := tr_sis sis_empty (history csix) true.

Simplification of sv_subst with match_sreg
Lemma sv_subst_pre_history sv
  (MSreg : match_sreg ctx (history csix) (crs0 ctx))
  :eval_sval ctx (sv_subst fSinit sis_history sv) = eval_sval ctx sv.
Proof.
  induction sv using sval_mut with
    (P0 := fun lsv => eval_list_sval ctx (lsv_subst fSinit sis_history lsv) =
      eval_list_sval ctx lsv)
    (P1 := fun sm => eval_smem ctx (sm_subst fSinit sis_history sm) = eval_smem ctx sm);
    intros; simpl in *; subst; auto.
  1: autodestruct; autorewrite with sempty; eauto.
  all: erewrite IHsv; auto; erewrite IHsv0; auto; erewrite IHsv1; auto.
Qed.

sis_target is obtained by applying the gluing invariants to sis_history

Definition sis_target :=
  let sis_H := sis_history in
  tr_sis sis_H (glue csix) false.

Lemma sis_target_ok_sis_history:
  sis_ok ctx sis_target -> sis_ok ctx sis_history.
Proof.
  apply tr_sis_okpreserv.
Qed.

Lemma sis_target_correct rs:
  match_invs ctx csix rs ->
  sem_sistate ctx sis_target rs (cm0 ctx).
Proof.
  unfold sem_sistate; simpl; unfold si_ok_subst.
  intros ((OKh & MSREGh) & (OKg & MSREGg)); intuition eauto.
  - intros r sv HINV; inv HINV; simpl; congruence.
  - autorewrite with sempty in H0; apply OKh in H; congruence.
  - unfold match_sreg, ext in *.
    intros r sv; autodestruct; intros GETR HINV; inv HINV;
    autorewrite with sempty; simpl; try congruence.
    erewrite MSREGh; eauto; congruence.
  - exploit sv_subst_pre_history; eauto.
    unfold sis_history; simpl; intros ESVEQ; rewrite ESVEQ in H0.
    apply OKg in H; congruence.
  - intros r sv; autodestruct; intros GETR HINV; inv HINV.
    exploit sv_subst_pre_history; eauto.
    unfold sis_history; simpl; intros ESVEQ; rewrite ESVEQ.
    erewrite MSREGg; eauto.
Qed.

sis_source has the same registers as sis_history but with the more restrictive precondition of sis_target

Program Definition sis_source :=
  let sis_H := sis_history in
  let sis_HG := sis_target in
  {| sis_pre := sis_pre sis_HG; sis_sreg := sis_sreg sis_H; sis_smem := fSinit |}.
Next Obligation.
  split; unfold sreg_ok; intros ((_ & H1 & H2) & H3 & H4); unfold si_ok_subst; repeat split; intros.
  all: erewrite <- eval_sval_preserved || erewrite eval_sval_preserved; eauto.
Qed.

Lemma sis_source_ok_sis_target:
  sis_ok ctx sis_source <-> sis_ok ctx sis_target.
Proof.
  unfold sis_source, sis_target; split.
  - intros [(PRE & REG & SUBST) MEM _]; apply <- ok_tr_sis.
    + constructor; assumption.
    + intro. exact SUBST.
  - intros []; constructor; simpl in *; tauto.
Qed.

Lemma sis_source_ok_sis_history:
  sis_ok ctx sis_source -> sis_ok ctx sis_history.
Proof.
  rewrite sis_source_ok_sis_target; exact sis_target_ok_sis_history.
Qed.

Lemma sis_source_correct:
  match_si ctx (history csix) (crs0 ctx) ->
  si_ok ctx (glue csix) ->
  sem_sistate ctx sis_source (crs0 ctx) (cm0 ctx).
Proof.
  unfold sem_sistate; simpl; unfold si_ok_subst.
  intros (OKh & MSREGh) OKg; intuition eauto.
  - intros r sv HINV; inv HINV; simpl; congruence.
  - autorewrite with sempty in H0; apply OKh in H; congruence.
  - unfold match_si, match_sreg, ext in *.
    intros r sv; autodestruct; intros GETR HINV; inv HINV;
    autorewrite with sempty; simpl; try congruence.
    erewrite MSREGh; eauto; congruence.
  - exploit sv_subst_pre_history; eauto.
    unfold sis_history; simpl; intros ESVEQ; rewrite ESVEQ in H0.
    apply OKg in H; congruence.
  - intros r sv; autodestruct; intros GETR HINV; inv HINV;
    autorewrite with sempty; simpl; try congruence.
    erewrite MSREGh; eauto; congruence.
Qed.

End InvariantApplication.


A notion of symbolic equality over substitution to be used in union of invariants.
Definition symbolic_eq (ctx: iblock_common_context) sis (sv1 sv2: sval): bool :=
  match eval_sval ctx (sv_subst (sis_smem sis) sis sv1),
        eval_sval ctx (sv_subst (sis_smem sis) sis sv2) with
  | Some v1, Some v2 => Val.eq v1 v2
  | Some _, None | None, Some _ => false
  | None, None => true
  end.

Union between trees of invariants (for jumptables)
Definition most_defined_sv (ctx: iblock_common_context) sis (sv1 sv2: sval): option sval :=
  if symbolic_eq ctx sis sv1 sv2 then Some sv2 else None.

Lemma most_defined_sv_preserved ctx sis:
  most_defined_sv (bcctx1 ctx) sis = most_defined_sv (bcctx2 ctx) sis.
Proof.
  unfold most_defined_sv, symbolic_eq.
  repeat (apply functional_extensionality; intros).
  rewrite !eval_sval_preserved; reflexivity.
Qed.

Program Definition union2_si_gen (ctx: iblock_common_context) sis (si1 si2: fpasv):
  { res: option fpasv |
    match res with
    | Some si =>
       fpa_ok si = List.app (fpa_ok si1) (fpa_ok si2)
       /\ PTree.combine_mostdef (most_defined_sv ctx sis) (fpa_reg si1) (fpa_reg si2) = Some (fpa_reg si)
    | None =>
       PTree.combine_mostdef (most_defined_sv ctx sis) (fpa_reg si1) (fpa_reg si2) = None
    end } :=
   SOME pt <- PTree.combine_mostdef (most_defined_sv ctx sis) (fpa_reg si1) (fpa_reg si2) IN
   Some
    {| fpa_ok := List.app (fpa_ok si1) (fpa_ok si2);
       fpa_reg := pt |}.
Next Obligation.
  apply in_or_app.
  exploit PTree.gcombine_mostdef_ok; eauto.
  unfold PTree.f_mostdef, most_defined_sv; repeat autodestruct;
  intros; inv H1; (left; eauto; fail) || right; eauto.
Qed.

Definition union2_si (ctx: iblock_common_context) sis (si1 si2: fpasv): option fpasv :=
  ` (union2_si_gen ctx sis si1 si2).

Lemma fpa_ok_union2_si ctx sis si1 si2 si:
  union2_si ctx sis si1 si2 = Some si ->
  fpa_ok si = List.app (fpa_ok si1) (fpa_ok si2).
Proof.
  unfold union2_si.
  destruct (union2_si_gen ctx sis si1 si2). simpl in *; intros; subst.
  intuition.
Qed.

Lemma fpa_reg_union2_si ctx sis si1 si2 si:
  union2_si ctx sis si1 si2 = Some si ->
  PTree.combine_mostdef (most_defined_sv ctx sis) (fpa_reg si1) (fpa_reg si2) = Some (fpa_reg si).
Proof.
  unfold union2_si.
  destruct (union2_si_gen ctx sis si1 si2). simpl in *; intros; subst.
  intuition.
Qed.

Fixpoint union_si (ctx: iblock_common_context) sis (lcsi: list csasv): option fpasv :=
  match lcsi with
  | nil => Some si_empty
  | csi :: l =>
      SOME si2 <- union_si ctx sis l IN
      union2_si ctx sis (siof csi) si2
  end.

Lemma union_si_single: forall ctx sis csi si,
  union_si ctx sis [csi] = Some si ->
  fpa_eq (siof csi) si.
Proof.
  unfold siof; intros.
  simpl in H; unfold union2_si in H.
  destruct (union2_si_gen ctx sis csi si_empty); simpl in *; subst.
  rewrite PTree.gcombine_mostdef_only_l in y.
  rewrite app_nil_r in y. destruct y; simpl in *.
  constructor; simpl.
  - split; intros; rewrite <- H || rewrite H; auto.
  - inv H0; reflexivity.
Qed.

Lemma fpa_ok_union_si (lcsi: list csasv): forall ctx sis csi sv si,
  In csi lcsi ->
  In sv (fpa_ok csi) ->
  union_si ctx sis lcsi = Some si ->
  In sv (fpa_ok si).
Proof.
  induction lcsi; simpl; try contradiction.
  intros ctx sis csi sv si HIN INCSI. autodestruct; intros HSI UNION2.
  destruct HIN; subst;
  apply fpa_ok_union2_si in UNION2; unfold siof in *; simpl in *.
  - rewrite UNION2. apply in_or_app; auto.
  - assert (EQFIN: In sv (fpa_ok f)) by eauto.
    rewrite UNION2. apply in_or_app; auto.
Qed.

Ltac destruct_sir sir :=
  destruct sir eqn:SIR;
  [| exploit PTree.gcombine_mostdef_none_rev; eauto; simpl in *; intuition congruence ].

Lemma fpa_reg_union_si (lcsi: list csasv): forall ctx sis csi si r sv1
  (HIN: In csi lcsi)
  (UNION: union_si ctx sis lcsi = Some si)
  (CSIR: csi ! r = Some sv1),
  exists sv2, si r = Some sv2
      /\ eval_sval ctx (sv_subst (sis_smem sis) sis sv1) =
         eval_sval ctx (sv_subst (sis_smem sis) sis sv2).
Proof.
  induction lcsi; simpl; try contradiction.
  intros until sv1; autodestruct; intros.
  destruct HIN; subst;
  apply fpa_reg_union2_si in UNION.
  - destruct_sir (si r).
    exists s; split; auto.
    exploit PTree.gcombine_mostdef_ok; eauto.
    unfold PTree.f_mostdef, most_defined_sv; simpl.
    rewrite CSIR; autodestruct. unfold symbolic_eq. repeat autodestruct.
    intros; destruct (Val.eq _ _); subst; inv EQ0; inv H; auto.
  - exploit IHlcsi; eauto. unfold si_apply; intros (sv2 & FR & SR).
    destruct_sir (si r).
    exploit PTree.gcombine_mostdef_ok; eauto.
    unfold PTree.f_mostdef, most_defined_sv; simpl.
    rewrite FR; autodestruct.
    + unfold symbolic_eq. repeat autodestruct.
      * intros; destruct (Val.eq _ _); subst; inv EQ0; inv H0.
        exists s; split; auto. rewrite SR, EQ1; reflexivity.
      * intros; inv H0. exists s; split; auto. rewrite SR, EQ0; reflexivity.
    + intros; inv H0. exists s; split; auto.
Qed.

Definition ctx_switch_prop ctx1 ctx2 :=
  ctx1 = ctx2 \/ (exists ctx, (ctx1 = bcctx1 ctx) /\ (ctx2 = bcctx2 ctx)).

Ltac destruct_ctx_sw hctx := destruct hctx as [CTXEQ|[ctx [HCTX1 HCTX2]]]; subst; auto.

Lemma match_sreg_union_si_H (lcsi: list csasv): forall
  ctx1 ctx2 sis rs m csi (si: fpasv)
  (HCTX: ctx2 = {| cge := cge ctx1; csp := csp ctx1; crs0 := rs; cmbc := (m, ctx_bc ctx1) |})
  (HIN: In csi lcsi)
  (UNION: union_si ctx1 sis lcsi = Some si)
  (SIS: sem_sistate ctx1 sis rs m)
  (LIVEOK: forall r : reg, build_frame sis r)
  (MATCH: match_sreg ctx2 si rs),
  match_sreg ctx2 csi rs.
Proof.
  intros. intros r sv CSI.
  eapply fpa_reg_union_si in HIN as [sv' [SI EQ]]. 2,3:eassumption.
  rewrite <- (MATCH r sv' SI).
  rewrite HCTX.
  case SIS as [? [MATCH_MEM MATCH_REG]].
  erewrite !sv_subst_correct in EQ; eauto.
  all:intro r'; unfold eval_osv; autodestruct; eauto.
Qed.

Lemma match_sreg_union_si_G (lcsi: list csasv): forall
  ctx1 ctx2 sis rs rs0 m m0 bc pge tpge sp0 csi (si: fpasv) symbols_preserved_rev
  (HCTX1: ctx1 = {| sge1 := pge; sge2 := tpge; sge_match := symbols_preserved_rev;
                    ssp := sp0; srs0 := rs0; smbc := (m0, bc) |})
  (HCTX2: ctx2 = {| cge := pge; csp := sp0; crs0 := rs; cmbc := (m, bc) |})
  (HIN: In csi lcsi)
  (UNION: union_si (bcctx2 ctx1) sis lcsi = Some si)
  (SIS: sem_sistate (bcctx1 ctx1) sis rs m)
  (LIVEOK: forall r : reg, build_frame sis r)
  (MATCH: match_sreg ctx2 si (eval_subst_si (bcctx1 ctx1) sis si)),
  match_sreg ctx2 csi (eval_subst_si (bcctx1 ctx1) sis si).
Proof.
  intros. intros r sv CSI.
  eapply fpa_reg_union_si in HIN as [sv' [SI EQ]]. 2,3:eassumption.
  rewrite <- (MATCH r sv' SI).
  rewrite HCTX2.
  case SIS as [? [MATCH_MEM MATCH_REG]].
  rewrite <- !eval_sval_preserved in EQ.
  erewrite !sv_subst_correct in EQ; eauto.
    2,3:intro r'; unfold eval_osv; autodestruct; eauto.
  rewrite HCTX1 in EQ. simpl in EQ. exact EQ.
Qed.

Computes the tree of invariants on final symbolic values
Definition si_sfv (ctx: iblock_common_context) sis (gm: node -> csasv) sfv: option fpasv :=
  match sfv with
  | Sgoto pc => Some (siof (gm pc))
  | Scall sig svid args res pc =>
      if test_clobberable (gm pc) res then
        Some (siof (csi_remove res (gm pc)))
      else None
  | Sbuiltin ef args bres pc =>
      match reg_builtin_res bres with
      | Some r =>
          if test_clobberable (gm pc) r then
            Some (siof (csi_remove r (gm pc)))
          else None
      | None =>
          if test_csifreem (gm pc) then Some (siof (gm pc))
          else None
      end
  | Stailcall sig svid args => Some (si_empty)
  | Sreturn osv => Some (si_empty)
  | Sjumptable sv lpc => union_si ctx sis (List.map gm lpc)
  end.

Lemma si_sfv_morph ctx sis gm sfv0 sfv1:
  sfv_simu ctx sfv0 sfv1 ->
  si_sfv ctx sis gm sfv0 = si_sfv ctx sis gm sfv1.
Proof.
  inversion 1; reflexivity.
Qed.


Applying the invariant on a symbolic state
Fixpoint tr_sstate (ctx: iblock_common_context) (gm: node -> csasv) ss: sstate :=
  match ss with
  | Sfinal sis sfv =>
      STBIND csi <- si_sfv ctx sis gm sfv IN
      Sfinal (tr_sis sis csi false) sfv
  | Scond cond args ifso ifnot =>
      let ifso' := tr_sstate ctx gm ifso in
      let ifnot' := tr_sstate ctx gm ifnot in
      Scond cond args ifso' ifnot'
  | Sabort => Sabort
  end.

Lemma get_soutcome_tr_sstate ctx gm ss sis sfv:
  get_soutcome ctx ss = Some (sout sis sfv) ->
  get_soutcome ctx (tr_sstate ctx gm ss)
    = option_map (fun si => sout (tr_sis sis si false) sfv) (si_sfv ctx sis gm sfv).
Proof.
  intros OUT; induction ss; simpl in *.
  - injection OUT as ? ?; subst; autodestruct.
  - revert OUT; case eval_scondition as [[]|]; auto; discriminate 1.
  - discriminate OUT.
Qed.


High-Level specification of the symbolic simulation test as predicate match_sexec_si


NOTE: we need to mix semantical simulation and syntactic definition on sfval in order to abstract the match_states of BTL_BlockSimulation. Indeed, the match_states involves match_function in match_stackframe. And, here, we aim to define a notion of simulation for defining match_function. A syntactic definition of the simulation on sfval avoids the circularity issue.

A predicate on symbolic execution success
Definition symb_exec_ok ctx ss sis sfv :=
  get_soutcome ctx ss = Some (sout sis sfv) /\ sis_ok ctx sis.

A version using existential quantifiers used to check invariant application
Definition trss_ok ctx ss := exists sis sfv, symb_exec_ok ctx ss sis sfv.

Property on the symbolic execution for a supposed target block
Definition match_sexec_target ctx (sis1EI: sistate) sfv1EI (ss2IE: sstate): Prop :=
  exists sis2IE sfv2IE, get_soutcome ctx ss2IE = Some (sout sis2IE sfv2IE)
    /\ (forall r, build_frame sis1EI r -> build_frame sis2IE r)
    /\ sistate_simu ctx sis1EI sis2IE
    /\ (forall rs m, sem_sistate ctx sis1EI rs m -> sfv_simu ctx sfv1EI sfv2IE).

Property on the symbolic execution for both block
Definition match_sexec_live ctx (ss1EI ss2IE:sstate): Prop :=
  forall sis1EI sfv1EI, symb_exec_ok ctx ss1EI sis1EI sfv1EI ->
  match_sexec_target ctx sis1EI sfv1EI ss2IE.

Symbolic simulation modulo abstract symbolic invariants

we check that ss1EH the symbolic state after output history invariant is redundant wrt the symbolic internal state sis1E (from ss1E) remark: checking the final symbolic value is useless, because we know that it is not changed when applying the history invariant on ss1E !
Definition match_sexec_redundant ctx ss1EH sis1E: Prop :=
  forall sis1EH sfv1EH, get_soutcome ctx ss1EH = Some (sout sis1EH sfv1EH) ->
  sistate_simu ctx sis1EH sis1E.

Definition match_sexec_si ctx (gm: gluemap) ib1 ib2 pc: Prop := forall sis1E sfv1E,
  let ss1E := sexec ib1 (sis_source (gm pc)) in
  symb_exec_ok (bcctx1 ctx) ss1E sis1E sfv1E ->
  let ss1EH := tr_sstate (bcctx1 ctx) (fun pc => history (gm pc)) ss1E in
  let ss1EI := tr_sstate (bcctx2 ctx) (fun pc => glue (gm pc)) ss1E in
  trss_ok (bcctx1 ctx) ss1EH /\
  trss_ok (bcctx1 ctx) ss1EI /\
  match_sexec_redundant (bcctx1 ctx) ss1EH sis1E /\
  match_sexec_live (bcctx2 ctx) ss1EI (sexec ib2 (sis_target (gm pc))).


A match_sexec_live version incorporating some elements of match_sexec_si to be used in refinement.
Definition match_sexec_live_ref ctx (gm: gluemap) (ss1E ss2IE:sstate): Prop :=
  forall sis1E sfv1E, symb_exec_ok (bcctx1 ctx) ss1E sis1E sfv1E ->
    exists ss1EH, tr_sstate (bcctx1 ctx) (fun pc => history (gm pc)) ss1E = ss1EH
    /\ trss_ok (bcctx1 ctx) ss1EH
      /\ exists ss1EI, tr_sstate (bcctx2 ctx) (fun pc => glue (gm pc)) ss1E = ss1EI
         /\ trss_ok (bcctx1 ctx) ss1EI
           /\ exists sis1EH sfv1EH, get_soutcome (bcctx1 ctx) ss1EH = Some (sout sis1EH sfv1EH)
              /\ sistate_simu (bcctx1 ctx) sis1EH sis1E
                /\ exists sis1EI sfv1EI, symb_exec_ok (bcctx2 ctx) ss1EI sis1EI sfv1EI
                   /\ match_sexec_target (bcctx2 ctx) sis1EI sfv1EI ss2IE.

Symbolic simulation modulo abstract symbolic invariants (refinement version)
Definition match_sexec_si_ref ctx (gm: gluemap) ib1 ib2 pc: Prop :=
  match_sexec_live_ref ctx gm (sexec ib1 (sis_source (gm pc))) (sexec ib2 (sis_target (gm pc))).

Instantiate and fix a context for symbolic execution
Definition instantiate_context (strict : bool) (P: simu_proof_context -> gluemap -> iblock -> iblock -> node -> Prop)
  gm ib1 ib2 pc: Prop := forall ctx, ctx_strict_reflect (snd (smbc ctx)) strict -> P ctx gm ib1 ib2 pc.

Final implication between both main properties
Theorem match_sexec_si_ref_imp ctx (gm: gluemap) ib1 ib2 pc:
  match_sexec_si_ref ctx gm ib1 ib2 pc ->
  match_sexec_si ctx gm ib1 ib2 pc.
Proof.
  unfold match_sexec_si_ref, match_sexec_si, match_sexec_live_ref,
  match_sexec_redundant, match_sexec_live, symb_exec_ok.
  intros SES sis1E sfv1E (SOUT1E & SOK1E). exploit SES; eauto.
  intros (ss1EH & TRSS_H & TRSS_OK_H & ss1EI & TRSS_G & TRSS_OK_G
  & sis1EH & sfv1EH & SOUT1EH & SSIMU_H & sis1EI & sfv1EI & (SOUT1EI & SOK1EI)
  & sis2IE & sfv2IE & SOUT2IE & BUILDF & SSIMU_G & SEMSIS).
  repeat split.
  - intuition subst; auto.
  - intuition subst; auto.
  - intros sis1EH' sfv1EH' SOUT1EH'.
    rewrite <- TRSS_H in SOUT1EH. rewrite SOUT1EH in SOUT1EH'; inv SOUT1EH'; auto.
  - intros sis1EI' sfv1E' (SOUT1EI' & SOK1EI').
    exists sis2IE; exists sfv2IE; split; auto.
    rewrite <- TRSS_G in SOUT1EI. rewrite SOUT1EI in SOUT1EI'; inv SOUT1EI'.
    split; auto.
Qed.