Module MemCsharpminorproof


Require
  OnMem
  MemCsharpminor.

Import
  Utf8
  Coqlib
  AST
  Maps
  Globalenvs
  Values
  Integers
  Floats
  Memory
  Csharpminorannot

  Util
  AssocList
  ListAdom
  ToString
  AdomLib
  AlarmMon
  AbMachineEnv
  Cells
  Sets
  PExpr
  AbMemSignatureCsharpminor
  PointsTo
  OnMem
  MemCsharpminor.

Set Implicit Arguments.

Section num.

  Context
    {num num_iter: Type}
    `{numToString: ToString num}
    `{numIterToString: ToString num_iter}
    (NumDom: ab_machine_env (var:=cell) num num_iter).

  Variable ge : genv.
  Variable max_concretize : N.

  Existing Instances t_leb_correct t_join_correct widen_mem_correct.

  Local Instance t_gamma : gamma_op (@t num) concrete_state := t_gamma NumDom ge.
  Local Instance iter_t_gamma : gamma_op (@iter_t num_iter) concrete_state := iter_t_gamma NumDom ge.
  Local Instance mem_cshm_dom : mem_dom _ _ _ := mem_cshm_dom NumDom ge max_concretize.

  Lemma read_cell_upd t e σ m x v c:
    cACtemp (plength σ) x
    read_cell ge ((PTree.set x v t, e) :: σ, m) c =
    read_cell ge ((t, e) :: σ, m) c.
Proof.
    destruct c as [f' x'|f' x'|b]; simpl; auto.
    intros _.
    rewrite ! get_stk_cons.
    destruct (Pos.eqb _ _); reflexivity.
    rewrite ! get_stk_cons.
    case (Pos.eqb_spec _ _).
    intros -> K. simpl. rewrite PTree.gso. reflexivity. intros ->; apply K; reflexivity.
    reflexivity.
  Qed.

  Lemma validate_volatile_ptr_in sz ptr nes b :
    ∀ stk m,
      sizes_gamma ge stk sz m
      validate_volatile_ptr ge sz ptr nes b = true
      ∀ blk,
        BlockSet.mem blk ptr
        ∃ g b, blk = ABglobal bGenv.find_symbol ge g = Some bSenv.block_is_volatile ge b.
