Module CsharpminorLogic


Require Import Coqlib.
Require Import Maps.
Require Import Integers.
Require Import Values.
Require Import Memory.
Require Import Utf8.
Require Import AST.
Require Import Events.
Require Import Globalenvs.
Require Import Smallstep.
Require Import Behaviors.
Require Import Csharpminor.
Require Import Switch.

Lemma eval_expr_det:
  ∀ ge e t m exp,
    ∀ v, eval_expr ge e t m exp v
    ∀ v', eval_expr ge e t m exp v' →
    v = v'.
Proof.
  induction 1; intros.
  - inv H0. congruence.
  - inv H0. destruct H, H2; congruence.
  - inv H0. congruence.
  - inv H1. rewrite (IHeval_expr _ H4) in H0. congruence.
  - inv H2. rewrite (IHeval_expr1 _ H6) in H1. rewrite (IHeval_expr2 _ H8) in H1. congruence.
  - inv H1. rewrite (IHeval_expr _ H4) in H0. congruence.
Qed.

Lemma eval_exprlist_det:
  ∀ ge e t m expl,
    ∀ vl, eval_exprlist ge e t m expl vl
    ∀ vl', eval_exprlist ge e t m expl vl' →
    vl = vl'.
Proof.
  induction 1; intros.
  inv H. auto.
  inv H1. f_equal. eapply eval_expr_det; eauto. eauto.
Qed.

Lemma alloc_variables_det:
  ∀ e1 m1 vars e2 m2 e2' m2',
    alloc_variables e1 m1 vars e2 m2 ->
    alloc_variables e1 m1 vars e2' m2' ->
    e2 = e2' /\ m2 = m2'.
Proof.
  induction 1.
  intro. inv H. auto.
  intro. inv H1.
  rewrite H in H9. inv H9. auto.
Qed.

Lemma bool_of_val_det:
  ∀ v b b',
    Val.bool_of_val v b ->
    Val.bool_of_val v b' ->
    b = b'.
Proof.
  intros. destruct H; inv H0; auto.
Qed.

Lemma switch_argument_det:
  ∀ b v n n',
    switch_argument b v n ->
    switch_argument b v n' ->
    n = n'.
Proof.
  intros. destruct H; inv H0; auto.
Qed.

Ltac determ :=
  repeat match goal with
           | H : eval_expr ?ge ?e ?t ?m ?exp ?v,
             H' : eval_expr ?ge ?e ?t ?m ?exp ?v' |- _ =>
             let EQ := fresh "EQ" in
             assert (EQ:=eval_expr_det _ _ _ _ _ _ H _ H');
               try (subst v'); clear H'
           | H : eval_exprlist ?ge ?e ?t ?m ?expl ?vl,
             H' : eval_exprlist ?ge ?e ?t ?m ?expl ?vl' |- _ =>
             let EQ := fresh "EQ" in
             assert (EQ:=eval_exprlist_det _ _ _ _ _ _ H _ H');
               subst vl'; clear H'
           | H : alloc_variables ?e1 ?m1 ?vars ?e2 ?m2,
             H' : alloc_variables ?e1 ?m1 ?vars ?e2' ?m2' |- _ =>
             let EQ := fresh "EQ" in
             let EQ1 := fresh "EQ1" in
             let EQ2 := fresh "EQ2" in
             assert (EQ:=alloc_variables_det _ _ _ _ _ _ _ H H');
               destruct EQ as [EQ1 EQ2];
               subst e2' m2'; clear H'
           | H : Val.bool_of_val ?v ?b,
             H' : Val.bool_of_val ?v ?b' |- _ =>
             let EQ := fresh "EQ" in
             assert (EQ:=bool_of_val_det _ _ _ H H');
               subst b; clear H'
           | H : switch_argument ?b ?v ?n,
             H' : switch_argument ?b ?v ?n' |- _ =>
             let EQ := fresh "EQ" in
             assert (EQ:=switch_argument_det _ _ _ _ H H');
               subst n; clear H'
           | H : ?A = Some ?x,
             H' : ?B = Some ?x' |- _ =>
             change B with A in H';
             let EQ := fresh "EQ" in
             assert (EQ:x = x') by congruence;
               subst x; clear H'
           | H : Genv.find_symbol _ ?i = Some ?x,
             H' : Genv.find_symbol _ ?i' = Some ?x |- _ =>
             let EQ := fresh "EQ" in
             assert (EQ:=Genv.genv_vars_inj _ _ _ H H'); subst i; clear H'
           | H : Vptr _ _ = Vptr _ _ |- _ => inv H
         end.

Section PROG.

Variable (prog: program).
Let ge := Genv.globalenv prog.

CoInductive hoare_stmt:
  ∀ (env:env)
    (pre_normal:temp_env -> mem -> Prop)
    (pre_label:ident -> temp_env -> mem -> Prop)
    (stmt:stmt)
    (post_normal:temp_env -> mem -> Prop)
    (post_return:val -> mem -> Prop)
    (post_exit:nat -> temp_env -> mem -> Prop)
    (post_goto:ident -> temp_env -> mem -> Prop), Prop :=

| hoare_Sskip:
  ∀ env pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) -> (post_normal le mem:Prop)) ->
    hoare_stmt env
               pre_normal pre_label
               Sskip
               post_normal post_return post_exit post_goto

| hoare_Sset:
  ∀ env id a pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ v, eval_expr ge env le mem a v /\
                    post_normal (PTree.set id v le) mem) ->
    hoare_stmt env
               pre_normal pre_label
               (Sset id a)
               post_normal post_return post_exit post_goto

| hoare_Sstore:
  ∀ env chunk addr a pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ vaddr v mem',
                 eval_expr ge env le mem addr vaddr /\
                 eval_expr ge env le mem a v /\
                 Mem.storev chunk mem vaddr v = Some mem' /\
                 post_normal le mem') ->
    hoare_stmt env
               pre_normal pre_label
               (Sstore chunk addr a)
               post_normal post_return post_exit post_goto

| hoare_Scall:
  ∀ env optid sig a bl pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ vf vargs fd,
                 eval_expr ge env le mem a vf /\
                 eval_exprlist ge env le mem bl vargs /\
                 Genv.find_funct ge vf = Some fd /\
                 funsig fd = sig /\
                 hoare_fun (fun vargs' mem' => vargs = vargs' /\ mem = mem')
                           fd
                           (fun v mem' =>
                              post_normal (Cminor.set_optvar optid v le) mem')) ->
    hoare_stmt env
               pre_normal pre_label
               (Scall optid sig a bl)
               post_normal post_return post_exit post_goto

| hoare_Sbuiltin:
  ∀ env optid ef bl pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ vargs ,
                 eval_exprlist ge env le mem bl vargs /\
                 (∃ t v mem', external_call ef ge vargs mem t v mem') /\
                 ∀ t v mem', external_call ef ge vargs mem t v mem' ->
                   (post_normal (Cminor.set_optvar optid v le) mem':Prop)) ->
    hoare_stmt env
               pre_normal pre_label
               (Sbuiltin optid ef bl)
               post_normal post_return post_exit post_goto

| hoare_Sseq:
  ∀ env s1 s2,
  ∀ pre_normal pre_label P
    post_normal post_return post_exit post_goto,
    hoare_stmt env
               pre_normal pre_label
               s1
               P post_return post_exit post_goto ->
    hoare_stmt env
               P pre_label
               s2
               post_normal post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sseq s1 s2)
               post_normal post_return post_exit post_goto

| hoare_Sifthenelse:
  ∀ env a s1 s2,
  ∀ pre_normal1 pre_normal2 pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ v b, eval_expr ge env le mem a v /\
                      Val.bool_of_val v b /\
                      if b then pre_normal1 le mem else pre_normal2 le mem) ->
    hoare_stmt env
               pre_normal1 pre_label
               s1
               post_normal post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal2 pre_label
               s2
               post_normal post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sifthenelse a s1 s2)
               post_normal post_return post_exit post_goto

| hoare_Sloop:
  ∀ env s,
  ∀ inv pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) -> (inv le mem:Prop)) ->
    hoare_stmt env
               inv pre_label
               s
               inv post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sloop s)
               post_normal post_return post_exit post_goto

| hoare_Sblock:
  ∀ env s,
  ∀ pre_normal pre_label
    post_normal post_return post_exit post_goto,
    hoare_stmt env
               pre_normal pre_label
               s
               post_normal post_return
               (fun n => match n with
                           | S n => post_exit n
                           | O => post_normal
                         end) post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sblock s)
               post_normal post_return post_exit post_goto

| hoare_Sexit:
  ∀ env x,
  ∀ pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) -> (post_exit x le mem:Prop)) ->
    hoare_stmt env
               pre_normal pre_label
               (Sexit x)
               post_normal post_return post_exit post_goto

| hoare_Sswitch:
  ∀ env islong a cases,
  ∀ pre_normal pre_label pre_case pre_default
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
       ∃ v n,
         switch_argument islong v n /\
         eval_expr ge env le mem a v /\
         pre_case n le mem /\
         (select_switch_case n cases = None -> (pre_default le mem:Prop))) ->

    hoare_lbl_stmt env
                   pre_case pre_default (fun _ _ => False) pre_label
                   cases
                   post_normal post_return post_exit post_goto ->

    hoare_stmt env
               pre_normal pre_label
               (Sswitch islong a cases)
               post_normal post_return post_exit post_goto

| hoare_Sreturn:
  ∀ env a,
  ∀ pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
               ∃ v mem',
                 match a with
                 | None => v = Vundef
                 | Some a => eval_expr ge env le mem a v
                 end /\
                 Mem.free_list mem (blocks_of_env env) = Some mem' /\
                 post_return v mem') ->
    hoare_stmt env
               pre_normal pre_label
               (Sreturn a)
               post_normal post_return post_exit post_goto

| hoare_Slabel:
  ∀ env lbl s,
  ∀ pre_normal pre_label
    post_normal post_return post_exit post_goto,
    hoare_stmt env
               (fun le mem => pre_label lbl le mem \/ pre_normal le mem) pre_label
               s
               post_normal post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Slabel lbl s)
               post_normal post_return post_exit post_goto

| hoare_Sgoto:
  ∀ env lbl,
  ∀ pre_normal pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) -> (post_goto lbl le mem:Prop)) ->
    hoare_stmt env
               pre_normal pre_label
               (Sgoto lbl)
               post_normal post_return post_exit post_goto

with hoare_lbl_stmt :
  ∀ (env:env)
    (pre_case:Z -> temp_env -> mem -> Prop)
    (pre_default:temp_env -> mem -> Prop)
    (pre_fallthrough:temp_env -> mem -> Prop)
    (pre_label:ident -> temp_env -> mem -> Prop)
    (lbl_stmt:lbl_stmt)
    (post_normal:temp_env -> mem -> Prop)
    (post_return:val -> mem -> Prop)
    (post_exit:nat -> temp_env -> mem -> Prop)
    (post_goto:ident -> temp_env -> mem -> Prop), Prop :=

| hoare_LSnil:
  ∀ env pre_case pre_default pre_fallthrough pre_label
    post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_default le mem:Prop) -> (post_normal le mem:Prop)) ->
    (∀ le mem, (pre_fallthrough le mem:Prop) -> (post_normal le mem:Prop)) ->
    hoare_lbl_stmt env
                   pre_case pre_default pre_fallthrough pre_label
                   LSnil
                   post_normal post_return post_exit post_goto

| hoare_LScons:
  ∀ lbl s ls env pre_case pre_default pre_fallthrough pre_label
    P post_normal post_return post_exit post_goto,

    hoare_stmt env
               (fun le mem =>
                  match lbl with
                  | None => pre_default le mem
                  | Some n => pre_case n le mem
                  end \/ pre_fallthrough le mem) pre_label
               s
               P post_return post_exit post_goto ->

    hoare_lbl_stmt env
                   pre_case
                   (match lbl with
                    | None => fun _ _ => False
                    | Some _ => pre_default
                   end)
                   P pre_label
                   ls
                   post_normal post_return post_exit post_goto ->

    hoare_lbl_stmt env
                   pre_case pre_default pre_fallthrough pre_label
                   (LScons lbl s ls)
                   post_normal post_return post_exit post_goto

with hoare_fun :
       ∀ (pre:list val -> mem -> Prop)
         (f:fundef)
         (post:val -> mem -> Prop), Prop :=

| hoare_Internal:
  ∀ f pre post,
    (∀ vargs mem, (pre vargs mem:Prop) ->
     list_norepet (map fst f.(fn_vars)) /\
     list_norepet f.(fn_params) /\
     list_disjoint f.(fn_params) f.(fn_temps) /\
     ∃ mem1 env le,
       alloc_variables empty_env mem f.(fn_vars) env mem1 /\
       bind_parameters f.(fn_params) vargs
                       (create_undef_temps f.(fn_temps)) = Some le /\
       ∃ inv,
         (∀ lbl le mem, (inv lbl le mem:Prop) -> ∀ k, find_label lbl (fn_body f) k <> None) /\
         hoare_stmt env
                    (fun le' mem' => le' = le /\ mem' = mem1) inv
                    (fn_body f)
                    (fun le mem =>
                       ∃ mem',
                         Mem.free_list mem (blocks_of_env env) = Some mem' /\
                         post Vundef mem') post
                    (fun _ _ _ => False) inv) ->
    hoare_fun pre
              (Internal f)
              post