Proof.
    unfold validate_volatile_ptr.
    intros stk m SZ.
    apply BSO.foldspec. 2: firstorder; fail.
    intros s s' _ () x H [R1 _] H1 H2 blk H4.
    2: easy.
    destruct (R1 _ H4) as [ -> | IN ].
    - destruct blk as [ ? ? | blk ]. easy.
      destruct (Genv.invert_symbol _ _) as [ g | ] eqn: Hg.
      exists g, blk. split. easy. split.
      now rewrite (Genv.invert_find_symbol _ _ Hg).
      destruct (ABTreeDom.get (ABglobal blk) sz) as [ | (?, (?, vol)) ] eqn: Hsz.
      easy.
      subst.
      generalizeb', SZ (ABglobal blk) _ _ _ b' Hsz). simpl. rewrite Hg.
      intros X. specialize (X _ eq_refl).
      easy.
      easy.
    - specialize (H1 eq_refl _ IN). eauto.
  Qed.

  Lemma forget_all_correct
        {A V: Type} (P: A → (cellV) → Prop)
        (P_ext: ∀ ρ ρ', (∀ x, ρ(x) = ρ'(x)) → ∀ m, ρ ∈ P m → ρ' ∈ P m)
        (F: AcellA)
        (PF: ∀ m ρ ρ' x, ρ ∈ P m → (∀ y, yx → ρ'(y) = ρ(y)) → ρ' ∈ P (F m x))
        l :
    ∀ m : A,
    ∀ ρ ρ' : cellV,
      ρ ∈ P m
      (∀ c, ρ c = ρ' cIn c l) →
      ρ' ∈ P (fold_left F l m).
Proof.
    induction l as [|i l IH] using rev_ind.
  - intros m ρ ρ' H K.
    apply (P_ext ρ). intros c; destruct (K c) as [ ? | () ]; assumption.
    assumption.
  - intros m ρ ρ' H K. rewrite fold_left_app. simpl.
    apply PF withc, if eq_dec c i then ρ c else ρ' c).
    eapply IH. eassumption.
    intros c. destruct (eq_dec c i). left; reflexivity.
    destruct (K c) as [X | X]. left; exact X.
    rewrite in_app in X. destruct X as [ X | [ X | () ] ].
    right; exact X. intuition.
    intros y H0. rewrite dec_eq_false; auto.
  Qed.

  Opaque ACTreeDom.set.
  Lemma ct_forget_all_get {A} l : ∀ m c,
    ACTreeDom.get c (@ct_forget_all A l m) =
    if in_dec eq_dec c l
    then All
    else ACTreeDom.get c m.
Proof.
    unfold ct_forget_all.
    induction l as [|j l IH] using rev_ind; intros m c. reflexivity.
    rewrite fold_left_app. simpl. rewrite ACTreeDom.gsspec.
    destruct (ACTree.elt_eq c j).
    rewrite (pred_dec_true (P:=In c _)). reflexivity.
    rewrite in_app. right; left; symmetry; assumption.
    rewrite IH.
    destruct in_dec. rewrite pred_dec_true. reflexivity. rewrite in_app. auto.
    rewrite pred_dec_false. reflexivity. rewrite in_app. simpl. intuition.
  Qed.

  Lemma pt_forget_all_correct stk l :
    ∀ m : points_to,
    ∀ ρ ρ' : cellval,
      ρ ∈ points_to_gamma ge stk m
      (∀ c, ρ c = ρ' cIn c l) →
      ρ' ∈ points_to_gamma ge stk (ct_forget_all l m).
Proof.
    intros m ρ ρ' H H0.
    eapply forget_all_correct; eauto.
    intros ρ0 ρ'0 H1 m0 H2 c. rewrite <- H1. auto.
    intros m0 ρ0 ρ'0 x H1 H2 c. rewrite ACTreeDom.gsspec.
    destruct (ACTree.elt_eq). exact I. rewrite H2; auto.
  Qed.

  Definition loose_upd_spec {B} (f: cellB) (x: cell) (v: B) (f': cellB) : Prop :=
    f'(x) = v ∧ ∀ y, ycells_disjoint(x) → f'(y) = f(y).

  Lemma nm_upd_correct nm c e n (ρ ρ': cellmach_num) :
    ρ ∈ γ(nm) →
    neval_mexpr ρ e
    ρ' ∈ loose_upd_spec ρ c n
    ρ' ∈ γ (nm_upd NumDom nm c e).
Proof.
    intros NM EV UPD.
    unfold nm_upd.
    match goal with
    | |- context [ @AbMachineEnv.assign ?var ?t ?iter_t ?H ?E ?v ?me ?ab ] =>
      generalize (@AbMachineEnv.assign_correct var t iter_t H E v me ρ n ab NM EV);
      destruct (@AbMachineEnv.assign var t iter_t H E v me ab)
    end.
    intros ().
    intros NM'.
    unfold nm_forget_all.
    eapply forget_all_correct.
    apply gamma_num_ext.
    intros [|m] ρ0 ρ'0 x0. intros ().
    eapply (forget_correct' NumDom).
    exact NM'.
    intros c'.
    destruct (cells_disjoint_dec c c') as [D | OV ].
    left. rewrite upd_o. exact (eq_sym (proj2 UPD _ D)).
    exact (not_eq_sym (cells_disjoint_not_eq D)).
    destruct (eq_dec c c'). left. subst. rewrite upd_s. exact (eq_sym (proj1 UPD)).
    right. apply overlap_correct'; auto.
  Qed.

  Lemma nm_upd_not_bot nm c e n (ρ: cellmach_num) :
    ρ ∈ γ(nm) →
    neval_mexpr ρ e
    nm_upd NumDom nm c eBot.
Proof.
    intros H H0 K.
    change (upd ρ c n ∈ γ(@Bot num)). rewrite <- K.
    eapply nm_upd_correct; try eassumption.
    split. apply upd_s.
    intros c' D. rewrite upd_o. reflexivity. exact (sym_not_eq (cells_disjoint_not_eq D)).
  Qed.

  Lemma tp_upd_correct (val:Type) (Γ: gamma_op type val)
        (HJ: join_op_correct type (type+⊤) val) tp ρ cells ty c v :
    v ∈ γ(ty) →
    In c cells
    ρ ∈ γ tp
    loose_upd_spec ρ c v ⊆ γ (tp_upd_cells tp cells ty).
Proof.
    intros Hty Hc G ρ' UPD c'.
    unfold tp_upd_cells. rewrite union_list_map_correct.
    pose proof (in_mapc, ct_upd tp c ty) _ _ Hc) as IN.
    simpl in IN.
    refine ((λ G, union_list_correct tp
             (mapc, ct_upd tp c ty) cells) _ ρ' IN G c') _).
    clear c' IN. intros c'.
    unfold ct_upd. rewrite ACTreeDom.gsspec. destruct ACTree.elt_eq. subst.
    rewrite (proj1 UPD); auto.
    rewrite ct_forget_all_get.
    destruct in_dec as [IN|NI]. exact I.
    rewrite (proj2 UPD). auto.
    destruct (overlap_correct _ _ NI); intuition.
  Qed.

  Lemma cell_in_bounds_range_perm κ (ab: @t num) c:
    am_get (cell_in_bounds ge κ ab c) = Some tt
    ∀ m σ b,
      msizes_gamma ge σ (ab_sz ab) →
      block_of_ablock ge σ (ablock_of_cell c) = Some b
      Mem.range_perm m b (fst (range_of_cell c)) (fst (range_of_cell c) + size_chunk (proj1_sig κ)) Cur Writable.
Proof.
    unfold cell_in_bounds.
    set (b := ablock_of_cell c).
    destruct (range_of_cell c) as (ofs, κ').
    destruct (ABTreeDom.get b (ab_sz ab)) as [|(hi, (perm, vol))] eqn: Hhi.
    simpl. intros ?; eq_some_inv.
    destruct (permission_can_write perm) eqn: Hrw. 2: simpl; intro; eq_some_inv.
    destruct zle as [L|]. 2: simpl; intro; eq_some_inv.
    destruct zle as [R|]. 2: simpl; intro; eq_some_inv.
    intros _.
    intros m σ b' H Hb o K.
    specialize (H _ _ _ _ _ Hhi Hb).
    eapply Mem.perm_implies. apply H. simpl in *. Psatz.lia.
    destruct perm; vauto; simpl in Hrw; eq_some_inv.
  Qed.
  Arguments cell_in_bounds_range_perm [κ] [ab] [c] _ [m] [σ] [b] _ _ _ _.
  Opaque cell_in_bounds.

  Lemma nassume_correct b nm F el :
    ∀ ρ,
      ρ ∈ γ(nm) →
      ∀ e, In e eleval_mexpr ρ (F e) (of_bool b) →
      ρ ∈ γ (nassume NumDom b nm F el).
Proof.
    unfold nassume.
    intros ρ G e E EV.
    rewrite union_list_map_correct.
    refine (union_list_correct _ _ (AbMachineEnv.assume (F e) b nm) _ _ _).
    refine (in_map _ el _ E).
    apply assume_correct; assumption.
  Qed.

  Lemma env_ok (ε: lvarset) (e: env) :
    (∀ x, PSet.mem x ε = dom x e) →
    ∀ i, is_local ε i = truee ! iNone.
Proof.
    unfold is_local, dom.
    intros H i. rewrite H. destruct (e ! i); intuition. eq_some_inv.
  Qed.

  Remark bind_parameters_zip formals : ∀ args te,
    bind_parameters formals args te =
    fold_left2acc id v, do te <- acc; ret (PTree.set id v te))
               (λ _ _ , None) (λ _ _, None)
               formals args (Some te).
Proof.
    induction formals as [|id formals IH].
    reflexivity.
    destruct args as [|v args]. reflexivity.
    simpl. intros te. rewrite IH. reflexivity.
  Qed.

  Lemma set_local_sizes_get μ vars sz b :
    list_norepet (map fst vars) →
    ABTreeDom.get b (set_local_sizes μ vars sz) =
    match b with
    | ABlocal μ' x => if peq μ' μ
                      then match assoc x vars with
                           | Some hi => Just (hi, (Freeable, false))
                           | None => ABTreeDom.get b sz
                           end
                      else ABTreeDom.get b sz
    | _ => ABTreeDom.get b sz
    end.
Proof.
    revert sz.
    unfold set_local_sizes.
    induction vars as [|(x,hi) vars IH].
    simpl. destruct b as [μ' ?|]; auto. destruct peq; auto.
    simpl. intros sz NR. rewrite IH. clear IH.
    fold (ABTreeDom.set sz (ABlocal μ x) (Just (hi, (Freeable, false)))). rewrite ABTreeDom.gsspec.
    destruct (ABTree.elt_eq b (ABlocal μ x)) as [EQ|ME].
  - subst. rewrite !peq_true.
    rewrite (proj2 (assoc_None x vars)). reflexivity.
    intros b K. inv NR. eapply H1. rewrite in_map_iff. eexists (x, _); vauto.
  - destruct b; auto.
    destruct peq; auto. subst.
    destruct @eq_dec; auto. subst.
    elim ME. reflexivity.
  - inv NR; auto.
  Qed.

  Lemma clear_local_sizes_get μ ε b sz :
    ABTreeDom.get b (clear_local_sizes μ ε sz) =
    match b with
    | ABlocal μ' x => if peq μ μ' && PSet.mem x ε then All else ABTreeDom.get b sz
    | _ => ABTreeDom.get b sz
    end.
Proof.
    unfold clear_local_sizes.
    apply PSetOp.foldspec.
    intros s s' a b0 x H [REM REM'] H1.
    unfold ABTreeDom.get in *. rewrite ABTree.grspec.
    destruct (ABTree.elt_eq b (ABlocal μ x)) as [EQ|ME]. subst. destruct peq. 2: congruence.
    rewrite H. reflexivity.
    rewrite H1.
    destruct b as [μ' x'|b]; auto.
    destruct peq; simpl; auto.
    subst.
    specialize (REM x'). specialize (REM' x').
    destruct (PSet.mem x' s).
    specialize (REM eq_refl). destruct REM as [REM|REM].
    elim ME. congruence. rewrite REM. reflexivity.
    destruct (PSet.mem x' s'). 2:auto.
    destruct (REM' eq_refl) as [_ Y]. discriminate.
    intros s H.
    destruct b as [μ' x'|b]; auto.
    destruct peq; simpl; auto.
    subst. specialize (H x').
    destruct (PSet.mem); auto. intuition.
  Qed.

  Lemma read_perm_non_volatile {V} (gv: globvar V) :
    perm_order (Genv.perm_globvar gv) Readable
    gvar_volatile gv = false.
Proof.
unfold Genv.perm_globvar. destruct (gvar_volatile gv). inversion 1. easy. Qed.

  Arguments ab_sz _ _/.
  Arguments ab_num _ _/.
  Arguments ab_tp _ _/.

  Lemma ab_alloc_global_ok:
    ∀ prog m, ge = Genv.globalenv prog ->
              Genv.init_mem prog = Some m ->
    ∀ b gv ab ab' id log,
      Genv.find_var_info ge b = Some gv ->
      Genv.invert_symbol ge b = Some id ->
      ab_alloc_global NumDom ge (Genv.perm_globvar gv) (gvar_volatile gv) b (gvar_init gv) (NotBot ab) = (ab', (nil, log)) ->
    msizes_gamma ge nil (ab_sz ab) ->
    ab_stk ab = Just nil ->
    mem_as_fun ge (nil, m) ∈ tnum_gamma _ ge nil (ab_nm ab) ->
    match ab' with
      | Bot => False
      | NotBot ab' =>
        msizes_gamma ge nil (ab_sz ab') /\
        ab_stk ab' = Just nil /\
        mem_as_fun ge (nil, m) ∈ tnum_gamma _ ge nil (ab_nm ab')
    end.
Proof.
    intros prog m H H0 b gv ab ab' id log H1 H2 H3 H4 H5 H6.
    eapply Genv.init_mem_characterization in H0. 2:rewrite <- H; apply H1.
    assert (gv.(gvar_volatile) = Senv.block_is_volatile ge b) as Hvol.
    { simpl. unfold Genv.block_is_volatile. rewrite H1. reflexivity. }
    clear H1.
    destruct H0 as (PERM & PERM' & INIT).
    specializeH, INIT (read_perm_non_volatile _ H)).
    rewrite <- H in INIT. clear H prog.
    revert log H3. unfold ab_alloc_global.
    apply am_bind_case.
    intros; eq_some_inv; subst. eapply H; eauto.
    intros; eq_some_inv.
    intros [sz ab''] log Hab'' log' eq. simpl in eq. eq_some_inv. subst.
    revert ab PERM PERM' INIT Hvol H4 H5 H6 log Hab''.
    change (Genv.init_data_list_size (gvar_init gv))
    with (0+Genv.init_data_list_size (gvar_init gv)).
    generalize 0 at 2 4 5 12 as offs.
    generalize (Genv.perm_globvar gv). intros perm.
    generalize (gvar_volatile gv). intros vol.
    generalize (gvar_init gv). intros i. clear gv.
    induction i as [ | a l IHl ].
    - unfold am_fold. simpl. destruct ab as [?[?[? ?]]].
      intros. inv Hab''. simpl. split. 2:auto.
      unfold sizes_gamma in *. intros.
      unfold ABTreeDom.get in H. rewrite ABTree.gsspec in H. destruct ABTree.elt_eq.
      + inv H. rewrite Z.add_0_r in PERM.
        simpl in H0. rewrite H2 in H0. inv H0. split. apply PERM.
        apply bool_leb_refl.
      + fold (ABTreeDom.get b0 s) in H. simpl in *. eauto.
    - change (a::l) with ((a::nil)++l)%list. intros *. rewrite am_fold_app.
      apply am_bind_case.
      intros x y l0 l' H; intros; eq_some_inv; subst; eapply H; eauto.
      intros; eq_some_inv.
      destruct ab as (abstk & absz & (abty & abnm)).
      intros [offs' ab'] log Ha PERM PERM' INIT Hvol SZ STK (TY & ρ' & COMPAT & NM) log' Hl.
      unfold am_fold in Ha. simpl in Ha.
      match type of Ha with
        | appcontext[if ?b then _ else _] => destruct b; [|discriminate]
      end.
      cut (match ab' with
             | Bot => False
             | NotBot ab' =>
               sizes_gamma ge nil (ab_sz ab') m
               ∧ ab_stk ab' = Just nil
               ∧ tnum_gamma _ ge nil (ab_nm ab') (mem_as_fun ge (nil, m))
           end).
      { intro. destruct ab'. contradiction. destruct H as (? & ? & ?).
        destruct a; simpl in Ha;
        repeat match type of Ha with
        | appcontext[ Genv.find_symbol ?ge ?i ] => destruct (Genv.find_symbol ge i); [| discriminate]
        | appcontext[ permission_can_read ?p ] => destruct (permission_can_read p)
        end;
        try (
            inv Ha; apply IHl in Hl; auto;
            ((intros; rewrite <- Z.add_assoc; (apply PERM || eapply PERM'; eauto)) ||
              now intros K; destruct (INIT K))). }
      clear Hl IHl.
      destruct a; [..|destruct (Genv.find_symbol ge i) eqn:EQSYMB; [|discriminate]];
      inv Ha;
      try match goal with
       | |- context [AbMachineEnv.assign ?c ?e ?ab] =>
          refine (let ASS := AbMachineEnv.assign_correct c (e:=e) _ NM _ in _);
            [repeat econstructor;
              auto using Zle_refl, Fleb_refl, floatofsingle_nan|
             destruct AbMachineEnv.assign as [|abnm']; [contradiction|simpl]]
      end;
      try match goal with
       | |- context [permission_can_read ?p] =>
          destruct (permission_can_read p) as [P|P];
           try (specialize (INIT P); destruct INIT);
           try now repeat (split; eauto)
      end;
      simpl.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2.
          simpl. unfold type_of_int.
          apply int_eq_intro. intros <-. reflexivity. intros _. exact I.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2.
          simpl. unfold type_of_int.
          apply int_eq_intro. intros <-. reflexivity. intros _. exact I.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2.
          simpl. unfold type_of_int.
          apply int_eq_intro. intros <-. reflexivity. intros _. exact I.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2. constructor.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2. constructor.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + split. auto. split. auto. split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2. constructor.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
      + unfold zero_all_cells.
        revert abstk absz abty abnm ρ' SZ STK NM TY COMPAT.
        induction (Z.to_N z) using N.peano_ind.
        * repeat (split; eauto).
        * setoid_rewrite N.peano_rect_succ.
          intros abstk absz abty abnm ρ' X H3 H4 H5 H7.
          eapply memory_chunk_fold_rec. 2:eapply IHn; now eauto.
          { clear abstk absz abty abnm ρ' X H3 H4 H5 H7.
            intros [|(abstk & absz & (abty & abnm))] c. contradiction.
            intros (SZ & STK & TY & ρ' & COMPAT & NM).
            destruct zle, Zdivide_dec; try now repeat (split; eauto). simpl.
            match goal with
              | |- context [AbMachineEnv.assign ?ce ?e ?ab] =>
                refine (let ASS := AbMachineEnv.assign_correct ce (e:=e)
                            (n:=match c with exist (Many32|Many64) H => match H with end
                                          | _ => _ end) _ NM _
                        in _)
            end.
            - destruct c as [[] []]; repeat constructor;
              auto using Zle_refl, Fleb_refl.
            - destruct AbMachineEnv.assign as [|abnm']; [contradiction|simpl].
              refine (let H':= H (proj1_sig c) (offs + Z.of_N n) _ _ _ in _).
              eauto.
              zify; omega. omega. auto. simpl.
              split. auto. split. auto. split.
              + intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
                subst. simpl. rewrite H', H2. destruct c as [[] []]; constructor.
                apply TY.
              + eexists. split. 2:apply ASS. intro. simpl.
                unfold upd. destruct (eq_dec _).
                subst c0. simpl. rewrite H', H2. destruct c as [[] []]; repeat econstructor.
                apply COMPAT. }
      + split. auto. split. auto.
        rewrite EQSYMB in H.
        destruct H as (b' & eq & H). inv eq.
        split.
        * intro. unfold ACTreeDom.get. rewrite ACTree.gsspec. destruct ACTree.elt_eq.
          subst. simpl. rewrite H, H2.
          eexists. split. apply BSO.mem_singleton. eauto. simpl. erewrite Genv.find_invert_symbol; eauto.
          apply TY.
        * eexists. split. 2:apply ASS. intro. simpl.
          unfold upd. destruct (eq_dec c).
          subst c. simpl. rewrite H, H2. repeat econstructor.
          apply COMPAT.
  Qed.

  Lemma ab_alloc_globals_ok:
    ∀ prog m, ge = Genv.globalenv prog ->
              Genv.init_mem prog = Some m ->
      list_norepet (map fst prog.(prog_defs)) ->
    ∀ b ab log,
      ab_alloc_globals NumDom ge prog.(prog_defs) = ((b, ab), (nil, log)) ->
      b = plength prog.(prog_defs) /\
    match ab with
      | Bot => False
      | NotBot ab =>
        msizes_gamma ge nil (ab_sz ab) /\
        ab_stk ab = Just nil /\
        mem_as_fun ge (nil, m) ∈ tnum_gamma _ ge nil (ab_nm ab)
    end.
Proof.
    intros. generalize (nth_def_block _ H H1).
    revert ab b log H2. clear H1.
    induction (prog_defs prog) using rev_ind.
    - intros. inv H2. simpl. split. auto.
      generalize (tnum_top_correct NumDom (Genv.globalenv prog) nil (mem_as_fun (Genv.globalenv prog) (nil, m))).
      unfold top, WProd.top_prod_bot, top, top_bot. destruct mach_top eqn:?. auto.
      simpl. intro. repeat (split;[auto; apply top_correct|]). intuition.
    - intros ab b log H2 H1.
      destruct x as (id & gd).
      edestruct H1 as ( H3 & H4 ).
      rewrite map_app, app_nth2, map_length, minus_diag. reflexivity.
      rewrite map_length. auto.
      unfold ab_alloc_globals in H2. rewrite am_fold_app in H2. revert log H2.
      apply am_bind_case.
      intros; eq_some_inv; subst; eauto.
      intros; eq_some_inv.
      intros [b' ab'] log Hb'ab'.
      edestruct IHl.
      + apply Hb'ab'.
      + intros. apply H1. rewrite map_app.
        destruct (le_lt_dec (List.length (map Some l)) idx).
        rewrite nth_overflow in H2 by auto. discriminate.
        rewrite app_nth1 by auto. auto.
      + subst b'. clear Hb'ab'.
        unfold am_fold. simpl. destruct gd as [ gf |gv ].
        * intros log' eq. eq_some_inv. subst. split.
          now rewrite plength_snoc.
          destruct ab' as [|(? & ? & ? & ?)]. contradiction.
          destruct H5 as (? & ? & ? & ?). simpl.
          split; [|split; [|split]]; auto.
          red. intros*. unfold ABTreeDom.get. setoid_rewrite ABTree.gsspec.
          destruct ABTree.elt_eq. 2: eauto.
          subst b. intro eq. inversion eq. clear eq. subst hi perm vol.
          split. intros ofs Hofs. assert (ofs = 0) by Psatz.lia. clear Hofs. subst ofs.
          eapply Genv.init_mem_characterization_2. 2: eassumption.
          apply do_opt_some_inv in H8. hsplit. simpl in *. eq_some_inv. subst b'.
          rewrite plength_length in H3. exact H3.
          exact I.
        *
          eapply am_bind_case.
          intros; eq_some_inv; subst; eauto.
          intros; eq_some_inv.
          intros x log'' AAG log' eq. eq_some_inv. subst x b. split.
          now rewrite plength_snoc.
          destruct ab' as [|ab']. contradiction.
          hsplit.
          eapply ab_alloc_global_ok; eauto.
          erewrite Genv.find_invert_symbol; eauto.
          rewrite plength_length; eauto.
  Qed.

  Lemma check_volatile_sound {tgt κ ab} :
    ∀ μ log, check_volatile NumDom ge max_concretize tgt κ ab = (μ, (nil, log)) →
         match ab_stk ab with Just ( _ :: σ ) => μ = plength σ | _ => False end
         match tgt with
         | inl (g, ofs) =>
           ∃ b, Genv.find_symbol ge g = Some b
           ∃ hi perm, ABTreeDom.get (ABglobal b) (ab_sz ab) = Just (hi, (perm, true))
         | inr addr =>
           ∃ tn ty ne, am_get (ab_eval_expr NumDom ge max_concretize ab addr) = Some (NotBot (tn, Just ty, ne)) ∧
           ∃ ptr,
             ty = TyPtr (Just ptr) ∧
              validate_volatile_ptr ge (ab_sz ab) ptr ne true
         end.
Proof.
    intros μ.
    unfold check_volatile.
    apply am_bind_case.
    intros; eq_some_inv; subst; eauto.
    intros; eq_some_inv.
    intros () log H.
    destruct (ab_stk _) as [ | [ | _ σ ] ]. easy. easy.
    split. inv H0. auto.
    destruct tgt as [ (g, ofs) | addr ].
    - destruct (Genv.find_symbol _ _) as [ b | ]; eq_some_inv.
      exists b; split. easy.
      destruct (ABTreeDom.get _ _) as [ | (?, (?, ())) ]; eq_some_inv.
      vauto.
    - revert log H. apply am_bind_case.
      intros; eq_some_inv; subst; eauto.
      intros; eq_some_inv.
      intros [ | ((tn & ty) & ne) ] log EV. easy.
      destruct ty as [ | [ | | | | | [ | ptr ] | [ | ptr ] | ] ]; try easy;
      intros log' H;
      eexists tn, _, ne;
      (split; [ now rewrite EV | eexists; split; [ vauto | ] ]).
      destruct validate_volatile_ptr. reflexivity. eq_some_inv.
  Qed.

  Lemma only_global_pointer_iff aptr :
    only_global_pointer ge aptr
    ∃ ptr, aptr = Just ptr
           ∀ p, BlockSet.mem p ptr → ∃ b, p = ABglobal b ∧ ∃ g, Genv.invert_symbol ge b = Some gSenv.public_symbol ge g.
Proof.
    unfold only_global_pointer.
    intros H.
    destruct aptr as [ | ptr ]. easy.
    exists ptr. split. easy.
    apply negb_true_iff in H. rewrite BSO.existsb_forall in H.
    intros p Hp. specialize (H p Hp).
    destruct p as [ ? ? | b ]. eq_some_inv.
    exists b. split. easy.
    destruct (Genv.invert_symbol ge b) as [ g | ] eqn: Hg. 2: eq_some_inv.
    apply negb_false_iff in H.
    vauto.
  Qed.

  Definition alloc_variables' e m v :=
    List.fold_lefte_m id_sz,
                    let '(e, m) := e_m in
                    let '(id, sz) := id_sz in
                    let '(m₁, b₁) := Mem.alloc m 0 sz in
                    (PTree.set id (b₁, sz) e, m₁)) v (e, m).

  Lemma alloc_variables_alt v:
    ∀ e m e' m',
      alloc_variables e m v e' m' ↔ alloc_variables' e m v = (e', m').
Proof.
    induction v as [|(id, sz) v IH].
    - intros e m e' m'. split; intros H.
      inv H. reflexivity.
      compute in H. eq_some_inv. subst. vauto.
    - intros e m e' m'.
      unfold alloc_variables'. simpl.
      destruct (Mem.alloc m 0 sz) as (m₁, b₁) eqn: H.
      change (alloc_variables e m ((id, sz) :: v) e' m' ↔ alloc_variables' (PTree.set id (b₁, sz) e) mv = (e', m')).
      etransitivity. 2: apply IH.
      split; intros H'. inv H'. congruence.
      vauto.
  Qed.

  Definition alloc_var := alloc_variables' empty_env.
  Lemma alloc_var_alt m v e' m' :
    alloc_variables empty_env m v e' m' ↔ alloc_var m v = (e', m').
Proof.
apply alloc_variables_alt. Qed.

  Lemma alloc_var_snoc m v id sz :
    alloc_var m (v ++ (id, sz) :: nil)%list =
    let '(e, m') := alloc_var m v in
    let '(m₁, b₁) := Mem.alloc m' 0 sz in
    (PTree.set id (b₁, sz) e, m₁).
Proof.
apply fold_left_app. Qed.

  Lemma alloc_var_guts m v:
    ∀ e' m',
    alloc_var m v = (e', m') →
    ∀ b,
      Mem.valid_block m b
      Mem.valid_block m' b
      (Mem.mem_contents m') !! b = (Mem.mem_contents m) !! b
      (Mem.mem_access m') !! b = (Mem.mem_access m) !! b.
Proof.
    induction v as [|(id, sz) v IH] using rev_ind.
    intros e' m' H b H0. compute in H. eq_some_inv. subst. auto.
    intros e' m' H b H0. rewrite alloc_var_snoc in H.
    destruct (alloc_var m v) as (e₀, m₀).
    destruct (Mem.alloc m₀ 0 sz) as (m₁, b₁) eqn: H₁. eq_some_inv. subst.
    specialize (IH _ _ eq_refl _ H0). destruct IH as (V & IHc & IHa).
    split. apply (Mem.valid_block_alloc _ _ _ _ _ H₁). exact V.
    Transparent Mem.alloc. unfold Mem.alloc in H₁. Opaque Mem.alloc.
    eq_some_inv. subst. simpl.
    split; (etransitivity; [ apply PMap.gso, Plt_ne, V | eassumption ]).
  Qed.

  Corollary alloc_var_load m v:
    ∀ e' m',
    alloc_var m v = (e', m') →
    ∀ b,
      Mem.valid_block m b
      ∀ κ o,
        Mem.load κ m' b o = Mem.load κ m b o.
Proof.
    intros e' m' H b H0 κ o.
    Transparent Mem.load. unfold Mem.load. Opaque Mem.load.
    destruct (proj2 (alloc_var_guts v H H0)) as (Hc & Ha).
    assert (∀ κ o p, Mem.valid_access m κ b o p <-> Mem.valid_access m' κ b o p) as Hv.
    { clear - Ha. intros κ o p. unfold Mem.valid_access, Mem.range_perm, Mem.perm. rewrite Ha. reflexivity. }
    repeat case (Mem.valid_access_dec); auto. congruence.
    clear -Hv. intros X Y; elim X. apply Hv, Y.
    clear -Hv. intros Y X; elim X. apply Hv, Y.
  Qed.

  Lemma alloc_variables_dom e m v e' m' :
    alloc_variables e m v e' m' →
    ∀ x, dom x e' = dom x e || in_dec peq x (List.map fst v).
Proof.
    clear.
    induction 1. intros; simpl; rewrite orb_false_r; reflexivity.
    intros x. simpl. rewrite (IHalloc_variables x). clear.
    rewrite dom_set.
    destruct peq. destruct peq. rewrite orb_true_r. reflexivity. intuition.
    simpl. destruct (dom x e). reflexivity. simpl.
    destruct peq. intuition. destruct (in_dec); reflexivity.
  Qed.

  Lemma in_elements_set {A} e x y (v: A):
    In x (PTree.elements (PTree.set y v e)) →
    x = (y, v) ∨ In x (PTree.elements e) ∧ fst xy.
Proof.
    destruct x as (x & v').
    intros H.
    apply PTree.elements_complete in H. rewrite PTree.gsspec in H. destruct peq. eq_some_inv. left. f_equal; auto.
    right. split. apply PTree.elements_correct; auto. easy.
  Qed.

  Lemma alloc_variables_perm_mono m e v e' m':
    alloc_variables e m v e' m' →
    ∀ b o p,
      Mem.perm m b o Cur pMem.perm m' b o Cur p.
Proof.
    induction 1; auto.
    intros b o p H1. eapply IHalloc_variables.
    eapply Mem.perm_alloc_1; eauto.
  Qed.

  Lemma alloc_variables_perm' e m v e' m':
    alloc_variables e m v e' m' →
  ∀ b hi, In (b, 0, hi) (blocks_of_env e') →
          In (b, 0, hi) (blocks_of_env e) ∨ Mem.range_perm m' b 0 hi Cur Freeable.
Proof.
    induction 1; intros b hi IN. left; exact IN.
    destruct (IHalloc_variables _ _ IN) as [IN'|R]; auto.
    clear IHalloc_variables.
    unfold blocks_of_env in IN'.
    rewrite in_map_iff in IN'. destruct IN' as (q & Hq & IN').
    apply in_elements_set in IN'. destruct IN' as [ -> | (IN' & Hq') ].
    2: left; apply in_map_iff; vauto.
    simpl in Hq. eq_some_inv. subst. right.
    intros o Ho. eapply Mem.perm_alloc_2 in H; eauto.
    eapply alloc_variables_perm_mono; eauto.
  Qed.

  Corollary alloc_variables_perm m v e m':
    alloc_variables empty_env m v e m' →
    ∀ b hi, In (b, 0, hi) (blocks_of_env e) →
            Mem.range_perm m' b 0 hi Cur Freeable.
Proof.
    intros H b hi H0.
    destruct (alloc_variables_perm' H b hi H0) as [K|K]. elim K. exact K.
  Qed.

  Lemma alloc_variables_env' e m v e' m':
    alloc_variables e m v e' m' →
    list_norepet (List.map fst v) →
    ∀ x y, PTree.get x e' = Some y
           PTree.get x e = Some yassoc x v = Some (snd y).
Proof.
    induction 1. auto.
    intros NR x y H1. inv NR. destruct (IHalloc_variables H5 _ _ H1) as [K|K].
    rewrite PTree.gsspec in K. destruct peq. eq_some_inv. subst. right. simpl. rewrite dec_eq_true. reflexivity.
    left. exact K. simpl. rewrite dec_eq_false. auto.
    intro; apply H4. apply assoc_in in K. rewrite in_map_iff. subst. eexists; split; eauto; reflexivity.
  Qed.

  Corollary alloc_variables_env m v e m':
    alloc_variables empty_env m v e m' →
    list_norepet (List.map fst v) →
    ∀ x y, PTree.get x e = Some y
           assoc x v = Some (snd y).
Proof.
    intros H H0 x y H1. destruct (alloc_variables_env' H H0 _ H1) as [X|X]; eauto. rewrite PTree.gempty in X. eq_some_inv.
  Qed.

  Lemma alloc_variables_load' e m v :
    ∀ e' m',
      alloc_variables e m v e' m' →
      ∀ x y,
        e' ! x = Some y
        e ! x = Some yMem.valid_block m' (fst y) ∧ ∀ o, ZMap.get o (Mem.mem_contents m') !! (fst y) = Undef.
Proof.
    setoid_rewrite alloc_variables_alt.
    induction v as [|(id, sz) v IH] using rev_ind.
    intros e' m' H x y H0. compute in H. eq_some_inv. left. congruence.
    intros e' m' H x y H0.
    unfold alloc_variables' in H. rewrite fold_left_app in H.
    assert ((let '(e, m) := alloc_variables' e m v in let '(m₁, b₁) := Mem.alloc m 0 sz in (PTree.set id (b₁, sz) e, m₁)) = (e', m')) as H' by
    exact H.
    clear H.
    destruct (alloc_variables' e m v) as (ee, mm). specialize (IH _ _ eq_refl).
    destruct (Mem.alloc mm 0 sz) as (m₁, b₁) eqn: H₁. eq_some_inv. subst.
    rewrite PTree.gsspec in H0.
    destruct peq. eq_some_inv. subst.
    right. split. apply (Mem.valid_new_block _ _ _ _ _ H₁).
    intros o.
    Transparent Mem.alloc. unfold Mem.alloc in *. Opaque Mem.alloc.
    eq_some_inv. subst. simpl. rewrite PMap.gss. apply ZMap.gi.
    specialize (IH _ _ H0). destruct IH as [IH | [V IH]].
    left; exact IH.
    right. split. exact (Mem.valid_block_alloc _ _ _ _ _ H_ V).
    intros o.
    specialize (IH o).
    Transparent Mem.alloc. unfold Mem.alloc in *. Opaque Mem.alloc.
    eq_some_inv. subst. simpl. rewrite PMap.gso. exact IH. apply Plt_ne, V.
  Qed.

  Corollary alloc_variables_load m v e m':
    alloc_variables empty_env m v e m' →
    ∀ x y, e ! x = Some y
           Mem.valid_block m' (fst y) ∧ ∀ o, ZMap.get o (Mem.mem_contents m') !! (fst y) = Undef.
Proof.
    intros H x y H0. destruct (alloc_variables_load' H _ H0); auto. rewrite PTree.gempty in *. eq_some_inv.
  Qed.

  Lemma alloc_var_inj m v :
    ∀ e' m', alloc_var m v = (e', m') →
             (Mem.valid_block mMem.valid_block m') ∧
             (∀ x y, e' ! x = Some yMem.valid_block m' (fst y) ∧ ¬ Mem.valid_block m (fst y)) ∧
             injective_mapx, fmap fst (e' ! x)).
Proof.
    induction v as [|(id, sz) v IH] using rev_ind.
    intros e' m' H. compute in H. eq_some_inv. subst. split. easy.
    split; repeat intro; rewrite PTree.gempty in *; simpl in *; eq_some_inv.
    intros e' m' H. rewrite alloc_var_snoc in H.
    destruct (alloc_var m v) as (e₀, m₀). specialize (IH _ _ eq_refl).
    destruct (Mem.alloc m₀ 0 sz) as (m₁, b₁) eqn: H₁. eq_some_inv. subst.
    destruct IH as (M & V & IH).
    split. intros b B. eapply Mem.valid_block_alloc; eauto.
    split. intros x y H. rewrite PTree.gsspec in H. destruct peq. eq_some_inv. subst.
    split. exact (Mem.valid_new_block _ _ _ _ _ H₁). intros X.
    apply (Mem.fresh_block_alloc _ _ _ _ _ H₁), M, X.
    destruct (V _ _ H); intuition.
    apply (Mem.valid_block_alloc _ _ _ _ _ H₁). eauto.
    intros x x' a Ha Ha'.
    rewrite PTree.gsspec in *. destruct peq. simpl in *. eq_some_inv. subst id a.
    destruct peq. easy.
    destruct (e₀ ! x') as [(?,?)|] eqn: Hx'; simpl in *; eq_some_inv; subst.
    exfalso. generalize (proj1 (V _ _ Hx')). exact (Mem.fresh_block_alloc _ _ _ _ _ H₁).
    destruct peq. simpl in *. eq_some_inv. subst.
    destruct (e₀ ! x) as [(?,?)|] eqn: Hx; simpl in *; eq_some_inv; subst.
    exfalso. generalize (proj1 (V _ _ Hx)). exact (Mem.fresh_block_alloc _ _ _ _ _ H₁).
    exact (IH _ _ _ Ha Ha').
  Qed.

  Lemma alloc_variables_build vars m m' :
    ∀e',
      alloc_variables empty_env m vars e' m' →
      ∀ x : PSet.elt,
        PSet.mem x (PSetOp.build (rev_map fst vars)) = dom x e'.
Proof.
    rewrite rev_map_correct.
    induction vars as [|v vars IH];
      intros e1 E1; inv E1.
    clear. intros x. unfold dom, empty_env. rewrite PTree.gempty.
    apply not_true_is_false. rewrite (PSetOp.mem_build x nil). auto.
    intros x. rewrite (alloc_variables_dom H6), dom_set, eq_iff_eq_true.
    setoid_rewrite PSetOp.mem_build. simpl.
    unfold dom, empty_env.
    rewrite !Bool.orb_true_iff, in_app, <- in_rev, PTree.gempty.
    split.
    - intros [?|[?|[]]].
      destruct in_dec; intuition.
      destruct peq; intuition.
    - simpl. intros [[?|?]|?].
      destruct peq; intuition. discriminate. destruct in_dec; intuition.
  Qed.

  Remark create_undef_temps_undef l x :
    extendx, (create_undef_temps l) ! x) x = Vundef.
Proof.
    simpl. generalize (create_undef_temps_get l x).
    destruct ((create_undef_temps l) ! x); intuition.
  Qed.

  Lemma push_frame_invariant vars :
    ∀ σ m tmp e m',
      alloc_variables empty_env m vars e m' →
      invariant ge (σ, m) →
      invariant ge ((tmp, e) :: σ, m').
Proof.
    intros σ m tmp e m' Hal Inv. split.
    + (* local freeable *)
      intros t0 e0 [H|H] b hi H0.
      eq_some_inv. subst t0 e0.

      eapply (alloc_variables_perm); eauto.
      intros o Ho. eapply (alloc_variables_perm_mono); eauto.
      eapply Inv.(localFreeable); eauto.
    + (* local not global *)
      intros t0 e0 [H|H] x b y b' z H0 H1.
      2: exact (Inv.(localNotGlobal) _ _ H _ _ H0 H1).
      eq_some_inv. subst t0 e0.
      pose proof (Inv.(globalValid) _ H0) as X.
      pose proof (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) Hal)) as (_ & Y & _).
      specialize (Y _ _ H1). destruct Y as [_ Y]. simpl in *. intros ->. exact (Y X).
    + (* local injective *)
      intros (f, x) (f', x') a. simpl.
      destruct (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) Hal)) as (_ & Q & H).
      rewrite ! get_stk_cons.
      case (Pos.eqb_spec). intros ->. simpl.
      case (Pos.eqb_spec). intros ->. simpl.
      intros H0 H1. f_equal. exact (H _ _ _ H0 H1).
      intros NE H0 H1. apply do_opt_some_inv in H0. destruct H0 as ((a' & al) & H0 & ?). simpl in *. eq_some_inv. subst a'.
      specialize (Q _ _ H0). simpl in Q.
      apply do_opt_some_inv in H1. destruct H1 as (tee & Htee & H1).
      apply do_opt_some_inv in H1. destruct H1 as ((a' & al') & H1 & ?). simpl in *. eq_some_inv. subst a'.
      apply get_stk_in in Htee. destruct tee as (te' & e'). simpl in *.
      assert (In (a, 0, al') (blocks_of_env e')) as Ha. unfold blocks_of_env. rewrite in_map_iff.
      eexists. split. 2: apply PTree.elements_correct, H1. reflexivity.
      elim (proj2 Q). eapply Inv.(localValid); eauto.
      intros NE.
      case (Pos.eqb_spec). intros ->. simpl.
      intros H0 H1.
      apply do_opt_some_inv in H0. destruct H0 as ((te', e') & Htee & H0).
      apply do_opt_some_inv in H0. destruct H0 as ((a', al) & Ha & ?). simpl in *. eq_some_inv. subst a'.
      apply do_opt_some_inv in H1. destruct H1 as ((a', al') & Ha' & ?). simpl in *. eq_some_inv. subst a'.
      specialize (Q _ _ Ha'). simpl in Q. elim (proj2 Q). clear Q.
      apply get_stk_in in Htee. eapply (Inv.(localValid)); eauto.
      intros _.
      exact (Inv.(@localInj _ _) (f, x) (f', x') a).
    + (* global valid *)
      simpl. intros x b H.
      destruct (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) Hal)) as (Q & _). apply Q.
      eapply Inv.(globalValid); eauto.
    + (* local valid *)
      simpl. intros t' e' [H|H] x b z H'. eq_some_inv. subst. apply (proj1 (alloc_variables_load Hal _ H')).
      destruct (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) Hal)) as (Q & _). apply Q.
      eapply Inv.(localValid); eauto.
    + (* no integer fragment *)
      intros ab b H o.
      simpl.
      pose proof (proj1 (alloc_var_alt _ _ _ _) Hal) as Hal'.
      destruct ab as [ f x | b' ].
      revert H. simpl.
      rewrite get_stk_cons.
      case Pos.eqb_spec. intros ->. simpl.
      destruct (e ! x) as [(b', s)|] eqn: Hb'; simpl; intro; eq_some_inv. subst b'.
      now rewrite (proj2 (alloc_variables_load Hal x Hb')).
      intros _ H. generalize (noIntFragment Inv (ABlocal f x) H o). simpl.
      assert (Mem.valid_block m b) as Hv.
      destruct (get_stk _ _) as [(t',e')|] eqn: HIN; simpl in H; eq_some_inv.
      apply get_stk_in in HIN.
      destruct (e' ! x) as [(b', z')|] eqn: HX; eq_some_inv; subst.
      apply (Inv.(localValid) _ _ HIN _ HX).
      rewrite (proj1 (proj2 (alloc_var_guts _ Hal' Hv))). exact id.
      generalize (noIntFragment Inv (ABglobal b') H o). simpl.
      assert (Mem.valid_block m b) as Hv.
      simpl in H. destruct (Genv.invert_symbol _ _) as [g|] eqn: Hg; eq_some_inv; subst b'.
      eapply Inv.(globalValid), Genv.invert_find_symbol, Hg.
      rewrite (proj1 (proj2 (alloc_var_guts _ Hal' Hv))). exact id.
  Qed.

  Lemma push_frame_sizes σ sz vars m t' e' m' :
    invariant ge ((t', e') :: σ, m') →
    list_norepet (map fst vars) →
    alloc_variables empty_env m vars e' m' →
    msizes_gamma ge σ sz
    m' ∈ sizes_gamma ge ((t', e') :: σ) (set_local_sizes (plength σ) vars sz).
Proof.
    intros INV NR AL SZ.
    intros b hi perm vol b' H H'. rewrite set_local_sizes_get in H; auto.
    destruct b as [μ' x|b]; simpl in H'; eq_some_inv.
  - destruct (peq _ _).
    + subst. rewrite get_stk_length in H'. simpl in H'.
      destruct (e' ! x) as [(q, s)|] eqn: Hx; eq_some_inv. subst q.
      erewrite (alloc_variables_env AL NR) in H; eauto. simpl in H. inv H.
      split.
      eapply Mem.range_perm_implies.
      apply (alloc_variables_perm AL). unfold blocks_of_env. rewrite in_map_iff.
      eexists . split. 2: apply PTree.elements_correct, Hx. reflexivity. vauto.
      exact I.
    + rewrite get_stk_cons in H'. rewrite (proj2 (Pos.eqb_neq _ _)) in H'; auto.
      specialize (SZ _ hi perm vol b' H H').
      split.
      intros o Ho. apply (alloc_variables_perm_mono AL).
      exact (proj1 SZ _ Ho).
      exact (proj2 SZ).
  - generalize (SZ _ hi perm vol b' H H').
    apply do_opt_some_inv in H'. hsplit. eq_some_inv. subst b'.
    intros [X Y]. split.
    intros ofs Ho. eapply alloc_variables_perm_mono; eauto.
    easy.
  Qed.

  Lemma push_frame_pt σ tmp e' ty :
    type_gamma (ptset_gamma ge σ) tytype_gamma (ptset_gamma ge ((tmp, e') :: σ)) ty.
Proof.
    intros (); simpl; try easy.
    intros b i.
    destruct ty as [ | | | | | [ | ptr ] | [ | ptr ] | ];
      try exact id;
    (
      intros (b' & Hb' & Hb); exists b'; split; [ exact Hb' | ];
      (destruct b' as [f' x|b']; simpl in *;
         [ apply do_opt_some_inv in Hb;
           destruct Hb as ((t'', e'') & H & Hb);
           apply do_opt_some_inv in Hb;
           destruct Hb as ((b', sz') & Hb & ?);
           eq_some_inv; subst b';
           rewrite (get_stk_tail _ _ _ H); simpl; rewrite Hb; reflexivity
         |
         now hsplit; eq_some_inv; subst; rewrite Hb
      ])
    ).
  Qed.

  Section ARGS_MATCH.
    Local Set Elimination Schemes.
    Variables (f: ident) (en : env) (tmp: temp_env) (σ: list (temp_env * env)) (m: mem) (ρn: cellmach_num).
    Inductive args_match : list exprlist vallist (type+⊤ * list (mexpr cell)) → Prop :=
    | AMnil : args_match nil nil nil
    | AMcons eargs vargs aargs e v ty ln me n :
        vVundef
        veval_expr ge en tmp m (expr_erase e) →
        vtoplift_gamma (type_gamma (ptset_gamma ge ((tmp, en) :: σ))) ty
        In me ln
        nN.ncompat vneval_mexpr ρn me
        (∀ x y, ACtemp x yme_free_var me → (x < Pos.succ (plength σ))%positive) →
        args_match eargs vargs aargs
        args_match (e :: eargs) (v :: vargs) ((ty, ln) :: aargs)
    .

    Lemma am_a_cons_inv eargs vargs a aargs :
      args_match eargs vargs (a :: aargs) →
      ∃ e eargs' v vargs' ty ln me n,
        eargs = e :: eargs' ∧
        vargs = v :: vargs' ∧
        a = (ty, ln) ∧
        vVundef
        veval_expr ge en tmp m (expr_erase e) ∧
        vtoplift_gamma (type_gamma (ptset_gamma ge ((tmp, en) :: σ))) ty
        In me ln
        n ∈ (N.ncompat veval_mexpr ρn me) ∧
        (∀ x y, ACtemp x yme_free_var me → (x < Pos.succ (plength σ))%positive) ∧
        args_match eargs' vargs' aargs.
Proof.
      inversion 1. repeat (econstructor (eauto)).
    Qed.
  End ARGS_MATCH.


  Lemma load_undef {m b} :
    (∀ o, ZMap.get o (Mem.mem_contents m) !! b = Undef) →
    ∀ κ o, extend (Mem.load κ m b) o = Vundef.
Proof.
    intros H κ o. simpl.
    generalize (Mem.load_result κ m b o).
    case Mem.load. 2: reflexivity.
    intros v Hv; specialize (Hv v eq_refl); subst v.
    destruct κ; simpl; rewrite ! H; reflexivity.
  Qed.


  Lemma ab_bind_parameters_sound e t σ m vars e' m' :
    invariant ge ((t, e) :: σ, m) →
    alloc_variables empty_env m vars e' m' →
    ∀ params eargs aargs vargs ρn,
    args_match e t σ m ρn eargs vargs aargs
    ∀ tmp tmp',
    bind_parameters params vargs tmp = Some tmp' →
    ∀ ab ab',
    am_get (ab_bind_parameters NumDom (Pos.succ (plength σ)) params aargs (ret (NotBot ab))) = Some ab' →
    mem_as_fun ge ((tmp, e') :: (t, e) :: σ, m) ∈ points_to_gamma ge ((tmp, e') :: (t, e) :: σ) (ab_tp ab) →
    ρn ∈ (compat (mem_as_fun ge ((tmp, e') :: (t, e) :: σ, m)) ∩ γ(ab_num ab)) →
    ∃ q,
      ab' = NotBot q
      ab_stk q = ab_stk ab
      ab_sz q = ab_sz ab
      mem_as_fun ge ((tmp', e') :: (t, e) :: σ, m') ∈ points_to_gamma ge ((tmp', e') :: (t, e) :: σ) (ab_tp q) ∧
      ∃ ρn', ρn' ∈ (compat (mem_as_fun ge ((tmp', e') :: (t, e) :: σ, m')) ∩ γ (ab_num q)).
Proof.
    intros INV Hm'.
    induction params as [|id params IH];
    intros eargs aargs vargs ρn AM tmp tmp' BIND ab ab' AB_BIND TP (CPT & NM);
    destruct ab as (stk & sz & tp & nm).
    + destruct aargs; simpl in AB_BIND, BIND; unfold ret in AB_BIND; eq_some_inv. subst ab'.
      destruct vargs; eq_some_inv. subst tmp'.
      exists (stk, (sz, (tp, nm))). split. reflexivity. split. reflexivity. split. reflexivity. split; [|exists ρn;split]; auto.
    - intros c. generalize (TP c). simpl.
      destruct (ACTreeDom.get c tp) as [|ty]. exact id.
      destruct c as [f' x ofs|f' x|b ofs]; simpl.
      rewrite ! get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e' ! x) as [(b,h)|] eqn: Hx. simpl.
      pose proof (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) Hm')) as (_ & Y & _).
      specialize (Y _ _ Hx). destruct Y as [_ Y]. simpl in Y.
      rewrite (not_valid_block_load_None Y). intros H. apply type_gamma_inv' in H. contradiction.
      exact id.
      intros NE.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e ! x) as [(b, h)|] eqn: Hx. simpl.
      assert (Mem.valid_block m b) as Hb. eapply INV.(localValid). left. reflexivity. exact Hx.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') Hb). exact id. exact id.
      intros NE'.
      destruct (get_stk f' σ) as [(t'', e'')|] eqn: Hf'. simpl.
      destruct (e'' ! x) as [(b, h)|] eqn: Hx. simpl.
      assert (Mem.valid_block m b) as Hb. eapply INV.(localValid). right. apply (get_stk_in Hf'). exact Hx.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') Hb). exact id. exact id. exact id. exact id.
      destruct (Genv.invert_symbol ge b) eqn: B; auto.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') (INV.(globalValid) _ (Genv.invert_find_symbol _ _ B))).
      exact id.
    - intros c. generalize (CPT c). simpl.
      destruct c as [f' x ofs|f' x|b ofs]; simpl.
      rewrite ! get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e' ! x) as [(b,s)|] eqn: Hx. simpl. intros _.
      pose proof (load_undef (proj2 (alloc_variables_load Hm' _ Hx)) (proj1_sig κ) ofs) as X. simpl in X. rewrite X. vauto. exact id.
      intros NE.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e ! x) as [(b,s)|] eqn: Hx. simpl.
      assert (Mem.valid_block m b) as Hb. eapply INV.(localValid). left. reflexivity. exact Hx.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') Hb).
      exact id. exact id.
      intros NE'.
      destruct (get_stk f' σ) as [(t'', e'')|] eqn: Hf'. simpl.
      destruct (e'' ! x) as [(b, h)|] eqn: Hx. simpl.
      assert (Mem.valid_block m b) as Hb. eapply INV.(localValid). right. apply (get_stk_in Hf'). exact Hx.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') Hb). exact id. exact id. exact id. exact id.
      destruct (Genv.invert_symbol ge b) eqn: B; auto.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) Hm') (INV.(globalValid) _ (Genv.invert_find_symbol _ _ B))).
      exact id.

    + destruct aargs as [|a aargs']. simpl in AB_BIND; unfold ret in AB_BIND; eq_some_inv.
      destruct (am_a_cons_inv AM) as
          (ex & eargs' & v & vargs' & ty & ln & me & n & -> & -> & -> & Def & EV & Vtp & Hme & (Ncpt & EVn) & Hfv & AM').
      assert (args_match e t σ m (upd ρn (ACtemp (Pos.succ (plength σ)) id) n) eargs' vargs' aargs') as AM''.
      {
        clear -AM' Hfv.
        induction AM'. constructor.
        econstructor; eauto. eapply eval_mexpr_fv. 2: eassumption.
        intros x H7. unfold upd. destruct (eq_dec x); auto.
        subst. specialize (H5 _ _ H7). Psatz.lia.
      }
      specialize (IH _ _ _ _ AM'' _ _ BIND).
      simpl in AB_BIND.
      assert (upd ρn (ACtemp (Pos.succ (plength σ)) id) n ∈ γ
        (union_list_mapne0, AbMachineEnv.assign (ACtemp (Pos.succ (plength σ)) id) ne0 nm) (NotBot nm) ln)).
      { rewrite union_list_map_correct.
        eapply union_list_correct. refine _. apply in_map, Hme.
        apply AbMachineEnv.assign_correct; eauto. }
      destruct (union_list_map _ _ _) as [|nm'] eqn: Hnm'. contradiction.
      specialize (IH _ _ AB_BIND).
      destruct IH as ((stk' & sz' & tp' & nm'') & -> & Hstk' & Hsz' & Htp' & Hq).
      simpl.
      * intros c. unfold ct_upd. rewrite ACTreeDom.gsspec. rewrite ct_forget_all_get.
        destruct (ACTree.elt_eq c). subst c. simpl.
        rewrite get_stk_cons, plength_cons. rewrite Pos.eqb_refl. simpl. rewrite PTree.gss.
        destruct ty. exact I. simpl. apply push_frame_pt; eauto.
        destruct in_dec. exact I.
        simpl. rewrite read_cell_upd.
        generalize (TP c). simpl.
        destruct (ACTreeDom.get). exact_, I).
        apply type_gamma_covariant, ptset_gamma_tmp. reflexivity.
        rewrite plength_cons. assumption.
      * split.
        eapply (N.compat_upd _x, x)); eauto.
        intros c. destruct (eq_dec _ _). subst. simpl.
        rewrite get_stk_cons, plength_cons, Pos.eqb_refl. simpl. rewrite PTree.gss. exact eq_refl.
        simpl.
        rewrite read_cell_upd. auto. now rewrite plength_cons. auto.
      * simpl in *. subst. eexists (stk, (sz, (tp', nm''))). vauto.
  Qed.

A (concrete) block is valid given a stack iff in this block is allocated * either a global symbol s * or a local variable x of a function f
  Definition valid_block (stk: list (temp_env * env)) b : Prop :=
     (∃ s, Genv.invert_symbol ge b = Some s)
   ∨ (∃ t e x sz, In (t, e) stke ! x = Some (b, sz)).

  Lemma valid_block_tmp stk stk' :
    stacks_tmp stk stk' →
    valid_block stk' ⊆ valid_block stk.
Proof.
    intros H b [Hb | Hb];[left; exact Hb|right].
    hsplit. apply (stacks_tmp_in H) in Hb.
    hsplit. vauto.
  Qed.

  Lemma pop_local_pt_get μ pt c :
    ACTree.get c (pop_local_pt μ pt) = do ty <- ACTree.get c pt;
    if if is_local_cell μ c
       then false
       else match ty with
            | (TyPtr (Just bs) | TyZPtr (Just bs)) =>
              negb (BSO.existsb (is_local_block μ) bs)
            | TyPtr All | TyZPtr All
            | TyFloat | TySingle | TyLong | TyInt | TyZero | TyIntPtr => true
            end
    then Some ty
    else None.
Proof.
    unfold pop_local_pt.
    rewrite ACTreeDom.SP.gfilter. auto.
  Qed.

  Lemma pop_frame_pt τ₀ ε₀ σ m m' (tp: points_to) c:
    (∀ b, bvalid_block σ →
          (∀ κ o, Mem.load κ m b o = Mem.load κ m' b o)
    ) →
    mem_as_fun ge ((τ₀, ε₀) :: σ, m) c
               toplift_gamma (type_gamma (ptset_gamma ge ((τ₀, ε₀) :: σ))) (ACTreeDom.get c tp) →
    mem_as_fun ge (σ, m') c
               toplift_gamma (type_gamma (ptset_gamma ge σ))
               (ACTreeDom.get c (pop_local_pt (plength σ) tp)).
Proof.
    intros Hb.
    unfold ACTreeDom.get. rewrite pop_local_pt_get.
    simpl.
    destruct (ACTree.get c tp) as [ty|]. 2: exact id.
    destruct (is_local_cell _ c) eqn: GL. exact_, I).
    simpl. match goal with |- appcontext[ if ?b then _ else _ ] => remember b as case eqn: Hcase end.
    revert Hcase. case case; clear case.
    2: exact_ _, I).
    intros Hcase. symmetry in Hcase.
    destruct c as [f x ofs|f x|b ofs]; simpl in *.
    - destruct (eq_dec _ _); simpl in GL; eq_some_inv. clear GL.
      rewrite get_stk_cons. rewrite (proj2 (Pos.eqb_neq _ _)); auto.
      destruct (get_stk f σ) as [(t, e)|] eqn: Hv. 2: exact id. simpl.
      destruct (e ! x) as [(b, s)|] eqn: Hv'. 2: exact id. simpl. rewrite Hb.
      destruct (Mem.load (proj1_sig κ) m' b ofs) as [()|]; simpl; try exact id.
      destruct ty as [ | | | | | [ | ptr ] | [ | ptr ] | ]; try exact id;
      intros (b' & Hb' & Hb''); exists b'; (split; [ exact Hb' | ]);
      (destruct b' as [g y|b''']; simpl in *; eq_some_inv;
       [ | exact Hb'' ]);
      revert Hb''; rewrite get_stk_cons;
      (rewrite (proj2 (Pos.eqb_neq _ _)); [ exact id | ]);
      apply negb_true_iff in Hcase;
      rewrite BSO.existsb_forall in Hcase; specialize (Hcase _ Hb'); simpl in Hcase;
      clear -Hcase; intros ->; rewrite eq_dec_true in Hcase; simpl in *; eq_some_inv.
      right. repeat eexists. exact (get_stk_in Hv). exact Hv'.
    - destruct (eq_dec _). simpl in GL; eq_some_inv.
      rewrite get_stk_cons. rewrite (proj2 (Pos.eqb_neq _ _)); auto.
      destruct (get_stk f σ) as [(t, e)|] eqn: Hv. 2: exact id. simpl.
      destruct (t ! x) as [v|] eqn: Hv'. 2: exact id.
      destruct v; try exact id.
      destruct ty as [ | | | | | [ | ptr ] | [ | ptr ] | ]; try exact id;
      intros (b' & Hb' & Hb''); exists b'; (split; [ exact Hb' | ]);
      (destruct b' as [g y|b''']; simpl in *; eq_some_inv;
       [ | exact Hb'' ]);
      revert Hb''; rewrite get_stk_cons;
      (rewrite (proj2 (Pos.eqb_neq _ _)); [ exact id | ]);
      apply negb_true_iff in Hcase;
      rewrite BSO.existsb_forall in Hcase; specialize (Hcase _ Hb'); simpl in Hcase;
      clear -Hcase; intros ->; rewrite eq_dec_true in Hcase; simpl in *; eq_some_inv.
    - destruct (Genv.invert_symbol ge b) as [s|] eqn: Hs. 2: exact id.
      rewrite Hb. simpl.
      destruct (Mem.load (proj1_sig κ) m' b ofs) as [()|]; simpl; try exact id.
      destruct ty as [ | | | | | [ | ptr ] | [ | ptr ] | ]; try exact id;
      intros (b' & Hb' & Hb''); exists b'; (split; [ exact Hb' | ]);
      (destruct b' as [g y|b''']; simpl in *; eq_some_inv;
       [ | exact Hb'' ]);
      revert Hb''; rewrite get_stk_cons;
      (rewrite (proj2 (Pos.eqb_neq _ _)); [ exact id | ]);
      apply negb_true_iff in Hcase;
      rewrite BSO.existsb_forall in Hcase; specialize (Hcase _ Hb'); simpl in Hcase;
      clear -Hcase; intros ->; rewrite eq_dec_true in Hcase; simpl in *; eq_some_inv.
      left. vauto.
  Qed.

  Lemma pop_frame_compat τ₀ ε₀ σ m m' :
    (∀ b, bvalid_block σ →
          (∀ κ o, Mem.load κ m b o = Mem.load κ m' b o)
    ) →
    compat (mem_as_fun ge ((τ₀, ε₀) :: σ, m)) ⊆
    compat (mem_as_fun ge (σ, m')).
Proof.
    intros Hb ρ H c.
    - generalize (H c). destruct c as [f x|f x|b]; simpl.
      + destruct (get_stk f σ) as [(t,e)|] eqn: Hv. 2: exact_, I).
        rewrite (get_stk_tail _ _ _ Hv). simpl.
        destruct (e ! x) as [(b, s)|] eqn: Hv'. 2: exact id. simpl.
        rewrite Hb. exact id.
        right. repeat eexists; eauto. exact (get_stk_in Hv).
      + destruct (get_stk f σ) as [(t,e)|] eqn: Hv. 2: exact_, I). simpl.
        rewrite (get_stk_tail _ _ _ Hv). exact id.
      + destruct (Genv.invert_symbol ge b) eqn: Hv. 2: exact id.
          rewrite Hb. exact id.
          left. vauto.
  Qed.

  Lemma pop_frame_invariant τ₀ ε₀ σ m m':
    invariant ge ((τ₀, ε₀) :: σ, m) →
    (∀ b, bvalid_block σ →
          (∀ lo hi p, Mem.range_perm m b lo hi Cur pMem.range_perm m' b lo hi Cur p) ∧
          (bMem.valid_block mbMem.valid_block m')
    ) →
    Mem.mem_contents m = Mem.mem_contents m' →
    invariant ge (σ, m').
Proof.
    intros INV HB Hc.
    split.
    - intros t0 e H b hi H0. simpl. apply HB. simpl in H. right.
      unfold blocks_of_env in H0. rewrite in_map_iff in H0. destruct H0 as ((x & b' & s') & Hb' & Hx).
      simpl in Hb'. eq_some_inv. subst.
      apply PTree.elements_complete in Hx. repeat econstructor; eauto.
      eapply INV. right. exact H. easy.
    - intros t0 e H. eapply INV. right. exact H.
    - intros x x' a H H'.
      generalize (INV.(@localInj _ _) x x' a).
      destruct x as (f, x), x' as (f', x'). simpl in *.
      destruct (get_stk f σ) as [(t,e)|] eqn: Hte; eq_some_inv.
      rewrite (get_stk_tail _ _ _ Hte).
      destruct (get_stk f' σ) as [(t',e')|] eqn: Hte'; eq_some_inv.
      rewrite (get_stk_tail _ _ _ Hte').
      simpl in *. auto.
    - intros x b H. generalize (INV.(globalValid) _ H). simpl.
      refine (proj2 (HB _ _)).
      left. apply Genv.find_invert_symbol in H. vauto.
    - intros t0 e H x b z H0. apply HB. vauto2. eapply INV.(localValid); eauto. right. eassumption.
    - simpl.
      intros ab b H o.
      assert (block_of_ablock ge ((τ₀, ε₀) :: σ) ab = Some b) as H'.
      { destruct ab as [ f x | b' ]. 2: exact H. simpl.
        rewrite get_stk_cons.
        case Pos.eqb_spec. 2: intros _; exact H.
        intros ->. exfalso. revert H. simpl.
        generalize (get_stk_lt (plength σ) σ).
        case get_stk. intros p X _; specialize (X _ eq_refl). apply Plt_ne in X. exact (X eq_refl).
        intros; eq_some_inv. }
      generalize (INV.(noIntFragment) ab H' o).
      simpl. rewrite Hc. exact id.
  Qed.

  Lemma pop_frame_sizes τ₀ (ε₀: env) σ m m' (sz: sizes) ε':
    invariant ge ((τ₀, ε₀) :: σ, m) →
    (∀ b, bvalid_block σ →
          (∀ lo hi p, Mem.range_perm m b lo hi Cur pMem.range_perm m' b lo hi Cur p)
    ) →
    msizes_gamma ge ((τ₀, ε₀) :: σ) sz
    m' ∈ sizes_gamma ge σ (clear_local_sizes (plength σ) ε' sz).
Proof.
    intros INV HB SZ.
    intros b hi perm vol b'.
    rewrite clear_local_sizes_get.
    intros Hhi Hb.
    split.
  - eapply HB;[|eapply (SZ b hi perm vol b')];
    destruct b as [f x|b];
    simpl in Hb; unfold is_local in Hb; eq_some_inv; auto.
    destruct (get_stk f σ) as [(t,e)|] eqn: Hv; eq_some_inv.
    destruct (e ! x) as [(b,s)|] eqn: Hv'; eq_some_inv. right. subst. repeat econstructor. exact (get_stk_in Hv). exact Hv'.
    left. destruct (Genv.invert_symbol ge b) eqn: Hs; eq_some_inv; subst. vauto.
    revert Hhi. destruct (peq _ f && PSet.mem x ε'). discriminate. auto.
    simpl.
    destruct (get_stk f σ) as [(t,e)|] eqn: Hv; eq_some_inv.
    now rewrite (get_stk_tail _ _ _ Hv).
  - specialize (SZ b). destruct b as [ μ' x | g ].
    destruct (peq _ _).
    + clear SZ. simpl in Hb.
      generalize (get_stk_lt μ' σ). destruct (get_stk _ _).
      intros X. specialize (X _ eq_refl). subst. Psatz.lia. eq_some_inv.
    + simpl in Hhi. apply (SZ _ _ _ b' Hhi). simpl.
      rewrite get_stk_cons, (proj2 (Pos.eqb_neq _ _)); auto.
    + apply (SZ _ _ _ b' Hhi Hb).
  Qed.

  Lemma free_list_ex tmp env stk m :
    invariant ge ((tmp, env) :: stk, m) →
    ∃ m', Mem.free_list m (blocks_of_env env) = Some m' ∧
          ∀ b, valid_block stk b
               (∀ κ ofs, Mem.load κ m b ofs = Mem.load κ m' b ofs) ∧
               (∀ lo hi p, Mem.range_perm m b lo hi Cur pMem.range_perm m' b lo hi Cur p) ∧
               (bMem.valid_block mbMem.valid_block m').
Proof.
    intros INV.
    simpl in *.
    assert (∀ x x' b, fmap fst env ! x = Some bfmap fst env ! x' = Some bx = x') as INJ.
    { intros x x' b H H0.
      pose proof (INV.(@localInj _ _) (plength stk, x) (plength stk, x') b) as INJ'.
      simpl in INJ'. rewrite (get_stk_length) in INJ'. simpl in INJ'.
      specialize (INJ' H H0). eq_some_inv. assumption.
    }
    assert (∀ x f' x' b b', fmap fst env ! x = Some b
                            (do t_e <- get_stk f' stk;
                             fmap fst (snd t_e) ! x') = Some b' →
                            bb') as INJ₀.
    { intros x f' x' b b' H H0 <-.
      pose proof (INV.(@localInj _ _) (plength stk, x) (f', x') b) as INJ'.
      simpl in INJ'. rewrite get_stk_length in INJ'.
      destruct (get_stk f' stk) as [(t,e)|] eqn: Hte; eq_some_inv.
      rewrite (get_stk_tail _ _ _ Hte) in INJ'. simpl in INJ'.
      specialize (INJ' H H0). simpl in *. eq_some_inv. subst.
      apply get_stk_lt in Hte. Psatz.lia.
    }
    assert (injective_mapx_v : ident * positive,
            do t_e <- get_stk (fst x_v) stk;
            fmap fst (snd t_e) ! (snd x_v))) as INJ₁.
    { intros (f, x) (f', x'). simpl. intros a H H0.
      pose proof (INV.(@localInj _ _) (f, x) (f', x') a) as INJ'. simpl in *.
      destruct (get_stk f stk) as [(t,e)|] eqn: Hte; eq_some_inv.
      rewrite (get_stk_tail _ _ _ Hte) in INJ'.
      destruct (get_stk f' stk) as [(t',e')|] eqn: Hte'; eq_some_inv.
      rewrite (get_stk_tail _ _ _ Hte') in INJ'.
      simpl in INJ'. auto.
    }
    generalize (INV.(localFreeable) _ _ (or_introl eq_refl)).
    generalize (INV.(localNotGlobal) _ _ (or_introl eq_refl)).
    setoid_rewrite (ptree_get_assoc env).
    generalizet e H, INV.(localFreeable) t e (or_intror H)).
    generalizet e H, INV.(localNotGlobal) t e (or_intror H)).
    setoid_rewrite (ptree_get_assoc env) in INJ.
    setoid_rewrite (ptree_get_assoc env) in INJ₀.
    generalize (PTree.elements_keys_norepet env).
    unfold blocks_of_env in *.
    generalize dependent (PTree.elements env).
    simpl.
    clear INV env.
    intros l.
    revert m.
    induction l as [|(x, (b, hi)) l IH]. intros m _ _ _ _. simpl. vauto.
    intros m INJ INJNR' LG LF LG' LF'.
    simpl in *. inv NR'.
    destruct (Mem.free m b 0 hi) as [m₁|] eqn: M₁.
    destruct (IH m₁) as (m' & M' & IH'); clear IH.
  + intros x0 x' b0 H H0.
    specialize (INJ x0 x' b0).
    assert (xx0).
    { intros ->.
      generalize (assoc_in x0 l). destruct (assoc x0 l).
      intros X. specialize (X _ eq_refl). apply H1.
      rewrite in_map_iff. eexists (x0, _). vauto.
      simpl in *. eq_some_inv.
    }
    assert (xx').
    { intros ->.
      generalize (assoc_in x' l). destruct (assoc x' l).
      intros X. specialize (X _ eq_refl). apply H1.
      rewrite in_map_iff. eexists (x', _). vauto.
      simpl in *. eq_some_inv.
    }
    rewrite !dec_eq_false in INJ; auto.
  + intros x0 f' x' b0 b' H H0.
    specialize (INJx0 f' x' b0 b').
    rewrite dec_eq_false in INJ₀.
    eauto. intros ->.
    generalize (assoc_in x0 l). destruct (assoc x0 l).
    intros X. specialize (X _ eq_refl). apply H1.
    rewrite in_map_iff. eexists (x0, _). vauto.
    simpl in *. eq_some_inv.
  + eauto.
  + eauto.
  + intros t0 e H b0 hi0 H0 ofs X.
    specialize (LF _ _ H _ _ H0 ofs X).
    eapply Mem.perm_free_1; eauto. left.
    rewrite in_map_iff in H0. destruct H0 as ((x' & b' & sz) & ? & H').
    simpl in *. eq_some_inv. subst.
    cut (bb0). auto.
    apply in_get_stk in H. destruct H as (n & Hn).
    apply (INJx n x'). rewrite dec_eq_true. reflexivity.
    apply (In_norepet_assoc _ _ _ (PTree.elements_keys_norepet _)) in H'.
    rewrite Hn.
    simpl. rewrite ptree_get_assoc.
    simpl. unfold PTree.elt in *. now rewrite H'.
  + intros x0 b0 y b' z H H0.
    eapply (LG' _ _ y); try eassumption.
    rewrite dec_eq_false. eassumption.
    intros ->. apply H1.
    apply assoc_in in H0. rewrite in_map_iff.
    eexists (y, _). vauto.
  + intros b0 hi0 H ofs Hofs.
    eapply Mem.perm_free_1; eauto. left.
    rewrite in_map_iff in H. destruct H as ((x' & b' & sz) & H & H').
    simpl in *. eq_some_inv. intro; subst.
    destruct (eq_dec x x'). subst.
    apply H1. rewrite in_map_iff. eexists (x', _). vauto.
    specialize (INJ x x' b).
    apply (In_norepet_assoc _ _ _ H2) in H'.
    rewrite dec_eq_true, dec_eq_false in INJ; auto.
    unfold PTree.elt in *.
    rewrite H' in INJ.
    specialize (INJ eq_refl eq_refl).
    auto.
    eapply LF'. vauto. eauto.
  + exists m'. split. easy.
    intros b0 [(s & H0)|( t & e & y & sz & LOC & H0)].
    - assert (b0b).
      { eapply LG'; eauto. apply Genv.invert_find_symbol; eauto.
        rewrite dec_eq_true. reflexivity. }
      split;[|split].
    * intros κ ofs. etransitivity. 2: eapply IH'; vauto. clear IH'.
      symmetry. eapply Mem.load_free; eauto.
    * intros lo0 hi0 p H'.
      eapply IH'. vauto.
      intros ofs Hofs; eapply Mem.perm_free_1; eauto.
    * intros H5. eapply IH'. vauto. eapply Mem.valid_block_free_1; eauto.
    - assert (b0b).
      {apply (in_get_stk) in LOC. destruct LOC as (f & Hf).
        generalize (INJx f y b b0). rewrite dec_eq_true. intros X.
        specialize (X eq_refl). rewrite Hf in X. simpl in *.
        rewrite H0 in X.
        specialize (X eq_refl).
        auto. }
      split;[|split].
    * etransitivity. 2: eapply IH'; eauto. clear IH'.
      symmetry. eapply Mem.load_free; eauto.
      vauto2.
    * intros lo0 hi0 p H5.
      eapply IH'. vauto2.
      intros ofs Hofs; eapply Mem.perm_free_1; eauto.
    * intros H5. eapply IH'. vauto2. eapply Mem.valid_block_free_1; eauto.
  +
    Transparent Mem.free.
    unfold Mem.free in M₁.
    Opaque Mem.free.
    destruct Mem.range_perm_dec; eq_some_inv. intuition.
  Qed.

  Lemma nm_forget_all_weaken cells nm :
    γ(nm) ⊆ γ(nm_forget_all NumDom cells nm).
Proof.
    induction cells as [ | c cells IH ] using rev_ind. exact_, id).
    intros ρ. unfold nm_forget_all. rewrite fold_left_app. simpl. fold (nm_forget_all NumDom cells nm).
    destruct (nm_forget_all _ _); intros K. exact (IH _ K).
    eapply (forget_correct'); eauto.
  Qed.

  Lemma pop_local_num_correct μ ε τ sz nm:
    γ nm ⊆ γ(pop_local_num NumDom μ ε τ sz (NotBot nm)).
Proof.
eauto using nm_forget_all_weaken. Qed.

  Lemma ab_alloc_global_ex:
    ∀ ab gv id m ab' log,
      ab_alloc_global NumDom ge (Genv.perm_globvar gv) (gvar_volatile gv) (Mem.nextblock m) gv.(gvar_init) ab = (ab', (nil, log)) ->
      ∃ m', Genv.alloc_global ge m (id, Gvar gv) = Some m'.
Proof.
    intros * EQ_AB. simpl.
    destruct (Mem.alloc m 0 (Genv.init_data_list_size (gvar_init gv))) eqn:EQ_ALLOC.
    assert (STORE:=EQ_ALLOC). eapply alloc_can_store in STORE. 2:apply init_data_list_size_pos.
    destruct STORE as [m' STORE]. rewrite STORE.
    erewrite <- Mem.alloc_result in EQ_AB by eauto.
    assert (PERM:=Mem.perm_alloc_2 _ _ _ _ _ EQ_ALLOC).
    setoid_rewrite (Genv.store_zeros_perm _ _ _ _ _ _ _ _ STORE) in PERM.
    destruct (Genv.store_init_data_list ge m' b 0 (gvar_init gv)) as [m''|] eqn:STORELST.
    { setoid_rewrite (Genv.store_init_data_list_perm _ _ _ _ _ _ _ _ _ STORELST) in PERM.
      destruct (Mem.range_perm_drop_2 m'' b 0 (Genv.init_data_list_size (gvar_init gv))
                                      (Genv.perm_globvar gv)).
      intro. apply PERM. eauto. }
    exfalso.
    revert ab log EQ_AB PERM STORELST. generalize (gvar_init gv). clear.
    intros l ab. unfold ab_alloc_global.
    change (Genv.init_data_list_size l) with (0 + Genv.init_data_list_size l).
    apply am_bind_case.
    intros; eq_some_inv; subst; eauto.
    intros; eq_some_inv.
    intros. clear log EQ_AB ab'.
    revert ab m' x l0 H PERM STORELST.
    generalize 0 at 7 8 9 10.
    induction l as [|id idl].
    - simpl. discriminate.
    - simpl.
      change (id::idl) with ((id::nil)++idl)%list.
      intros z ab m' x.
      rewrite am_fold_app.
      apply am_bind_case.
      intros; eq_some_inv; subst; eauto.
      intros; eq_some_inv.
      intros x0 l H l0 H0 PERM STORELST.
      unfold am_fold in H. simpl in H. destruct x0.
      destruct (Genv.store_init_data ge m' b z id) eqn:EQSTORE.
      + assert (z0 = z + Genv.init_data_size id).
        { clear H0.
          match type of H with appcontext[if ?b then _ else _] => destruct b end.
          destruct id; try now inv H.
          destruct (Genv.find_symbol ge i); inv H; auto.
          inv H. }
        subst z0.
        eapply IHidl. 3:eauto.
        apply H0. intros.
        assert (PERM':Mem.perm m' b ofs k Freeable).
        { apply PERM. pose proof Genv.init_data_size_pos id. omega. }
        clear - EQSTORE PERM'. destruct id; simpl in EQSTORE;
        eauto using Mem.perm_store_1.
        inv EQSTORE. auto.
        destruct Genv.find_symbol. eauto using Mem.perm_store_1. discriminate.
      + clear IHidl. clear H0.
        pose proof (init_data_list_size_pos idl).
        destruct id; simpl in EQSTORE; [..|discriminate|destruct (Genv.find_symbol ge i)];
        match type of EQSTORE with
          | Mem.store ?c m' b z ?v = None =>
            destruct (Mem.valid_access_store m' c b z v); [
              split;[eapply Mem.range_perm_implies; [
                        repeat intro; eapply PERM;
                        unfold size_chunk, Genv.init_data_size in *;
                        omega
                       |constructor]
                |apply Z.divide_1_l || (destruct Zdivide_dec; [auto|discriminate])]
              |congruence]
          | _ => idtac
        end.
        destruct Zdivide_dec; discriminate.
  Qed.

  Lemma ab_alloc_globals_ex:
    ∀ gl b ab log, ab_alloc_globals NumDom ge gl = ((b, ab), (nil, log)) ->
    ∃ m, Genv.alloc_globals ge Mem.empty gl = Some m /\
         Mem.nextblock m = b.
Proof.
    induction gl using rev_ind.
    - simpl. intros. inv H. eauto.
    - intros ab. unfold ab_alloc_globals.
      rewrite am_fold_app.
      apply am_bind_case.
      intros; eq_some_inv; subst; eauto.
      intros; eq_some_inv.
      intros [b ab'] log Hbab'. destruct (IHgl _ _ _ Hbab') as (m & Hm & Ham). subst b.
      erewrite <- Genv.alloc_globals_app; eauto. simpl.
      unfold am_fold. simpl.
      pose proof Genv.alloc_global_nextblock ge x m.
      destruct x as [id [gf|gv]]. simpl in *.
      + intros.
        destruct (Mem.alloc m 0 1) as [m' b0] eqn:EQm'.
        edestruct alloc_can_drop as [m'' Hm'']. eauto. rewrite Hm''.
        eexists. split. eauto. rewrite H. inv H0. auto. auto.
      + apply am_bind_case.
        intros; eq_some_inv; subst; eauto.
        intros; eq_some_inv.
        intros ab'' log' Hab'' b' log'' eq. eq_some_inv. subst.
        edestruct ab_alloc_global_ex as [m' Hm']; eauto.
        rewrite Hm'. eauto.
  Qed.

  Lemma init_mem_sound :
   ∀ (defs : list (ident * globdef fundef unit)) (a : option concrete_state),
   Init_Mem ge defs a → γ (AbMemSignatureCsharpminor.init_mem _ defs) a.
Proof.
    intros defs a (prog & Hge & Hdefs & H). simpl. subst defs.
    unfold init_mem.
    eapply am_assert_spec. eauto. constructor. intro.
    apply am_bind_case. eauto. constructor. intros [b ab] log AAG. simpl.
    edestruct ab_alloc_globals_ex as (m & EQm & NEXT). eauto.
    erewrite H by eauto.
    rewrite Hge in EQm. apply norepet_map_iff in H0.
    eapply ab_alloc_globals_ok in AAG; eauto.
    destruct AAG.
    destruct ab as [|ab]. contradiction.
    destruct ab as (stk, (sz, (tp, nm))).
    destruct H2 as (? & ? & ? & ?).
    simpl in *. subst stk.
    split;[| | |split]; auto.
  + vauto.
  + intros _. split; try easy.
    intros x b0 Hb0. subst ge.
    eapply Genv.find_symbol_not_fresh; eauto.
    intros ab b0 H3.
    exact (init_mem_no_int_fragment prog EQm b0).
  Qed.

  Ltac ung :=
  repeat
  match goal with
  | G : γ _ _ |- _ => unfold γ in G
  | G : t_gamma ?a ?b |- _ =>
    let STK := fresh "STK" in
    let INV := fresh "INV" in
    let SZ := fresh "SZ" in
    let TPNM := fresh "TPNM" in
    destruct G as [STK INV SZ TPNM]
  | G : ?NOTALLAll_ |- _ =>
    let HH := fresh "HH" in
    assert (HH:NOTALLAll) by discriminate;
    specialize (G HH);
    clear HH
  | G : stack_elt_gamma _ (_, _) |- _ =>
    destruct G
  | G : stack_elt_gamma _ ?a |- _ =>
    destruct a as (? & ?);
    destruct G
  | G : list_gamma _ _ (_ :: _) |- _ =>
    apply gamma_cons_inv in G;
    destruct G as (? & ? & ? & ? & G)
  | G : list_gamma _ (_ :: _) _ |- _ =>
    apply gamma_cons_inv_r in G;
    destruct G as (? & ? & ? & ? & G)
  | G : list_gamma _ nil _ |- _ =>
    apply gamma_nil_r in G
  | G : list_gamma _ _ nil |- _ =>
    apply gamma_nil_l in G
  | G : MemCsharpminor.tnum_gamma _ _ _ _ _ |- _ =>
    let TP := fresh "TP" in
    let ρn := freshn" in
    let CPT := fresh "CPT" in
    let NM := fresh "NM" in
    destruct G as (TP & ρn & CPT & NM)
  end.

  Lemma forget_temp_sound x t e stk m σ ab :
    ((t, e) :: stk, m) ∈ γ (Just σ, ab) →
    ∀ v, ((PTree.set x v t, e) :: stk, m) ∈ γ (forget_cell NumDom (Just σ, ab) (ACtemp (plength stk) x)).
Proof.
    intros G v.
    destruct ab as ( sz & tp & nm ). ung. simpl in *. ung. subst.
    generalize (@forget_correct _ _ _ _ NumDom (ACtemp (plength stk) x) _ (N.compat_fun v) _ NM).
    unfold forget_cell.
    destruct (forget _ _) as [ | nm' ]. exact id. intros Hnm'.
    split.
    - vauto.
    - intros _. eapply invariant_tmp; eauto. vauto.
    - eapply sizes_gamma_tmp; eauto. vauto.
    - split; simpl.
      + intros c.
        rewrite ACTreeDom.gsspec. destruct (ACTree.elt_eq c _). exact I.
        generalize (TP c). simpl. rewrite (read_cell_upd); eauto.
        destruct (ACTreeDom.get c _). easy. simpl.
        apply type_gamma_covariant, ptset_gamma_tmp. vauto.
      + eexists. split; eauto.
        intros c. unfold upd. simpl. destruct (eq_dec c). subst. simpl.
        rewrite get_stk_length. simpl. rewrite PTree.gss. apply N.ncompat_compat_fun.
        rewrite read_cell_upd; eauto.
  Qed.

  Lemma list_gamma_len {t B} {G: gamma_op t B} {x y} :
    list_gamma G x yplength x = plength y.
Proof.
    intros H.
    etransitivity.
    2: refine (Forall2_indx y, plength x = plength y) _ _ H).
    now rewrite map_plength.
    reflexivity.
    intros x0 y0 l l' H0 H1 H2. rewrite ! plength_cons. congruence.
  Qed.

  Lemma noerror_correct e ab :
    match am_get (noerror NumDom ge max_concretize e ab) with
    | Some tt => ∀ tmp env stk m, ((tmp, env) :: stk, m) ∈ γ(ab) →
                                    ∃ v, eval_expr ge env tmp m (expr_erase e) vvVundef
    | None => True
    end.
Proof.
    destruct ab as ([|[|(ε',τ') σ']], (sz, (tp, nm))); try exact I.
    unfold noerror. simpl.
    apply am_bind_case. easy. intros; exact I.
    simpl. intros ((tp', nm'), me) log ME tmp env stk m H.
    fold (γ (gamma_op:=t_gamma)) in H. ung. simpl in *. ung. eq_some_inv. subst.
    generalize (nconvert_sound _ max_concretize _ eq_refl (env_ok _ _ H0) INV SZ (tp, nm) (conj TP (ex_intro _ _ (conj CPT NM))) e). simpl.
    rewrite (list_gamma_len STK) in ME.
    rewrite ME. intros (_ & NB).
    destruct (NoError.noerror_eval_expr_def NB) as (v & DEF & EV).
    vauto.
  Qed.

  Existing Instance UnitGamma.
  Lemma noerror_sound: ∀ e ab, Noerror ge eab) ⊆ γ (noerror NumDom ge max_concretize e ab).
Proof.
    intros q ab.
    generalize (noerror_correct q ab). destruct (noerror _ _ _ q ab) as ((), ([|],?)).
    2: exact_ _ _, I).
    intros X a [t e s m H Nb]. specialize (X t e s m H).
    destruct X as (v & V & Def).
    specialize (Nb _ V Def). rewrite Nb; reflexivity.
  Qed.

  Existing Instance boolean_partitionning.

  Lemma assume_sound :
   ∀ (e : expr) (ab : t) (a : option (bool * concrete_state)),
   Assume ge eab) a → γ (assume _ ge max_concretize e ab) a.
Proof.
    intros e ([|[|(ε,τ) σ]] & sz & (tp & nm)) cs ASSUME; try exact I.
    destruct ASSUME as [t' e' s m H K].
    ung. simpl in *. ung. eq_some_inv. subst x x0.
    unfold assume. simpl.
    apply am_bind_case. easy. intros; exact I.
    intros ((tp', nm'), nc) log' NC.
    assert ( TN : (mem_as_fun ge ((t', e') :: s, m)) ∈ tnum_gamma _ ge ((t', e') :: s) (tp, nm)) by exact (conj TP (ex_intro _ _ (conj CPT NM))).
    generalize (nconvert_sound _ max_concretize _ eq_refl (env_ok _ _ H0) INV SZ (tp, nm) TN (bool_expr e)).
    rewrite (list_gamma_len STK) in NC.
    simpl. rewrite NC. intros (MONO & NB).
    destruct (NoError.noerror_eval_expr_def NB) as (v & DEF & EV).
    assert (Hb:∃ b, v = Val.of_bool b ∧ ∃ v', eval_expr ge e' t' m (expr_erase e) v' ∧ Val.bool_of_val v' b).
    {
      clear -EV DEF.
      assert (∀ v b, Val.cmp_bool Cne v (Vint Int.zero) = Some bVal.bool_of_val v b).
      { clear. intros [ | i | i | f | f | b i ]; simpl; intros; eq_some_inv.
        subst. constructor. }
      destruct e as [ ? | ? | ? | ? | op ? ? | ? ? ];
        try destruct op;
      simpl in EV; NoError.eval_expr_inv; simpl in *; eq_some_inv; subst;
      unfold Val.cmp, Val.cmpu, Val.cmpf, Val.cmpfs in *;
      try
      match goal with
        |- appcontext[ Val.of_optbool ?o ] =>
        let b := fresh in
        destruct o as [ b | ] eqn: K;
          [ | elim (DEF eq_refl) ];
          exists b; split;
          [ reflexivity | ];
          eexists; split; [ repeat (econstructor; eauto) | eauto ];
          eauto
      end.
      unfold Val.cmp; rewrite K; simpl; bif; constructor.
      unfold Val.cmpu; rewrite K; simpl; bif; constructor.
      unfold Val.cmpf; rewrite K; simpl; bif; constructor.
      unfold Val.cmpfs; rewrite K; simpl; bif; constructor.
      unfold Val.cmpl in EV.
      destruct (Val.cmpl_bool _ _ _) eqn: K; simpl in EV; eq_some_inv; subst.
      eexists. split. reflexivity.
      eexists. split. repeat (econstructor; simpl; eauto).
      unfold Val.cmpl; rewrite K; reflexivity.
      destruct b; constructor.
      unfold Val.cmplu in EV.
      destruct (Val.cmplu_bool _ _ _) eqn: K; simpl in EV; eq_some_inv; subst.
      eexists. split. reflexivity.
      eexists. split. repeat (econstructor; simpl; eauto).
      unfold Val.cmplu; rewrite K; reflexivity.
      destruct b; constructor.
    }
    destruct Hb as (b & -> & v' & EV' & Hb).
    specialize (K _ b Hb EV').
    rewrite K.
    set (f b := do z <- nassume NumDom b nm' id nc; NotBot (AbMem (Just ((ε, τ) :: σ)) sz tp' z)).
    fold (f true). fold (f false).
    apply boolean_partition. unfold f. clear f.
    destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) TN)) as (TP' & (ρn' & Nc' & NM')).
    generalize (nconvert_ex NumDom max_concretize ε sz eq_refl (env_ok _ _ H0) INV (tp, nm) TN (bool_expr e)).
    rewrite NC. simpl. intros (MONO' & lpe & Hlpe & X).
    specialize (X _ EV DEF TP' _ Nc' NM').
    destruct X as (pe & lme' & me & n & INp & Hpe & Hlme' & Hpn & INn & Hn & Hnty & ME).
    refine ((λ X Y, botbind_spec _ _ _ Y _ X) _ _).
    eapply nassume_correct.
    exact NM'. eauto.
    destruct b; inv Hn; exact ME.
    intros a' A'. constructor. red. simpl. econstructor; auto. auto. auto. split.
    auto.
    eauto.
  Qed.

  Ltac ssubst :=
    repeat
    match goal with
    | H : ?a = ?b |- _ => first [subst a | subst b]
    end.

  Lemma ab_eval_expr_correct (ab: @t num) stk m (G: (stk, m) ∈ γ ab) e :
    match am_get (ab_eval_expr _ ge max_concretize ab e) with
    | Some res =>
        match res with
        | NotBot (tn, ty, ln) =>
          tnum_mono NumDom ge stk (ab_nm ab) tn
          ∃ env tmp stk',
          stk = (tmp, env) :: stk' ∧
          ∀ ρ',
           ρ' ∈ compat (mem_as_fun ge (stk, m)) →
          ρ' ∈ γ(snd tn) →
          ∃ v, eval_expr ge env tmp m (expr_erase e) vvVundef
               v ∈ (toplift_gamma (type_gamma (ptset_gamma ge stk)) ty) ∧
               ∃ me n, In me ln
                       N.ncompat v n
                       (∀ x y : ident, me_free_var me (ACtemp x y) → (x < Pos.succ (plength stk'))%positive) ∧
                       eval_mexpr ρ' me n
          | Bot => False
       end
    | None => True
    end.
Proof.
    unfold ab_eval_expr.
    destruct ab as ([|[|(ε, τ) σ]] & sz & tp & nm); try exact I.
    simpl.
    apply am_bind_case. easy. vauto.
    intros ((tp', nm'), lpe) log LPE.
    apply am_bind_case. easy. vauto.
    intros lme log' LME.
    ung.
    assert (TN : mem_as_fun ge (stk, m) ∈ tnum_gamma _ ge stk (tp, nm)) by exact (conj TP (ex_intro _ ρn (conj CPT NM))).
    simpl in *. ung. destruct x. subst. simpl in *.
    pose proof (env_ok _ _ H0) as Tmp.
    generalize (convert_to_me NumDom max_concretize _ (list_gamma_len STK) Tmp INV SZ (tp, nm) TN e).
    simpl. rewrite LPE. simpl. rewrite LME.
    intros (MONO & v & V & DEF & H).
    destruct (MONO _ (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM)))) as (TP' & NM').
    destruct lpe as [|pelpe].
    - hsplit. edestruct H; eauto. apply NM'; auto. hsplit. auto.
    -
    rewrite union_list_map_correct.
    match goal with |- appcontext[ union_list _ (map ?f _) ] => set ( func := f ) end.
    Opaque union_list.
    simpl. split. exact MONO.
    eexists _, _, _. split. reflexivity.
    clear NM'.
    intros ρ' CPT' NM'.
    specialize (H TP' ρ' CPT' NM').
    destruct H as (pe & lme' & me & n & Hpe & Hv & Hme & Hlme & Hlme' & Nvn & Htn & Hn).
    exists v. split. exact V. split. exact DEF.
    pose proof (in_map func _ _ Hpe) as IN.
    split.
    apply (@union_list_correct (_+⊤) val All (toplift_gamma ((type_gamma (ptset_gamma ge ((t, e0) :: x0))))) _ _ _ _ v IN).
    eapply (pt_eval_pexpr_correct (list_gamma_len STK) _ Hv).
    clear -DEF. destruct v; apply (DEF eq_refl) || exact I.
    exists me, n. split. eauto. split. assumption. split. 2:now intuition.
    intros x y FV.
    destruct (N.nconvert_fv _ _ _ _ _ _ _ _ NumDom sz _ _ _ _ Hme _ Hlme' _ FV) as (? & FV' & <- ).
    assert (HT:=convert_free_def NumDom max_concretize (plength σ) ε sz Tmp INV e (tp, nm) TN).
    rewrite LPE in HT. specialize (HT _ Hpe _ FV'). revert HT. simpl.
    generalize (TP (ACtemp x y)). simpl.
    destruct (get_stk _ _) as [ (?, ?) | ] eqn: Hk.
    intros _ _. generalize (get_stk_lt _ _ Hk). rewrite plength_cons. Psatz.lia.
    intros _ X. elim (X eq_refl).
    Grab Existential Variables. eauto.
    Transparent join.
  Qed.

  Lemma ab_eval_exprlist_correct ε τ σ m :
    ∀ eargs (ab: @t num) (Hstk: ab_stk abAll) (G: ((τ, ε) :: σ, m) ∈ γ ab),
      match am_get (ab_eval_exprlist NumDom ge max_concretize ab eargs) with
      | None => True
      | Some Bot => False
      | Some (NotBot (ab', aargs)) =>
        ab_stk ab = ab_stk ab' ∧
        ab_sz ab = ab_sz ab' ∧
        tnum_mono NumDom ge ((τ,ε) :: σ) (ab_nm ab) (ab_nm ab')
        ∧
        ∀ ρn,
          ρncompat (mem_as_fun ge ((τ, ε) :: σ, m)) →
          ρn ∈ γ (ab_num ab) →
            ∃ vargs,
              eval_exprlist ge ε τ m (List.map expr_erase eargs) vargs
              args_match ε τ σ m ρn eargs vargs aargs
      end.
Proof.
    induction eargs as [ | e eargs IH ]; intros ab Hstk G.
    - split. auto. split. auto. split. auto. apply tnum_mono_refl. intuition vauto2.
    - simpl.
      apply am_bind_case. easy. intros; exact I.
      pose proof (ab_eval_expr_correct G e) as X.
      intros [ | (((tp, nm), ty), margs) ] log K; rewrite K in X.
      exact X.
      destruct X as (MONO & (i & j & k & ? & X)).
      eq_some_inv. subst i j k.
      apply am_bind_case. easy. intros; exact I.
      assert (t_gamma (with_tnum_ : tnum, (tp, nm)) ab) ((τ, ε) :: σ, m)) as G'.
      {
        destruct ab as (stk & sz & tp₀ & nm₀). simpl in *.
        ung. simpl in *.
        unfold with_tnum. split; vauto; eauto.
        destruct (MONO _ (Pun.mem_as_fun_pun_u8 _ _ (noIntFragment (INV Hstk)))). split. auto. eauto. split. auto. eauto.
      }
      specialize (IH (with_tnum_ : tnum, (tp, nm)) ab) Hstk G').
      intros [ | (ab', aargs') ] log' K'; rewrite K' in IH. exact IH.
      destruct IH as (? & ? & MONO' & IH).
      split. auto. split. auto.
      split. eauto using tnum_mono_trans.
      destruct ab' as (stk' & sz' & tp' & nm'). simpl in *.
      intros ρn CPT NM.
      destruct ab as (stk & sz & tp₀ & nm₀). simpl in *.
      ung. simpl in *. specialize (INV Hstk).
      destruct (MONO _ (Pun.mem_as_fun_pun_u8 _ _ (noIntFragment INV))) as
      (TPm & NMm). vauto.
      specialize (X ρn CPT (NMm _ CPT NM)).
      destruct X as (v & EV & Def & TY & me & n & Hme & Hvn & FV & MEV).
      specialize (IH ρn CPT).
      destruct IH as (vargs & Hvargs & IH'). apply NMm; eauto.
      exists (v :: vargs). split. vauto.
      econstructor; eauto.
  Qed.
  Global Opaque ab_eval_exprlist.

  Existing Instance stack_elt_gamma.
  Lemma assign_in_correct (μ: fname) (x: ident) (a: expr) (ab: t) stk m :
    (stk, m) ∈ γ(ab) →
    match am_get (assign_in NumDom ge max_concretize μ x a ab) with
    | Some ab' =>
        match ab' with
        | Bot => False
        | NotBot ab' =>
          ab_stk ab = ab_stk ab' ∧
          ab_sz ab = ab_sz ab' ∧
          ∃ env tmp stk',
          stk = (tmp, env) :: stk' ∧
          match get_stk_dep μ stk with
          | inleft H =>
            let 'exist ((pre, (tmp', env')), post) _ := H in
            ∃ v, eval_expr ge env tmp m (expr_erase a) vvVundef
                 (pre ++ (PTree.set x v tmp', env') :: post, m)%list ∈ γ(ab')
          | inright _ => True
          end
        end
    | None => True
    end.
Proof.
    unfold assign_in, assign_cells.
    intros G.
    destruct ab as ([|?], ?). exact I.
    apply am_bind_case. easy. intros; exact I.
    intros res log Hres. simpl.
    generalize (ab_eval_expr_correct G a). rewrite Hres.
    destruct res as [ | (((tp, nm), ty), lme) ]. intros ().
    intros (MONO & ε₀ & τ₀ & σ & -> & H).
    ung. simpl in *.
    destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) TPNM)) as (TP' & ρn & CPT' & NM').
    specialize (H ρn CPT' NM').
    destruct H as (v & V & Def & Hty & me & n & INme & Cptn & _ & EVn).
    simpl.
    rewrite union_list_map_correct.
    unfold nm_upd_cells. simpl.
    assert (X:=union_list_correct (NotBot nm) (G:=gamma_bot mach_gamma)
                                  (map (nm_upd NumDom nm (ACtemp μ x)) lme)).
    setoid_rewrite in_map_iff in X.
    specializem, X _ m (ex_intro _ me (conj eq_refl INme))).
    assert (HX:γ (nm_upd NumDom nm (ACtemp μ x) me) (upd ρn (ACtemp μ x) n)).
    { eapply forget_all_correct. apply gamma_num_ext.
      { clear. intros [|m]. intros ? ? ? (). simpl.
        intros ρ ρ' x H H0. eapply forget_correct'; eauto. }
      eapply AbMachineEnv.assign_correct; try eassumption.
      intuition. }
    specialize (X _ HX). clear HX.
    destruct (union_list (NotBot nm) (map _ lme)) as [|nm']. simpl. exact X.
    simpl. split. reflexivity. split. reflexivity. eexists _, _, _. split. reflexivity.
    destruct (get_stk_dep) as [ IN | ].
    destruct IN as (((pre & (tmp', env')) & post), IN). simpl in IN.
    destruct IN as (STK' & IN & Hstk).
    apply Pos.eqb_eq in Hstk.
    rewrite <- (rev_involutive (_ ++ _ :: nil)) in IN.
    apply rev_inj in IN.
    rewrite rev_app_distr, rev_involutive in IN. simpl in IN.
    exists v. split. easy. split. easy.
    assert (∀ q, qACtemp μ xmem_as_fun ge ((τ₀, ε₀) :: σ, m) q = mem_as_fun ge (pre ++ (PTree.set x v tmp', env') :: post, m) q)%list as Hupd.
    { intros q H. rewrite IN.
      unfold temp_env in *.
      destruct q as [f y|f y|b];
      simpl. rewrite ! get_stk_app, ! plength_cons.
      case (Pos.ltb_spec); auto. intros LT.
      rewrite ! get_stk_cons.
      case (Pos.eqb_spec); easy.
      rewrite ! get_stk_app, ! plength_cons.
      case (Pos.ltb_spec); auto. intros LT.
      rewrite ! get_stk_cons.
      case (Pos.eqb_spec); auto. clear LT. intros ->.
      simpl. rewrite PTree.gso. easy. intros ->. subst. now apply H.
      reflexivity. }
    assert (mem_as_fun ge (pre ++ (PTree.set x v tmp', env') :: post ,m) (ACtemp μ x) = v)%list as Hupd'.
    { unfold temp_env in *. simpl. rewrite get_stk_app.
      simpl. case (Pos.ltb_spec). intros LT.
      rewrite get_stk_cons.
      rewrite Hstk, (Pos.eqb_refl). simpl. rewrite PTree.gss. reflexivity.
      rewrite plength_cons, Pplus_one_succ_l. Psatz.lia.
      }
    split;[| | |split].
    destruct pre as [|(?, ?) pre].
    + ung. simpl in *; eq_some_inv; ssubst; vauto.
    + ung. simpl in *. eq_some_inv. ssubst. constructor.
      easy.
      apply Forall2_app_inv_r in STK. hsplit.
      apply list_append_map_inv in H1. hsplit. ssubst.
      fold (map γ (l0 ++ l3)).
      rewrite map_app.
      apply Forall2_app. auto.
      inversion H; clear H. ssubst.
      symmetry in H1; apply map_cons_inv in H1. hsplit.
      ssubst. vauto.
    + intros _.
      eapply invariant_tmp. 2: eassumption.
      unfold stacks_tmp. rewrite IN, !map_app. reflexivity.
    + simpl. eapply (sizes_gamma_tmp). 2: eassumption.
      unfold stacks_tmp. rewrite IN, !map_app. reflexivity.
    + simpl. intros c.
      eapply ACTreeDom.gamma_set.
      eapply pt_forget_all_correct.
      eapply points_to_gamma_tmp. 2: eassumption.
      unfold stacks_tmp. rewrite IN, !map_app. reflexivity.
      intros. left. reflexivity.
      simpl. ssubst. rewrite get_stk_length'. simpl. rewrite PTree.gss.
      destruct ty as [|ty]. exact I.
      revert Hty.
      apply type_gamma_covariant, ptset_gamma_tmp.
      unfold stacks_tmp. rewrite IN, !map_app. reflexivity.
      exact Hupd.
    + exists (upd ρn (ACtemp μ x) n). split.
      eapply (N.compat_upd _ id); eauto.
      intros q. destruct (eq_dec q) as [ -> | ME]. auto. symmetry; apply Hupd; exact ME.
      apply X.
    + easy.
  Qed.

  Lemma assign_sound :
   ∀ (x : ident) (e : expr) (ab : t) (a : option concrete_state),
   Assign ge x eab) a → γ (assign NumDom ge max_concretize x e ab) a.
Proof.
    intros x e ab a H.
    unfold assign.
    destruct ab as ([|[|(ε,τ) σ]] & sz & tp & nm); try (exact I).
    simpl.
    generalize (@assign_in_correct (plength σ) x e (Just ((ε,τ) :: σ), (sz, (tp, nm)))).
    fold stack. fold (@tnum num).
    destruct (assign_in _) as (ab', ([|], ?)). 2: intros; exact I. simpl.
    destruct H as [ tmp en stk m H Ha ].
    intros H'. specialize (H' _ _ H).
    destruct ab' as [|ab']. destruct H'.
    hsplit. subst. eq_some_inv. subst. ung. simpl in *. ung. eq_some_inv. subst.
    destruct (get_stk_dep) as [ ( ((pre, (tmp', env')), post) & EQ ) | (_ & K) ].
    2: rewrite (list_gamma_len STK), get_stk_length in K; eq_some_inv.
    simpl in *.
    destruct EQ as ( EQ & IN & Hlen).
    rewrite <- (rev_involutive (_ ++ _ :: nil)) in IN.
    apply rev_inj in IN.
    rewrite rev_app_distr, rev_involutive in IN. simpl in IN.
    apply Pos.eqb_eq in Hlen.
    rewrite (list_gamma_len STK), get_stk_length in EQ. eq_some_inv. subst.
    hsplit.
    erewrite Ha; eauto.
    hnf.
    destruct pre. simpl in *. eq_some_inv. subst. easy.
    exfalso. clear - IN Hlen STK.
    simpl in *. eq_some_inv. subst.
    rewrite (list_gamma_len STK), plength_app, plength_cons in Hlen.
    simpl in *. rewrite Ppred_minus, Pplus_one_succ_r in Hlen.
    zify. rewrite Pos2Z.inj_sub in Hlen; Psatz.lia.
  Qed.

  Lemma assign_any_sound :
   ∀ (x : ident) (ty: typ) (ab : t) (a : option concrete_state),
   AssignAny x tyab) a → γ (assign_any NumDom x ty ab) a.
Proof.
    intros x ty ab cs [ t e s m G (v & Hty' & ->) ].
    destruct ab as ([|[|(ε,τ) stk]] & sz & tp & nm); try exact I.
    unfold assign_any.
    ung. simpl in *. ung. eq_some_inv. subst x0 x1.
    assert (mem_as_fun ge ((PTree.set x v t, e) :: s, m) ∈ upd_spec (mem_as_fun ge ((t, e) :: s, m)) (ACtemp (plength stk) x) v) as Hupd.
    {
      intros c.
      case @eq_dec.
      intros ->.
      simpl. rewrite (list_gamma_len STK), get_stk_length. simpl. now rewrite PTree.gss.
      destruct c; simpl; auto;
      rewrite ! get_stk_cons;
      case (Pos.eqb_spec _ _); auto;
      intros -> NE;
      simpl. rewrite PTree.gso; auto. rewrite (list_gamma_len STK) in NE; congruence.
    }
    assert (stacks_tmp ((t, e) :: s) ((PTree.set x v t, e) :: s)) by reflexivity.
    assert (ty <> VP -> ty <> VIP ->
        points_to_gamma ge ((PTree.set x v t, e) :: s) (ACTreeDom.set tp (ACtemp (plength stk) x) (Just match ty with VI => TyInt | VF => TyFloat | VS => TySingle | _ => TyLong end))
                            (mem_as_fun ge ((PTree.set x v t, e) :: s, m))) as PT'.
    {
      intros.
      eapply points_to_gamma_tmp; eauto.
      intros c. generalize (TP c). rewrite (Hupd c), ACTreeDom.gsspec.
      unfold eq_dec, ACellDec.
      destruct (ACTree.elt_eq c). 2: exact id.
      intros _.
      destruct ty; try congruence; apply gamma_typ_inv in Hty'; hsplit;
      subst v; exact I.
    }
    assert (v ∈ γ ty) as Hty by exact Hty'.
    destruct ty; try exact I;
    apply gamma_typ_inv in Hty; hsplit;
    match goal with
    | |- appcontext[ AbMachineEnv.forget ?c ?nm ] =>
      generalize (AbMachineEnv.forget_correct c ρn (N.compat_fun v) _ NM);
      destruct (AbMachineEnv.forget c nm) as [|nm'];
      [exact id|simpl; intros Hnm; split; simpl; vauto]
    end;
    repeat
    match goal with
    | |- sizes_gamma _ _ _ _ => eapply sizes_gamma_tmp; eauto
    | |- appcontext[invariant] => intros _; eapply invariant_tmp; eauto
    | |- appcontext[@γ tnum] => simpl
    | |- __ => split
    | |- appcontext[points_to_gamma] => apply PT'; discriminate
    | |- appcontext[ACTreeDom.gamma_tree gamma_typ] =>
      eapply ACTreeDom.gamma_set; eauto;
      [rewrite Hupd, dec_eq_true; subst v; vauto
      |intros; rewrite Hupd, dec_eq_false;easy]
    | |- ∃ _, _mach_gamma _ _ =>
      eexists; split;[|eassumption];
      (eapply (N.compat_upd _ id); eauto using N.ncompat_compat_fun)
    end.
  Qed.

  Lemma vload_sound : ∀ rettemp gofs κ args ab,
    VLoad ge rettemp gofs κ argsab) ⊆ γ (vload NumDom ge max_concretize rettemp gofs κ args ab).
Proof.
    intros rettemp gofs κ args ab.
    pose proof (@ab_eval_expr_correct ab) as AEEC.
    unfold vload.
    apply am_assert_spec. easy. easy. intros Hchunk.
    match goal with
    | |- appcontext[ match ?x with _ => _ end ] => destruct x as [tgt | ] eqn: Htgt
    end.
    2: easy.
    apply am_bind_case. easy. easy.
    intros μ' log Hvol. apply check_volatile_sound in Hvol.
    destruct ab as ([ | [ | (ε,τ) stk] ] & sz & (tp & nm)).
    easy. easy.
    simpl in *.
    destruct Hvol as [ -> Hvol ].
    intros a [ t e s m G H' ].
    refine (let H := H' _ in _). now destruct κ.
    clear H'.
    ung. simpl in *. ung. eq_some_inv. subst.
    destruct gofs as [ (g, ofs) | ].
    - (* global *)
      destruct args as [ | ? ? ]; eq_some_inv. subst.
      destruct Hvol as ( b & Hg & hi & perm & Hvol ).
      generalize (SZ (ABglobal b) hi perm true b Hvol).
      simpl. rewrite (Genv.find_invert_symbol _ _ Hg).
      intros Hsz; destruct (Hsz eq_refl) as (Hrange & Hvol'). clear Hsz.
      specializetr vres, H tr vres _ _ _ (conj eq_refl eq_refl) Hg).
      rewrite Hvol' in H.
      specialize (H (Events.Event_vload κ g ofs match κ with Mint64 => Events.EVlong Int64.zero | Mfloat64 => Events.EVfloat Float.zero | Mfloat32 => Events.EVsingle Float32.zero | _ => Events.EVint Int.zero end :: nil) (Val.load_result κ match κ with Mint64 => Vlong Int64.zero | Mfloat64 => Vfloat Float.zero | Mfloat32 => Vsingle Float32.zero | _ => Vint Int.zero end)).
      destruct H as (v & Hvdef & H' & -> ).
      destruct κ; easy || (repeat (econstructor; try eassumption)).
      simpl in *.
      destruct rettemp as [ r | ].
      rewrite (list_gamma_len STK).
      destruct κ; try easy; ( apply assign_any_sound || apply forget_temp_sound );
      repeat (econstructor; eauto);
      destruct v; (elim (Hvdef eq_refl) || exact H' || exact I).
      split; simpl; eauto; vauto.

    - (* local *)
      destruct args as [ | addr [ | ? ? ] ]; eq_some_inv; subst.
      destruct Hvol as ((tp', nm') & ty & ne & EV & Hvol).
      specializeG, AEEC ((t, e) :: s) m G addr). rewrite EV in AEEC.
      edestruct AEEC as (MONO & AEEC'). split; eauto; vauto.
      clear AEEC.
      destruct AEEC' as (i & j & k & ? & AEEC). eq_some_inv. subst i j k.
      destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM))))) as (TP' & ρ' & CPT' & NM').
      specialize (AEEC ρ' CPT' NM').
      destruct AEEC as (v & Hv & Hvdef & Hty & ne' & n & Hne' & Hvn & Hn' & Hn ).
      destruct Hvol as (ptr & -> & Hvol).
      generalize (type_gamma_inv _ _ _ Hty).
      intros (b & i & -> & blk & Hblk & Hb').
      generalize (validate_volatile_ptr_in SZ Hvol _ Hblk).
      intros (g & b' & -> & Hg & Hvol').
      simpl in Hb'. rewrite (Genv.find_invert_symbol _ _ Hg) in Hb'. eq_some_inv. subst.
      specialize (H (Events.Event_vload κ g i match κ with Mint64 => Events.EVlong Int64.zero | Mfloat64 => Events.EVfloat Float.zero | Mfloat32 => Events.EVsingle Float32.zero | _ => Events.EVint Int.zero end :: nil) (Val.load_result κ match κ with Mint64 => Vlong Int64.zero | Mfloat64 => Vfloat Float.zero | Mfloat32 => Vsingle Float32.zero | _ => Vint Int.zero end) _ _ _ Hv Hg).
      rewrite Hvol' in H. destruct H as (v & Hvdef' & H' & -> ).
      destruct κ; easy || (repeat (econstructor; try eassumption)).
      destruct rettemp as [ r | ].
      rewrite (list_gamma_len STK).
      destruct κ; try easy; ( apply assign_any_sound || apply forget_temp_sound );
      repeat (econstructor; eauto);
      destruct v; (elim (Hvdef' eq_refl) || exact H' || exact I).
      split; simpl; eauto; vauto.
  Qed.

  Lemma store_sound α :
   ∀ (κ : memory_chunk) (dst src : expr) (ab : t)
   (a : option concrete_state),
   Store ge κ dst srcab) a
   → γ (store NumDom ge max_concretize α κ dst src ab) a.
Proof.
    intros κ dst src ab a [ t e s m G H ].
    unfold store.
    match goal with
        |- γ (match ?κ0 with _ => _ end) _ =>
        remember κ0 as κ'
    end.
    destruct κ' as [κ''|]. 2:constructor.
    assert (κ = proj1_sig κ'').
    { destruct κ; inv Heqκ'; reflexivity. }
    subst κ. clear Heqκ'.
    destruct ab as ([|[|(ε,τ) σ]] & sz & tp & nm); try (exact I).
    simpl. apply am_assert_spec. easy. intros; exact I.
    intros Hcast.
    ung. simpl in *. ung. eq_some_inv. subst x0 x.
    pose proof (deref_expr_correct NumDom max_concretize _ eq_refl (env_ok _ _ H1) INV SZ (Some α) (tp, nm) (conj TP (ex_intro _ _ (conj CPT NM))) κ'' dst) as DEREF.
    apply am_bind_case. easy. easy.
    intros ((tp', nm'), cells) log Hcells.
    rewrite (list_gamma_len STK) in Hcells. simpl in *. rewrite Hcells in DEREF.
    destruct DEREF as (TN' & NB & DEREF).
    apply NoError.noerror_eval_expr in NB. destruct NB as (v & Hv).
    specialize (DEREF _ Hv). destruct DEREF as (ab & b & i & -> & Habi & Hab & Hal).
    specializev m' S, H v _ m' S Hv).
    destruct cells as [|ccells]. elim Habi; fail.
    match goal with
    | |- appcontext[ am_rev_map ?f ?l ] =>
      pose proof (am_rev_map_correct f l) as CIB
    end.
    apply am_bind_case. easy. easy. intros TT log' Hcib.
    rewrite Hcib in CIB. destruct CIB as [_ CIB'].
    assert (∀ c, In c (c₀ :: cells) → ∀ b, block_of_ablock ge ((t, e) :: s) (ablock_of_cell c) = Some b
                                   Mem.range_perm m b (fst (range_of_cell c)) (fst (range_of_cell c) + size_chunk (proj1_sig κ'')) Cur Writable) as CIB.
    {
      intros c IN b' Hb'.
      specialize (CIB' _ IN). destruct CIB' as (() & CIB' & _).
      apply (cell_in_bounds_range_perm CIB' SZ Hb').
    } clear CIB'.
    unfold assign_cells.
    match goal with
    | |- appcontext[ ab_eval_expr ?D ?G ?M ?ab ?e ] =>
      pose proofs m G, @ab_eval_expr_correct ab s m G e) as Hsrc
    end.
    specialize (Hsrc ((t, e) :: s) m).
    simpl in *.
    apply am_bind_case. easy. intros; exact I.
    intros q log'' Hq. rewrite Hq in Hsrc.
    destruct (tnum_m_stronger TN' (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM))))) as (TP' & (ρn' & Nc' & NM')).
    destruct q as [|(((tp'', nm'') & ty) & ln)].
    destruct Hsrc. split; eauto; vauto.
    edestruct Hsrc as (MONO & Hsrc'). split; eauto; vauto.
    clear Hsrc.
    destruct Hsrc' as (j & k & l & ? & Hsrc); eq_some_inv; subst j k l.
    apply am_assert_spec. easy. intros; exact I.
    intros Htypes.
    destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP' (ex_intro _ _ (conj Nc' NM'))))) as (TP'' & ρ' & CPT' & NM'').
    specialize (Hsrc _ CPT' NM'').
    destruct Hsrc as (v & Hsrc & Def & Hvty & me & n & INme & Hvn & _ & Hn).
    specializem', H v m' Hsrc).
    destruct (Mem.valid_access_store m (proj1_sig κ'') b (Int.unsigned i) v) as (m' & Hm').
    { split. simpl.
      specialize (CIB _ Habi b). rewrite ablock_of_cell_from in CIB.
      specialize (CIB Hab).
      destruct ab; try exact CIB. simpl in Hab. eq_some_inv.
      exact Hal.
    } specialize (H m' Hm'). subst a.
    rewrite union_list_map_correct.
    set (l := map (nm_upd_cells NumDom nm'' (c₀ :: cells)) ln).
    assert (Val.load_result (proj1_sig κ'') v = v) as Hlr.
    {
      clear -Htypes Hvty Hcast Hsrc.
      destruct ty as [| ty ].
      simpl in *; eq_some_inv.
      apply type_gamma_inv in Hvty.
      destruct ty; hsplit;
      destruct κ'' as [[] []]; eq_some_inv;
      intuition; hsplit; subst; try reflexivity;
      simpl; f_equal;
      destruct src; simpl in *; eq_some_inv;
      destruct u; eq_some_inv; NoError.eval_expr_inv; simpl in Hsrc; eq_some_inv;
      repeat
      match goal with
      | H : appcontext[Val.sign_ext _ ?a] |- _ => destruct a; inversion H; clear H;
                                                  apply Int.sign_ext_widen; intuition
      | H : appcontext[Val.zero_ext _ ?a] |- _ => destruct a; inversion H; clear H;
                                                  apply Int.zero_ext_widen; intuition
      end.
    }
    assert (loose_upd_spec
              (mem_as_fun ge ((t, e) :: s, m))
              (cell_from ab (Int.unsigned i, κ''))
              v
              (mem_as_fun ge ((t, e) :: s, m'))) as Hupd.
    {
      split.
      + destruct ab as [f x|b']; simpl in *.
        revert Hab.
        rewrite get_stk_cons. case (Pos.eqb_spec). simpl.
        destruct (_ ! x) as [(b' & ?) | ]; intros; eq_some_inv. simpl. subst b'.
        rewrite (Mem.load_store_same _ _ _ _ _ _ Hm'); auto.
        destruct (get_stk f _) as [(? & e')|]; intros; eq_some_inv. simpl.
        destruct (e' ! x) as [(b' & ?) | ]; eq_some_inv; subst b'. simpl.
        rewrite (Mem.load_store_same _ _ _ _ _ _ Hm'); auto.
        destruct (Genv.invert_symbol ge b'); eq_some_inv; subst b'.
        rewrite (Mem.load_store_same _ _ _ _ _ _ Hm'); auto.
      +
        pose proof (Mem.store_valid_access_3 _ _ _ _ _ _ Hm') as [_ AL].
        intros [f x i' κ'|f x|b' i' κ'] DIS; simpl.
        * rewrite get_stk_cons.
          case (Pos.eqb_spec). intros ->. simpl.
          destruct (_ ! x) as [(b' & s') | ] eqn: Hb'; auto; eq_some_inv. simpl.
          destruct (Zdivide_dec (align_chunk (proj1_sig κ')) i') as [AL'|NA]. apply align_chunk_pos.
          2: rewrite !(misaligned_load NA); reflexivity.
          rewrite (Mem.load_store_other _ _ _ _ _ _ Hm'). reflexivity.
          {
            destruct ab as [f' x'|b'']; simpl in Hab; eq_some_inv.
            destruct (eq_dec b' b); auto.
            right. ssubst.
            assert ((plength s, x) = (f', x')).
            {
              pose proof (INV.(@localInj _ _) (plength s, x) (f', x') b) as LOCALS. simpl in LOCALS.
              rewrite get_stk_length in LOCALS. simpl in LOCALS. rewrite Hb' in LOCALS.
              specialize (LOCALS eq_refl).
              revert LOCALS.
              revert Hab.
              rewrite get_stk_cons.
              case (Pos.eqb_spec). simpl. intros ->.
              destruct (_ ! x') as [(b', s'')|]; intros; eq_some_inv. subst b'.
              specialize (LOCALS eq_refl). congruence.
              destruct (get_stk f' _) as [(?, e')|]; eq_some_inv. simpl.
              destruct (e' ! _) as [(b', s'')|]; intros; eq_some_inv. subst b'.
              specialize (LOCALS eq_refl). congruence.
              intros; eq_some_inv.
            }
            eq_some_inv. ssubst.
            destruct DIS as [DIS|DIS]. simpl in DIS. unfold is_true in DIS; hsplit; eq_some_inv.
            simpl in DIS. clear -DIS. intuition.
            apply do_opt_some_inv in Hab. destruct Hab as (sym & Hsym & Hab). eq_some_inv. subst b''.
            pose proof (INV.(localNotGlobal) _ _ (or_introl eq_refl)) as K.
            left. apply not_eq_sym. eapply K. apply Genv.invert_find_symbol, Hsym. exact Hb'.
          }
          destruct (get_stk f _) as [(t' & e')|] eqn: He'; auto; eq_some_inv. simpl.
          destruct (e' ! _) as [(b' & s') | ] eqn: Hb'; auto; eq_some_inv. simpl.
          destruct (Zdivide_dec (align_chunk (proj1_sig κ')) i') as [AL'|NA]. apply align_chunk_pos.
          2: rewrite !(misaligned_load NA); reflexivity.
          rewrite (Mem.load_store_other _ _ _ _ _ _ Hm'). reflexivity.
          {
            destruct ab as [f' x'|b'']; simpl in Hab; eq_some_inv.
            destruct (eq_dec b' b); auto.
            right.
            assert ((f, x) = (f', x')).
            {
              subst b'.
              revert Hab.
              generalize (INV.(@localInj _ _) (f, x) (f', x') b). simpl.
              rewrite (get_stk_tail _ _ _ He'). simpl. rewrite Hb'. simpl.
              destruct (get_stk f' _) as [(?, e'')|]; intros; eq_some_inv. simpl in *.
              destruct (e'' ! x') as [(b', s'')|]; eq_some_inv. subst b'. auto.
            }
            eq_some_inv. subst f' x'.
            destruct DIS as [DIS|DIS]. simpl in DIS; unfold is_true in DIS; hsplit; eq_some_inv.
            simpl in DIS. clear -DIS. intuition.
            apply do_opt_some_inv in Hab. destruct Hab as (sym & Hsym & Hab). eq_some_inv. subst b''.
            left. apply not_eq_sym. eapply INV.(localNotGlobal).
            right. eapply get_stk_in, He'.
            apply Genv.invert_find_symbol, Hsym. exact Hb'.
          }
        * reflexivity.
        * destruct (Genv.invert_symbol ge b') as [sym'|] eqn: Hsym'; auto; eq_some_inv.
          destruct (Zdivide_dec (align_chunk (proj1_sig κ')) i') as [AL'|NA]. apply align_chunk_pos.
          2: rewrite !(misaligned_load NA); reflexivity.
          rewrite (Mem.load_store_other _ _ _ _ _ _ Hm'). reflexivity.
          {
            destruct ab as [f' x'|b'']; simpl in Hab; eq_some_inv.
            left. intros ->.
            revert Hab. rewrite get_stk_cons.
            case (Pos.eqb_spec). simpl. intros ->.
            pose proof (INV.(localNotGlobal) _ _ (or_introl eq_refl)) as GE.
            destruct (_ ! x') as [(?,?)|] eqn: Hex'; intro; eq_some_inv.
            eapply GE. eapply Genv.invert_find_symbol. eassumption. exact Hex'.
            auto.
            destruct (get_stk f' _) as [(t',e')|] eqn: Hf's. simpl.
            destruct (e' ! x') as [(?,?)|] eqn: He'x'; intros; eq_some_inv.
            eapply INV.(localNotGlobal).
            right. eapply get_stk_in. exact Hf's.
            eapply Genv.invert_find_symbol. eassumption. exact He'x'.
            auto. intros; eq_some_inv.
            apply do_opt_some_inv in Hab. destruct Hab as (sym & Hsym & Hab). eq_some_inv. subst b''.
            destruct (eq_dec b' b); auto. right. subst b'.
            clear - AL AL' DIS.
            destruct DIS as [DIS|DIS]; simpl in DIS.
            unfold is_true in DIS; hsplit; eq_some_inv.
            intuition.
          }
    }
    setn'' := λ c, if cells_disjoint_dec (cell_from ab (Int.unsigned i, κ'')) c
                     then ρ'(c) else if eq_dec (cell_from ab (Int.unsigned i, κ'')) c
                                     then n
                                     else N.compat_fun (mem_as_fun ge ((t, e) :: s, m') c)).
    assert (Hρn''ρn:loose_upd_spec ρ' (cell_from ab (Int.unsigned i, κ'')) n ρn'').
    { clear. unfold ρn''. split. destruct cells_disjoint_dec as [K|K].
      elim (cells_disjoint_not_eq K eq_refl).
      rewrite dec_eq_true. reflexivity.
      intros y H. destruct cells_disjoint_dec. reflexivity.
      eapply cells_disjoint_overlap; eauto. }
    assert (Hρn'' : γ (union_list (NotBot nm'') l) ρn'').
    { eapply union_list_correct. refine _.
      subst l. rewrite in_map_iff. exists me. split. reflexivity. exact INme.
      unfold nm_upd_cells. rewrite union_list_map_correct.
      eapply union_list_correct; eauto. refine _.
      rewrite in_map_iff. vauto.
      eapply nm_upd_correct; try eassumption. }
    destruct (union_list (NotBot _) l) as [|nmo]. contradiction.
    split;[| | |split].
  - simpl. vauto.
  - intros _. split.
    + intros f t' e' H'.
      intros hi H o' Ho'.
      eapply Mem.perm_store_1. eassumption.
      eapply INV; eassumption.
    + eapply INV; eauto.
    + eapply INV.(localInj).
    + simpl.
      intros ? b0 H. eapply (Mem.store_valid_block_1) . eassumption. eapply INV; eauto.
    + intros t' e' H' ? b' z H. eapply Mem.store_valid_block_1; try eassumption.
      eapply INV.(localValid); eauto.
    + simpl.
      intros ab0 b0 H o.
      Transparent Mem.store. unfold Mem.store in Hm'. Opaque Mem.store.
      destruct Mem.valid_access_dec; eq_some_inv. rewrite <- Hm'.
      generalize (INV.(noIntFragment) _ H o). simpl.
      rewrite PMap.gsspec. case peq. intros <-.
      assert ((o < Int.unsigned i \/ o >= Int.unsigned i + Z.of_nat (Datatypes.length (encode_val (proj1_sig κ'') v))) \/ (Int.unsigned i <= o < Int.unsigned i + Z.of_nat (Datatypes.length (encode_val (proj1_sig κ'') v)))) as Hr by Psatz.lia.
      destruct Hr as [ Hout | Hin ].
      * rewrite Mem.setN_outside. exact id. exact Hout.
      * intros _. generalize (Mem.setN_in _ _ _ (Mem.mem_contents m) !! b0 Hin).
        destruct (ZMap.get _ _) as [ | ? | () ? ? ]; try (intros; exact I).
        clear.
        destruct κ'' as [() ()]; destruct v; simpl; try (intuition congruence);
        try (unfold inj_bytes; rewrite in_map_iff; intros ( ? & ? & ? ); discriminate).
      * intros _. exact id.
  - simpl.
    intros b' hi perm vol b'' Hhi Hb''. split. intros o' Ho'.
    eapply Mem.perm_store_1. eassumption.
    eapply SZ; eassumption.
    eapply SZ; eassumption.
  - refine (tp_upd_correct _ (c₀ :: cells) ty _ Habi TP'' Hupd).
    destruct ty as [ | ty ]. exact I.
    apply type_gamma_inv in Hvty.
    clear -Hvty Htypes.
    destruct ty;
    destruct κ'' as [[][]]; simpl in Htypes;
    eq_some_inv; hsplit; intuition; hsplit; subst;
    vauto; assumption.
  - exists ρn''.
    split.

  + unfold ρn''.
    intros c. specialize (CPT' c). simpl in *.
    destruct (cells_disjoint_dec).
    generalize (proj2 Hupd c). simpl. intros Y. rewrite Y. auto. auto.
    destruct (eq_dec (cell_from _ _) c). subst c. generalize (proj1 Hupd). simpl. intros ->. assumption.
    apply N.ncompat_compat_fun.

  + auto.
  Qed.

  Lemma vstore_sound : ∀ rettemp gofs κ args ab,
    VStore ge rettemp gofs κ argsab) ⊆ γ (vstore NumDom ge max_concretize rettemp gofs κ args ab).
Proof.
    intros rettemp gofs κ args ab.
    pose proof (@ab_eval_expr_correct ab) as AEEC.
    unfold vstore.
    match goal with
    | |- appcontext[ match ?x with _ => _ end ] => destruct x as [(tgt, arg) | ] eqn: Htgt
    end.
    2: easy.
    apply am_bind_case. easy. easy.
    intros μ' log Hvol. apply check_volatile_sound in Hvol.
    intros a [ t e s m G H ].
    ung. simpl in *.
    destruct ab as ([ | [ | (ε,τ) stk] ] & sz & (tp & nm)).
    easy. easy.
    simpl in *.
    destruct TPNM as (TP & ρ & CPT & NM).
    ung. subst. eq_some_inv. subst.
    destruct Hvol as [ -> Hvol ].
    apply am_bind_case. easy. easy.
    intros abval log' Harg.
    generalizeG, AEEC ((t, e) :: s) m G arg). rewrite Harg. intros Hev.
    destruct abval as [ | (((tp', nm') & oty) & lne) ].
    destruct Hev. vauto2.
    destruct Hev as (MONO & Hev). vauto2.
    destruct Hev as (i & j & k & ? & Hev). eq_some_inv. subst i j k.
    destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM))))) as (TP' & ρ' & CPT' & NM').
    specialize (Hev ρ' CPT' NM').
    destruct Hev as ( v & Hv & Hvdef & Hty & ne' & n & Hne' & Hvn & Hn' & Hn ).
    apply am_assert_spec. easy. easy.
    destruct oty as [ | ty ]. easy. intros Htyκ.
    assert (∃ v': Events.eventval,
               Events.eventval_match ge v' (AST.type_of_chunk κ) (Val.load_result κ v)) as v'.
    { apply type_gamma_inv in Hty.
      destruct ty, κ; eq_some_inv; hsplit; subst; vauto;
      try (destruct Hty; hsplit; subst; vauto);
      apply only_global_pointer_iff in Htyκ;
      destruct Htyκ as ( ptr' & -> & Hptr );
      match goal with
      | Hpt : γ (Just _) _ |- _ =>
        destruct Hpt as (ab & Hab & Habb); specialize (Hptr ab Hab);
        idtac end;
      destruct Hptr as (b' & -> & g' & Hg' & Hgp);
      simpl in *;
      rewrite Hg' in Habb; eq_some_inv; subst;
      eexists (Events.EVptr_global g' _); econstructor; eauto;
      apply Genv.invert_find_symbol, Hg'.
    }
    destruct v' as (v' & Hv').
    destruct gofs as [ (g, ofs) | ].
    - (* global *)
      destruct args as [ | arg' [ | ? ? ] ]; eq_some_inv. subst.
      destruct Hvol as ( b & Hg & hi & perm & Hvol ).
      generalize (SZ (ABglobal b) hi perm true b Hvol).
      simpl. rewrite (Genv.find_invert_symbol _ _ Hg).
      intros Hsz; destruct (Hsz eq_refl) as (Hrange & Hvol'). clear Hsz.
      specializetr, H tr _ _ _ _ v (conj eq_refl (conj eq_refl eq_refl)) Hg Hv).
      rewrite Hvol' in H.
      refine (let H' := H (Events.Event_vstore κ g ofs v' :: nil) _ in _).
      vauto.
      clear H; subst a.
      destruct rettemp as [ r | ].
      + rewrite (list_gamma_len STK). apply forget_temp_sound. split; vauto; eauto.
      + split; eauto; vauto.

    - (* local *)
      unfold with_tnum. simpl.
      destruct args as [ | addr [ | arg' [ | ? ? ] ] ]; eq_some_inv; subst.
      destruct Hvol as ((tp'', nm'') & ptr' & ne & EV & Hvol).
      specializeG, AEEC ((t, e) :: s) m G addr). rewrite EV in AEEC.
      edestruct AEEC as (MONO' & AEEC'). split; eauto; vauto.
      clear AEEC.
      destruct AEEC' as (? & ? & ? & ? & AEEC). eq_some_inv. subst x x1 x2.
      destruct (tnum_m_stronger MONO' (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM))))) as (TP'' & ρ'' & CPT'' & NM'').
      specialize (AEEC ρ'' CPT'' NM'').
      destruct AEEC as ( v'' & Hv'' & Hvdef' & Hty' & ne'' & n' & Hne'' & Hvn' & Hn'' & Hn''' ).
      simpl in *.
      destruct Hvol as (ptr & -> & Hvol).
      apply type_gamma_inv in Hty'.
      destruct Hty' as (b & i & -> & Hpt').
      destruct Hpt' as (blk & Hblk & Hb').
      generalize (validate_volatile_ptr_in SZ Hvol _ Hblk).
      intros (g & b' & -> & Hg & Hvol').
      simpl in Hb'. rewrite (Genv.find_invert_symbol _ _ Hg) in Hb'. eq_some_inv. subst.
      refine (let H' := H (Events.Event_vstore κ g i v' :: nil) _ _ _ _ v (conj eq_refl Hv'') Hg Hv _ in _).
      vauto.
      clear H.
      simpl in Hvol'. rewrite Hvol' in H'. subst a.
      destruct rettemp as [ r | ].
      + rewrite (list_gamma_len STK). apply forget_temp_sound. split; vauto; eauto.
      + split; eauto; vauto.
  Qed.

  Lemma push_frame_sound :
   ∀ (f : function) (args : list expr)
   (ab : t) (a : option concrete_state),
   PushFrame ge f argsab) a → γ (push_frame NumDom ge max_concretize f args ab) a.
Proof.
    intros fn args.
    intros (stk & sz & (tp & nm)) cs (σ & m & H & NR & PF).
    ung. simpl in *.
    unfold push_frame. simpl.
    destruct stk as [|σ']. simpl. apply am_bind_case. easy. easy.
    intros [|(?,?)] ? ?. easy. destruct (ab_bind_parameters _) as (?, (?,?)); exact I.
    assert ( ∃ m1 e1, alloc_variables empty_env m fn.(fn_vars) e1 m1) as (m1 & e1 & H1).
    {
      generalize fn.(fn_vars). intros l.
      generalize empty_env, m.
      induction l as [|(v,sz') l IH]. vauto2.
      intros e m'.
      destruct (Mem.alloc m' 0 sz') as [m'' b] eqn:AL.
      destruct (IH (PTree.set v (b, sz') e) m'') as (m1 & e1 & H1).
      vauto2.
    }
    specializevargs Hvargs, PF vargs Hvargs m1 e1 H1).
    simpl. apply am_bind_case. easy. easy.
    destruct σ as [|(t, e) σ].

  - (* empty stack: initial call to main *)
    specialize (PF _ eq_refl).
    simpl in STK. ung. subst σ'.
    generalize dependent (fn_params fn). intros [|] PF.
    {
    specialize (PF _ eq_refl).
    subst cs.
    unfold ab_bind_parameters.
    destruct args. 2: easy.
    simpl.
    intros x log X; vm_compute in X. eq_some_inv. subst x.
    split; [| | |split].
    + hnf. simpl. constructor. 2: vauto.
      exact (alloc_variables_build H1).
    + simpl. intros _. eapply push_frame_invariant; eauto.
    + simpl. refine (push_frame_sizes _ NR _ _); eauto using push_frame_invariant.
    + intros c. generalize (TP c). simpl.
      destruct (ACTreeDom.get c tp) as [|bs]. exact id.
      destruct c as [f x ofs|f x|b ofs]; simpl.
      rewrite get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e1 ! x) as [(b, h)|] eqn: Hx; auto. simpl.
      pose proof (load_undef (proj2 (alloc_variables_load H1 _ Hx)) (proj1_sig κ) ofs) as X. simpl in X. rewrite X. exact id. rewrite get_stk_nil. intros _. exact id.
      rewrite get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      generalize (create_undef_temps_get (fn_temps fn) x).
      destruct ((create_undef_temps (fn_temps fn)) ! x); intuition. subst; auto.
      rewrite get_stk_nil. easy.
      destruct (Genv.invert_symbol ge b) eqn: B; auto. simpl.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) H1) (INV.(globalValid) _ (Genv.invert_find_symbol _ _ B))).
      destruct (Mem.load (proj1_sig κ) m b ofs) as [()|]; try exact id.
      intros Hty. apply type_gamma_inv' in Hty.
      destruct Hty as [ ([ | ptr ] & Hty & [ -> | -> ])| -> ];
        try exact I;
      destruct Hty as (q & Q & Q');
      (exists q; split; [ exact Q | ];
              destruct q; simpl in *; eq_some_inv; exact Q'
      ).
    + exists ρn. split; auto.
      intros c. generalize (CPT c). simpl.
      destruct c as [f x ofs|f x|b ofs]; simpl;
      try rewrite get_stk_nil;
      try rewrite get_stk_cons;
      intros X.
      case (Pos.eqb_spec); intros _. simpl.
      destruct (e1 ! x) as [(b,s)|] eqn: Hx. simpl.
      generalize (load_undef (proj2 (alloc_variables_load H1 _ Hx)) (proj1_sig κ) ofs). simpl. intros Y. rewrite Y. exact X.
      exact X. rewrite get_stk_nil. exact X.
      case (Pos.eqb_spec); intros _. simpl.
      generalize (create_undef_temps_get (fn_temps fn) x).
      destruct ((create_undef_temps (fn_temps fn)) ! x); intuition. subst; auto.
      rewrite get_stk_nil. exact X.
      destruct (Genv.invert_symbol ge b) eqn: B; auto.
      rewrite (alloc_var_load _ (proj1 (alloc_var_alt _ _ _ _) H1) (INV.(globalValid) _ (Genv.invert_find_symbol _ _ B))).
      exact X.
    }

    destruct args;
    intros x log X; vm_compute in X; eq_some_inv; subst x.
    exact I.

  - (* non empty stack. *)
    intros x log X.
    assert (ab_stk (Just σ', (sz, (tp, nm))) ≠ All) as Hstk by (simpl; congruence).
    generalize (@ab_eval_exprlist_correct e t σ m args _ Hstk).
    unfold tnum in *. rewrite X. clear X. simpl.
    intros H. destruct x as [ | (ab'' & ab_args) ].
    destruct H. split; eauto; vauto.
    destruct H as (EQstk & EQsz & MONO & Hargs). split; eauto; vauto.
    destruct (Hargs _ CPT NM) as (vargs & EVL & AM). clear Hargs.
    specialize (PF _ EVL).
    unfold with_stk. simpl.
    match goal with |- appcontext[ ab_bind_parameters ?D ?μ ?f ?a (NotBot ?t, (nil, _))] => set (ab' := t) end.
    specialize (INV Hstk).
    assert (((∀ t0, ∃ t1, bind_parameters fn.(fn_params) vargs t0 = Some t1) ∧ ∃ ab'', am_get (ab_bind_parameters NumDom (plength σ') fn.(fn_params) ab_args (ret (NotBot ab'))) = Some ab'')
            ∨ am_get (ab_bind_parameters NumDom (plength σ') fn.(fn_params) ab_args (ret (NotBot ab'))) = None) as T1.
    {
      clear PF.
      setoid_rewrite bind_parameters_zip.
      generalize (NotBot ab'). clear ab'.
      revert args ab_args vargs AM EVL.
      generalize fn.(fn_params).
      intros formals.
      induction formals as [|id formals IH];
      intros args ab_args vargs AM EVL b; inv AM; simpl; vauto.
      specialize (IH _ _ _ H7). inv EVL.
      specialize (IH H13).
      match goal with |- appcontext[ ab_bind_parameters _ _ _ _ ?x ] =>
                      set (y := x) end.
      specialize (IH (fst y)).
      destruct IH as [ IH' | IH' ]; [ left | right ]; eauto.
      hsplit. split. intros t0. specialize (IH' (PTree.set id v t0)). eauto. eauto.
    }
    simpl in T1. destruct T1 as [(T1 & Hab'')|T1].
    2: destruct (ab_bind_parameters _) as (?, ([|], ?)); simpl in T1; eq_some_inv; exact I.
    specialize (T1 (create_undef_temps fn.(fn_temps))). hsplit. specialize (PF t1 T1).
    rewrite PF; clear cs PF.
    destruct (ab_bind_parameters _ _ _ _ _) as (q,([|],?)) eqn: Q; simpl in Hab''; eq_some_inv; subst q.
    red. red. simpl. match goal with |- ?xgamma_bot _ _ => set (cs := x) end.
    assert (γ(ab_stk ab') (fst cs)) as G1.
    { subst cs ab'. simpl in *. ung. subst. simpl. constructor.
      exact (alloc_variables_build H1). vauto. }
    assert (invariant ge cs) as INV'.
    {
      subst cs. refine (push_frame_invariant _ _ INV); eauto.
    }
    specialize (MONO _ (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM)))).
    edestructK q, @ab_bind_parameters_sound e t σ m fn.(fn_vars) e1 m1 INV K fn.(fn_params) args ab_args vargs ρn q _ _ T1 ab')
      as ((stk' & sz' & tp' & nm') & -> & Hstk' & Hsz & Htp & ρn' & Hnm);
      simpl; eauto.
    {
      rewrite (list_gamma_len STK), plength_cons in Q.
      rewrite Q; reflexivity.
    }
    {
      subst ab' cs. simpl. intros c. pose proof (proj1 MONO c) as X.
      simpl. destruct (ACTreeDom.get c _) as [|ty]. exact X.
      apply push_frame_pt; auto.
      revert X. simpl.
      destruct c as [f' x'|f' x'|b]; simpl.
      destruct (get_stk f' (_ :: σ)) as [ (?, ?) | ] eqn: X. simpl.
      rewrite (get_stk_tail _ _ _ X). exact id. simpl. now intros K; apply type_gamma_inv' in K.
      destruct (get_stk f' (_ :: σ)) as [ (?, ?) | ] eqn: X. simpl.
      rewrite (get_stk_tail _ _ _ X). exact id. simpl. now intros K; apply type_gamma_inv' in K.
      exact id.
    }
    {
      subst ab' cs. simpl.
      split; auto.
      intros c; specialize (CPT c).
      destruct c as [f' x'|f' x'|b]; simpl in *.
      rewrite get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      destruct (e1 ! x') as [(b, h)|] eqn: Hx. simpl.
      pose proof (alloc_var_inj _ _ (proj1 (alloc_var_alt _ _ _ _) H1)) as (_ & Y & _).
      specialize (Y _ _ Hx). destruct Y as [_ Y]. simpl in Y.
      rewrite (not_valid_block_load_None Y). vauto. vauto.
      intros _. exact CPT.
      rewrite get_stk_cons.
      case (Pos.eqb_spec). simpl. intros ->.
      generalize (create_undef_temps_get fn.(fn_temps) x'). destruct ((create_undef_temps _) ! x').
      intros; hsplit; subst; vauto. intros _; vauto.
      intros _. exact CPT. exact CPT.
      apply (proj2 MONO ρn CPT); auto.
    }
    split;[| | |split].
    + rewrite Hstk'. subst ab'. econstructor; eauto.
      hnf.
      eapply alloc_variables_build; eauto.
    + intros _. exact INV'.
    + generalize (push_frame_sizes INV' NR H1 SZ).
      rewrite Hsz. simpl.
      simpl in STK. rewrite (list_gamma_len STK). rewrite <- EQsz. exact id.
    + exact Htp.
    + exists ρn'. eauto.
  Qed.

  Lemma pop_frame_sound :
   ∀ (ret : option expr) (rettemp : option ident) (ab : t)
   (a : option concrete_state),
   PopFrame ge ret rettempab) a
   → γ (AbMemSignatureCsharpminor.pop_frame _ ret rettemp ab) a.
Proof.
    Hint Resolve env_ok.
    intros ret rettemp (σ & sz & tp & nm) a H.
    destruct H as [ tmp env stk m H PF ].
    destruct σ as [|[|(ε₀,τ₀) σ]]. exact I. exact I.
    ung. simpl in STK. ung. eq_some_inv. subst x x0.
    destruct σ as [|(ε₁,τ₁) σ].

  - (* return from main *)
    destruct ret as [ret|]. 2: exact I.
    ung. subst.
    simpl. unfold pop_frame; simpl.
    generalizeG, @ab_eval_expr_correct (Just ((ε₀,τ₀) :: nil), (sz, (tp, nm))) ((tmp, env) :: nil) m G ret).
    match goal with
      | |- appcontext [am_get ?X] =>
        match goal with
          | |- appcontext [do ret' <- ?Y; _] => change X with Y;
            destruct Y as ([|(((tp', nm') & ty) & ln)], ([|],?))
        end
    end.
    intros X; eelim X; split; eauto; vauto.
    intros _; exact I.
    2: intros _; destruct ty as [ | () ]; exact I.
    simpl.
    intros X.
    destruct ty as [|ty]. exact I.
    apply am_bind_case. easy. intros; exact I. intros () log Hty.
    edestruct X as (MONO & Y). split; eauto; vauto. clear X.
    destruct Y as (i & j & k & ? & X). eq_some_inv. subst i j k.
    destruct (tnum_m_stronger MONO (conj (Pun.mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) (conj TP (ex_intro _ _ (conj CPT NM))))) as (TP' & ρ' & CPT' & NM').
    specialize (X ρ' CPT' NM').
    destruct X as (v & V & Def & TY & _).
    assert (TY' := type_gamma_inv _ _ _ TY). clear TY. simpl in TY'.
    assert (∃ i, v = Vint i).
    { destruct ty; eq_some_inv; hsplit; vauto. }
    hsplit. subst v.
    specializem' M', PF m' M' _ V _ eq_refl).
    destruct (free_list_ex INV) as (m' & Hm' & Hm''). specialize (PF _ Hm'). subst a.
    split;[| | |split].
  + vauto.
  + intros _. eapply pop_frame_invariant. eauto.
    intros b H; split.
    intros lo hi p H1. apply Hm''; assumption.
    apply Hm'', H.
    apply (free_list_contents Hm').

  + eapply pop_frame_sizes; eauto. simpl. eapply Hm''.
  + intros c. refine (pop_frame_pt tmp env m m' tp c _ (TP c)). eapply Hm''.
  + exists ρn. split; auto.
    refine (pop_frame_compat m' _ CPT). eapply Hm''.

  - (* regular case *)
    simpl. unfold pop_frame. simpl.
    destruct (free_list_ex INV) as (m' & Hm' & Hm''). specialize (PF _ Hm').
    destruct ret as [ret|].
    destruct rettemp as [rettemp|].
    + apply am_bind_case. easy. intros; exact I.
      intros ab' log Hab'.
      generalize (@assign_in_correct (plength σ) rettemp ret (Just ( (ε₀,τ₀) :: (ε₁,τ₁) :: σ), (sz, (tp, nm)))).
      fold stack; fold (@tnum num).
      rewrite Hab'. simpl.
      intros X. specialize (X ((tmp, env) :: stk) m).
      refine (let K := X _ in _).
      econstructor; vauto. eauto. eauto.
      destruct ab' as [|(σ' & sz' & tp' & nm')]. destruct K. clear X.
      hsplit. simpl in *. subst sz' σ'. eq_some_inv. subst env0 tmp0 stk'. ung.
      subst.
      destruct get_stk_dep as [IN|(NI & _)].
      2: clear - STK NI; rewrite (list_gamma_len STK), ! plength_cons in NI; simpl in *; apply Pos.leb_le in NI; Psatz.lia.
      destruct IN as (((pre & (tmp', env')) & post) & EQ & IN & Hlen).
      simpl in *.
      rewrite <- (rev_involutive (_ ++ _ :: _)) in IN.
      apply rev_inj in IN.
      rewrite ! rev_app_distr in IN. simpl in IN. rewrite rev_involutive in IN.
      rewrite (list_gamma_len STK) in EQ.
      generalize (get_stk_length' ((tmp, env) :: nil) x x0). simpl. intros X.
      rewrite X in EQ. clear X. eq_some_inv. subst.
      apply (Pos.eqb_eq) in Hlen.
      rewrite (list_gamma_len STK) in Hlen.
      destruct pre as [ | ? [ | ? ? ] ]; simpl in *; eq_some_inv; subst; simpl in *.
      rewrite plength_cons in *. Psatz.lia.
      2: clear -Hlen; rewrite plength_app, Ppred_minus, plength_cons, Pplus_one_succ_r in Hlen; zify; rewrite ! Pos2Z.inj_sub in Hlen; Psatz.lia.
      hsplit.
      subst. clear STK SZ TP CPT NM ρn. ung. simpl in *. ung. eq_some_inv. subst x x0. eq_some_inv. subst x1 x2.
      erewrite PF; eauto. clear PF.
      assert (∀ b : block, valid_block ((PTree.set rettemp v tmp', env') :: post) b
                           ∀ (κ : memory_chunk) (o : Z), Mem.load κ m b o = Mem.load κ m' b o) as Hb'.
      { intros b ?. refine (proj1 (Hm'' b _)). eapply valid_block_tmp; eauto. reflexivity. }
      assert (∀ b : block, valid_block ((PTree.set rettemp v tmp', env') :: post) b
                           (∀ (lo hi : Z) p, Mem.range_perm m b lo hi Cur pMem.range_perm m' b lo hi Cur p) ∧
                           (bMem.valid_block mbMem.valid_block m')) as Hb''.
      { intros b ?. eapply Hm''; eauto. eapply valid_block_tmp; eauto. reflexivity. }
      generalize (pop_local_num_correct (Pos.succ (plength σ)) ε₀ τ₀ sz nm' _ NM).
      destruct (pop_local_num _ _ _ _). intros (). intros NM'.
      constructor; vauto;[| |split].
      * intros _. eapply pop_frame_invariant; eauto.
        apply (free_list_contents Hm').
      * simpl.
        generalize (pop_frame_sizes ε₀ INV0x y, proj1 (Hb'' x y)) SZ).
        rewrite (list_gamma_len STK), plength_cons. exact id.
      * intros c.
        generalize (pop_frame_pt tmp env _ _ tp' c Hb' (TP c)).
        rewrite (list_gamma_len STK), plength_cons. exact id.
      * exists ρn. split. 2: exact NM'.
        generalize CPT. apply pop_frame_compat. exact Hb'.
    + match goal with |- context[ noerror ?D ?G ?N ?e ?ab ] => generalize (noerror_correct e ab) end.
      destruct (noerror _) as ((), ([|],?)).
      2: exact_, I).
      simpl in *. intros H. edestruct H as (v & V & Def). econstructor; simpl; eauto. vauto.
      intuition eauto. vauto. clear H.
      specialize (PF _ V).
      ung. subst. destruct x. subst. rewrite plength_cons.
      generalize (pop_local_num_correct (Pos.succ (plength σ)) ε₀ τ₀ sz nm _ NM).
      rewrite (list_gamma_len STK).
      destruct (pop_local_num _ _ _ _). intros (). intros NM'.
      simpl.
      constructor; vauto;[| |split].
      * intros _. eapply pop_frame_invariant; eauto. eapply Hm''.
        apply (free_list_contents Hm').
      * simpl. erewrite <- plength_cons.
        eapply pop_frame_sizes; eauto. simpl. eapply Hm''.
      * intros c. generalize (TP c). simpl. erewrite <- plength_cons.
        apply pop_frame_pt. eapply Hm''.
      * exists ρn. split. 2: exact NM'.
        generalize CPT. apply pop_frame_compat. eapply Hm''.
    + destruct rettemp. exact I.
      specialize (PF _ eq_refl). ung. subst. destruct x. subst. simpl in *.
      rewrite plength_cons.
      generalize (pop_local_num_correct (Pos.succ (plength σ)) ε₀ τ₀ sz nm _ NM).
      destruct (pop_local_num _ _ _ _). intros (). intros NM'.
      constructor; vauto;[| |split].
      * intros _. eapply pop_frame_invariant; eauto. eapply Hm''.
        apply (free_list_contents Hm').
      * simpl.
        generalize (pop_frame_sizes ε₀ INVx y, proj1 (proj2 (Hm'' x y)) ) SZ).
        rewrite (list_gamma_len STK), plength_cons. exact id.
      * intros c.
        generalize (pop_frame_pt tmp env _ _ tp cx y, proj1 (Hm'' x y) ) (TP c)).
        rewrite (list_gamma_len STK), plength_cons. exact id.
      * exists ρn. split. 2: exact NM'.
        generalize CPT. apply pop_frame_compat. eapply Hm''.
  Qed.

  Existing Instance ListIncl.
  Lemma deref_fun_sound :
   ∀ (e : expr) (ab : t) (a : option (ident * fundef)),
   DerefFun ge eab) a → γ (deref_fun NumDom ge max_concretize e ab) a.
Proof.
    intros e ([|[|(ε,τ) σ]] & sz & (tp & nm)) fifd DF; try exact I.
    destruct DF as [ tmp en stk m H DF ].
    ung. simpl in *. ung. eq_some_inv. subst x x0.
    unfold deref_fun. simpl.
    apply am_bind_case. easy. intros; exact I.
    intros ((tp', nm'), cells) log Cells.
    generalizeU, @deref_expr_correct _ _ NumDom ge max_concretize _ ε sz en tmp _ stk eq_refl (env_ok ε en U) INV SZ None (tp, nm) (conj TP (ex_intro _ _ (conj CPT NM))) (exist _ Mint32 I) e).
    rewrite <- (list_gamma_len STK).
    rewrite Cells. simpl.
    intros X. edestruct X as (TN' & NB & Y); auto.
    destruct (NoError.noerror_eval_expr_def NB) as (v & DEF & EV).
    destruct (Y v EV) as (ab & b & i & Hv & Habi & Hab & Hal). clear X Y.
    with_am_map'.
    intros (_ & L).
    destruct (L (cell_from ab (Int.unsigned i, exist _ Mint32 I)) Habi) as ((f, fd) & Hf & K); clear L.
    destruct ab as [|b']; try discriminate.
    simpl in Hf.
    simpl in Hab. destruct (Genv.invert_symbol ge b') eqn: IS; eq_some_inv. subst.
    destruct (Int.unsigned i) eqn: Hi; try discriminate.
    specialize (DF b f).
    destruct (Genv.find_funct_ptr ge b). 2: discriminate.
    inv Hf.
    apply (f_equal Int.repr) in Hi. rewrite Int.repr_unsigned in Hi. subst i.
    rewrite (DF _ EV eq_refl). exact K.
    apply Genv.invert_find_symbol, IS.
  Qed.

  Instance widen_mem_correct:
    widen_op_correct (iter_t +⊥) (t +⊥) concrete_state.
Proof.
    split.
    - intros x a Ha. eapply botbind_spec, Ha. intros a' [ STK INV SZ TN ]. constructor; auto.
      destruct a' as (? & ? & ? & ?). destruct TN as (TP & NM). split; auto.
      destruct NM as (ρ' & A & B). eexists. split. eauto.
      apply init_iter_correct. auto.
    - unfold widen_mem. intros x y cs Hcs.
      destruct x as [|(stk & sz & tp & nm)]. contradiction.
      destruct Hcs as [ STK INV SZ (TP & ρ & CPT & NM) ].
      simpl.
      match goal with
      | |- context [?A ▽ ?B] => pose proof (widen_incr A B _ NM) as Q; destruct (AB)
      end.
      unfold ACTree.elt in *. destruct y as [|(STK' & SZ' & TP' & NUM')].
      + split.
        constructor; auto. split. auto. eexists. split. eauto. apply Q.
        eapply botbind_spec. 2:apply Q.
        constructor; auto. split. auto. eexists. split. eauto. auto.
      + match goal with
        | |- context [(match stk with All => All | Just STK0 => @?A STK0 end)] =>
          remember (match stk with All => All | Just STK0 => A STK0 end) as STK''
        end.
        cbv beta in HeqSTK''. unfold top_op in STK''.
        assertSTK'' (fst cs)).
        { destruct stk. subst. constructor. destruct STK'. subst. constructor.
          subst. apply list_widen_incr, STK. intros x y f Hf.
          apply (join_correct (join_op_correct := join_stack_elt_correct)). left. exact Hf. }
        assert (STK'' ≠ Allinvariant ge cs).
        { intros. apply INV. destruct stk. congruence. discriminate. }
        assert (sizes_gamma ge (fst cs) (sizes_widen sz SZ') (snd cs)).
        { unfold sizes_widen. apply sizes_join_correct. auto. }
        assert (points_to_gamma ge (fst cs) (points_to_widen tp TP') (mem_as_fun ge cs)).
        { apply ACTreeDom.join_tree_correct. refine _. auto. }
        split.
        * constructor. auto. auto. auto. split. auto.
          eexists. split. eauto. apply Q.
        * eapply botbind_spec.
          constructor. auto. auto. auto. split. auto.
          eexists. split. eauto. eauto. apply Q.
  Qed.

  Lemma mem_cshm_dom_correct:
    mem_dom_spec _ ge mem_cshm_dom t_gamma (gamma_bot iter_t_gamma).
Proof.
    constructor; try (now refine _).
  - (* assign *)
    exact assign_sound.

  - exact assign_any_sound.

  - (* store *)
    exact store_sound.

  - (* assume *)
    exact assume_sound.

  - (* no error *)
    exact noerror_sound.

  - (* ab_vload *)
    exact vload_sound.

  - (* ab_vstore *)
    exact vstore_sound.

  - (* deref fun *)
    exact deref_fun_sound.

  - (* pop frame *)
    exact pop_frame_sound.
  - (* push frame *)
    exact push_frame_sound.
  - (* init_mem *)
    exact init_mem_sound.
  Qed.

End num.