| hoare_External:
  ∀ ef pre post,
    (∀ vargs mem, (pre vargs mem:Prop) ->
                  (∃ t v mem', external_call ef ge vargs mem t v mem') /\
                  ∀ t v mem', external_call ef ge vargs mem t v mem' ->
                              (post v mem':Prop)) ->
    hoare_fun pre
              (External ef)
              post.

Lemma hoare_stmt_pre_impl:
  ∀ env
    pre_normal pre_label post_normal post_return post_exit post_goto
    pre_normal' pre_label',
    (∀ le mem, (pre_normal' le mem:Prop) -> (pre_normal le mem:Prop)) ->
    (∀ lbl le mem, (pre_label' lbl le mem:Prop) -> (pre_label lbl le mem:Prop)) ->
  ∀ stmt,
    hoare_stmt env pre_normal pre_label stmt post_normal post_return post_exit post_goto ->
    hoare_stmt env pre_normal' pre_label' stmt post_normal post_return post_exit post_goto

with hoare_lbl_stmt_pre_impl:
  ∀ env
    pre_case pre_default pre_fallthrough pre_label post_normal post_return post_exit post_goto
    pre_case' pre_default' pre_fallthrough' pre_label',
    (∀ i le mem, (pre_case' i le mem:Prop) -> (pre_case i le mem:Prop)) ->
    (∀ le mem, (pre_default' le mem:Prop) -> (pre_default le mem:Prop)) ->
    (∀ le mem, (pre_fallthrough' le mem:Prop) -> (pre_fallthrough le mem:Prop)) ->
    (∀ lbl le mem, (pre_label' lbl le mem:Prop) -> (pre_label lbl le mem:Prop)) ->
  ∀ stmt,
    hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label stmt post_normal post_return post_exit post_goto ->
    hoare_lbl_stmt env pre_case' pre_default' pre_fallthrough' pre_label' stmt post_normal post_return post_exit post_goto

with hoare_fun_pre_impl:
  ∀ pre pre' post,
    (∀ args mem, (pre' args mem:Prop) -> (pre args mem:Prop)) ->
  ∀ f,
    hoare_fun pre f post ->
    hoare_fun pre' f post.
Proof.
  - intros. destruct H1; try now (econstructor; eauto).
    + eapply hoare_Sloop with (inv:=inv). eauto. eauto.
    + econstructor; eauto. eapply hoare_lbl_stmt_pre_impl; eauto. auto.
    + eapply hoare_Slabel. eapply hoare_stmt_pre_impl, H1; intuition auto.
  - intros. destruct H3; econstructor; eauto.
    eapply hoare_stmt_pre_impl, H3; intuition auto. destruct lbl; auto.
    eapply hoare_lbl_stmt_pre_impl, H4; intuition auto. destruct lbl; auto.
  - intros. destruct H0; econstructor; eauto.
Qed.

Lemma hoare_stmt_post_impl:
  ∀ env
    pre_normal pre_label post_normal post_return post_exit post_goto
    post_normal' post_return' post_exit' post_goto',
    (∀ le mem, (post_normal le mem:Prop) -> (post_normal' le mem:Prop)) ->
    (∀ v mem, (post_return v mem:Prop) -> (post_return' v mem:Prop)) ->
    (∀ x le mem, (post_exit x le mem:Prop) -> (post_exit' x le mem:Prop)) ->
    (∀ lbl le mem, (post_goto lbl le mem:Prop) -> (post_goto' lbl le mem:Prop)) ->
  ∀ stmt,
    hoare_stmt env pre_normal pre_label stmt post_normal post_return post_exit post_goto ->
    hoare_stmt env pre_normal pre_label stmt post_normal' post_return' post_exit' post_goto'

with hoare_lbl_stmt_post_impl:
  ∀ env
    pre_case pre_default pre_fallthrough pre_label post_normal post_return post_exit post_goto
    post_normal' post_return' post_exit' post_goto',
    (∀ le mem, (post_normal le mem:Prop) -> (post_normal' le mem:Prop)) ->
    (∀ v mem, (post_return v mem:Prop) -> (post_return' v mem:Prop)) ->
    (∀ x le mem, (post_exit x le mem:Prop) -> (post_exit' x le mem:Prop)) ->
    (∀ lbl le mem, (post_goto lbl le mem:Prop) -> (post_goto' lbl le mem:Prop)) ->
  ∀ stmt,
    hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label stmt post_normal post_return post_exit post_goto ->
    hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label stmt post_normal' post_return' post_exit' post_goto'

with hoare_fun_post_impl:
  ∀ pre post post',
    (∀ val mem, (post val mem:Prop) -> (post' val mem:Prop)) ->
  ∀ f,
    hoare_fun pre f post ->
    hoare_fun pre f post'.
Proof.
  - intros. destruct H3; try now (econstructor; eauto).
    + econstructor. firstorder.
    + econstructor. intros. edestruct H3 as (vaddr & v & mem' & A & B & C & D); eauto 10.
    + econstructor. intros. edestruct H3 as (vf & vargs & fd & A & B & C & D & E); eauto.
      eexists. eexists. eexists. split. eauto. split. eauto.
      split. eauto. split. auto.
      eapply hoare_fun_post_impl; eauto. eauto.
    + econstructor. intros. edestruct H3 as (vargs & A & (t & v & mem' & B) & C); eauto.
      eauto 10.
    + econstructor. eapply hoare_stmt_post_impl; eauto.
      destruct x; eauto.
    + econstructor. intros. edestruct H3 as (v & mem' & A & B & C); destruct a; eauto 10.
  - intros. destruct H3; econstructor; eauto.
  - intros. destruct H0.
    + econstructor; eauto. intros.
      edestruct H0 as (A & B & C & mem1 & env & le & D & E & inv & F & G); eauto.
      split. auto. split. auto. split. auto. eexists. eexists. eexists.
      split. eauto. split. eauto. exists inv. split. auto.
      eapply hoare_stmt_post_impl, G.
      intros. edestruct H2 as (mem' & I & J). eauto.
      auto. auto. auto.
    + econstructor. intros.
      edestruct H0 as ((t & v & mem' & A) & B); eauto 10.
Qed.

Lemma hoare_stmt_wp_aux:
  ∀ stmt env pre_normal pre_label post_normal post_return post_exit post_goto,
    (∀ le mem, (pre_normal le mem:Prop) ->
       ∃ P, P le mem /\
            hoare_stmt env P (fun _ _ _ => False) stmt
                       post_normal post_return post_exit post_goto) ->
    (∀ lbl le mem, (pre_label lbl le mem:Prop) ->
       ∃ P, P lbl le mem /\
            hoare_stmt env (fun _ _ => False) P stmt
                       post_normal post_return post_exit post_goto) ->
    hoare_stmt env pre_normal pre_label
               stmt post_normal post_return post_exit post_goto
with hoare_lbl_stmt_wp_aux:
  ∀ lbl_stmt env pre_case pre_default pre_fallthrough pre_label
    post_normal post_return post_exit post_goto,
    (∀ n le mem, (pre_case n le mem:Prop) ->
       ∃ P, P n le mem /\
            hoare_lbl_stmt env P (fun _ _ => False) (fun _ _ => False) (fun _ _ _ => False)
                           lbl_stmt
                           post_normal post_return post_exit post_goto) ->
    (∀ le mem, (pre_default le mem:Prop) ->
       ∃ P, P le mem /\
            hoare_lbl_stmt env (fun _ _ _ => False) P (fun _ _ => False) (fun _ _ _ => False)
                           lbl_stmt
                           post_normal post_return post_exit post_goto) ->
    (∀ le mem, (pre_fallthrough le mem:Prop) ->
       ∃ P, P le mem /\
            hoare_lbl_stmt env (fun _ _ _ => False) (fun _ _ => False) P (fun _ _ _ => False)
                           lbl_stmt
                           post_normal post_return post_exit post_goto) ->
    (∀ lbl le mem, (pre_label lbl le mem:Prop) ->
       ∃ P, P lbl le mem /\
            hoare_lbl_stmt env (fun _ _ _ => False) (fun _ _ => False) (fun _ _ => False) P
                           lbl_stmt
                           post_normal post_return post_exit post_goto) ->
    hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label
                   lbl_stmt post_normal post_return post_exit post_goto.
Proof.
  2:intros; destruct lbl_stmt. intros; destruct stmt.
  - (* Sskip *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Sset *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Sstore *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Scall *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Sbuiltin *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Sseq *)
    econstructor.
    apply hoare_stmt_wp_aux. 3:apply hoare_stmt_wp_aux. 3:exact (fun le mem H => H).
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_post_impl, H7; eauto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_post_impl, H7; eauto.
      exists P0. split. auto. eapply hoare_stmt_pre_impl, H12; now intuition auto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_pre_impl, H12; now intuition auto.
  - (* Sifthenelse *)
    econstructor.
    2:apply hoare_stmt_wp_aux. 2:exact (fun le mem H => H).
    3:apply hoare_stmt_wp_aux. 3:exact (fun le mem H => H).
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP.
      edestruct H8 as (v & b & EVAL & Hvb & Hb). now eauto.
      exists v. exists b. destruct b; eauto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_pre_impl; eauto. contradiction.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_pre_impl; eauto. contradiction.
  - (* Sloop *)
    intros.
    apply hoare_Sloop with
      (inv := fun le mem => ∃ P, P le mem /\
                                 hoare_stmt env P (fun _ _ _ => False)
                                            stmt P post_return post_exit post_goto),
      hoare_stmt_wp_aux.
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. exists inv. eauto.
    + intros. destruct H1 as (P & HP & HPP).
      exists P. split. auto. eapply hoare_stmt_post_impl, HPP; eauto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP.
      exists P. split. auto. eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, H7; auto.
      intros. exists inv. split. auto. eapply hoare_stmt_pre_impl, H7; now intuition.
  - (* Sblock *)
    constructor. eapply hoare_stmt_wp_aux.
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. eauto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP. eauto.
  - (* Sexit *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. eauto.
  - (* Sswitch *)
    econstructor. 2:apply hoare_lbl_stmt_wp_aux.
    2:exact (fun n le mem H => H). 2:exact (fun le mem H => H). 2:contradiction.
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP.
      edestruct H12 as (v & n & Hvn & Hv & Hnc & Hnd). now eauto.
      eexists. eexists. split. eauto. split. auto.
      split.
      eexists. split. 2:eapply hoare_lbl_stmt_pre_impl, H13; intuition eauto. auto.
      eexists. split. 2:eapply hoare_lbl_stmt_pre_impl, H13; intuition eauto. auto.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_lbl_stmt_pre_impl, H13; now intuition.
  - (* Sreturn *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* Slabel *)
    constructor. eapply hoare_stmt_wp_aux.
    + intros le mem [?|?].
      edestruct H0 as (P & HP & HPP). now eauto. inv HPP. exists (P l). split. auto.
      eapply hoare_stmt_pre_impl, H11; now intuition.
      edestruct H as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_stmt_pre_impl, H11; now intuition.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_stmt_pre_impl, H11; now intuition.
  - (* Sgoto *)
    constructor. intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* LSnil *)
    constructor.
    + intros. edestruct H0 as (P & HP & HPP). now eauto. inv HPP. auto.
    + intros. edestruct H1 as (P & HP & HPP). now eauto. inv HPP. auto.
  - (* LScons *)
    econstructor. apply hoare_stmt_wp_aux. 3:apply hoare_lbl_stmt_wp_aux.
    5:exact (fun le mem H => H).
    + intros le mem [?|?]. destruct o.
      * edestruct H as (P & HP & HPP). now eauto. inv HPP. exists (P z). split. auto.
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, H16; intuition eauto.
        exists P0. split. auto. eapply hoare_lbl_stmt_pre_impl, H17; intuition eauto.
      * edestruct H0 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, H16; intuition eauto.
      * edestruct H1 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
        destruct o; eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, H16; try now intuition eauto.
    + intros. edestruct H2 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, H16; intuition.
      exists P0. split. auto. eapply hoare_lbl_stmt_pre_impl, H17; now eauto.
    + intros. edestruct H as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_lbl_stmt_pre_impl, H17; now intuition.
    + intros. edestruct H0 as (P & HP & HPP). destruct o; now eauto. inv HPP. exists P. split. auto.
      destruct o; eapply hoare_lbl_stmt_pre_impl, H17; now intuition.
    + intros. edestruct H2 as (P & HP & HPP). now eauto. inv HPP. exists P. split. auto.
      eapply hoare_lbl_stmt_pre_impl, H17; now intuition.
Qed.

Lemma hoare_stmt_wp:
  ∀ stmt env post_normal post_return post_exit post_goto,
    hoare_stmt env
               (fun le mem =>
                  ∃ P, P le mem /\
                       hoare_stmt env P (fun _ _ _ => False) stmt
                                  post_normal post_return post_exit post_goto)
               (fun lbl le mem =>
                  ∃ P, P lbl le mem /\
                       hoare_stmt env (fun _ _ => False) P stmt
                                  post_normal post_return post_exit post_goto)
               stmt post_normal post_return post_exit post_goto.
Proof.
intros. apply hoare_stmt_wp_aux; auto. Qed.

Lemma hoare_fun_wp:
  ∀ f post,
    hoare_fun (fun args mem => ∃ P, P args mem /\ hoare_fun P f post) f post.
Proof.
  destruct f; intros.
  - constructor.
    intros vargs mem (P & HP & HPP). inv HPP.
    specialize (H1 _ _ HP).
    destruct H1 as (A & B & C & mem1 & env & le & D & E & inv & F & G).
    split. auto. split. auto. split. auto.
    exists mem1. exists env. exists le. split. auto. split. auto.
    exists inv. split. auto. auto.
  - constructor. intros vargs mem (P & HP & HPP). inv HPP. auto.
Qed.

Lemma hoare_stmt_pre_exists:
  ∀ T stmt env pre_normal pre_label post_normal post_return post_exit post_goto,
    (∀ x:T, hoare_stmt env (pre_normal x) (pre_label x)
                       stmt post_normal post_return post_exit post_goto) ->
    hoare_stmt env (fun le mem => ∃ x, pre_normal x le mem)
               (fun lbl le mem => ∃ x, pre_label x lbl le mem)
               stmt post_normal post_return post_exit post_goto.
Proof.
  intros. eapply hoare_stmt_pre_impl, hoare_stmt_wp.
  - intros le mem (x & Hx). exists (pre_normal x).
    split. auto. eapply hoare_stmt_pre_impl, (H x). auto. destruct 1.
  - intros lbl le mem (x & Hx). exists (pre_label x).
    split. auto. eapply hoare_stmt_pre_impl, (H x). destruct 1. auto.
Qed.

Lemma hoare_stmt_pre_or:
  ∀ stmt env pre_normal1 pre_normal2 pre_label1 pre_label2
    post_normal post_return post_exit post_goto,
    hoare_stmt env pre_normal1 pre_label1
               stmt post_normal post_return post_exit post_goto ->
    hoare_stmt env pre_normal2 pre_label2
               stmt post_normal post_return post_exit post_goto ->
    hoare_stmt env (fun le mem => pre_normal1 le mem \/ pre_normal2 le mem)
               (fun lbl le mem => pre_label1 lbl le mem \/ pre_label2 lbl le mem)
               stmt post_normal post_return post_exit post_goto.
Proof.
  intros.
  eapply hoare_stmt_pre_impl, hoare_stmt_pre_exists
  with (pre_normal := fun b => if b:bool then pre_normal1 else pre_normal2)
       (pre_label := fun b => if b:bool then pre_label1 else pre_label2).
  destruct 1. exists true. auto. exists false. auto.
  destruct 1. exists true. auto. exists false. auto.
  destruct x; auto.
Qed.

Lemma hoare_fun_pre_exists:
  ∀ T f pre post,
    (∀ x:T, hoare_fun (pre x) f post) ->
    hoare_fun (fun vargs mem => ∃ x, pre x vargs mem) f post.
Proof.
  intros. eapply hoare_fun_pre_impl, hoare_fun_wp.
  intros args mem (x & Hx). exists (pre x). auto.
Qed.

Lemma hoare_fun_pre_or:
  ∀ f pre1 pre2 post,
    hoare_fun pre1 f post ->
    hoare_fun pre2 f post ->
    hoare_fun (fun vargs mem => pre1 vargs mem \/ pre2 vargs mem) f post.
Proof.
  intros.
  eapply hoare_fun_pre_impl, hoare_fun_pre_exists
  with (pre := fun b => if b:bool then pre1 else pre2).
  destruct 1. exists true. auto. exists false. auto.
  destruct x; auto.
Qed.

Lemma hoare_stmt_sp_aux:
  ∀ stmt env pre_normal pre_label post_normal0 post_return0 post_exit0 post_goto0,
    hoare_stmt env pre_normal pre_label stmt post_normal0 post_return0 post_exit0 post_goto0 ->
  ∀ post_normal post_return post_exit post_goto,
    (∀ le mem, (∀ P, hoare_stmt env pre_normal pre_label stmt
                               P (fun _ _ => True)
                               (fun _ _ _ => True) (fun _ _ _ => True) ->
                     P le mem) ->
               (post_normal le mem:Prop)) ->
    (∀ v mem, (∀ P, hoare_stmt env pre_normal pre_label stmt
                               (fun _ _ => True) P
                               (fun _ _ _ => True) (fun _ _ _ => True) ->
                     P v mem) ->
               (post_return v mem:Prop)) ->
    (∀ x le mem, (∀ P, hoare_stmt env pre_normal pre_label stmt
                                 (fun _ _ => True) (fun _ _ => True)
                                 P (fun _ _ _ => True) ->
                       P x le mem) ->
                 (post_exit x le mem:Prop)) ->
    (∀ lbl le mem, (∀ P, hoare_stmt env pre_normal pre_label stmt
                                    (fun _ _ => True) (fun _ _ => True)
                                    (fun _ _ _ => True) P ->
                         P lbl le mem) ->
                   (post_goto lbl le mem:Prop)) ->
    hoare_stmt env pre_normal pre_label stmt
               post_normal post_return post_exit post_goto

with hoare_lbl_stmt_sp_aux:
 ∀ lbl_stmt env pre_case pre_default pre_fallthrough pre_label
   post_normal0 post_return0 post_exit0 post_goto0,
   hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                  post_normal0 post_return0 post_exit0 post_goto0 ->
 ∀ post_normal post_return post_exit post_goto,
   (∀ le mem, (∀ P, hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                                   P (fun _ _ => True)
                                   (fun _ _ _ => True) (fun _ _ _ => True) ->
                    P le mem) ->
              (post_normal le mem:Prop)) ->
   (∀ v mem, (∀ P, hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                                   (fun _ _ => True) P
                                   (fun _ _ _ => True) (fun _ _ _ => True) ->
                    P v mem) ->
              (post_return v mem:Prop)) ->
   (∀ x le mem, (∀ P, hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                                     (fun _ _ => True) (fun _ _ => True)
                                     P (fun _ _ _ => True) ->
                      P x le mem) ->
                (post_exit x le mem:Prop)) ->
   (∀ lbl le mem, (∀ P, hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                                       (fun _ _ => True) (fun _ _ => True)
                                       (fun _ _ _ => True) P ->
                        P lbl le mem) ->
                  (post_goto lbl le mem:Prop)) ->
   hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label lbl_stmt
                  post_normal post_return post_exit post_goto

with hoare_fun_sp_aux:
  ∀ f pre post0, hoare_fun pre f post0 ->
  ∀ post,
    (∀ v mem, (∀ P, hoare_fun pre f P -> P v mem) ->
              (post v mem:Prop)) ->
    hoare_fun pre f post.
Proof.
  - destruct stmt; intros * H; inv H; intros.
    + (* Sskip *)
      constructor. intros le mem PRE. apply H. intros. inv H4. auto.
    + (* Sset *)
      constructor. intros le mem PRE.
      edestruct H9 as (v & EVAL & POST); eauto.
      exists v. split. auto.
      apply H. intros. inv H3.
      edestruct H14 as (v' & EVAL' & POST'); eauto.
      determ. auto.
    + (* Sstore *)
      constructor. intros le mem PRE.
      edestruct H10 as (vaddr & v & mem0 & EVAL1 & EVAL2 & MEM & POST); eauto.
      exists vaddr. exists v. exists mem0. split. auto. split. auto. split. auto.
      apply H. intros. inv H3.
      edestruct H15 as (vaddr' & v' & mem0' & EVAL1' & EVAL2' & MEM' & POST'); eauto.
      determ. auto.
    + (* Scall *)
      constructor. intros.
      edestruct H11 as (vf & vargs & fd & EVAL1 & EVAL2 & FD & SIG & POST); eauto.
      exists vf. exists vargs. exists fd. split. auto. split. auto. split. auto. split. auto.
      eapply hoare_fun_sp_aux. apply POST.
      intros. apply H. intros. inv H5.
      edestruct H18 as (vf' & vargs' & fd' & EVAL1' & EVAL2' & FD' & SIG' & POST'); eauto.
      determ.
      apply H4. apply POST'.
    + (* Sbuiltin *)
      constructor. intros le mem PRE.
      edestruct H10 as (vargs & EVAL & (t & v & mem0 & EXT) & POST); eauto.
      exists vargs. split. auto. split. eauto.
      intros. apply H. intros. inv H4.
      edestruct H16 as (vargs' & EVAL' & (t' & v' & mem0' & EXT') & POST'); eauto.
      determ. eauto.
    + (* Sseq *)
      econstructor.
      * eapply (hoare_stmt_sp_aux _ _ _ _ _ _ _ _ H5).
        exact (fun _ _ H => H).
        intros. apply H0. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H12; eauto.
        intros. apply H1. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H12; eauto.
        intros. apply H2. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H12; eauto.
      * eapply hoare_stmt_sp_aux.
        eapply hoare_stmt_pre_impl, H10; auto.
        intros. eapply H3, hoare_stmt_post_impl, H5; auto.
        intros. apply H. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H17; eauto.
        intros. apply H4. apply H12.
        intros. apply H0. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H17; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H12; auto.
        intros. apply H1. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H17; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H12; auto.
        intros. apply H2. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H17; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H12; auto.
    + (* Sifthenelse *)
      apply hoare_Sifthenelse
      with (pre_normal1 := fun le mem =>
              pre_normal le mem /\ ∃ v, eval_expr ge env le mem e vVal.bool_of_val v true)
           (pre_normal2 := fun le mem =>
              pre_normal le mem /\ ∃ v, eval_expr ge env le mem e vVal.bool_of_val v false);
      [|eapply hoare_stmt_sp_aux|eapply hoare_stmt_sp_aux].
      * intros. edestruct H6 as (v & b & EVAL & BOV & PRE); eauto.
        exists v. exists b. split. auto. split. auto. destruct b; eauto.
      * eapply hoare_stmt_pre_impl, H11; eauto.
        intros le' mem' (PRE & v & EVAL & BOV).
        edestruct H6 as (v' & b' & EVAL' & BOV' & PRE'); eauto.
        determ. auto.
      * intros. apply H. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H19; eauto.
        intros le' mem' (PRE & v & EVAL & BOV).
        edestruct H14 as (v' & b' & EVAL' & BOV' & PRE'); eauto.
        determ. auto.
      * intros. apply H0. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H19; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
      * intros. apply H1. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H19; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
      * intros. apply H2. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H19; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
      * eapply hoare_stmt_pre_impl, H12; eauto.
        intros le' mem' (PRE & v & EVAL & BOV).
        edestruct H6 as (v' & b' & EVAL' & BOV' & PRE'); eauto.
        determ. auto.
      * intros. apply H. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H20; eauto.
        intros le' mem' (PRE & v & EVAL & BOV).
        edestruct H14 as (v' & b' & EVAL' & BOV' & PRE'); eauto.
        determ. auto.
      * intros. apply H0. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H20; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
      * intros. apply H1. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H20; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
      * intros. apply H2. intros. inv H4. apply H3. eapply hoare_stmt_pre_impl, H20; eauto.
        intros le' mem' (PRE & v' & EVAL' & BOV').
        edestruct H14 as (v'' & b'' & EVAL'' & BOV'' & PRE''); eauto.
        determ. auto.
    + (* Sloop *)
      apply hoare_Sloop with
      (inv := fun le mem =>
                ∀ P, hoare_stmt env P pre_label stmt
                                P (fun _ _ => True) (fun _ _ _ => True) (fun _ _ _ => True) ->
                     (∀ le' mem', pre_normal le' mem' -> P le' mem')->
                     P le mem).
      eauto.
      eapply hoare_stmt_sp_aux.
      * eapply hoare_stmt_pre_impl, H5; auto.
        intros. apply H4; auto. eapply hoare_stmt_post_impl, H5; auto.
      * intros. apply H4. eapply hoare_stmt_pre_impl, H6; eauto. intros. apply H8; eauto.
      * intros. apply H0. intros. apply H4. inv H6.
        eapply hoare_stmt_post_impl, hoare_stmt_pre_impl, H12; auto. intros. apply H6; auto.
        eapply hoare_stmt_post_impl, H12; auto.
      * intros. apply H2. intros. apply H4. inv H6.
        eapply hoare_stmt_post_impl, hoare_stmt_pre_impl, H12; auto. intros. apply H6; auto.
        eapply hoare_stmt_post_impl, H12; auto.
      * intros. apply H3. intros. apply H4. inv H6.
        eapply hoare_stmt_post_impl, hoare_stmt_pre_impl, H12; auto. intros. apply H6; auto.
        eapply hoare_stmt_post_impl, H12; auto.
    + (* Sblock *)
      constructor. eapply (hoare_stmt_sp_aux _ _ _ _ _ _ _ _ H4).
      * intros. apply H. intros. apply H3. inv H5. eapply hoare_stmt_post_impl, H10; eauto.
      * intros. apply H0. intros. apply H3. inv H5. eapply hoare_stmt_post_impl, H10; eauto.
      * intros []; intros.
        apply H. intros.
        apply (H3n, match n with
                          | 0%nat => P | S _ => λ _ _, True
                        end)).
        inv H5. eapply hoare_stmt_post_impl, H10; eauto.
        apply H1. intros.
        apply (H3n, match n with
                          | 0%nat => λ _ _, True | S n => P n
                        end)).
        inv H5. eapply hoare_stmt_post_impl, H10; eauto.
      * intros. apply H2. intros. apply H3. inv H5. eapply hoare_stmt_post_impl, H10; eauto.
    + (* Sexit *)
      constructor. intros. apply H1. intros. inv H5. auto.
    + (* Sswitch *)
      apply hoare_Sswitch
      with (pre_case := fun n le mem =>
              pre_normal le mem /\
              ∃ v, switch_argument b v n /\ eval_expr ge env le mem e v)
           (pre_default := fun le mem =>
              pre_normal le mem /\
              ∃ v n, switch_argument b v n /\ eval_expr ge env le mem e v /\
                     select_switch_case n l = None).
      { intros. edestruct H10 as (v & n & SWITCH & EVAL & SELc & SELd); now eauto 20. }
      { eapply hoare_lbl_stmt_sp_aux. eapply hoare_lbl_stmt_pre_impl, H11; eauto.
        - intros i le mem (PRE & (v & SWITCH & EVAL)).
          edestruct H10 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
        - intros le mem (PRE & (v & n & SWITCH & EVAL & SEL)).
          edestruct H10 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
        - intros. apply H. intros. apply H3. inv H4.
          eapply hoare_lbl_stmt_pre_impl, H18; eauto.
          intros i le' mem' (PRE & (v & SWITCH & EVAL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
          intros le' mem' (PRE & (v & n & SWITCH & EVAL & SEL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
        - intros. apply H0. intros. apply H3. inv H4.
          eapply hoare_lbl_stmt_pre_impl, H18; eauto.
          intros i le' mem' (PRE & (v' & SWITCH & EVAL)).
          edestruct H17 as (v'' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
          intros le' mem' (PRE & (v' & n & SWITCH & EVAL & SEL)).
          edestruct H17 as (v'' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
        - intros. apply H1. intros. apply H3. inv H4.
          eapply hoare_lbl_stmt_pre_impl, H18; eauto.
          intros i le' mem' (PRE & (v & SWITCH & EVAL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
          intros le' mem' (PRE & (v & n & SWITCH & EVAL & SEL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
        - intros. apply H2. intros. apply H3. inv H4.
          eapply hoare_lbl_stmt_pre_impl, H18; eauto.
          intros i le' mem' (PRE & (v & SWITCH & EVAL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto.
          intros le' mem' (PRE & (v & n & SWITCH & EVAL & SEL)).
          edestruct H17 as (v' & n' & SWITCH' & EVAL' & PRE'1 & PRE'2). now eauto.
          determ. auto. }
    + (* Sreturn *)
      constructor. intros.
      edestruct H4 as (v & mem' & EVAL & MEM' & POST'). now eauto.
      exists v. exists mem'. split. auto. split. auto. apply H0. intros. inv H5.
      edestruct H10 as (v' & mem'' & EVAL' & MEM'' & POST''); eauto. destruct o; determ; auto. congruence.
    + (* Slabel *)
      constructor. eapply (hoare_stmt_sp_aux _ _ _ _ _ _ _ _ H9).
      * intros. apply H. intros. apply H3. inv H4. eapply hoare_stmt_post_impl, H15; eauto.
      * intros. apply H0. intros. apply H3. inv H4. eapply hoare_stmt_post_impl, H15; eauto.
      * intros. apply H1. intros. apply H3. inv H4. eapply hoare_stmt_post_impl, H15; eauto.
      * intros. apply H2. intros. apply H3. inv H4. eapply hoare_stmt_post_impl, H15; eauto.
    + (* Sgoto *)
      constructor. intros. apply H2. intros. inv H5. auto.
  - destruct lbl_stmt; intros * H; inv H; intros.
    + (* LSnil *)
      constructor; intros; apply H; intros; inv H6; auto.
    + (* LScons Some *)
      econstructor.
      * eapply hoare_stmt_sp_aux. apply H12. exact (fun _ _ H => H).
        intros. apply H0. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H19; eauto.
        intros. apply H1. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H19; eauto.
        intros. apply H2. intros. inv H4. apply H3. eapply hoare_stmt_post_impl, H19; eauto.
      * eapply hoare_lbl_stmt_sp_aux. eapply hoare_lbl_stmt_pre_impl, H13; auto.
        intros. eapply H3, hoare_stmt_post_impl, H12; auto.
        intros. apply H. intros. inv H4. apply H3. eapply hoare_lbl_stmt_pre_impl, H20; eauto.
        intros. apply H4. apply H19.
        intros. apply H0. intros. inv H4. apply H3. eapply hoare_lbl_stmt_pre_impl, H20; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H19; auto.
        intros. apply H1. intros. inv H4. apply H3. eapply hoare_lbl_stmt_pre_impl, H20; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H19; auto.
        intros. apply H2. intros. inv H4. apply H3. eapply hoare_lbl_stmt_pre_impl, H20; eauto.
        intros. apply H4. eapply hoare_stmt_post_impl, H19; auto.
  - destruct f; intros * H; inv H; intros.
    + constructor. intros.
      edestruct H2 as (A & B & C & mem1 & env & le & D & E & inv & F & G); eauto.
      split. auto. split. auto. split. auto. exists mem1. exists env. exists le.
      split. auto. split. auto.
      exists (fun lbl le0 mem0 =>
                (∀ k, find_label lbl (fn_body f) kNone) /\
                ∀ P, hoare_stmt env
                                (fun le' mem' => le' = le /\ mem' = mem1)
                                P (fn_body f)
                                (fun _ _ => True) (fun _ _ => True)
                                (fun _ _ _ => True) P ->
                P lbl le0 mem0).
      split. destruct 1. auto.
      eapply hoare_stmt_sp_aux.
      * eapply hoare_stmt_pre_impl, G. auto.
        intros. destruct H1. apply (H3 inv).
        eapply hoare_stmt_post_impl, G; auto.
      * intros.
        refine (let H1':=H1 _ _ in _).
        { eapply hoare_stmt_post_impl, hoare_stmt_pre_impl, G; auto. exact (fun _ _ H => H).
          intros. destruct H3. apply (H4 inv).
          eapply hoare_stmt_post_impl, G; auto. }
        simpl in H1'. destruct H1' as (mem' & MEM' & POST0).
        exists mem'. split. auto.
        apply H. intros. inv H3.
        edestruct H6 as (A' & B' & C' & mem1' & env' & le' & D' & E' & inv' & F' & G'); eauto.
        determ.
        refine (let H1':=H1 _ _ in _).
        { eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G'; auto.
          intros. destruct H3. apply (H4 inv').
          eapply hoare_stmt_post_impl, G'; auto. exact (fun _ _ H => H). }
        destruct H1' as (mem'' & MEM'' & HP).
        determ. auto.
      * intros. apply H. intros. inv H3.
        edestruct H6 as (A' & B' & C' & mem1' & env' & le' & D' & E' & inv' & F' & G'); eauto.
        determ.
        apply H1.
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G'; auto.
        intros. destruct H3. apply (H4 inv').
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G'; auto.
      * intros. apply (H1 (fun _ _ _ => False)).
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G; auto.
        intros. destruct H3. apply (H4 inv).
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G; auto.
      * intros. split.
        eapply F. apply (H1 inv).
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G; auto.
        intros. destruct H3. apply (H4 inv).
        eapply hoare_stmt_pre_impl, hoare_stmt_post_impl, G; auto.
        intros. apply (H1 P).
        eapply hoare_stmt_pre_impl, H3; auto.
        intros. destruct H4. apply H5. apply H3.
    + constructor.
      intros vargs mem PRE.
      edestruct H2 as ((t & v & mem' & EXT) & A); eauto.
      split. eauto.
      intros. apply H.
      intros. inv H1.
      edestruct H5 as ((t' & v' & mem'' & EXT') & A'); eauto.
Qed.

Lemma hoare_stmt_sp:
  ∀ stmt env pre_normal pre_label post_normal post_return post_exit post_goto,
    hoare_stmt env pre_normal pre_label stmt post_normal post_return post_exit post_goto ->
    hoare_stmt env pre_normal pre_label stmt
               (fun le mem =>
                  ∀ P, hoare_stmt env pre_normal pre_label stmt
                                  P (fun _ _ => True)
                                  (fun _ _ _ => True) (fun _ _ _ => True) ->
                       P le mem)
               (fun v mem =>
                  ∀ P, hoare_stmt env pre_normal pre_label stmt
                                  (fun _ _ => True) P
                                  (fun _ _ _ => True) (fun _ _ _ => True) ->
                       P v mem)
               (fun x le mem =>
                  ∀ P, hoare_stmt env pre_normal pre_label stmt
                                  (fun _ _ => True) (fun _ _ => True)
                                  P (fun _ _ _ => True) ->
                       P x le mem)
               (fun lbl le mem =>
                  ∀ P, hoare_stmt env pre_normal pre_label stmt
                                  (fun _ _ => True) (fun _ _ => True)
                                  (fun _ _ _ => True) P ->
                       P lbl le mem).
Proof.
  intros. eapply hoare_stmt_sp_aux; eauto.
Qed.

Lemma hoare_fun_sp:
  ∀ f pre post, hoare_fun pre f post ->
    hoare_fun pre f (fun v mem => ∀ P, hoare_fun pre f P -> P v mem).
Proof.
  intros. eapply hoare_fun_sp_aux; eauto.
Qed.

Lemma hoare_stmt_post_forall:
  ∀ T stmt env pre_normal pre_label post_normal post_return post_exit post_goto,
    inhabited T ->
    (∀ x:T, hoare_stmt env pre_normal pre_label
                       stmt (post_normal x) (post_return x) (post_exit x) (post_goto x)) ->
    hoare_stmt env pre_normal pre_label stmt
               (fun le mem => ∀ x, post_normal x le mem)
               (fun v mem => ∀ x, post_return x v mem)
               (fun xx le mem => ∀ x, post_exit x xx le mem)
               (fun lbl le mem => ∀ x, post_goto x lbl le mem).
Proof.
  intros. destruct H.
  eapply hoare_stmt_post_impl, hoare_stmt_sp, (H0 X); instantiate; simpl.
  - intros. apply H. eapply hoare_stmt_post_impl, H0; eauto.
  - intros. apply H. eapply hoare_stmt_post_impl, H0; eauto.
  - intros. apply H. eapply hoare_stmt_post_impl, H0; eauto.
  - intros. apply H. eapply hoare_stmt_post_impl, H0; eauto.
Qed.

Lemma hoare_stmt_post_and:
  ∀ stmt env pre_normal pre_label
    post_normal1 post_normal2 post_return1 post_return2 post_exit1 post_exit2 post_goto1 post_goto2,
    hoare_stmt env pre_normal pre_label
               stmt post_normal1 post_return1 post_exit1 post_goto1 ->
    hoare_stmt env pre_normal pre_label
               stmt post_normal2 post_return2 post_exit2 post_goto2 ->
    hoare_stmt env pre_normal pre_label stmt
               (fun le mem => post_normal1 le mem /\ post_normal2 le mem)
               (fun v mem => post_return1 v mem /\ post_return2 v mem)
               (fun x le mem => post_exit1 x le mem /\ post_exit2 x le mem)
               (fun lbl le mem => post_goto1 lbl le mem /\ post_goto2 lbl le mem).
Proof.
  intros.
  eapply hoare_stmt_post_impl, hoare_stmt_post_forall
  with (post_normal := fun b => if b:bool then post_normal1 else post_normal2)
       (post_return := fun b => if b:bool then post_return1 else post_return2)
       (post_exit := fun b => if b:bool then post_exit1 else post_exit2)
       (post_goto := fun b => if b:bool then post_goto1 else post_goto2).
  split. apply (H1 true). apply (H1 false).
  split. apply (H1 true). apply (H1 false).
  split. apply (H1 true). apply (H1 false).
  split. apply (H1 true). apply (H1 false).
  exists. exact true.
  destruct x; auto.
Qed.

Lemma hoare_fun_post_forall:
  ∀ T f pre post,
    inhabited T ->
    (∀ x:T, hoare_fun pre f (post x)) ->
    hoare_fun pre f (fun mem v => ∀ x, post x mem v).
Proof.
  intros. destruct H.
  eapply hoare_fun_post_impl, hoare_fun_sp, (H0 X).
  intros. apply H. eapply hoare_fun_post_impl, H0; eauto.
Qed.

Lemma hoare_fun_post_and:
  ∀ f pre post1 post2,
    hoare_fun pre f post1 ->
    hoare_fun pre f post2 ->
    hoare_fun pre f (fun mem v => post1 mem v /\ post2 mem v).
Proof.
  intros.
  eapply hoare_fun_post_impl, hoare_fun_post_forall
  with (post := fun b => if b:bool then post1 else post2).
  split. apply (H1 true). apply (H1 false).
  exists. exact true.
  destruct x; auto.
Qed.

Definition safe k s :=
  ∀ tr s' k',
    starN step ge k' s tr s' ->
    Nostep (semantics prog) s' ->
    (∀ r, ¬final_state s' r) ->
    (k <= k')%nat.

Lemma safe_step:
  ∀ s k s' t,
    step ge s t s' ->
    (∀ s' t, step ge s t s' -> safe k s') ->
    safe (S k) s.
Proof.
  unfold safe. repeat intro. destruct H1.
  edestruct H2; eauto.
  eapply le_n_S; eauto.
Qed.

Ltac safe_step :=
  lazymatch goal with
    | |- safe (S ?k) ?st =>
      eapply safe_step; [now (econstructor; eauto) | ];
      let s' := fresh "s'" in
      let tr := fresh "tr" in
      let Hs' := fresh "Hs'" in
      intros s' tr Hs'; inv Hs'; try contradiction; [];
      determ
    | |- safe ?k ?st =>
      destruct k; [repeat intro; apply le_0_n|safe_step]
  end.

Lemma safe_le:
  ∀ s k k', safe k s -> (k' <= k)%nat -> safe k' s.
Proof.
  repeat intro. eapply le_trans, H; eauto.
Qed.

Hint Resolve safe_le.
Local Hint Extern 1 (_ <= _)%nat => omega.
Local Hint Extern 1 (_ < _)%nat => omega.

Definition hoare_stmt_sem
           (fuel:nat)
           (env:env)
           (pre_normal:temp_env -> mem -> Prop)
           (pre_label:ident -> temp_env -> mem -> Prop)
           (stmt:stmt)
           (post_normal:temp_env -> mem -> Prop)
           (post_return:val -> mem -> Prop)
           (post_exit:nat -> temp_env -> mem -> Prop)
           (post_goto:ident -> temp_env -> mem -> Prop) : Prop :=
  ∀ f k,
    (∀ le' mem', post_normal le' mem' ->
                 safe fuel (State f Sskip k env le' mem')) ->

    (∀ mem' v, post_return v mem' ->
               safe fuel (Returnstate v (call_cont k) mem')) ->

    (∀ le' mem' x, post_exit x le' mem' ->
                   safe fuel (State f (Sexit x) k env le' mem')) ->

    (∀ le' mem' k' lbl, post_goto lbl le' mem' ->
                        call_cont k = call_cont k' ->
                        safe fuel (State f (Sgoto lbl) k' env le' mem')) ->

    (∀ le mem, pre_normal le mem -> safe fuel (State f stmt k env le mem)) /\
    (∀ le mem lbl s' k', find_label lbl stmt k = Some (s', k') ->
                         pre_label lbl le mem ->
                         safe fuel (State f s' k' env le mem)).

Definition hoare_fun_sem
           (fuel:nat)
           (pre:list val -> mem -> Prop)
           (f:fundef)
           (post:val -> mem -> Prop) :=
  ∀ k, is_call_cont k ->

    (∀ mem' v, post v mem' ->
               safe fuel (Returnstate v k mem')) ->

    ∀ args mem,
      pre args mem -> safe fuel (Callstate f args k mem).

Definition hoare_lbl_stmt_sem (fuel:nat)
           (env:env)
           (pre_case:Z -> temp_env -> mem -> Prop)
           (pre_default:temp_env -> mem -> Prop)
           (pre_fallthrough:temp_env -> mem -> Prop)
           (pre_label:ident -> temp_env -> mem -> Prop)
           (lbl_stmt:lbl_stmt)
           (post_normal:temp_env -> mem -> Prop)
           (post_return:val -> mem -> Prop)
           (post_exit:nat -> temp_env -> mem -> Prop)
           (post_goto:ident -> temp_env -> mem -> Prop)
           : Prop :=
  ∀ f k,
    (∀ le' mem', post_normal le' mem' ->
                 safe fuel (State f Sskip k env le' mem')) ->

    (∀ mem' v, post_return v mem' ->
               safe fuel (Returnstate v (call_cont k) mem')) ->

    (∀ le' mem' x, post_exit x le' mem' ->
                   safe fuel (State f (Sexit x) k env le' mem')) ->

    (∀ le' mem' k' lbl, post_goto lbl le' mem' ->
                        call_cont k = call_cont k' ->
                        safe fuel (State f (Sgoto lbl) k' env le' mem')) ->

    (∀ le mem n lbl_stmt', pre_case n le mem ->
       select_switch_case n lbl_stmt = Some lbl_stmt' ->
       safe fuel (State f (seq_of_lbl_stmt lbl_stmt') k env le mem)) /\
    (∀ le mem, pre_default le mem ->
       safe fuel (State f (seq_of_lbl_stmt (select_switch_default lbl_stmt)) k env le mem)) /\
    (∀ le mem, pre_fallthrough le mem ->
       safe fuel (State f (seq_of_lbl_stmt lbl_stmt) k env le mem)) /\
    (∀ le mem lbl s' k', find_label_ls lbl lbl_stmt k = Some (s', k') ->
                  pre_label lbl le mem ->
                  safe fuel (State f s' k' env le mem)).

Scheme stmt_ind_2 := Induction for stmt Sort Prop
  with lbl_stmt_ind_2 := Induction for lbl_stmt Sort Prop.
Combined Scheme stmt_lbl_stmt_ind from stmt_ind_2, lbl_stmt_ind_2.

Lemma hoare_correct_aux:
  ∀ fuel,
    (∀ env,
        (∀ stmt pre_normal pre_label post_normal post_return post_exit post_goto,
          hoare_stmt env pre_normal pre_label stmt
                     post_normal post_return post_exit post_goto ->
          hoare_stmt_sem fuel env pre_normal pre_label stmt
                         post_normal post_return post_exit post_goto) /\
        (∀ lbl_stmt pre_case pre_default pre_fallthrough pre_label
           post_normal post_return post_exit post_goto,
            hoare_lbl_stmt env pre_case pre_default pre_fallthrough pre_label
                           lbl_stmt
                           post_normal post_return post_exit post_goto ->
            hoare_lbl_stmt_sem fuel env pre_case pre_default pre_fallthrough pre_label
                               lbl_stmt
                               post_normal post_return post_exit post_goto)) /\
    (∀ pre f post,
       hoare_fun pre f post ->
       hoare_fun_sem fuel pre f post).
Proof.
  induction fuel using lt_wf_ind.
  assert (INDs := fun fuel' Hfuel' env => proj1 (proj1 (H fuel' Hfuel') env)).
  assert (INDls := fun fuel' Hfuel' env => proj2 (proj1 (H fuel' Hfuel') env)).
  assert (INDf := fun fuel' Hfuel' => proj2 (H fuel' Hfuel')).
  clear H.
  split; [intros env; apply stmt_lbl_stmt_ind|destruct f]; intros.
  - (* Sskip *)
    inv H. split; intros. 2:discriminate. auto.
  - (* Sset *)
    inv H. split; intros. 2:discriminate.
    edestruct H9 as (v & Hv & POST); eauto. safe_step. eauto.
  - (* Sstore *)
    inv H. split; intros. 2:discriminate.
    edestruct H10 as (vaddr & v & mem' & Hvaddr & Hv & MEM' & POST); eauto. safe_step. eauto.
  - (* Scall *)
    inv H. split; intros. 2:discriminate.
    edestruct H11 as (vf & vargs & fd & Hvf & Hvargs & Hfd & SIG & POST); eauto. safe_step.
    eapply INDf. auto. apply POST. exact I. 2:simpl; auto.
    intros. safe_step. eauto.
  - (* Sbuitin *)
    inv H. split; intros. 2:discriminate.
    edestruct H10 as (vargs & Hvargs & (t & v & mem' & EXT) & POST); eauto. safe_step. eauto.
  - (* Sseq *)
    inv H1. split; intros.
    + safe_step. edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step. edestruct INDs as [IND _]; eauto.
      intros. safe_step. eauto.
    + simpl in H5. destruct (find_label lbl s (Kseq s0 k)) eqn:?.
      * inv H5.
        eapply H. 6:apply Heqo. eauto.
        intros. safe_step. edestruct INDs as [IND _]; eauto.
        auto. intros. safe_step. eauto. auto. auto.
      * eapply H0; eauto.
  - (* Sifthenelse *)
    inv H1. split; intros.
    + edestruct H8 as (v & b & Hv & Hvb & PRE); eauto. safe_step.
      destruct b0; (edestruct INDs as [IND _]; [..|apply IND]); eauto.
    + simpl in H5. destruct (find_label lbl s k) eqn:?.
      * inv H5. eapply H; eauto.
      * eapply H0; eauto.
  - (* Sloop *)
    inv H0. split; intros.
    + safe_step. edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step.
      edestruct INDs as [IND _]; [..|apply IND]; eauto.
      econstructor; eauto.
      intros. safe_step. eauto.
      auto.
    + eapply H. 6:apply H5. eauto.
      intros. safe_step.
      edestruct INDs as [IND _]; [..|apply IND]; eauto.
      econstructor; eauto.
      auto. intros. safe_step. eauto.
      auto. auto.
  - (* Sblock *)
    inv H0. split; intros.
    + safe_step. edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step. eauto.
      destruct x; intros; safe_step; eauto.
    + eapply H. 6:eauto. eauto.
      intros. safe_step. eauto.
      auto. destruct x; intros; safe_step; eauto. auto. auto.
  - (* Sexit *)
    inv H. split; intros. 2:discriminate. auto.
  - (* Sswitch *)
    inv H0. split; intros.
    + edestruct H11 as (v' & n' & Hv'n' & Hv' & PREc & PREd). eauto. safe_step.
      edestruct INDls as [INDc [INDd _]]; eauto.
      specialize (fun lbl_stmt' => INDc le mem _ lbl_stmt' PREc).
      unfold select_switch. destruct select_switch_case; auto.
    + eapply H; eauto.
  - (* Sreturn *)
    inv H. split; intros. 2:discriminate.
    edestruct H4 as (v & mem' & Hv & MEM' & POST). eauto.
    destruct o; safe_step; eauto.
  - (* Slabel *)
    inv H0. split; intros.
    + safe_step. edestruct INDs as [IND _]; eauto.
    + simpl in H4. destruct ident_eq.
      * inv H4. edestruct H as [IND _]; eauto.
      * eapply H; eauto.
  - (* Sgoto *)
    inv H. split; intros. auto. discriminate.
  - (* LSnil *)
    inv H. split. discriminate. split. auto. split. auto. discriminate.
  - (* LScons *)
    inv H1. split; [destruct o; simpl; [intros; destruct zeq|]|split; [destruct o|split]]; intros.
    + inv H6. safe_step.
      edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step.
      edestruct INDls as [_ [_ [IND _]]]; [..|eapply IND]; eauto.
      intros. safe_step. eauto.
      simpl. auto.
    + edestruct H0 as [IND _]; eauto.
    + edestruct H0 as [IND _]; eauto.
    + edestruct H0 as [_ [IND _]]; eauto.
    + safe_step. edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step.
      edestruct INDls as [_ [_ [IND _]]]; eauto.
      intros. safe_step. eauto.
      simpl. auto.
    + safe_step.
      edestruct INDs as [IND _]; [..|apply IND]; eauto.
      intros. safe_step.
      edestruct INDls as [_ [_ [IND _]]]; eauto.
      intros. safe_step. eauto.
      simpl. auto.
    + simpl in H5. destruct (find_label lbl s (Kseq (seq_of_lbl_stmt l) k)) eqn:?.
      * inv H5. eapply H with (k:=Kseq _ _ ); eauto; intros.
        safe_step. edestruct INDls as [_ [_ [IND _]]]; [..|eapply IND]; eauto.
        safe_step. eauto.
      * eapply H0; eauto.
  - (* Internal *)
    inv H. intros k Hk ? args mem ?.
    edestruct H2 as (A & B & C & mem1 & env & le & ALLOC & BIND & inv & FIND & HOARE). eauto.
    specialize (fun fu Hfu => INDs fu Hfu _ _ _ _ _ _ _ _ HOARE). clear HOARE INDls INDf.
    safe_step.
    edestruct INDs as [IND _]; eauto.
    + intros. destruct H1 as (mem'0 & MEM'0 & POST). safe_step. eauto.
    + intros. replace (call_cont k) with k by (destruct k; contradiction || reflexivity). eauto.
    + contradiction.
    + induction fuel%nat using lt_wf_ind. rename H1 into IND.
      intros.
      destruct (find_label lbl (fn_body f) (call_cont k')) as [[s'' k'']|] eqn:?.
      2:now (edestruct FIND; eauto).
      safe_step. rewrite H15 in Heqo. inv Heqo.
      replace (call_cont k') with k in H15 by (destruct k; auto; contradiction).
      eapply INDs; eauto.
      * intros. destruct H4 as (mem2 & FREE & POST). safe_step. eauto.
      * intros. replace (call_cont k) with k by (destruct k; contradiction || reflexivity). eauto.
      * contradiction.
  - (* External *)
    intros k Hk ? args mem ?. inv H. edestruct H4 as ((t & v & mem' & EXT) & POST). eauto.
    safe_step. eauto.
Qed.

Lemma hoare_stmt_correct:
  ∀ env stmt pre_normal pre_label post_normal post_return post_exit post_goto,
    hoare_stmt env pre_normal pre_label stmt
               post_normal post_return post_exit post_goto ->
    ∀ fuel, hoare_stmt_sem fuel env pre_normal pre_label stmt
                           post_normal post_return post_exit post_goto.
Proof.
  intros. apply hoare_correct_aux. auto.
Qed.

Lemma hoare_fun_correct:
  ∀ pre f post,
    hoare_fun pre f post ->
    ∀ fuel, hoare_fun_sem fuel pre f post.
Proof.
  intros. apply hoare_correct_aux. auto.
Qed.

Lemma hoare_prog:
  ∀ f b mem,
    Genv.find_symbol ge (prog_main prog) = Some b ->
    Genv.find_funct_ptr ge b = Some f ->
    funsig f = signature_main ->
    Genv.init_mem prog = Some mem ->
    hoare_fun (fun vargs mem' => vargs = nil /\ mem' = mem)
              f
              (fun v mem => ∃ r, v = Vint r) ->
    ∀ tr, program_behaves (semantics prog) (Goes_wrong tr) ->
          False.
Proof.
  intros.
  inv H4.
  - assert (∀ n, safe n s).
    { intros. inv H5.
      change ge0 with ge in *. clear ge0.
      assert (mem = m0) by congruence. subst m0.
      assert (b = b0) by congruence. subst b0.
      assert (f = f0) by congruence. subst f0.
      eapply hoare_fun_correct. apply H3. constructor.
      intros. destruct H5 as [r Hr]. subst v.
      repeat intro. inv H5. edestruct H11. constructor. inv H12.
      simpl. auto. }
    inv H6.
    apply star_starN in H8. destruct H8 as [n Hn].
    pose proof (H4 (S n) _ _ _ Hn H9 H10).
    omega.
  - eapply H6. econstructor; eauto.
Qed.


Lemma hoare_loop_pre_unroll:
  ∀ env s,
  ∀ pre_normal pre_label P
    post_normal post_return post_exit post_goto,
    hoare_stmt env
               pre_normal pre_label
               s
               P post_return post_exit post_goto ->
    hoare_stmt env
               P (fun _ _ _ => False)
               (Sloop s)
               post_normal post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sloop s)
               post_normal post_return post_exit post_goto.
Proof.
  intros. inv H0.
  eapply hoare_Sloop, hoare_stmt_pre_impl, hoare_stmt_pre_or.
  4:eapply hoare_stmt_post_impl, H; auto. 5:eapply hoare_stmt_post_impl, H6; auto.
  2:exact (fun le mem H => H). simpl; auto. simpl; auto. simpl; auto. simpl; auto.
Qed.

Definition loop_unstable_invariant
           env inv pre_normal pre_label s post_return post_exit post_goto :=
  ∃ inv',
    (∀ le mem, (inv' le mem:Prop) -> (inv le mem:Prop)) /\
    (∀ le mem, (pre_normal le mem:Prop) -> (inv' le mem:Prop)) /\
    hoare_stmt env inv' pre_label s inv' post_return post_exit post_goto.

Lemma hoare_Sloop_unstable:
  ∀ env s inv pre_normal pre_label post_normal post_return post_exit post_goto,
    loop_unstable_invariant env
                            inv pre_normal pre_label
                            s
                            post_return post_exit post_goto ->
    hoare_stmt env
               pre_normal pre_label
               (Sloop s)
               post_normal post_return post_exit post_goto.
Proof.
  intros. destruct H as (inv' & Hinv'1 & Hinv'2 & HOARE).
  econstructor. 2:apply HOARE. auto.
Qed.

Lemma loop_unstable_invariant_seq:
  ∀ env s inv inv' pre_normal pre_label
    post_return post_exit post_goto post_return' post_exit' post_goto',
    loop_unstable_invariant env
                            inv pre_normal pre_label
                            s
                            post_return post_exit post_goto ->
    hoare_stmt env
               inv pre_label
               s
               inv' post_return' post_exit' post_goto' ->
    (∀ le mem, pre_normal le mem -> inv' le mem) ->
    loop_unstable_invariant env
                            inv' pre_normal pre_label
                            s
                            post_return' post_exit' post_goto'.
Proof.
  intros. destruct H as (inv0 & Hinv01 & Hinv02 & HOARE).
  exists (fun le mem => inv0 le mem /\ inv' le mem). split. 2:split.
  - now intuition.
  - now intuition.
  - eapply hoare_stmt_post_impl, hoare_stmt_post_and.
    5:eapply hoare_stmt_pre_impl, HOARE.
    7:eapply hoare_stmt_pre_impl, H0.
    simpl; now intuition. simpl; now intuition. simpl; now intuition. simpl; now intuition.
    simpl; now intuition. simpl; now intuition. simpl; now intuition. simpl; now intuition.
Qed.

End PROG.