Module MemCsharpminor


Require ArithLib.
Require ListAdom.
Require Mconvert.
Require Pun.
Require AbMemSignatureCsharpminor.
Require DebugCshm DebugAbMachineEnv.

Import
  Utf8
  String
  Coqlib
  Maps
  Integers
  Floats
  Values
  Memory
  Globalenvs
  AST
  Cminor
  Csharpminor
  Util
  AssocList
  ToString
  Sets
  AdomLib
  AlarmMon
  ListAdom
  AbMemSignatureCsharpminor
  AbMachineEnv
  PExpr
  TypesDomain
  MemChunkTree
  Cells
  Pun
  NoError
  PointsTo
  Mconvert
  FastArith.

Open Scope string.

Set Implicit Arguments.
Unset Elimination Schemes.


Definition injective_map {A B} (m: Aoption B) : Prop :=
  ∀ x x' a,
    m(x) = Some am(x') = Some a
    x = x'.

Definition stacks_tmp (stk stk': list (temp_env * env)) : Prop :=
  map snd stk =
  map snd stk'.

Lemma stacks_tmp_in : ∀ stk stk',
  stacks_tmp stk stk' →
  ∀ t e,
    In (t, e) stk' →
    ∃ t', In (t', e) stk.
Proof.
  unfold stacks_tmp.
  induction stk as [|(t, e) stk IH]; intros stk' H.
  generalize (map_nil_inv _ _ (eq_sym H)). intros -> ? ? ().
  destruct stk' as [|(t', e') stk'].
  apply map_nil_inv in H. eq_some_inv.
  simpl in H. eq_some_inv.
  subst.
  intros t0 e [?|H]. eq_some_inv. subst. vauto.
  edestruct IH. eassumption. eassumption.
  vauto.
Qed.

Lemma get_stk_tmp : ∀ stk stk',
  stacks_tmp stk stk' →
  ∀ f,
  option_map snd (get_stk f stk) =
  option_map snd (get_stk f stk').
Proof.
  unfold stacks_tmp.
  induction stk as [|(t, e) stk IH] using rev_ind; intros stk' H.
  generalize (map_nil_inv _ _ (eq_sym H)). intros -> ?. reflexivity.
  assert (∃ pre q, stk' = pre ++ q :: nil)%list as X.
  { exists (removelast stk'), (last stk' (t, e)).
    rewrite (@app_removelast_last _ stk' (t, e)) at 1.
    reflexivity.
    intros ->; destruct stk; simpl in H; eq_some_inv. }
  hsplit. subst. rewrite ! map_app in H. simpl in H.
  unfold get_stk. rewrite ! rev_app_distr. simpl.
  apply snoc_eq_inv in H. destruct H as [ H -> ].
  specialize (IH _ H).
  intros [ f | f | ].
  exact (IH (xO f)).
  exact (IH (Pos.pred_double f)).
  reflexivity.
Qed.

Remark block_of_ablock_tmp ge : ∀ stk stk',
  stacks_tmp stk stk' →
  ∀ ab, block_of_ablock ge stk ab = block_of_ablock ge stk' ab.
Proof.
  unfold stacks_tmp.
  intros stk stk' H.
  intros [f x|b]; simpl; auto.
  generalize (get_stk_tmp H f).
  destruct (get_stk f stk') as [ (t, e) | ];
  simpl; intros X; eq_some_inv.
  destruct X as ((t',e') & X & <-). rewrite X. reflexivity.
  destruct (get_stk _ _); auto; simpl in *; eq_some_inv.
Qed.

Module N := Nconvert ACTree.
Module PT := N.PT.
Export PT.
Definition types : Type := ACTreeDom.t AbTy.t.

Definition lvarset : Type := PSet.t.
Definition tvarset : Type := PSet.t.
Definition stack : Type := list (lvarset * tvarset).

Instance fname_leb : leb_op fname := flat_leb_op Pos.eqb.
Instance fname_join : join_op fname (fname+⊤) := flat_join_op Pos.eqb.
Instance lvarset_leb : leb_op lvarset := flat_leb_op PSet.beq.
Instance lvarset_join : join_op lvarset (lvarset+⊤) := flat_join_op PSet.beq.

Instance types_leb : leb_op types :=
  ACTreeDom.leb_tree _ _.
Proof.
abstract (destruct x; auto). Defined.

Instance types_join : join_op types types :=
  ACTreeDom.join_tree _ _.
Proof.
abstract (destruct x; auto). Defined.

Definition types_widen : types -> types -> types := join.

Definition sizes : Type := ABTreeDom.t (Z * (permission * bool)).
Definition sizes_gamma ge stk : gamma_op sizes mem :=
  λ sz m,
  ∀ b hi perm vol b',
    ABTreeDom.get b sz = Just (hi, (perm, vol)) →
    block_of_ablock ge stk b = Some b' →
    Mem.range_perm m b' 0 hi Cur perm
 ∧ Bool.leb vol (Senv.block_is_volatile ge b').

Instance BoolEqDec : EqDec bool := eq_dec_of_beq _ Bool.eqb_true_iff.
Definition permission_beq (x y: permission) : bool :=
  match x, y with
  | Freeable, Freeable
  | Writable, Writable
  | Readable, Readable
  | Nonempty, Nonempty
    => true
  | _, _ => false
  end.

Lemma permission_beq_iff x y : permission_beq x y = truex = y.
Proof.
split. now destruct x, y; intros; eq_some_inv. now intros <-; destruct x. Qed.
Instance PermissionEqDec : EqDec permission := eq_dec_of_beq _ permission_beq_iff.

Instance PermissionToString : ToString permission :=
  λ perm,
  match perm with
  | Freeable => "Freeable"
  | Writable => "Writable"
  | Readable => "Readable"
  | Nonempty => "Nonempty"
  end%string.

Instance sizes_leb : leb_op sizes :=
  leb (leb_op:=ABTreeDom.leb_tree (flat_leb_op eq_dec) _).
Proof.
abstract now unfold leb, flat_leb_op; intros x; rewrite eq_dec_true. Defined.

Instance sizes_join : join_op sizes sizes := ABTreeDom.join_tree (flat_join_op eq_dec) _.
Proof.
abstract now unfold join, flat_join_op; intros x; rewrite eq_dec_true. Defined.

Definition sizes_widen : sizes -> sizes -> sizes := join.

Instance sizes_top_correct ge stk : top_op_correct sizes mem (G:=sizes_gamma ge stk).
Proof.
  intros x b hi b'. unfold top. rewrite ABTreeDom.get_top. inversion 1.
Qed.

Instance sizes_leb_correct ge stk : leb_op_correct sizes mem (G:=sizes_gamma ge stk).
Proof.
  intros a1 a2 H m G b hi perm vol b' B B'.
  specialize (G b hi perm vol b').
  unfold leb, sizes_leb, sizes in H. rewrite ABTreeDom.leb_le in H.
  specialize (H b). rewrite B in H.
  destruct (ABTreeDom.get b a1); simpl in H. discriminate.
  unfold leb, flat_leb_op in H. InvBooleans.
  subst. exact (G eq_refl B').
Qed.

Instance sizes_join_correct ge stk : join_op_correct sizes sizes mem
                            (GA:=sizes_gamma ge stk) (GB:=sizes_gamma ge stk).
Proof.
  intros x y a H b hi perm vol b' G B'.
  unfold join, sizes_join in G. rewrite ABTreeDom.get_joinwiden in G.
  destruct (ABTreeDom.get b x) eqn: Bx. discriminate.
  destruct (ABTreeDom.get b y) eqn: By. discriminate.
  unfold flat_join_op in G. simpl in G.
  destruct (@eq_dec _ _); subst; simpl in *; inv G.
  destruct H as [H|H]; specialize (H b hi perm vol b');
  first [ specialize (H Bx) | specialize (H By) ];
  exact (H B').
Qed.

Lemma ptset_gamma_tmp ge : ∀ stk stk',
  stacks_tmp stk stk' →
  ∀ bs, ptset_gamma ge stk bsptset_gamma ge stk' bs.
Proof.
  intros stk stk' H bs v.
  destruct v; try exact id.
  intros (b' & Hb' & K).
  exists b'. split. exact Hb'.
  erewrite <- block_of_ablock_tmp; eassumption.
Qed.

Lemma points_to_gamma_tmp ge : ∀ stk stk',
  stacks_tmp stk stk' →
  ∀ ptr, points_to_gamma ge stk ptrpoints_to_gamma ge stk' ptr.
Proof.
  intros stk stk' H ptr ρ Ρ c.
  generalizec).
  destruct (ACTreeDom.get c ptr). exact id.
  apply ptset_gamma_tmp. exact H.
Qed.

Lemma sizes_gamma_tmp ge stk stk' :
  stacks_tmp stk stk' →
  ∀ sz, sizes_gamma ge stk szsizes_gamma ge stk' sz.
Proof.
  intros H sz a A b hi b' B B' ofs Hofs.
  apply (A b hi b'); auto.
  rewrite (block_of_ablock_tmp ge H).
  assumption.
Qed.

Section num.

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

  Lemma gamma_num_ext :
   ∀ ρ ρ' : cellmach_num,
   (∀ x : cell, ρ x = ρ' x) → ∀ m : num +⊥, γ m ρ → γ m ρ'.
Proof.
    intros ρ ρ' H [|m]. intros (). replace ρ' with ρ. exact id.
    apply Axioms.extensionality. exact H.
  Qed.

  Definition compat := (λ ρ ρn, N.compat _x, x) ρ ρn).

  Variable ge : genv.
  Local Instance AblockToString : ToString ablock := AblockToString ge.
  Local Instance CellToString : ToString cell := CellToString ge.

  Record invariant (cs: concrete_state) : Prop :=
    { localFreeable :
        ∀ t e, In (t, e) (fst cs) →
          ∀ b hi, In (b, 0, hi) (blocks_of_env e) →
                  Mem.range_perm (snd cs) b 0 hi Cur Freeable
    ; localNotGlobal :
        ∀ t e, In (t, e) (fst cs) →
             ∀ x b y b' z,
               Genv.find_symbol ge x = Some b
               e ! y = Some (b', z) →
               bb'
    ; localInj :
   injective_mapx_v,
                  do t_e <- get_stk (fst x_v) (fst cs);
                  fmap fst ((snd t_e) ! (snd x_v)))
   ; globalValid:
   ∀ x b, Genv.find_symbol ge x = Some b
          bMem.valid_block (snd cs)
   ; localValid:
       ∀ t e, In (t, e) (fst cs) →
                ∀ x b z,
               e ! x = Some (b, z) →
               bMem.valid_block (snd cs)
   ; noIntFragment:
       ∀ ab b,
         block_of_ablock ge (fst cs) ab = Some b
         ∀ o, match ZMap.get o (Mem.mem_contents (snd cs)) !! b with Fragment (Vint _) _ _ => False | _ => True end
   }.

  Lemma invariant_tmp : ∀ stk stk',
    stacks_tmp stk stk' →
    ∀ m, invariant (stk, m) → invariant (stk', m).
Proof.
    intros stk stk' H m INV.
    split.
  - intros t e H0.
    apply (stacks_tmp_in H) in H0. hsplit.
    generalize (INV.(localFreeable) t' e H0). auto.
  - intros t e H0.
    apply (stacks_tmp_in H) in H0. hsplit.
    generalize (INV.(localNotGlobal) t' e H0). auto.
  - intros (f, x) (f', x') b.
    generalize (INV.(@localInj _) (f, x) (f', x') b). clear -H.
    simpl.
    generalize (get_stk_tmp H f').
    generalize (get_stk_tmp H f).
    destruct (get_stk f stk') as [ (t, e) |]; simpl; intros X; eq_some_inv;
    hsplit; subst; rewrite X.
    2: intros; eq_some_inv.
    destruct (get_stk f' stk') as [ (t', e') |]; simpl; intros X'; eq_some_inv;
    hsplit; subst; rewrite X'.
    2: intros; eq_some_inv.
    easy.
  - exact INV.(globalValid).
  - intros t e H0.
    apply (stacks_tmp_in H) in H0. hsplit.
    generalize (INV.(localValid) _ _ H0). auto.
  - intros ab b. rewrite <- (block_of_ablock_tmp ge H). apply INV.
  Qed.

  Lemma invariant_block_of_ablock_inj stk m :
    (stk, m) ∈ invariant
    ∀ abbabb₂,
      block_of_ablock ge stk ab₁ = Some b₁ →
      block_of_ablock ge stk ab₂ = Some b₂ →
      ab₁ ≠ ab₂ →
      b₁ ≠ b₂.
Proof.
    intros INV [f x|b] b₁ [f' x'|b'] b₂; simpl.
    - generalize (INV.(localInj) (f, x) (f', x')). simpl.
      destruct (get_stk f stk) as [(t, e)|]. 2: intros; eq_some_inv.
      destruct (get_stk f' stk) as [(t', e')|]. 2: intros; eq_some_inv.
      simpl.
      destruct (e!x) as [(b, ?)|]. 2: intros; eq_some_inv.
      destruct (e'!x') as [(b', ?)|]. 2: intros; eq_some_inv.
      simpl. intros H H0 H1 H2 ?. eq_some_inv. subst. apply H2. specialize (H _ eq_refl eq_refl). congruence.
    - destruct (get_stk f stk) as [(t, e)|] eqn: A. 2: intros; eq_some_inv.
      simpl. destruct (e!x) as [(b, ?)|] eqn: Hx. 2: intros; eq_some_inv.
      destruct (Genv.invert_symbol ge b') as [q|] eqn: Q; intros; eq_some_inv.
      subst. apply not_eq_sym.
      exact (INV.(localNotGlobal) t e (get_stk_in A) _ _ (Genv.invert_find_symbol _ _ Q) Hx).
    - destruct (Genv.invert_symbol ge b) as [q|] eqn: Q; intro; eq_some_inv.
      destruct (get_stk f' stk) as [(t', e')|] eqn: A. 2: intros; eq_some_inv.
      simpl. destruct (e'!x') as [(b', ?)|] eqn: Hy; intros; eq_some_inv. subst.
      exact (INV.(localNotGlobal) t' e' (get_stk_in A) _ _ (Genv.invert_find_symbol _ _ Q) Hy).
    - destruct (Genv.invert_symbol ge b) as [q|] eqn: Q; intro; eq_some_inv.
      destruct (Genv.invert_symbol ge b') as [q'|] eqn: Q'; intros; eq_some_inv.
      congruence.
  Qed.

  Lemma assign_const c i (nm : num) :
    ∀ ρ, ρ ∈ γ(nm) →
    match AbMachineEnv.assign c (me_const_i _ i) nm with
    | Bot => False
    | NotBot nm' => (upd ρ c (MNint i)) ∈ γ(nm')
    end.
Proof.
    intros ρ H.
    assert (eval_mexpr ρ (me_const_i _ i) (MNint i)) as V
    by (repeat econstructor; reflexivity).
    generalize (AbMachineEnv.assign_correct c nm H V).
    destruct AbMachineEnv.assign; exact id.
  Qed.

  Definition tnum : Type := (types * num)%type.
  Definition tnum_iter : Type := (types * num_iter)%type.

  Instance tnum_gamma : gamma_op tnum (cellval) :=
    λ τ_ν ρ,
    let '(τ, ν) := τ_ν in
    ρ ∈ γ(τ) ∧
    ∃ ρ',
      ρ' ∈ (compat ρ ∩ γ ν).

  Instance tnum_iter_gamma : gamma_op tnum_iter (cellval) :=
    λ τ_ν ρ,
    let '(τ, ν) := τ_ν in
    ρ ∈ γ(τ) ∧
    ∃ ρ',
      ρ' ∈ (compat ρ ∩ γ ν).

  Instance tnum_leb_correct : leb_op_correct (types * num) (cell -> val).
Proof.
    - intros (τ,ν) (τ',ν'). unfold leb, WProd.leb_prod. rewrite andb_true_iff.
      intros [H K] ρ (TP & ρ' & NC & NM).
      split. eapply leb_correct; eauto.
      exists ρ'. split. exact NC.
      eapply leb_correct; eauto.
  Qed.

  Instance tnum_top_correct : top_op_correct ((types * num)+⊥) (cell -> val).
Proof.
    intros ρ. unfold top, WProd.top_prod_bot, top, top_bot. simpl.
    eapply botbind_spec. intros.
    - split. apply top_correct.
      exists (N.compat_fun ∘ ρ). split. 2:eauto.
      intros c. apply N.ncompat_compat_fun.
    - apply top_correct.
  Qed.

  Instance tnum_join_correct : join_op_correct (types * num) ((types * num)+⊥) (cell -> val).
Proof.
    intros (τ,ν) (τ',ν') ρ.
    unfold join, WProd.join_prod_bot, join, join_bot_res. simpl.
    intros [(TP & ρ' & H & K)|(TP & ρ' & H & K)].
    - eapply botbind_spec. intros. split.
      eapply join_correct. eauto.
      exists ρ'. split. assumption. eauto.
      eapply join_correct. eauto.
    - eapply botbind_spec. intros. split.
      eapply join_correct. eauto.
      exists ρ'. split. assumption. eauto.
      eapply join_correct. eauto.
  Qed.

  Section REALIZATION.


    Definition is_int (tp: types) (c: cell) : bool :=
      ACTreeDom.get c tpJust VI.

    Lemma is_int_intro (tp: types) (ρ: cellval) (TP: ρ ∈ γ tp) (c: cell) (P: boolProp):
      (P false) →
      (ρ c ∈ γ VIP true) →
      P (is_int tp c).
Proof.
      intros F T.
      generalize (TP c).
      unfold is_int.
      case (ACTreeDom.get). intros _; exact F.
      intros (); ( exact T || intros _; exact F ).
    Qed.
    Global Opaque is_int.

    Definition realize_u8_from_κ (κ: typed_memory_chunk) (ab: ablock) (base: Z) (shift: Z) (tp: types) (nm: num) : tnum+⊥ :=
      let uc : cell := cell_from ab (base + shift, exist _ Mint8unsigned I) in
      let c : cell := cell_from ab (base, κ) in
      do nm' <- assign uc (MEbinop Oand (MEbinop Oshru (MEvar MNTint c) (MEconst _ (MNint (Int.repr (8 * shift))))) (MEconst _ (MNint (Int.repr 255)))) nm;
      NotBot (ACTreeDom.set tp uc (Just VI), nm').

    Definition tnum_mono (x y: tnum) : Prop :=
      ∀ ρ, Pun.pun_u8 ρ →
           ρ ∈ γ x
           ρ ∈ γ (fst y) ∧
           ∀ ρ',
             compat ρ ρ' →
             ρ' ∈ γ (snd x) → ρ' ∈ γ (snd y).

    Lemma tnum_m_stronger :
      ∀ x y, tnum_mono x y
          Pun.pun_u8 ∩ γ (x) ⊆ γ (y).
Proof.
      intros (tp, nm) (tp', nm') M ρ (Hpun & TP & ρ' & CPT & NM).
      destruct (M ρ Hpun (conj TP (ex_intro _ _ (conj CPT NM)))) as (TP' & K). vauto.
    Qed.

    Lemma tnum_mono_refl x : tnum_mono x x.
Proof.
      destruct x as (tp, nm).
      intros ρ Hpun (TP & NM). auto.
    Qed.

    Lemma tnum_mono_trans y x z :
      tnum_mono x ytnum_mono y z
      tnum_mono x z.
Proof.
      unfold tnum_mono.
      intros Hxy Hyz ? Hp Hx.
      destruct (Hxy _ Hp Hx) as (Hy1 & Hxy').
      assert (ρ ∈ γ y) as Hy. destruct y; split. auto.
      destruct x, Hx. hsplit. eauto.
      destruct (Hyz _ Hp Hy) as (Hz1 & Hyz').
      eauto.
    Qed.

    Definition tnum_mono' (x: tnum) (y': tnum+⊥) : Prop :=
      ∀ ρ, Pun.pun_u8 ρ →
           ρ ∈ γ x
           match y' with
           | Bot => False
           | NotBot y =>
             ρ ∈ γ (fst y) ∧
             ∀ ρ',
               compat ρ ρ' →
               ρ' ∈ γ (snd x) → ρ' ∈ γ (snd y)
           end.

    Lemma realize_u8_from_κ_sound (κ: typed_memory_chunk) ab base shift (tp: types) (nm: num) :
      (Archi.big_endian = false) →
      (align_chunk (proj1_sig κ) | base) →
      0 <= shift < size_chunk (proj1_sig κ) ∧ size_chunk (proj1_sig κ) <= 4 →
      let c : cell := cell_from ab (base, κ) in
      is_int tp c
      tnum_mono' (tp, nm) (realize_u8_from_κ κ ab base shift tp nm).
Proof.
      intros LE AL Hshift c Hint ρ Hpun (TP & ρ' & CPT & NM).
      assert (Int.ltu (Int.repr (8 * shift)) Int.iwordsize) as Hshift'.
      {
        set (s := List.map Z.of_nat (seq 0 4)).
        apply (forallb_forallx, Int.ltu (Int.repr (8 * x)) Int.iwordsize) s).
        unfold s. exact eq_refl.
        unfold s. apply in_map_iff. exists (Z.to_nat shift). split.
        apply Z2Nat.id. easy.
        apply In_seq. Psatz.lia. zify. rewrite Z2Nat.id; Psatz.lia.
      }
      unfold realize_u8_from_κ. fold c.
      set (uc := cell_from ab (base + shift, exist _ Mint8unsigned I)).
      assert (∃ i, ρ c = Vint i) as Hi.
      {
        revert Hint. apply (is_int_intro TP); intros Hint.
        exact (false_not_true Hint _). intros _.
        AbTy.gamma_inv. exact Hint.
      }
      clear Hint. destruct Hi as (i & Hi).
      assert (ρ' c = MNint i) as Hi'.
      { specialize (CPT c). rewrite Hi in CPT. auto. }
      set (me := (MEbinop Oand
                          (MEbinop Oshru (MEvar MNTint c)
                                   (MEconst cell (MNint (Int.repr (8 * shift)))))
                          (MEconst cell (MNint (Int.repr 255))))).
      set (n := Int.and (Int.shru i (Int.repr (8 * shift))) (Int.repr 255)).
      assert (eval_mexpr ρ' me (MNint n)) as MEV.
      { econstructor; vauto. econstructor; vauto. }
      generalize (@assign_correct _ _ _ _ NumDom uc me ρ' (MNint n) nm NM MEV).
      destruct (assign _ _ _) as [ | nm' ] eqn: Hassn. exact id.
      intros NM'.
     assertuc = Vint n) as Huc.
     {
       unfold n.
       change (Int.repr 255) with (Int.repr (two_p 8 - 1)).
       erewrite <- Int.zero_ext_and. 2: Psatz.lia.
       destruct (Hpun ab κ base AL) as (bytes & Hnof & Hbytes & Hlen & Huc).
       assert (NPeano.ltb (Z.to_nat shift) (Datatypes.length bytes)) as LT.
       { rewrite Hlen. apply NPeano.ltb_lt. zify. unfold size_chunk_nat.
         rewrite ! Z2Nat.id; Psatz.lia. }
       specialize (Huc (Z.to_nat shift) LT).
       rewrite Z2Nat.id in Huc. 2: easy. fold uc in Huc. rewrite Huc.
       fold c in Hbytes. rewrite Hi in Hbytes.
       revert Hlen Hbytes. clear -LE Hshift Hnof.
       Opaque Z.mul.
       unfold decode_val.
       destruct bytes as [ | b₀ [ | b₁ [ | b₂ [ | b₃ [ | bbytes ] ] ] ] ].
       - (* length 0 *) discriminate.
       - (* length 1: Mint8signed Mint8unsigned *)
       destruct κ as [()]; try discriminate; intros _; simpl in Hshift;
       assert (shift = 0) by Psatz.lia; clear Hshift; subst shift;
       rewrite Int.shru_zero;
       destruct b₀; try discriminate; intros X; inv X;
       simpl; f_equal.
       rewrite Int.zero_sign_ext_narrow. reflexivity. Psatz.lia.
       rewrite Int.zero_ext_narrow. reflexivity. Psatz.lia.
       - (* length 2: Mint16signed Mint16unsigned *)
       destruct κ as [()]; try discriminate; intros _; simpl in Hshift;
       assert (shift = 0 ∨ shift = 1) as Hs by Psatz.lia; clear Hshift;
       destruct Hs; subst shift;
       destruct b₀; try discriminate;
       destruct b₁; try discriminate;
       intros X; inv X;
       simpl; f_equal.
       rewrite Int.shru_zero, Int.zero_sign_ext_narrow. 2: Psatz.lia.
       rewrite decode_int_1, (decode_int_LE_2 LE).
       rewrite Z.mul_comm. apply z8.
       apply (decode_shift_1s LE).
       rewrite Int.shru_zero, Int.zero_ext_narrow. 2: Psatz.lia.
       rewrite decode_int_1, (decode_int_LE_2 LE).
       rewrite Z.mul_comm. apply z8.
       rewrite <- (decode_shift_1u LE). reflexivity.
       - (* length = 3 *) destruct κ as [()]; discriminate.
       - (* length = 4: Mint32 Mfloat32 Many32 *)
       assert (shift = 0 ∨ shift = 1 ∨ shift = 2 ∨ shift = 3) as Hs by Psatz.lia; clear Hshift.
       destruct κ as [()]; try discriminate;
       intros _;
       destruct b₀; try discriminate;
       destruct b₁; try discriminate;
       destruct b₂; try discriminate;
       destruct b₃; try discriminate;
       simpl;
       try (rewrite ! andb_false_r; discriminate);
       intros X; inversion X; clear X; subst;
       simpl; f_equal.
       repeat (destruct Hs as [ Hs | Hs ]); subst shift.
       simpl. f_equal. rewrite Int.shru_zero.
       unfold decode_int, rev_if_be. rewrite LE. simpl. rewrite Z.add_0_r.
       apply z8.
       simpl. f_equal. apply NS1, LE.
       simpl. f_equal. apply NS2, LE.
       simpl. f_equal. apply NS3, LE.
       exfalso. revert H0 Hnof. clear. bif. destruct v; intros X; inv X. exact false_eq_true_False.
       exfalso. revert H0 Hnof. clear. bif. destruct v; intros X; inv X. exact false_eq_true_False.
       - (* length ≥ 5 *)
       destruct κ as [()]; try discriminate;
       exfalso; simpl in Hshift; clear -Hshift; Psatz.lia.
     }
      split.
      - intros c'. unfold fst, upd. rewrite ACTreeDom.gsspec.
        case (ACTree.elt_eq). intros ->.
        rewrite Huc. exact I.
        intros _. apply (TP c').
      - clear ρ' CPT NM Hi' MEV NM'.
        unfold snd.
        intros ρ' CPT NM.
        assert (ρ' c = MNint i) as Hi'.
        { specialize (CPT c). rewrite Hi in CPT. auto. }
        assert (eval_mexpr ρ' me (MNint n)) as MEV.
        { econstructor; vauto. econstructor; vauto. }
        generalize (@assign_correct _ _ _ _ NumDom uc me ρ' (MNint n) nm NM MEV).
        rewrite Hassn.
        assert (ρ' +[ uc => MNint n ] = ρ') as EXT.
        { apply Axioms.extensionality. intros c'.
          unfold upd. case eq_dec. intros ->.
          generalize (CPT uc). rewrite Huc. exact id.
          reflexivity. }
        setoid_rewrite EXT. exact id.
    Qed.

    Definition cell_mem_rect {T: Type} :
      T
      (ablockZtyped_memory_chunkT) →
      cellT :=
      λ b r c,
      match c with
      | AClocal f x ofs κ => r (ABlocal f x) ofs κ
      | ACglobal g ofs κ => r (ABglobal g) ofs κ
      | ACtemp _ _ => b
      end.

    Definition cell_mem_rect_intro {T: Type} (P: cellTProp) :
      ∀ b r,
        (∀ f r, P (ACtemp f r) b) →
        (∀ ab ofs κ, P (cell_from ab (ofs, κ)) (r ab ofs κ)) →
        ∀ c, P c (cell_mem_rect b r c) :=
      λ b r B R c,
      match c with
      | AClocal f x ofs κ => R (ABlocal f x) ofs κ
      | ACglobal g ofs κ => R (ABglobal g) ofs κ
      | ACtemp f r => B f r
      end.
    Global Opaque cell_mem_rect.

    Definition optimistic_realization_one (c: cell) (tn: tnum) : tnum+⊥ :=
      let '(tp, nm) := tn in
      match ACTreeDom.get c tp with
      | Just _ => NotBot tn
      | All =>
        cell_mem_rect
          (NotBot tn)
          (λ ab ofs κ,
           match κ with
           | exist Mint8unsigned _ =>
             let (base, shift) := Z.div_eucl ofs 4 in
             let base := 4 * base in
             let c32 := cell_from ab (base, exist _ Mint32 I) in
             if is_int tp c32
             then realize_u8_from_κ (exist _ Mint32 I) ab base shift tp nm
             else
               let (base, shift) := Z.div_eucl ofs 2 in
               let base := 2 * base in
               let c16u := cell_from ab (base, exist _ Mint16unsigned I) in
               if is_int tp c16u
               then realize_u8_from_κ (exist _ Mint16unsigned I) ab base shift tp nm
               else
               let c16s := cell_from ab (base, exist _ Mint16signed I) in
               if is_int tp c16s
               then realize_u8_from_κ (exist _ Mint16signed I) ab base shift tp nm
               else
               let c8s := cell_from ab (ofs, exist _ Mint8signed I) in
               if is_int tp c8s
               then realize_u8_from_κ (exist _ Mint8signed I) ab ofs 0 tp nm
               else NotBot tn
           | exist Mint32 _ =>
             if ArithLib.divide ofs F4 then
             let c := cell_from ab (ofs, κ) in
             let c8 shift := cell_from ab (ofs + shift, exist _ Mint8unsigned I) in
             if is_int tp (c8 0) then
             if is_int tp (c8 1) then
             if is_int tp (c8 2) then
             if is_int tp (c8 3) then
               do nm' <- assign c
                   (MEbinop Oadd (MEvar MNTint (c8 0))
                   (MEbinop Oadd (MEbinop Oshl (MEvar MNTint (c8 1)) (MEconst _ (MNint (Int.repr 8))))
                   (MEbinop Oadd (MEbinop Oshl (MEvar MNTint (c8 2)) (MEconst _ (MNint (Int.repr 16))))
                   (MEbinop Oshl (MEvar MNTint (c8 3)) (MEconst _ (MNint (Int.repr 24)))))))
                   nm;
               NotBot (ACTreeDom.set tp c (Just VI), nm')
             else NotBot tn
             else NotBot tn
             else NotBot tn
             else NotBot tn
             else NotBot tn
           | _ => NotBot tn
           end)
          c
      end.

    Lemma optimistic_realization_one_sound (c: cell) (tn: tnum) :
      tnum_mono' tn (optimistic_realization_one c tn).
Proof.
      destruct tn as (tp, nm).
      intros ρ Hpun (TP, NM). simpl.
      destruct (ACTreeDom.get _ _). 2: auto.
      apply cell_mem_rect_intro. auto.
      clear c.
      intros ab ofs [() ()]; auto.
      - elim_div. intros (-> & [ Hrem | ? ]). Psatz.lia. 2: Psatz.lia.
        bif'.
        apply realize_u8_from_κ_sound; auto.
        simpl. eexists; apply Z.mul_comm.
        simpl. Psatz.lia.
        split; assumption.
        elim_div. intros (Hdiv & [ Hrem' | ? ]). Psatz.lia. 2: Psatz.lia.
        bif'.
        apply realize_u8_from_κ_sound; auto.
        simpl. eexists; apply Z.mul_comm.
        simpl. Psatz.lia.
        split; assumption.
        bif'.
        apply realize_u8_from_κ_sound; auto.
        simpl. eexists; apply Z.mul_comm.
        simpl. Psatz.lia.
        split; assumption.
        bif'.
        apply realize_u8_from_κ_sound; auto.
        simpl. eexists; symmetry; apply Z.mul_1_r.
        simpl. Psatz.lia.
        split; assumption.
        auto.
      - assert (Hdiv:=ArithLib.divide_ok ofs F4). destruct ArithLib.divide. 2: auto.
        destruct Hdiv as [Hdiv _]. specialize (Hdiv eq_refl).
        set (c8 shift := cell_from ab (ofs + shift, exist _ Mint8unsigned I)).
        fold (c8 0). fold (c8 1). fold (c8 2). fold (c8 3).
        apply (is_int_intro TP). auto. intros Hint₀.
        apply (is_int_intro TP). auto. intros Hint₁.
        apply (is_int_intro TP). auto. intros Hint₂.
        apply (is_int_intro TP). auto. intros Hint₃.
        destruct NM as (ρ' & CPT & NM).
        AbTy.gamma_inv. hsplit.
        match goal with
        | |- appcontext[ assign ?cx ?mex _ ] =>
          set (c := cx); set (me := mex)
        end.
        set (n := Int.add i (Int.add (Int.shl i0 (Int.repr 8))
           (Int.add (Int.shl i1 (Int.repr 16)) (Int.shl i2 (Int.repr 24))))).
        assert (∀ ρ', compat ρ ρ' → eval_mexpr ρ' me (MNint n)) as MEV.
        {
          clear ρ' CPT NM. intros ρ' CPT.
          generalize (CPT (c8 0)). rewrite Hint₀. simpl. intros H₀.
          generalize (CPT (c8 1)). rewrite Hint₁. simpl. intros H₁.
          generalize (CPT (c8 2)). rewrite Hint₂. simpl. intros H₂.
          generalize (CPT (c8 3)). rewrite Hint₃. simpl. intros H₃.
          unfold me.
          econstructor.
          constructor. symmetry. eassumption. constructor.
          econstructor.
          econstructor.
          constructor. symmetry. eassumption. constructor. vauto. vauto.
          econstructor.
          econstructor.
          constructor. symmetry. eassumption. constructor. vauto. vauto.
          econstructor.
          constructor. symmetry. eassumption. constructor. vauto. vauto.
          vauto. vauto. vauto.
        }
        generalize (@assign_correct _ _ _ _ NumDom c me ρ' _ nm NM (MEV _ CPT)).
        destruct (assign _ _ _) as [ | nm' ] eqn: Hassn; simpl. exact id.
        unfold n in MEV.
        intros NM'.
          assertc = Vint (Int.add i (Int.add (Int.shl i0 (Int.repr 8)) (Int.add (Int.shl i1 (Int.repr 16)) (Int.shl i2 (Int.repr 24)))))) as Hc.
          {
            clear NM'.
            destruct (Hpun ab (exist _ Mint32 I) _ Hdiv) as (bytes & Hnof & Hbytes & Hlen & Huc).
            etransitivity.
            exact Hbytes. clear Hbytes. simpl in *.
            destruct bytes as [ | b₀ [ | b₁ [ | b₂ [ | b₃ [ | bbytes ] ] ] ] ];
              try discriminate.
            clear Hlen.
            generalize (Huc 0%nat eq_refl). simpl. fold (c8 0). rewrite Hint₀.
            destruct bas [ | i₀ | ]; try discriminate. intros X; inv X.
            generalize (Huc 1%nat eq_refl). simpl. fold (c8 1). rewrite Hint₁.
            destruct bas [ | i₁ | ]; try discriminate. intros X; inv X.
            generalize (Huc 2%nat eq_refl). simpl. fold (c8 2). rewrite Hint₂.
            destruct bas [ | i₂ | ]; try discriminate. intros X; inv X.
            generalize (Huc 3%nat eq_refl). simpl. fold (c8 3). rewrite Hint₃.
            destruct bas [ | i₃ | ]; try discriminate. intros X; inv X.
            unfold decode_val. simpl. rewrite decode_int_LE_4. 2: auto.
            rewrite ! decode_int_1.
            f_equal.
            rewrite Int.add_unsigned. f_equal.
            rewrite ! Int.shl_mul_two_p.
            change (two_p (Int.unsigned (Int.repr 8))) with 256.
            change (two_p (Int.unsigned (Int.repr 16))) with 65536.
            change (two_p (Int.unsigned (Int.repr 24))) with 16777216.
            rewrite ! Z.mul_add_distr_r.
            assert (Int.unsigned (Int.mul (Int.zero_ext 8 (Int.repr (Byte.unsigned i₂))) (Int.repr 65536)) +
                    Int.unsigned
                      (Int.mul (Int.zero_ext 8 (Int.repr (Byte.unsigned i₃)))
                               (Int.repr 16777216)) < Int.modulus).
            { clear.
              generalize (mul_byte1 i₂).
              generalize (mul_byte2 i₃).
              change Int.modulus with 4294967296.
              Psatz.lia. }
            rewrite unsigned_add. f_equal.
            clear. apply Z.eqb_eq.
            revert i₀. apply (for_all_byte_correct). vm_compute. reflexivity.
            rewrite unsigned_add. f_equal.
            clear. apply Z.eqb_eq.
            revert i₁. apply (for_all_byte_correct). vm_compute. reflexivity.
            f_equal.
            clear. apply Z.eqb_eq.
            revert i₂. apply (for_all_byte_correct). vm_compute. reflexivity.
            clear. apply Z.eqb_eq.
            revert i₃. apply (for_all_byte_correct). vm_compute. reflexivity.
            auto.
            rewrite unsigned_add. 2: auto.
            generalize (mul_byte0 i₁).
            generalize (mul_byte1 i₂).
            generalize (mul_byte2 i₃).
            change Int.modulus with 4294967296.
            clear. Psatz.lia.
          }
          split.
          * intros c'.
            unfold ACTreeDom.get. rewrite ACTree.gsspec. case (ACTree.elt_eq).
            intros ->. unfold typed_memory_chunk. fold c. rewrite Hc. exact I.
            intros _. exact (TP c').
          * clear ρ' CPT NM NM'.
            intros ρ' CPT NM.
            generalize (@assign_correct _ _ _ _ NumDom c me ρ' (MNint n) nm NM).
            rewrite Hassn.
            assert (ρ' +[ c => MNint n ] = ρ') as EXT.
            { apply Axioms.extensionality. intros c'.
              unfold upd. case eq_dec. intros ->.
              generalize (CPT c). rewrite Hc. exact id.
              reflexivity. }
            setoid_rewrite EXT. intros X; apply X.
            apply MEV, CPT.
    Qed.
    Global Opaque optimistic_realization_one.

    Definition optimistic_realization (cells: CellSet.t) (tn: tnum) : tnum+⊥ :=
      CellSet.foldc tn, bind tn (optimistic_realization_one c)) cells (NotBot tn).

    Lemma optimistic_realization_sound cells tn:
      tnum_mono' tn (optimistic_realization cells tn).
Proof.
      destruct tn as (tp, nm).
      intros ρ Hpun (TP & NM).
      unfold optimistic_realization.
      apply CSO.foldspec; auto.
      clear - Hpun NM.
      intros cells cells' tn tn' c Hc Hrem TN.
      destruct tn' as [ | tn' ]. exact TN.
      destruct TN as [TP' NM'].
      simpl.
      generalize (optimistic_realization_one_sound c tn' Hpun).
      destruct (optimistic_realization_one _ _) as [ | (tp', nm') ].
      intros X; apply X.
      destruct tn' as (tp', nm'). split. exact TP'. hsplit. eauto.
      intros [ TP'' NM'' ].
      destruct tn'. split. auto. hsplit; eauto.
      split. exact TP''.
      intros ρ' CPT NMx. simpl. apply NM''; auto.
    Qed.
    Global Opaque optimistic_realization.

  End REALIZATION.

  Variable max_concretize : N.

  Section CONVERT.

Current function.
  Variable μ : fname.

Set of local variables.
  Variable ε : lvarset.

  Definition is_local (v: ident) : bool := PSet.mem v ε.

  Definition addr_of (v: ident) : BlockSet.t :=
    if is_local v
    then BSO.singleton (ABlocal μ v)
    else match Genv.find_symbol ge v with
         | Some b => BSO.singleton (ABglobal b)
         | None => BlockSet.empty
         end.

  Variable sz : sizes.
  Variable pt : points_to.

  Definition me_of_pe (nm: num) (pe: pexpr cell) : am_map_t (mexpr cell) :=
    match N.nconvert ge μ _ _x, x) _ _ NumDom sz nm pt pe with
    | inl me =>
      if AbMachineEnv.noerror me nm
      then Result me
      else Error (Some me)
                 ("me_of_pe(" ++ to_string pe ++ "): error")
    | inr (res, msg) => Error res
                    ("me_of_pe(" ++ to_string pe ++ "): conversion failed: " ++ msg)
    end.

  Definition check_align (nm: num) (sz: Z) (me: mexpr cell) : bool :=
    match is_one sz with
    | false =>
           is_bot (AbMachineEnv.assume
              (MEbinop (Ocmp Cne)
                 (MEbinop Omodu me (me_const_i _ (Int.repr sz)))
                 (me_const_i _ Int.zero))
              true nm)
    | e => e
    end.

  Definition deref_pexpr (nm: num) κ (lpe: list (pexpr cell)) : alarm_mon (CellSet.t+⊤) :=
    let al := align_chunk (proj1_sig κ) in
    am_fold
      (λ acc pe,
       do offs <-
          match pexpr_get_ty pe with
          | VP =>
            match me_of_pe nm pe with
            | Result me =>
              do _ <- am_assert (check_align nm al me)
                 ("bad align (" ++ to_string (proj1_sig κ) ++ "): " ++ to_string me);
              ret (concretize_int max_concretize me nm)
            | Errorc me e =>
              do _ <- alarm_am (e tt);
              ret match me with
                  | Some me => concretize_int max_concretize me nm
                  | None => NotBot All
                  end
            end
          | ty =>
            do _ <- alarm_am ("deref_pexpr: maybe not a pointer (" ++ to_string ty ++ ")");
            ret (NotBot All)
          end;
       ret match acc with
           | All => All
           | Just acc =>
             match pt_eval_pexpr ge μ pt pe with
             | All => All
             | Just ptr => fmap (CellSet.union acc) (set_product ptr κ offs)
             end
           end)
      lpe
      (Just CellSet.empty).

  Lemma deref_pexpr_inv nm κ lpe :
    match am_get (deref_pexpr nm κ lpe) with
    | Some cells =>
      let al := align_chunk (proj1_sig κ) in
    ∀ pe, In pe lpe
          pexpr_get_ty pe = VP
          ∃ me,
            me_of_pe nm pe = Result me
            check_align nm al me = true
            let ofs := concretize_int max_concretize me nm in
            match cells with All => True | Just cells =>
            ofsNotBot All
            ∃ ptr, pt_eval_pexpr ge μ pt pe = Just ptr
            ∀ b i,
              BlockSet.mem b ptr
              i ∈ γ(ofs) →
              CellSet.mem (cell_from b (Int.unsigned i, κ)) cells
            end
    | None => True
    end.
Proof.
    unfold deref_pexpr.
    set (al := align_chunk (proj1_sig κ)).
    match goal with |- appcontext[ am_fold ?f ] => set ( F := f ) end.
    induction lpe as [|pe lpe IH] using rev_ind.
    intros _ (); fail.
    unfold ACTree.elt in IH.
    rewrite am_fold_app.
    apply am_bind_case. intros; exact I.
    intros acc REC. rewrite REC in IH. simpl in IH. clear REC.
    subst F. unfold am_fold. simpl.
    destruct (pexpr_get_ty pe) eqn:TY; try exact I.
    destruct (me_of_pe nm pe) as [me|] eqn: ME. 2: exact I.
    destruct (check_align nm al me) eqn: ALIGN. 2: exact I.
    intros pe' IN. rewrite in_app in IN.
    destruct IN as [IN| [ <- | () ]].
  - specialize (IH _ IN). destruct IH as (Ty & me' & IH); hsplit.
    split. assumption.
    exists me'. split. assumption. split. assumption.
    destruct acc as [|acc]. exact I. hsplit.
    destruct (pt_eval_pexpr _ _ _ pe) as [ | ptr' ]. exact I.
    destruct (set_product) as [|cells]. exact I.
    split. assumption.
    exists ptr. split. assumption.
    intros; rewrite CellSet.mem_union; bleft; auto.
  - split. assumption.
    exists me. split. assumption. split. assumption.
    destruct acc as [|acc]. exact I.
    destruct (pt_eval_pexpr _ _ _ pe) as [ | ptr' ]. exact I.
    destruct (concretize_int _ me nm) as [|[|offs]] eqn: Hoffs.
    + split. discriminate.
      exists ptr'. split. reflexivity.
      intros b i H ().
    + exact I.
    + split. discriminate.
      exists ptr'. split. reflexivity.
      intros b i H H0.
      generalize (set_product_correct _ κ (@NotBot _ (Just offs)) _ _ H H0).
      intros. rewrite CellSet.mem_union. bright. auto.
  Qed.

  Definition constant_of_constant (c: Csharpminor.constant) : constant :=
    match c with
    | Csharpminor.Ointconst i => Ointconst i
    | Csharpminor.Olongconst i => Olongconst i
    | Csharpminor.Ofloatconst f => Ofloatconst f
    | Csharpminor.Osingleconst f => Osingleconst f
    end.

  Definition convert_err (e:expr) msg :=
    ("convert(" ++ to_string e ++ "): " ++ msg ++ new_line ++
     "Function: " ++ to_string μ ++ new_line ++
     "Locals: " ++ to_string ε ++ new_line ).
  Opaque convert_err.

  Definition convert_t A := tnum -> alarm_mon (tnum * A).
  Local Instance convert_monad : monad convert_t :=
    {
      ret A := λ a tn, ret (tn, a);
      bind A B := λ m f tn, do (tn', a) <- m tn; f a tn'
    }.

  Let get_tnum : convert_t tnum := λ tn, ret (tn, tn).
  Let lift {A} (m: alarm_mon A) : convert_t A :=
    λ tn, fmapx, (tn, x)) m.

  Local Notation "'alarm' e" := ((λ tn, (((tn, tt), (λ _, e) :: nil))):(convert_t _)) (at level 100).

  Let convert_var (e: expr) (c: cell) : convert_t (list (pexpr cell)) :=
    λ tn,
    let '(tp, nm) := tn in
    match ACTreeDom.get c tp with
    | Just ty => ret (PEvar c ty :: nil) tn
    | All => (do _ <- alarm (convert_err e ("bad temp in " ++ to_string μ)); ret nil) tn
    end.

  Let realize (cells: CellSet.t) : convert_t (list cell) :=
    λ tn,
    match optimistic_realization cells tn with
    | Bot => ret nil tn
    | NotBot tn' => ret (CellSet.elements cells) tn'
    end.

  Fixpoint convert (e: expr) : convert_t (list (pexpr cell)) :=
    let err := convert_err e in
    match e with
    | Evar v => convert_var e (ACtemp μ v)
    | Eaddrof i =>
      if is_local i
      then ret (PElocal _ i :: nil)
      else match Genv.find_symbol ge i with
           | Some _ => ret (PEconst _ (Some (Oaddrsymbol i Int.zero)) VP :: nil)
           | None => do _ <- alarm (err "bad global variable");
                     ret nil
           end
    | Econst cst =>
      let cst := constant_of_constant cst in
      ret (PEconst _ (Some cst) (AbTy.eval_constant cst) :: nil)
    | Eunop op e1 =>
      do e1' <- convert e1;
      ret (rev_map (fun e => PEunop op e (AbTy.eval_unop op (pexpr_get_ty e))) e1')
    | Ebinop op e1 e2 =>
      do e1' <- convert e1;
      do e2' <- convert e2;
      ret (map2e1 e2,
                 PEbinop op e1 e2
                         (AbTy.eval_binop op (pexpr_get_ty e1) (pexpr_get_ty e2))
                ) e1' e2')

    | Eload κ e1 =>
      do e1' <- convert e1;
      do κ <-
        match κ return convert_t (option typed_memory_chunk) with
          | Many32 | Many64 =>
            do _ <- alarm (err "Many32 or Many64"); ret None
          | κ => ret (Some (exist _ κ I:typed_memory_chunk))
        end;
      do (tp, nm) <- get_tnum;
      do cells <- match κ with Some κ => lift (deref_pexpr nm κ e1') | None => ret All end;
      match cells with
      | All => do _ <- alarm (err "too many cells"); ret nil
      | Just cells =>
        do cells <- realize cells;
        λ tn,
        let '(tp, nm) := tn in
        lift (
      am_map' (λ c, match ACTreeDom.get c tp with
                    | All => Error None (err ("bad cell: " ++ to_string c))
                    | Just ty => Result (PEvar c ty)
                    end)
              cells
          )
             tn
      end
    end.

  Definition me_of_pe_list nm := am_map' (me_of_pe nm).

  Definition nconvert (e:expr) : convert_t (list (mexpr cell)) :=
    do lpe <- convert e;
    λ tn, lift (me_of_pe_list (snd tn) lpe) tn.

  Definition deref_expr_err (nm: num) (e: expr) (κ: typed_memory_chunk) (lpe: list (pexpr cell)) :=
    ("deref_expr(" ++ to_string e ++ ", " ++ to_string (proj1_sig κ) ++ ") " ++ to_string lpe )%string.
  Opaque deref_expr_err.

  Definition deref_expr κ (e: expr) : convert_t (list cell) :=
    do lpe <- convert e;
    λ tn,
      let '(tp, nm) := tn in
      lift (
      do cells <- deref_pexpr nm κ lpe;
      match cells with
      | All => do _ <- alarm_am (deref_expr_err nm e κ lpe);
                ret nil
      | Just cell_set => ret (CellSet.elements cell_set)
      end
      ) tn.

  Section CONVERT_CORRECT.
    Context (e: env) (tmp: temp_env) (m: mem).
    Context (stk: list (temp_env * env)).
    Context (Hstk: μ = plength stk).
    Let ρ := mem_as_fun ge ((tmp, e) :: stk, m).
    Local Instance pt_gamma : gamma_op points_to _ := points_to_gamma ge ((tmp, e) :: stk).
    Hypothesis E: ∀ i, is_local i = truee ! iNone.
    Hypothesis INV : invariant ((tmp, e) :: stk, m).
    Hypothesis SZ: msizes_gamma ge ((tmp, e) :: stk) sz.
    Hypothesis PT: ρ ∈ γ(pt).

    Lemma Tmp: ∀ i v, tmp ! i = Some v → ρ (ACtemp μ i) = v.
Proof.
      intros i v H.
      subst ρ.
      simpl. rewrite Hstk, get_stk_length. simpl. now rewrite H.
    Qed.
    Hint Resolve Tmp.

    Lemma me_of_pe_correct (nm: num) (ρn: cellmach_num) (NM: ρn ∈ γ(nm)) pe :
      match me_of_pe nm pe with
      | Result me => N.nconvert ge μ _ _x, x) _ _ NumDom sz nm pt pe = inl me ∧ ¬ error_mexpr ρn me
      | Errorc _ _ => True
      end.
Proof.
      unfold me_of_pe.
      destruct N.nconvert as [me'|(?,?)]. 2: exact I.
      destruct (AbMachineEnv.noerror me' nm) eqn: NB. 2: exact I.
      split. reflexivity.
      exact (noerror_correct _ NM NB).
    Qed.

    Ltac with_me_of_pe q :=
      repeat match goal with
      | H : appcontext[ me_of_pe ?nm ?pe ] |- _ =>
        generalize (me_of_pe_correct pe);
        destruct (me_of_pe nm pe); q
      end.

    Lemma me_of_pe_list_noerror (nm: num) (ρn: cellmach_num) (NM: ρn ∈ γ(nm)) lpe :
      match am_get (me_of_pe_list nm lpe) with
      | Some lme => (∀ me, In me lme → ¬ error_mexpr ρn me)
                  ∧ (∀ pe, In pe lpe → ∃ me, In me lme
                     N.nconvert ge μ _ _x, x) _ _ NumDom sz nm pt pe = inl me)
      | None => True
      end.
Proof.
      unfold me_of_pe_list.
      with_am_map'.
      intros (H & H'). split.
      intros me ME. destruct (H me ME) as (pe & PE & K).
      generalize (me_of_pe_correct nm ρn NM pe). rewrite PE. intuition.
      intros pe PE. destruct (H' pe PE) as (me & ME & K).
      generalize (me_of_pe_correct nm ρn NM pe). rewrite ME.
      intros [? _]. vauto.
    Qed.

    Lemma pexpr_get_ty_ok (pe: pexpr cell) v :
      eval_pexpr ge m ρ e pe vvVundefv ∈ γ(pexpr_get_ty pe).
Proof.
      intros V D. eapply pexpr_get_ty_ok; eauto.
    Qed.

    Lemma check_align_correct (nm: num) (ρn: cellmach_num) (NM: ρn ∈ γ(nm)) al me :
      al = 1 ∨ al = 2 ∨ al = 4 ∨ al = 8 →
      check_align nm al me = true
      ∀ n, eval_mexpr ρn me (MNint n) →
           Int.unsigned n = al * Int.unsigned (Int.divu n (Int.repr al)).
Proof.
      unfold check_align.
      apply is_one_intro. intros _ _ n _. rewrite Int.divu_one. Psatz.lia.
      clear al.
      intros al Hz Hsz AL n EV.
      clear -AL EV NM Hsz.
      pose proof (AbMachineEnv.assume_bot NumDom _ _ AL NM) as K. simpl in K.
      assert (Int.eq (Int.repr al) Int.zero = false)
      by intuition (subst al; reflexivity).
      destruct (Int.cmp Cne (Int.modu n (Int.repr al)) Int.zero) eqn: L.
      elim K. econstructor; vauto.
      replace Ntrue with (of_bool (Int.cmp Cne (Int.modu n (Int.repr al)) Int.zero)).
      vauto. rewrite L. reflexivity.
      simpl in L. apply negb_false_iff in L.
      generalize (Int.eq_spec (Int.modu n (Int.repr al)) Int.zero). rewrite L. clear L.
      intro.
      assert (0 < al <= Int.max_unsigned).
      { destruct Hsz as [|[|[|]]]; subst; compute; split; auto; discriminate. }
      assert (Int.repr alInt.zero) as Hal.
      clear -Hsz. repeat destruct Hsz as [ Hsz | Hsz ]; rewrite Hsz; discriminate.
      rewrite (Int.modu_divu_Euclid n (Int.repr al)) at 1; auto. rewrite H0, Int.add_zero.
      unfold Int.mul. rewrite Int.unsigned_repr, Z.mul_comm, Int.unsigned_repr. auto.
      Psatz.lia.
      unfold Int.divu. rewrite !Int.unsigned_repr; try Psatz.lia.
      split.
      assert (0 <= Int.unsigned n / al).
      apply Z.div_pos. pose proof (Int.unsigned_range n). omega. omega.
      Psatz.nia.
      assert (Int.unsigned n / al * al = Int.unsigned n - Int.unsigned n mod al).
      { rewrite Zmod_eq; omega. }
      rewrite H2. pose proof (Int.unsigned_range n). unfold Int.max_unsigned.
      destruct (Z_mod_lt (Int.unsigned n) al). omega. omega.
      split.
      apply Z.div_pos. pose proof (Int.unsigned_range n). Psatz.lia.
      destruct Hsz as [Hsz|[Hsz|[Hsz|Hsz]]]; subst al; reflexivity.
      apply Z.div_le_upper_bound. rewrite Int.unsigned_repr; Psatz.lia.
      pose proof (Int.unsigned_range n).
      rewrite Int.unsigned_repr by Psatz.lia.
      unfold Int.max_unsigned. Psatz.nia.
    Qed.

    Ltac subs := repeat match goal with H : ?a = ?b |- _ => subst a || subst b end.

    Let Hpun : ρ ∈ pun_u8 := mem_as_fun_pun_u8 _ _ INV.(noIntFragment).

    Lemma convert_wf (q: expr):
      ∀ (tn: tnum) (TN: ρ ∈ γ tn),
      match am_get (convert q tn) with
      | Some (tn', lp) =>
        tnum_mono tn tn'
        ∧
        ∀ pe, In pe lpN.nconvert_def_pre ge e ρ pe
      | None => True
      end.
Proof.
      induction q; intros (tp, nm) TN.
    - simpl.
      set (c := ACtemp μ i).
      generalize (proj1 TN c).
      destruct (ACTreeDom.get c tp) as [ty|]. exact id.
      intros TY. simpl.
      split. apply tnum_mono_refl.
      intros pe [ <- | () ]. simpl. intros X. rewrite X in TY.
      AbTy.gamma_inv. auto.
    - simpl.
      generalize (E i). destruct is_local.
      intros (A & _). split. apply tnum_mono_refl.
      intros pe [ <- | () ]. simpl. unfold env_as_fun.
      destruct (e ! i) as [()|]. intro; eq_some_inv.
      elim (A eq_refl eq_refl).
      intros _.
      destruct (Genv.find_symbol ge i) eqn: FS.
      split. apply tnum_mono_refl.
      intros pe [ <- | () ]. simpl. rewrite FS. intro; eq_some_inv.
      exact I.
    - split. apply tnum_mono_refl.
      intros pe [ <- | () ].
      destruct c; exact I.
    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp', nm'), lp) LP.
      specialize (IHq (tp, nm) TN). rewrite LP in IHq.
      destruct IHq as ( MONO & IHq ).
      split. easy.
      intros pe PE.
      rewrite rev_map_correct, <- in_rev, in_map_iff in PE.
      destruct PE as (pe' & <- & PE).
      simpl. apply IHq; auto.
    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp1) LP1.
      specialize (IHq1 (tp, nm) TN). rewrite LP1 in IHq1.
      destruct IHq1 as ( MONO1 & IHq1 ).
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), lp2) LP2.
      specialize (IHq2 (tp1, nm1) (tnum_m_stronger MONO1 (conj (mem_as_fun_pun_u8 _ _ INV.(noIntFragment)) TN))). rewrite LP2 in IHq2.
      destruct IHq2 as ( MONO2 & IHq2 ).
      split. eauto using tnum_mono_trans.
      destruct b;
      try (
        intros pe PE;
        rewrite map2_correct in PE; unfold map2_spec in PE;
        rewrite <- in_rev, in_flat_map in PE;
        destruct PE as (pe1 & PE1 & PE);
        rewrite in_map_iff in PE;
        destruct PE as (pe2 & <- & PE);
        vauto).
    - rename m0 into κ.
      simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp) LP.
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), κ') K. assert (Hκ: tp2 = tp1nm2 = nm1 ∧ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; subs; eauto).
      destruct Hκ as (-> & -> & [Hκ ->]). clear K.
      apply am_bind_case. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case. discriminate.
      intros x Hcells Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv nm1 (exist _ κ Hκ) lp).
      rewrite Hcells.
      intros K.
      specialize (IHq (tp, nm) TN). rewrite LP in IHq.
      destruct IHq as ( MONO1 & IHq ).
      assert (ρ ∈ γ (tp1, nm1)) as H1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun H1).
      intros TP' NM'.
      with_am_map'.
      simpl.
      intros (X & Y).
      split. eauto using tnum_mono_trans.
      clear Hreal.
      intros pe PE.
      destruct (X pe PE) as (c & C & C').
      simpl in *.
      destruct (ACTreeDom.get c _) as [|ty] eqn: TY; inversion C.
      unfold N.nconvert_def_pre.
      generalize (TP' c).
      rewrite TY. fold ρ. intros A L. rewrite L in A.
      AbTy.gamma_inv. auto.
    Qed.

    Lemma convert_free_def (q: expr) :
      ∀ (tn: tnum) (TN: ρ ∈ γ tn),
      match am_get (convert q tn) with
      | Some (tn', lp) =>
          ∀ pe, In pe lp ->
          ∀ x, pe_free_var pe x -> ρ xVundef
      | None => True
      end.
Proof.
      induction q; intros tn TN.
    - destruct tn as (tp, nm). destruct TN as (TP, NM).
      simpl.
      set (c := ACtemp μ i).
      generalize (TP c).
      destruct (ACTreeDom.get c tp). exact id.
      intros H pe [<-|[]] x -> EQ. rewrite EQ in H. AbTy.gamma_inv. exact H.
    - simpl. destruct is_local. now intros pe [<-|[]] x [].
      destruct (Genv.find_symbol ge i). now intros pe [<-|[]] x []. exact I.
    - now intros pe [<-|[]] x [].
    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp) LP pe PE.
      rewrite rev_map_correct, <- in_rev, in_map_iff in PE.
      destruct PE as (pe' & <- & PE).
      specialize (IHq _ TN).
      rewrite LP in IHq. simpl. apply IHq; auto.
    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp1) LP1.
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), lp2) LP2.
      specialize (IHq1 _ TN). rewrite LP1 in IHq1.
      generalize (convert_wf q1 _ TN). rewrite LP1. intros [MONO1 _].
      specialize (IHq2 _ (tnum_m_stronger MONO1 (conj Hpun TN))).
      generalize (convert_wf q2 _ (tnum_m_stronger MONO1 (conj Hpun TN))). rewrite LP2. intros [MONO2 _].
      rewrite LP2 in IHq2.
      simpl in IHq1, IHq2.
      destruct b;
      try (
        intros pe PE;
        rewrite map2_correct in PE; unfold map2_spec in PE;
        rewrite <- in_rev, in_flat_map in PE;
        destruct PE as (pe1 & PE1 & PE);
        rewrite in_map_iff in PE;
        destruct PE as (pe2 & <- & PE);
        intros x [Hx|Hx]; eauto).
    - rename m0 into κ.
      simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp) LP.
      generalize (convert_wf q _ TN). rewrite LP. intros [MONO1 _].
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), κ') K. assert (Hκ: tp2 = tp1nm2 = nm1 ∧ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as (-> & -> & [Hκ ->]). clear K.
      apply am_bind_case. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case. discriminate.
      intros x Hcells Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv nm1 (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros K.
      assert (ρ ∈ γ (tp1, nm1)) as H1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun H1).
      intros TP' NM'.
      with_am_map'.
      simpl in *.
      intros (X & Y) pe PE.
      destruct (X pe PE) as (c & C & C').
      destruct (ACTreeDom.get c _) as [|ty] eqn: TY; inversion C.
      intros ? <- Hdef.
      generalize (TP' c).
      fold ρ. rewrite TY, Hdef. intro; AbTy.gamma_inv. easy.
    Qed.

    Remark align_chunk_4_cases (κ: memory_chunk) :
      align_chunk κ = 1 ∨ align_chunk κ = 2 ∨ align_chunk κ = 4 ∨ align_chunk κ = 8.
Proof.
destruct κ; vauto. Qed.

    Lemma convert_correct (q: expr) :
      ∀ (tn: tnum) (TN: ρ ∈ γ tn),
      match am_get (convert q tn) with
      | Some (tn', lp) =>
        ∀ v, eval_expr ge e tmp m q v
             ∃ a, In a lpeval_pexpr ge m ρ e a v
      | None => True
      end.
Proof.
      Ltac ok :=
        let K := fresh in
        intros ? K; eexists; split; [left; reflexivity| eval_expr_inv; try econstructor; eauto].
      induction q; intros (tp, nm) TN; simpl;
      try (split;[ok|auto]).
    - set (c := ACtemp μ i).
      destruct TN as (TP & NM).
      specialize (TP c). destruct (ACTreeDom.get c tp). exact I.
      subst c. intros ? K.
      eval_expr_inv. rewrite (Tmp _ K) in TP. vauto2.
    - bif'. ok. unfold env_as_fun. apply E in Heqb. subs. constructor.
      inversion H; clear H; subs.
      rewrite H0. auto. congruence.
      bif. simpl.
      ok. inversion H; clear H; subs. exfalso. generalize (proj2 (E i)). intuition congruence.
      simpl. rewrite H1. reflexivity. subs. simpl. auto.
      exact I.
    - intros v V. eval_expr_inv. eexists. split. left; reflexivity. destruct c; simpl in *; eq_some_inv; subs; econstructor; simpl; eauto.
    - unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp', nm'), lp) Hlp. specialize (IHq _ TN). rewrite Hlp in IHq.
      intros v K. eval_expr_inv. destruct (IHq _ H) as (a & A & B). clear IHq.
      exists (PEunop u a (AbTy.eval_unop u (pexpr_get_ty a))). split.
      rewrite rev_map_correct, <- in_rev. exact (in_map _ _ _ A).
      econstructor; eauto.
      assert (v = Vundef \/ vVundef) as D by (destruct v; auto; right; discriminate).
      destruct D. auto. right. eapply AbTy.eval_unop_correct; eauto.
      apply pexpr_get_ty_ok. auto. intros ->. destruct u; simpl in *; congruence.
    - unfold bind. simpl.
      apply am_bind_case. intros; exact I. intros ((tp1, nm1), lp1) Hq1.
      apply am_bind_case. intros; exact I. intros ((tp2, nm2), lp2) Hq2.
      specialize (IHq1 _ TN).
      generalize (convert_wf q1 (tp, nm) TN). rewrite Hq1. intros [MONO1 _].
      rewrite Hq1 in IHq1.
      specialize (IHq2 _ (tnum_m_stronger MONO1 (conj Hpun TN))).
      rewrite Hq2 in IHq2.
      simpl in *.
        assert (∀ v (P: Prop), (vVundefP) → (v = VundefP)) as X by (clear; intuition tauto).
        destruct b;
          try (
              intros v V; eval_expr_inv;
              destruct (IHq1 _ H) as (a1 & A1 & A1');
              destruct (IHq2 _ H0) as (a2 & A2 & A2');
              try (eexists; split; [ eapply in_map2; eassumption | econstructor; eauto];
                   apply X; intros Hv;
                   eapply AbTy.eval_binop_correct; eauto;
                   apply pexpr_get_ty_ok; eauto;
                   intros ->;
                   simpl in *; eq_some_inv;
                   try (destruct v1);
                   try (unfold Val.cmp, Val.cmpu, Val.cmpf, Val.cmpfs, Val.cmpl, Val.cmplu in *; destruct c);
                   simpl in *;
                   eq_some_inv; hsplit;
                   congruence)).

  - unfold bind. simpl.
      rename m0 into κ.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp) Hlp. specialize (IHq _ TN). rewrite Hlp in IHq.
      generalize (convert_wf q (tp, nm) TN). rewrite Hlp. intros [MONO1 _].
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), κ') K. assert (Hκ:tp2 = tp1 /\ nm2 = nm1 /\ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as (-> & -> & [Hκ ->]). clear K.
      apply am_bind_case. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case. discriminate.
      intros x Hcells Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv nm1 (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros Q.
      assert (ρ ∈ γ (tp1, nm1)) as Hρ1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun Hρ1).
      intros TP' NM'.
      with_am_map'.
      intros (X & Y).
      intros v H. eval_expr_inv.
      destruct v1; try discriminate.
      specialize (IHq _ H1).
      destruct IHq as (a & A & B).
      specialize (Q _ A). destruct Q as (Q & me & ME & CA & CI & CS).
      destruct CS as (ptr & Hptr & CS).
      generalize (pt_eval_pexpr_correct Hstk PT B). rewrite Hptr; clear Hptr.
      intros (ab & IN & BA).
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      generalize (me_of_pe_correct _ ρn NM a). rewrite ME. intros (NC & NB).
      destruct (N.nconvert_correct ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz nm1 ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ B _ PT _ NC) as (n & Z & EV & ?).
      discriminate.
      simpl in Z. subst n.
      set (c := cell_from ab (Int.unsigned i, exist _ κ Hκ)).
      edestruct (Y c) as (pe & PE).
      apply CellSet.elements_spec. subst c. apply CS. apply IN.
      eapply concretize_int_correct.
      eauto. assumption.
      destruct PE as [PE PE'].
      specialize (TP' c).
      destruct (ACTreeDom.get c _); inversion PE; clear PE. subst pe.
      exists (PEvar c t). split. exact PE'.
      assertc = v).
      { subst c. simpl in H. destruct ab as [f loc|b'].
        - simpl in BA.
          unfold ρ. simpl.
          destruct (get_stk _ _) as [ (? & e') | ]; eq_some_inv. simpl.
          destruct (e' ! loc) as [(b' & ?)|]; eq_some_inv. simpl.
          subs. rewrite H. reflexivity.
        - unfold ρ. simpl in *. destruct (Genv.invert_symbol); eq_some_inv. subs. rewrite H.
          reflexivity. }
      constructor. auto. right. subs. auto.
    Qed.

    Lemma SZ' :
      ∀ (b : ABTree.elt) (hi : Z) (md : permission * bool)
        (b' : block),
        ABTreeDom.get b sz = Just (hi, md)
        → block_of_ablock ge ((tmp, e) :: stk) b = Some b'
        → Mem.range_perm m b' 0 hi Cur Nonempty.
Proof.
      intros b hi md b' H H0 i Hi.
      generalize (@SZ b hi (fst md) (snd md) b').
      rewrite <- surjective_pairing.
      intros X; specialize (X H H0).
      eapply Mem.perm_implies. apply (proj1 X), Hi. vauto.
    Qed.

    Lemma convert_noerror (q: expr) :
      ∀ (tn: tnum) (TN: ρ ∈ γ tn),
      match am_get (convert q tn) with
      | Some (tn', lp) => (∀ pe, In pe lp → ¬ error_pexpr ge m ρ e pe) → ¬ error_expr ge e tmp m q
      | None => True
      end.
Proof.
      unfold ρ.
      induction q; intros (tp, nm) TN.
    - simpl. set (c := ACtemp μ i). destruct (ACTreeDom.get c tp) eqn: TY.
      exact I. intros NB. specialize (NB _ (or_introl eq_refl)).
      intros B. subst c.
      inv B; apply NB.
      simpl. rewrite get_stk_length. simpl. rewrite H0. reflexivity.
      simpl. rewrite get_stk_length. simpl. rewrite H0. reflexivity.
    - generalize (E i). simpl. destruct is_local.
      intros [K _]. specialize (K eq_refl).
      intros _ B. inversion B. intuition.
      intros [_ K]. destruct (Genv.find_symbol ge i) eqn: FS.
      intros _ B; inversion B. congruence.
      exact I.
    - intros _ B. inversion B.
    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp', nm'), lp) LP. specialize (IHq _ TN). rewrite LP in IHq. simpl in IHq.
      simpl. intros NB.
      assert ( ∀ pe, In pe lp → ¬ error_pexpr ge m ρ e pe) as NB'.
      {
        intros pe PE B. eapply NB.
        rewrite rev_map_correct, <- in_rev, in_map_iff. eexists. split. reflexivity. exact PE.
        vauto.
      }
      specialize (IHq NB').
      intros B. apply error_expr_Unop_inv in B.
      destruct B as [(v & V & B) | B]. 2: auto.
      generalize (convert_correct q _ TN). rewrite LP. simpl.
      intros X. specialize (X _ V). destruct X as (pe & PE & EV).
      eapply NB.
      rewrite rev_map_correct, <- in_rev, in_map_iff. eexists. split. reflexivity. exact PE.
      vauto.

    - simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp1) LP1. specialize (IHq1 _ TN).
      rewrite LP1 in IHq1. simpl in IHq1.
      generalize (convert_wf q1 (tp, nm) TN). rewrite LP1. intros [MONO1 _].
      generalize (convert_correct q1 _ TN). rewrite LP1. intros H1.
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), lp2) LP2.
      generalize (convert_correct q2 _ (tnum_m_stronger MONO1 (conj Hpun TN))).
      rewrite LP2. intros H2.
      specialize (IHq2 _ (tnum_m_stronger MONO1 (conj Hpun TN))).
      rewrite LP2 in IHq2. simpl in IHq2.
      destruct lp2 as [|pe2 lp2].
      destruct (noerror_eval_expr (IHq2_ K, False_ind _ K))) as (v2 & Hv2).
      destruct (H2 v2 Hv2) as (? & () & _).
      destruct lp1 as [|pe1 lp1].
      destruct (noerror_eval_expr (IHq1_ K, False_ind _ K))) as (v1 & Hv1).
      destruct (H1 v1 Hv1) as (? & () & _).
      unfold am_get in H1, H2.
      destruct b;
        try (with_am_map'; intros [_ Y]);
        intros NB;
      try (
          assert ( ∀ pe, In pe (pe1::lp1) → ¬ error_pexpr ge m ρ e pe) as NB1
            by (intros pe PE B; eapply NB; [ apply in_map2; first [ eassumption | left; reflexivity] | vauto]);
          specialize (IHq1 NB1));
      intros B; apply error_expr_Binop_inv in B;
      destruct B as [(v & w & V & W & B) | [B | (v & V & B)] ]; auto;
      try (specialize (H1 _ V); destruct H1 as (pe1' & PE1 & EV1));
      try (specialize (H2 _ W); destruct H2 as (pe2' & PE2 & EV2));
      try (
          assert ( ∀ pe, In pe (pe2::lp2) → ¬ error_pexpr ge m ρ e pe) as NB2
            by (intros pe PE B'; eapply NB; [ apply in_map2; first [ eassumption | left; reflexivity] | vauto]);
          specialize (IHq2 NB2)
        ); auto;
      try (
      (eapply NB; [apply in_map2; eauto | vauto])
        ).
    - rename m0 into κ. simpl. unfold bind. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lp) LP. specialize (IHq _ TN).
      generalize (convert_correct q _ TN). rewrite LP. intros H1.
      rewrite LP in IHq. simpl in IHq.
      generalize (convert_wf q (tp, nm) TN); rewrite LP. intros (MONO1 & WF).
      apply am_bind_case. intros; exact I.
      intros ((tp2, nm2), κ') K. assert (Hκ:tp2 = tp1 /\ nm2 = nm1 /\ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as (-> & -> & [Hκ ->]). clear K.
      apply am_bind_case. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case. discriminate.
      intros x Hcells Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv nm1 (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros Q.
      simpl.
      assert (ρ ∈ γ (tp1, nm1)) as Hρ1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun Hρ1).
      intros TP' NM'.
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      with_am_map'.
      simpl.
      intros (X & Y) NB.
      assert ( ∀ pe, In pe lp → ¬ error_pexpr ge m ρ e pe) as NB'.
      {
        intros pe PE B.
        specialize (Q _ PE). destruct Q as (Q & me & ME & CI & CS).
        generalize (me_of_pe_correct nm1 ρn NM pe). rewrite ME. intros (NC & MEB).
        applyP, N.nconvert_def ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ PT _ P me NC MEB B).
        intuition.
      }
      specialize (IHq NB').
      intros B. apply error_expr_Load_inv in B.
      destruct B as [(v' & V' & B) | B]. 2: easy.
      specialize (H1 _ V'). destruct H1 as (pe & PE & EV).
      specialize (Q pe PE). destruct Q as (TY & me & ME & CA & CI & CS).
      destruct CS as (ptr & Hptr & OFS).
      generalize (pt_eval_pexpr_correct Hstk PT EV). rewrite Hptr; clear Hptr.
      intros PTR.
      generalize (me_of_pe_correct _ _ NM pe). rewrite ME. clear ME. intros (ME & MEB).
      generalizeP, error_pexpr_def (N.nconvert_def ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ PT _ P me ME MEB) EV).
      intros Z. specialize (Z (WF _ PE)).
      generalize (pexpr_get_ty_ok EV Z).
      rewrite TY.
      destruct v'; destruct 1. clear TY.
      clear Z.
      destruct (N.nconvert_correct _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV _ PT _ ME) as (n & Z & EV' & ?).
      discriminate.
      simpl in Z. subst n.
      destruct PTR as (bs & BS & Hb).
      assert (∃ v, Mem.load κ m b (Int.unsigned i) = Some vvVundef) as (v & V & DEF).
      {
        set (c := cell_from bs (Int.unsigned i, exist _ κ Hκ)).
        destruct (Y c) as (pe' & PE' & IN).
        rewrite <- CellSet.elements_spec. subst c. eapply OFS; auto.
        eapply concretize_int_correct; eauto.
        generalize (TP' c).
        destruct (ACTreeDom.get _ _) as [|ty]. discriminate. inversion PE'. subst pe'. clear PE'.
        unfold ρ. simpl.
        destruct bs as [ f r | ab ]; simpl in *; eq_some_inv.
        * destruct (get_stk _ _) as [ (? & e') | ]. 2: eq_some_inv. simpl in *.
          destruct (e' ! r) as [(?,?)|]; eq_some_inv. simpl in *. subst b.
          destruct (Mem.load) as [v|].
          intros V. exists v. split. reflexivity. intros ->. destruct ty; destruct V.
          destruct ty; destruct 1.
        * destruct Genv.invert_symbol; eq_some_inv. subst c ab.
          destruct (Mem.load) as [v|].
          intros V. exists v. split. reflexivity. intros ->. destruct ty; destruct V.
          destruct ty; destruct 1.
      }
      inversion B. auto. congruence. congruence.
    Qed.

    Lemma deref_expr_correct (tn: tnum) (TN: ρ ∈ γ tn) κ q :
      match am_get (deref_expr κ q tn) with
      | Some (tn', cells) =>
        tnum_mono tn tn' ∧
        ¬ error_expr ge e tmp m q
        ∀ v, eval_expr ge e tmp m q v
        ∃ ab b i, v = Vptr b i
                  In (cell_from ab (Int.unsigned i, κ)) cells
                  block_of_ablock ge ((tmp, e) :: stk) ab = Some b
                  (align_chunk (proj1_sig κ) | Int.unsigned i)
      | None => True
      end.
Proof.
      unfold deref_expr.
      unfold bind at 1. simpl.
      apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lpe) LPE.
      unfold lift, fmap. simpl.
      apply am_bind_case. intros; exact I.
      intros cells'. simpl.
      apply am_bind_case. intros; eq_some_inv.
      intros [|cells]. intros; eq_some_inv.
      intros Hcells X; eq_some_inv; subst cells'.
      generalize (deref_pexpr_inv nm1 κ lpe).
      rewrite Hcells.
      intros Q.
      generalize (convert_correct q _ TN). rewrite LPE. simpl.
      intros CC.
      generalize (convert_wf q _ TN); rewrite LPE. simpl.
      intros [MONO1 WF].
      split. easy.
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      split.
    - generalize (convert_noerror q _ TN). rewrite LPE. simpl. intros X. apply X.
      intros pe PE.
      destruct (Q _ PE) as (TY & me & ME & ? & OFS). clear Q.
      generalize (me_of_pe_correct _ _ NM pe). rewrite ME. clear ME. intros (ME & MEB).
      generalizeP, N.nconvert_def ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ PT _ P me ME MEB).
      intros Y. specialize (Y (WF _ PE)).
      exact Y.
    - intros v EV.
      specialize (CC _ EV).
      destruct CC as (pe & PE & EV').
      specialize (Q pe PE). destruct Q as (TY & me & ME & CA & ? & ptr & Hptr & OFS).
      generalize (pt_eval_pexpr_correct Hstk PT EV'). rewrite Hptr; clear Hptr.
      intros PTR.
      generalize (me_of_pe_correct nm1 _ NM pe). rewrite ME. clear ME. intros (ME & MEB).
      generalizeP, error_pexpr_def (N.nconvert_def ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ PT _ P me ME MEB) EV').
      intros Y. specialize (Y (WF _ PE)).
      generalize (pexpr_get_ty_ok EV' Y).
      rewrite TY.
      destruct v; destruct 1. clear Y.
      destruct PTR as (ab & AB & Hab).
      eexists ab, b, i.
      destruct (N.nconvert_correct _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV' _ PT _ ME) as (n & Z & EVn & ?).
      discriminate.
      simpl in Z. subst n.
      repeat (refine (conj _ _)).
      reflexivity. 2: assumption.
      apply CellSet.elements_spec.
      apply OFS. apply AB.
      eapply concretize_int_correct. apply NM. assumption.
      rewrite (check_align_correct _ NM (align_chunk_4_cases _) CA EVn).
      rewrite Z.mul_comm. eexists. reflexivity.
    Qed.

    Lemma nconvert_sound (tn: tnum) (TN: ρ ∈ γ tn) (q: expr) :
      match am_get (nconvert q tn) with
      | Some (tn', _) =>
        tnum_mono tn tn' ∧
        ¬ error_expr ge e tmp m q
      | None => True
      end.
Proof.
      unfold nconvert. unfold bind. simpl. apply am_bind_case. intros; exact I.
      intros ((tp1, nm1), lpe) LPE.
      generalize (convert_wf q _ TN). rewrite LPE. intros [MONO1 WF].
      generalize (convert_noerror q _ TN). rewrite LPE. simpl. intros H.
      unfold lift, fmap. apply am_bind_case. intros; exact I.
      intros lme Hlme.
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      generalize (me_of_pe_list_noerror nm1 _ NM lpe).
      rewrite Hlme.
      intros [NBE ME].
      split. easy.
      apply H. clear H.
      intros pe PE.
      specialize (ME _ PE). destruct ME as (me & ME & Hme).
      refine (N.nconvert_def _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ SZ' _ _ _ Nc
                            (@invariant_block_of_ablock_inj _ _ INV)
                            NM
                            _ PT
                            _
                            (WF _ PE)
                            _
                            Hme
                            (NBE _ ME)).
    Qed.

    Lemma nconvert_ex (tn: tnum) (TN: ρ ∈ γ(tn)) (q: expr) :
      match am_get (nconvert q tn) with
      | Some ((tp', nm'), lme) =>
        tnum_mono tn (tp', nm') ∧
        ∃ lpe, am_get (convert q tn) = Some ((tp', nm'), lpe) ∧
        ∀ v, eval_expr ge e tmp m q v
             vVundef
        ∀ (TP: ρ ∈ γ tp') (ρn: cellmach_num) (Nc: compat ρ ρn) (NM: ρn ∈ γ nm'),
             ∃ pe me n,
               In pe lpe
               eval_pexpr ge m ρ e pe v
               N.nconvert ge μ _ _x, x) _ _ NumDom sz nm' pt pe = inl me
               In me lme
               N.ncompat v n
               N.wtype_num n (pexpr_get_ty pe) ∧
               eval_mexpr ρn me n
      | None => True
      end.
Proof.
      unfold nconvert. unfold bind. simpl. apply am_bind_case. intros; exact I.
      intros ((tp', nm'), lpe) LPE.
      generalize (convert_correct q _ TN). rewrite LPE. intros H.
      generalize (convert_wf q _ TN). rewrite LPE. intros [MONO _].
      destruct (proj2 (tnum_m_stronger MONO (conj Hpun TN))) asn' & Nc' & NM').
      generalize (me_of_pe_list_noerror nm' _ NM' lpe).
      destruct (me_of_pe_list _ lpe) as (nle & [|]). 2: exact id.
      simpl.
      intros [NB PE].
      split. exact MONO.
      exists lpe. split. reflexivity.
      intros v EV Def.
      intros TP ρn Nc NM.
      destruct (H v EV) as (pe & IN & EV'); clear H.
      destruct (PE pe IN) as (me & IN' & ME).
      destruct (N.nconvert_correct _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV' _ PT _ ME Def) as (n & N).
      exists pe, me, n. intuition.
    Qed.

    Lemma convert_to_me (tn: tnum) (TN: ρ ∈ γ(tn)) (q: expr) :
      match am_get (convert q tn) with
      | Some ((tp', nm'), lpe) =>
        match am_get (me_of_pe_list nm' lpe) with
        | Some lme =>
          tnum_mono tn (tp', nm') ∧
          ∃ v, eval_expr ge e tmp m q vvVundef
          ∧ ∀ (TP: ρ ∈ γ tp') (ρn: cellmach_num) (Nc: compat ρ ρn) (NM: ρn ∈ γ nm'),
            ∃ pe me n,
                 In pe lpe
                 eval_pexpr ge m ρ e pe v
                 N.nconvert ge μ _ _x, x) _ _ NumDom sz nm' pt pe = inl me
                 In me lme
                 N.ncompat v n
                 N.wtype_num n (pexpr_get_ty pe) ∧
                 eval_mexpr ρn me n
        | None => True
        end
      | None => True
      end.
Proof.
      generalize (nconvert_sound _ TN q).
      generalize (nconvert_ex _ TN q).
      unfold nconvert, bind. simpl.
      destruct (convert q _) as (((tp', nm'), lpe) & [|]). 2: intros; exact I.
      simpl.
      destruct (me_of_pe_list _ lpe) as (lme & [|]). 2: intros; exact I.
      simpl. intros (MONO & lpe' & Hlpe' & H) (_ & NB); eq_some_inv; subst.
      destruct (noerror_eval_expr_def NB) as (v & Def & V).
      specialize (H v V Def).
      split. exact MONO.
      exists v. split. exact V. split. exact Def.
      intros TP ρn Nc NM.
      specialize (H TP _ Nc NM).
      destruct H as (pe & me & n & Hn & Hpn & Hvn & Htn & Vn).
      exists pe, me, n. intuition.
    Qed.

  End CONVERT_CORRECT.

  End CONVERT.

  Definition t : Type := (stack+⊤ * (sizes * (points_to * tnum)))%type.
  Definition iter_t : Type := (stack+⊤ * (sizes * (points_to * (types * num_iter))))%type.

  Definition AbMem stk sz pt tp nm : t := (stk, (sz, (pt, (tp, nm)))).
  Definition ab_stk (ab: t) : stack+⊤ := fst ab.
  Definition ab_sz (ab: t) : sizes := fst (snd ab).
  Definition ab_pt (ab: t) : points_to := fst (snd (snd ab)).
  Definition ab_nm (ab: t) : tnum := snd (snd (snd ab)).
  Definition ab_tp (ab: t) : types := fst (snd (snd (snd ab))).
  Definition ab_num (ab: t) : num := snd (snd (snd (snd ab))).

  Arguments ab_stk ab /.
  Arguments ab_sz ab /.
  Arguments ab_pt ab /.
  Arguments ab_nm ab /.
  Arguments ab_tp ab /.
  Arguments ab_num ab /.

  Definition ab_stk_iter (ab: iter_t) : stack+⊤ := fst ab.
  Definition ab_sz_iter (ab: iter_t) : sizes := fst (snd ab).
  Definition ab_pt_iter (ab: iter_t) : points_to := fst (snd (snd ab)).
  Definition ab_nm_iter (ab: iter_t) : tnum_iter := snd (snd (snd ab)).
  Definition ab_tp_iter (ab: iter_t) : types := fst (snd (snd (snd ab))).
  Definition ab_num_iter (ab: iter_t) : num_iter := snd (snd (snd (snd ab))).

  Definition with_stk (f: stack+⊤ → stack+⊤) (ab: t) : t :=
    AbMem (f (ab_stk ab)) (ab_sz ab) (ab_pt ab) (ab_tp ab) (ab_num ab).

  Definition with_sz (f: sizessizes) (ab: t) : t :=
    AbMem (ab_stk ab) (f (ab_sz ab)) (ab_pt ab) (ab_tp ab) (ab_num ab).

  Definition with_pt (f: points_topoints_to) (ab: t) : t :=
    AbMem (ab_stk ab) (ab_sz ab) (f (ab_pt ab)) (ab_tp ab) (ab_num ab).

  Definition with_tp (f: typestypes) (ab: t) : t :=
    AbMem (ab_stk ab) (ab_sz ab) (ab_pt ab) (f (ab_tp ab)) (ab_num ab).

  Definition with_num (f: numnum) (ab: t) : t :=
    AbMem (ab_stk ab) (ab_sz ab) (ab_pt ab) (ab_tp ab) (f (ab_num ab)).

  Definition with_tnum (f: tnumtnum) (ab: t) : t :=
    (ab_stk ab, (ab_sz ab, (ab_pt ab, f (ab_nm ab)))).

  Definition dom {A} (x: positive) (m: PTree.t A) : bool :=
    match PTree.get x m with
    | Some _ => true
    | None => false
    end.

  Remark dom_set {A} x m y (a: A) : dom x (PTree.set y a m) = peq x y || dom x m.
Proof.
    unfold dom. rewrite PTree.gsspec. destruct peq; reflexivity.
  Qed.

  Instance stack_elt_gamma : gamma_op (lvarset * tvarset) (temp_env * env) :=
    λ ltv te_e,
    ∀ x, PSet.mem x (fst ltv) = dom x (snd te_e).

  Instance join_stack_elt_correct :
    join_op_correct (lvarset*tvarset) ((lvarset * tvarset)+⊤) (temp_env * env).
Proof.
    unfold join_op_correct, join, WProd.join_prod_top, fname_join, lvarset_join, join, flat_join_op.
    intros (ε,τ) (ε',τ') (t & e) [K|K];
    destruct (PSet.beq ε _) eqn: Hε; try exact I;
    destruct (PSet.beq τ _); try exact I;
    eauto.
    intros x. erewrite pset_beq_mem; eauto.
  Qed.

  Instance leb_stack_elt_correct :
    leb_op_correct (lvarset * tvarset) (temp_env * env).
Proof.
    unfold leb_op_correct, leb, WProd.leb_prod, fname_leb, lvarset_leb, leb, flat_leb_op.
    intros (ε,τ) (ε',τ') LE (t & e) K. apply andb_true_iff in LE.
    simpl in *.
    intros x. rewrite <- (pset_beq_mem _ _ (proj1 LE)). auto.
  Qed.

  Record gamma (ab: t) (cs: concrete_state) : Prop :=
  { _ : fst cs ∈ γ(ab_stk ab)
  ; _ : ab_stk abAllinvariant cs
  ; _ : snd cssizes_gamma ge (fst cs) (ab_sz ab)
  ; _ : mem_as_fun ge cs ∈ (points_to_gamma ge (fst cs) (ab_pt ab) ∩ γ(ab_nm ab))
  }.
  Instance t_gamma : gamma_op t concrete_state := gamma.

  Record iter_gamma (ab: iter_t) (cs: concrete_state) : Prop :=
  { _ : fst cs ∈ γ(ab_stk_iter ab)
  ; _ : ab_stk_iter abAllinvariant cs
  ; _ : snd cssizes_gamma ge (fst cs) (ab_sz_iter ab)
  ; _ : mem_as_fun ge cs ∈ (points_to_gamma ge (fst cs) (ab_pt_iter ab) ∩ γ(ab_nm_iter ab))
  }.
  Instance iter_t_gamma : gamma_op iter_t concrete_state := iter_gamma.

  Definition split_t (ab:t) : option (ident * lvarset * tvarset * stack * sizes * points_to * tnum) :=
    match ab_stk ab with
    | All | Just nil => None
    | Just ((ε, τ) :: σ) =>
      let '(sz, (pt, tn)) := (snd ab) in
      Some (plength σ, ε, τ, σ, sz, pt, tn)
    end.

  Definition noerror (exp: expr) (ab: t) : alarm_mon unit :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, pt, tn) =>
      do _ <- nconvert μ ε sz pt exp tn;
      ret tt
    | None => do _ <- alarm_am "anomaly (noerror)"; ret tt
    end.

  Definition ab_eval_expr (ab: t) (a: expr)
  : alarm_mon ((tnum * PTSet * AbTy.t+⊤ * list (mexpr cell)) +⊥) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, pt, tn) =>
      do (tn', lp) <- convert μ ε sz pt a tn;
      let '(tp', nm') := tn' in
      do ln <- me_of_pe_list μ sz pt nm' lp;
      match lp with nil => ret Bot | _ =>
      let '(a', ty) := union_list_mape, (pt_eval_pexpr ge μ pt e, Just (pexpr_get_ty e))) (All, All) lp in
      ret (NotBot (tn', a', ty, ln))
      end
    | None => do _ <- alarm_am "ab_eval_expr failed"; ret Bot
    end.

  Fixpoint ab_eval_exprlist (ab: t) (e: list expr) : alarm_mon ((t * list (PTSet * AbTy.t+⊤ * list (mexpr cell)))+⊥) :=
    match e with
    | nil => ret (NotBot (ab, nil))
    | e :: e' =>
      do r <- ab_eval_expr ab e;
      match r with
      | Bot => ret Bot
      | NotBot (tn, pt, ty, me) =>
        do m <- ab_eval_exprlist (with_tnum_, tn) ab) e';
        match m with
        | Bot => ret Bot
        | NotBot (ab, y) => ret (NotBot (ab, (pt, ty, me) :: y))
        end
      end
    end.

  Definition nm_forget_all :=
    fold_leftm c, bind m (AbMachineEnv.forget c)).

  Definition nm_upd (nm:num) (c:cell) (e:(mexpr cell)) : num+⊥ :=
    let nm' := AbMachineEnv.assign c e nm in
    nm_forget_all (overlap c) nm'.

  Definition nm_upd_cells (nm:num) (l:list cell) (e:(mexpr cell)) : num+⊥ :=
    union_list_mapc, nm_upd nm c e) (NotBot nm) l.

  Definition ct_forget_all {A: Type} := fold_left (λ (m: ACTreeDom.t A) c, ACTreeDom.set m c All).

  Definition ct_upd {A} (m:ACTreeDom.t A) (c:cell) (a:A+⊤) : ACTreeDom.t A :=
    ACTreeDom.set (ct_forget_all (overlap c) m) c a.

  Definition pt_upd (pt:points_to) (l:list cell) (s:BlockSet.t+⊤) : points_to :=
    union_list_mapc, ct_upd pt c s) pt l.

  Definition tp_upd_cells (tp:types) (l:list cell) (tt:AbTy.t+⊤) : types :=
    union_list_mapc, ct_upd tp c tt) tp l.


  Definition chunk_type (κ: typed_memory_chunk) (ty: AbTy.t+⊤) : bool :=
    match ty with
    | All => false
    | Just ty' =>
      match κ, ty' with
      | exist (Mint8signed | Mint8unsigned | Mint16signed | Mint16unsigned) _, VI
      | exist Mint32 _, (VI | VP | VIP)
      | exist Mint64 _, VL
      | exist Mfloat64 _, VF
      | exist Mfloat32 _, VS
        => true
      | _, _ => false
      end
    end.

  Definition assign_cells (κ: option typed_memory_chunk) (c: list cell) (a:expr) (ab:t) : alarm_mon (t+⊥) :=
    do res <- ab_eval_expr ab a;
    match res with
    | NotBot ((tp, nm), ptr, ty, ln) =>
        do _ <- am_assert match κ with Some κ => chunk_type κ ty | None => true end
                             ("bad type: " ++ to_string ty);
        ret (
            do nm' <- union_list_map (nm_upd_cells nm c) (NotBot nm) ln;
            let tp' := tp_upd_cells tp c ty in
            ret (AbMem (ab_stk ab) (ab_sz ab) (pt_upd (ab_pt ab) c ptr) tp' nm')
        )
    | Bot => ret Bot
    end.

  Definition assign_in (μ: fname) (x: ident) (a: expr) (ab: t) : alarm_mon (t+⊥) :=
    (assign_cells None (ACtemp μ x :: nil) a ab).

  Definition assign (x:ident) (a:expr) (ab:t) : alarm_mon (t+⊥) :=
    match ab_stk ab with
    | Just (_::σ) => assign_in (plength σ) x a ab
    | _ => do _ <- alarm_am "assign failed"; ret Bot
    end.

  Definition assign_any (x: ident) (ty: AbTy.t) (ab: t) : alarm_mon (t+⊥) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, pt, tn) =>
      let '(tp, nm) := tn in
      let c := ACtemp μ x in
      do nm' <-
            match ty with
            | VI | VL | VF | VS => ret (AbMachineEnv.forget c nm)
            | _ => do _ <- alarm_am ("assign_any: bad type " ++ to_string ty); ret Bot
            end;
        ret (
        do nm' <- nm';
        ret (with_num_, nm')
                      (with_ptpt, ACTreeDom.set pt c (Just BlockSet.empty))
                               (with_tptp, ACTreeDom.set tp c (Just ty)) ab))))
    | None => do _ <- alarm_am "assign_any failed"; ret Bot
    end.

  Definition permission_can_write (p: permission) : bool :=
    match p with
    | (Freeable | Writable) => true
    | (Readable | Nonempty) => false
    end.

  Definition cell_in_bounds (κ:typed_memory_chunk) (ab: t) (c: cell) : alarm_mon unit :=
    let b := ablock_of_cell c in
    let '(ofs, _) := range_of_cell c in
    match ABTreeDom.get b (ab_sz ab) with
    | All => do _ <- alarm_am ("block of unknown size: " ++ to_string b); ret tt
    | Just (hi, (perm, vol)) =>
      if permission_can_write perm then
      if zle 0 ofs && zle (ofs + size_chunk (proj1_sig κ)) hi
      then ret tt
      else do _ <- alarm_am ("cell out of range("++ to_string hi ++"): " ++ to_string c); ret tt
      else do _ <- alarm_am ("read-only cell: " ++ to_string c); ret tt
    end.

  Definition expr_has_cast_for_chunk (e: expr) (κ: typed_memory_chunk) : bool :=
    match κ, e with
    | exist Mint8signed _, Eunop Ocast8signed _
    | exist Mint8unsigned _, Eunop Ocast8unsigned _
    | exist Mint16signed _, Eunop Ocast16signed _
    | exist Mint16unsigned _, Eunop Ocast16unsigned _
    | exist Mint32 _, _
    | exist Mint64 _, _
    | exist Mfloat32 _, _
    | exist Mfloat64 _, _
      => true
    | _, _ => false
    end.

  Definition store (κ: memory_chunk) (a e: expr) (ab: t) : alarm_mon (t+⊥) :=
    let κ : option typed_memory_chunk :=
        match κ with
          | Many32 | Many64 => None
          | κ => Some (exist _ κ I)
        end
    in
    match κ with
      | None =>
        do _ <- alarm_am "invalid store memory chunk"; ret Bot
      | Some κ =>
        match split_t ab with
          | Some (μ, ε, τ, σ, sz, pt, tn) =>
            do _ <- am_assert (expr_has_cast_for_chunk e κ)
                  ("Uncasted expr for " ++ to_string (proj1_sig κ) ++ ": " ++ to_string e);
            do (tn', cells) <- deref_expr μ ε sz pt κ a tn;
            let ab := with_tnum_, tn') ab in
            match cells with nil => ret Bot | _ =>
              do _ <- am_rev_map (cell_in_bounds κ ab) cells;
              assign_cells (Some κ) cells e ab
            end
          | None => do _ <- alarm_am "store failed"; ret Bot
        end
    end.

  Definition nassume b nm (F: mexpr cellmexpr cell) e : num+⊥ :=
    union_list_mapme, AbMachineEnv.assume (F me) b nm) (NotBot nm) e.

  Definition bool_expr (e: expr) : expr :=
    match e with
    | Ebinop (Ocmp _| Ocmpu _| Ocmpf _| Ocmpfs _| Ocmpl _| Ocmplu _) _ _ => e
    | _ => Ebinop (Ocmp Cne) e (Econst (Csharpminor.Ointconst Int.zero))
    end.

  Definition assume (b: expr) (ab: t) : alarm_mon (t+⊥ * t+⊥) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, pt, tn) =>
      let b := bool_expr b in
      do (tn', b') <- nconvert μ ε sz pt b tn;
      let '(tp, nm) := tn' in
      ret
        (do nz <- nassume true nm id b'; ret (AbMem (Just ((ε,τ)::σ)) sz pt tp nz),
         do z <- nassume false nm id b'; ret (AbMem (Just ((ε,τ)::σ)) sz pt tp z))
    | None => do _ <- alarm_am "assume failed"; ret (Bot, Bot)
    end.

  Definition deref_fun (e: expr) (ab: t) : alarm_mon (list (ident * fundef)) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, pt, tn) =>
      do (tn, cells) <- deref_expr μ ε sz pt (exist _ Mint32 I) e tn;
      am_map' (λ c,
              match c with
              | ACglobal b 0 (exist Mint32 _) =>
                match Genv.find_funct_ptr ge b with
                | Some f =>
                  match Genv.invert_symbol ge b with
                  | Some i => Result (i,f)
                  | None => Error None "unknown function"
                  end
                | None => Error None ("bad function pointer " ++ to_string b)
                end
              | _ => Error None ("bad function pointer (2)")
              end) cells
    | None => do _ <- alarm_am "anomaly: deref_fun";
              ret nil
    end.

  Definition ab_bind_parameters (μ: ident)
             (formals: list ident) (args: list (PTSet * AbTy.t+⊤ * list (mexpr cell)))
             : alarm_mon (t+⊥) → alarm_mon (t+⊥) :=
    fold_left2
      (λ acc id v,
       do ab' <- acc;
       ret (
           do ab' <- ab';
           let c := ACtemp μ id in
           let '(ptr, ty, ln) := v in
           do nm' <- union_list_mapme, AbMachineEnv.assign c me (ab_num ab')) (NotBot (ab_num ab')) ln;
           ret (AbMem (ab_stk ab') (ab_sz ab')
                      (ct_upd (ab_pt ab') c ptr) (ct_upd (ab_tp ab') c ty)
                      nm'))
      )
      (λ _ _, do _ <- alarm_am "ab_bind_parameters: too many formals"; ret Bot)
      (λ _ _, do _ <- alarm_am "ab_bind_parameters: too many arguments"; ret Bot)
      formals args.

  Definition set_local_sizes μ vars : sizessizes :=
    fold_leftsz x_hi, ABTreeDom.set sz (ABlocal μ (fst x_hi)) (Just (snd x_hi, (Freeable, false))))
              vars.

  Definition push_frame_def: function) (args: list expr) (ab:t) : alarm_mon (t+⊥) :=
    do σ' <- match ab_stk ab with
                | All => do _ <- alarm_am "no stack"; ret nil
                | Just σ' => ret σ'
                end;
      let μ := plength σ' in
      do res <- ab_eval_exprlist ab args;
      match res with
      | Bot => ret Bot
      | NotBot (ab, eargs) =>
      let ε := PSetOp.build (rev_map fst μ_def.(fn_vars)) in
      let τ := PSetOp.build_def.(fn_temps) ++ μ_def.(fn_params)) in
      let σ := (ε, τ) :: σ' in
      let ab' := with_stk_, Just σ) (with_sz (set_local_sizes μ μ_def.(fn_vars)) ab) in
      ab_bind_parameters μ μ_def.(fn_params) eargs (ret (NotBot ab'))
      end.

Invalidate cells local to μ.
  Definition is_local_cell (μ: fname) (c: cell) : bool :=
    match c with
    | AClocal μ' _ _ _
    | ACtemp μ' _ => eq_dec μ μ'
    | ACglobal _ _ _ => false
    end.

  Definition is_local_block (μ: fname) (ab: ablock) : bool :=
    match ab with
    | ABlocal μ' _ => eq_dec μ μ'
    | ABglobal _ => false
    end.

  Definition pop_local_tp (μ: fname) : typestypes :=
    ACTreeDom.SP.filterc _, negb (is_local_cell μ c)).

Invalidate pointers to cells local to μ.
  Definition pop_local_pt (μ: fname) : points_topoints_to :=
    ACTreeDom.SP.filterc bs, negb (is_local_cell μ c) && negb (BSO.existsb (is_local_block μ) bs)).

  Definition clear_local_sizes μ ε : sizessizes :=
    PSet.foldx, ABTree.remove (ABlocal μ x)) ε.

  Definition all_local_cells (μ: ident) (ε: lvarset) (sz: sizes) : list cell :=
    PSet.foldx,
         let z := match ABTree.get (ABlocal μ x) sz with Some (z, _) => z | None => Z0 end in
         memory_chunk_foldacc κ,
           let k := align_chunk (proj1_sig κ) in
           N.peano_rect _
             acc
             (λ n, cons (AClocal μ x (Z.of_N n * k) κ))
             (Z.to_N (z / k))
         )
    ) ε nil.

  Definition pop_local_num (μ: ident) (ε: lvarset) (τ: tvarset) (sz: sizes) : num+⊥ → num+⊥ :=
    (nm_forget_all (all_local_cells μ ε sz)) ∘ (nm_forget_all (List.map (ACtemp μ) (PSet.elements τ))).

  Definition pop_frame (res: option (expr)) (temp : option (ident)) (ab: t) : alarm_mon (t+⊥) :=
    match ab_stk ab, res, temp with
    | Just ((ε₀, τ₀)::(ε,τ)::σ), Some r, Some x =>
      let μ := plength σ in
      let μ₀ := Pos.succ μ in
(
      do ab' <- assign_in μ x r ab;
      match ab' with
      | NotBot ab'' =>
        ret (
            do nm' <- pop_local_num μ₀ ε₀ τ₀ (ab_sz ab'') (NotBot (ab_num ab''));
            ret (with_sz (clear_local_sizes μ₀ ε₀)
                (with_tp (pop_local_tp μ₀)
                (with_pt (pop_local_pt μ₀)
                (with_stk_, Just ((ε,τ)::σ))
                (with_num_, nm') ab''))))))
      | Bot => ret Bot
      end
      )
    | Just ((ε,τ)::nil), Some r, _ =>
      let μ := xH in
      do ret' <- ab_eval_expr ab r;
      match ret' with
      | Bot => ret Bot
      | NotBot (_, ty, _) =>
        match ty with
        | Just VI =>
          ret (NotBot (with_sz (clear_local_sizes μ ε)
                           (with_tp (pop_local_tp μ)
                           (with_pt (pop_local_pt μ)
                           (with_stk_, Just nil) ab)))))
        | _ => do _ <- alarm_am "main ret not an int"; ret Bot
        end
      end
    | Just (_::nil), _, _ =>
      do _ <- alarm_am "pop_frame failed at main"; ret Bot
    | Just ((ε,τ)::σ), None, None =>
      let μ := plength σ in
        ret (
            do nm' <- pop_local_num μ ε τ (ab_sz ab) (NotBot (ab_num ab));
            ret (with_sz (clear_local_sizes μ ε)
                (with_tp (pop_local_tp μ)
                (with_pt (pop_local_pt μ)
                (with_stk_, Just σ)
                (with_num_, nm') ab))))))
    | Just ((ε,τ)::σ), Some r, None =>
      let μ := plength σ in
      do _ <- noerror r ab;
        ret (
            do nm' <- pop_local_num μ ε τ (ab_sz ab) (NotBot (ab_num ab));
            ret (with_sz (clear_local_sizes μ ε)
                (with_tp (pop_local_tp μ)
                (with_pt (pop_local_pt μ)
                (with_stk_, Just σ)
                (with_num_, nm') ab))))))
    | _, _, _ => do _ <- alarm_am ("pop_frame failed: " ++ to_string temp ++ " := " ++ to_string res ); ret Bot
    end.

  Let bad_align {T} (r: T) := do _ <- alarm_am "misaligned init data"; ret r.
  Instance InitDataToString : ToString init_data :=
    λ id,
    match id with
    | Init_int8 i => "int8: " ++ to_string i
    | Init_int16 i => "int16: " ++ to_string i
    | Init_int32 i => "int32: " ++ to_string i
    | Init_int64 i => "int64: " ++ to_string i
    | Init_float32 f => "float32: " ++ to_string f
    | Init_float64 f => "float64: " ++ to_string f
    | Init_space n => "space: " ++ to_string n
    | Init_addrof i o => "&" ++ to_string i ++ " + " ++ to_string o
    end.

  Definition type_of_chunk (κ: typed_memory_chunk) : AbTy.t :=
    match κ with
    | exist (Mint8signed | Mint8unsigned |
             Mint16signed | Mint16unsigned |
             Mint32) _
      => VI
    | exist Mint64 _ => VL
    | exist Mfloat64 _ => VF
    | exist Mfloat32 _ => VS
    | exist _ H => match H with end
    end.

  Definition permission_can_read (p: permission) : { perm_order p Readable } + { p = Nonempty } :=
    match p with
    | Freeable => left (perm_F_any Readable)
    | Writable => left perm_W_R
    | Readable => left (perm_refl Readable)
    | Nonempty => right eq_refl
    end.

  Definition zero_all_cells (b: block) (base: Z) (sz: Z) (ab: t): t+⊥ :=
    N.peano_rect
      (λ _, t+⊥)
      (NotBot ab)
      (λ ofs ab,
       memory_chunk_fold
         (λ ab κ,
          let ofs' := base + Z.of_N ofs in
          if Zdivide_dec (align_chunk (proj1_sig κ)) ofs' (align_chunk_pos (proj1_sig κ))
          && zle (Z.of_N ofs + size_chunk (proj1_sig κ)) sz then
            do ab <- ab;
              let c := ACglobal b ofs' κ in
              do nm <- AbMachineEnv.assign c
                (match κ with
                   | exist (Mint8signed | Mint8unsigned | Mint16signed | Mint16unsigned | Mint32) _
                     => me_const_i _ Int.zero
                   | exist Mint64 _ => me_const_l _ Int64.zero
                   | exist (Mfloat32) _ => me_const_s _ Float32.zero
                   | exist (Mfloat64) _ => me_const_f _ Float.zero
                   | exist _ H => match H with end
                 end)
                (ab_num ab);
              ret
                (with_tp (ACTree.set c (type_of_chunk κ))
                (with_pt (ACTree.set c BlockSet.empty)
                (with_num_, nm) ab)))
          else ab
         ) ab)
      (Z.to_N sz).

  Definition ab_alloc_global perm vol b (gv: list init_data) ab : alarm_mon (t+⊥) :=
    do sz_ab' <- am_fold
      (λ (acc:_*(_+⊥)) id,
       let '(ofs, ab) := acc in
       let ofs' := ofs + Genv.init_data_size id in
       let c κ H := ACglobal b ofs (exist _ κ H) in
       if match id with
          | Init_space _
          | Init_int8 _ => true
          | Init_int16 _ => Zdivide_dec (align_chunk Mint16signed) ofs eq_refl
          | Init_int32 _
          | Init_addrof _ _ => Zdivide_dec (align_chunk Mint32) ofs eq_refl
          | Init_int64 _ => Zdivide_dec (align_chunk Mint64) ofs eq_refl
          | Init_float32 _ => Zdivide_dec (align_chunk Mfloat32) ofs eq_refl
          | Init_float64 _ => Zdivide_dec (align_chunk Mfloat64) ofs eq_refl
          end then
       match id return alarm_mon (Z*t+⊥) with
       | Init_int32 n =>
          ret (ofs',
                  if permission_can_read perm
                  then
                  do ab' <- ab;
                  do nm <- AbMachineEnv.assign (c Mint32 I) (me_const_i _ n) (ab_num ab');
                  ret (with_tp (ACTree.set (c Mint32 I) VI)
                      (with_pt (ACTree.set (c Mint32 I) BlockSet.empty)
                      (with_num_, nm) ab')))
                  else ab)
       | Init_int64 n =>
          ret (ofs',
                  if permission_can_read perm
                  then
                  do ab' <- ab;
                  do nm <- AbMachineEnv.assign (c Mint64 I) (me_const_l _ n) (ab_num ab');
                  ret (with_tp (ACTree.set (c Mint64 I) VL)
                      (with_pt (ACTree.set (c Mint64 I) BlockSet.empty)
                      (with_num_, nm) ab')))
                  else ab)
       | Init_addrof s o =>
         match Genv.find_symbol ge s with
         | Some b' => ret (ofs',
                              if permission_can_read perm
                              then
                              do ab' <- ab;
                              do nm <- AbMachineEnv.assign (c Mint32 I) (me_const_i _ o) (ab_num ab');
                              ret (with_tp (ACTree.set (c Mint32 I) VP)
                                  (with_pt (ACTree.set (c Mint32 I) (BSO.singleton (ABglobal b')))
                                  (with_num_, nm) ab')))
                              else ab)
         | None => do _ <- alarm_am ("ab_alloc_globals: not a symbol ("
                                        ++ to_string s ++ ")"); ret (ofs', ab)
         end
       | Init_space s =>
         ret (ofs',
                 if negb (permission_can_read perm)
                 then ab else do ab' <- ab; zero_all_cells b ofs s ab')
       | Init_float64 f =>
         ret (ofs',
           if negb (permission_can_read perm)
           then ab
           else
             do ab' <- ab;
             do nm <- AbMachineEnv.assign (c Mfloat64 I) (MEconst _ (MNfloat f)) (ab_num ab');
             ret (with_tp (ACTree.set (c Mfloat64 I) VF)
                 (with_pt (ACTree.set (c Mfloat64 I) BlockSet.empty)
                 (with_num_, nm) ab'))))
       | Init_float32 f =>
         ret (ofs',
           if negb (permission_can_read perm)
           then ab
           else
             do ab' <- ab;
             do nm <- AbMachineEnv.assign (c Mfloat32 I) (MEconst _ (MNsingle f)) (ab_num ab');
             ret (with_tp (ACTree.set (c Mfloat32 I) VS)
                 (with_pt (ACTree.set (c Mfloat32 I) BlockSet.empty)
                 (with_num_, nm) ab'))))
       | Init_int8 i =>
         ret (ofs',
                 if permission_can_read perm
                 then
                 do ab' <- ab;
                 do nm <- AbMachineEnv.assign (c Mint8unsigned I) (MEunop Ocast8unsigned (me_const_i _ i)) (ab_num ab');
                 ret (with_tp (ACTree.set (c Mint8unsigned I) VI)
                     (with_pt (ACTree.set (c Mint8unsigned I) BlockSet.empty)
                     (with_num_, nm) ab')))
                 else ab)
       | Init_int16 i =>
         ret (ofs',
                 if permission_can_read perm
                 then
                 do ab' <- ab;
                 do nm <- AbMachineEnv.assign (c Mint16unsigned I) (MEunop Ocast16unsigned (me_const_i _ i)) (ab_num ab');
                 ret (with_tp (ACTree.set (c Mint16unsigned I) VI)
                     (with_pt (ACTree.set (c Mint16unsigned I) BlockSet.empty)
                     (with_num_, nm) ab')))
                 else ab)
       end
       else bad_align acc)
      gv
      (0, ab);
    let '(sz, ab') := sz_ab' in
    ret (do ab'' <- ab'; ret (with_sz (ABTree.set (ABglobal b) (sz, (perm, vol))) ab'')).

  Definition ab_alloc_globals (gl: list (ident * globdef fundef unit)) : alarm_mon (block * t+⊥) :=
    (am_fold
       (λ acc ig,
        let '(b, ab) := acc in
        let b' := Pos.succ b in
        match ig with
        | (_, Gfun _) => ret (b', fmap (with_sz (ABTree.set (ABglobal b) (1, (Nonempty, false)))) ab)
        | (_, Gvar gv) =>
            do ab' <- ab_alloc_global (Genv.perm_globvar gv) (gvar_volatile gv) b gv.(gvar_init) ab;
            ret (b', ab')
        end)
       gl
       (xH, do t <- ⊤; ret (Just nil, t))).

  Definition init_mem (defs:list (ident * globdef fundef unit)) : alarm_mon (t+⊥) :=
    do _ <- am_assert (norepet_map fst defs) ("duplicate name in globals");
    do ab <- ab_alloc_globals defs;
    ret (snd ab).

  Definition validate_volatile_ptr (sz: sizes) (ptr: BlockSet.t) (lne: list (mexpr cell)) : boolbool :=
    BlockSet.fold
      (λ blk (acc: bool),
       if acc
       then
         match blk with
         | ABlocal _ _ => false
         | ABglobal b =>
           match Genv.invert_symbol ge b with
           | None => false
           | Some g =>
             match ABTreeDom.get blk sz with
             | Just (hi, (perm, vol)) =>
               vol
             | All => false
             end
           end
         end
       else acc)
      ptr.

  Definition check_volatile (tgt: (ident * int) + expr) (κ: memory_chunk) (ab: t) : alarm_mon ident :=
    do _ <-
    match tgt with
    | inl (g, ofs) =>
      match Genv.find_symbol ge g with
      | None => alarm_am "check_volatile: bad global"
      | Some b =>
        let b := ABglobal b in
        match ABTreeDom.get b (ab_sz ab) with
        | Just (hi, (perm, vol)) =>
          am_assert vol ("check_volatile: not volatile")
        | All => alarm_am "check_volatile: unknown size"
        end
      end
    | inr addr =>
      do z <- ab_eval_expr ab addr;
      match z with
      | NotBot (tn, Just ptr, Just VP, ne) =>
        am_assert (validate_volatile_ptr (ab_sz ab) ptr ne true) ("check_volatile: bad pointer")
      | _ => alarm_am "check_volatile: unresolved expression"
      end
    end;
    match ab_stk ab with
    | Just ( _ :: σ ) => ret (plength σ)
    | _ => do _ <- alarm_am "check_volatile: no stack"; ret xH
    end.

  Definition forget_cell (ab: t) (c: cell) : t+⊥ :=
    do nm' <- AbMachineEnv.forget c (ab_num ab);
    NotBot (with_ptpt, ACTreeDom.set pt c All)
           (with_tptp, ACTreeDom.set tp c (⊤))
           (with_num_, nm') ab))).

  Definition vload (rettemp: option ident) (g_ofs: option (ident * int)) (κ: memory_chunk) (args: list expr) (ab: t) : alarm_mon (t+⊥) :=
    do _ <- am_assert match κ with Mint64 | Mint32 | Mfloat32 | Mfloat64 => true | _ => false end "vload: bad chunk";
    match
    match g_ofs, args with
    | Some q, nil => Some (inl q)
    | None, addr :: nil => Some (inr addr)
    | _, _ => None
    end
    with
    | None => do _ <- alarm_am "vload: bad arguments"; ret Bot
    | Some tgt =>
    do μ <- check_volatile tgt κ ab;
    match rettemp with
    | None => ret (NotBot ab)
    | Some r =>
      match κ with
      | Mint64 => assign_any r VL ab
      | Mfloat64 => assign_any r VF ab
      | Mfloat32 => assign_any r VS ab
      | _ => ret (forget_cell ab (ACtemp μ r))
      end
    end
    end.

  Definition only_global_pointer (aptr: BlockSet.t+⊤) : bool :=
    match aptr with
    | All => false
    | Just ptr =>
      negb (BSO.existsbp,
                             match p with
                             | ABglobal b => match Genv.invert_symbol ge b with Some g => negb (Senv.public_symbol ge g) | None => true end
                             | ABlocal _ _ => true
                             end) ptr)
    end.

  Definition vstore (rettemp: option ident) (g_ofs: option (ident * int)) (κ: memory_chunk) (args: list expr) (ab: t) : alarm_mon (t+⊥) :=
    match
    match g_ofs, args with
    | Some q, arg :: nil => Some (inl q, arg)
    | None, addr :: arg :: nil => Some (inr addr, arg)
    | _, _ => None
    end
    with
    | None => do _ <- alarm_am "vstore: bad arguments"; ret Bot
    | Some (tgt, arg) =>
    do μ <- check_volatile tgt κ ab;
    do v <- ab_eval_expr ab arg;
    match v with
    | Bot => ret Bot
    | NotBot (tn, ptr, oty, lne) =>
      do _ <- am_assert
            match oty with
            | Just ty =>
              match ty, κ with
              | VL, Mint64
              | VS, Mfloat32
              | VF, Mfloat64
              | VI, ( Mint32 | Mint8signed | Mint8unsigned | Mint16signed | Mint16unsigned )
                => true
              | (VIP|VP), Mint32 => only_global_pointer ptr
              | _, _ => false
              end
            | All => false
            end
            ("vstore: cannot prove that the argument " ++ to_string arg ++ " is well-typed " ++ to_string oty);
      let ab := with_tnum_, tn) ab in
      match rettemp with
      | None => ret (NotBot ab)
      | Some r => ret (forget_cell ab (ACtemp μ r))
      end
    end
    end.

  Instance widen_mem : widen_op (iter_t+⊥) (t+⊥) :=
    {| widen A B :=
         let '(NUM_it_ret, NUM_ret) :=
           match A with
           | Bot => init_iter Bot
           | NotBot (_, (_, (_, (_, NUM)))) => NUM
           end
           match B with
           | Bot => Bot
           | NotBot (_, (_, (_, (_, NUM)))) => NotBot NUM
           end
         in
         match B with
         | Bot =>
           match A with
           | Bot => (Bot, Bot)
           | NotBot (STK, (SZ, (PT, (TP, _)))) =>
             (NotBot (STK, (SZ, (PT, (TP, NUM_it_ret)))),
              do NUM_ret <- NUM_ret;
              ret (STK, (SZ, (PT, (TP, NUM_ret)))))
           end
         | NotBot (STK', (SZ', (PT', (TP', _)))) =>
           match A with
           | Bot =>
             (NotBot (STK', (SZ', (PT', (TP', NUM_it_ret)))),
              do NUM_ret <- NUM_ret;
              ret (STK', (SZ', (PT', (TP', NUM_ret)))))
           | NotBot (STK, (SZ, (PT, (TP, NUM)))) =>
             let STK_ret :=
               match STK, STK' with
               | Just STK, Just STK' => list_widen join STK STK'
               | _, _ => All
               end
             in
             let SZ_ret := sizes_widen SZ SZ' in
             let PT_ret := points_to_widen PT PT' in
             let TP_ret := types_widen TP TP' in
             (NotBot (STK_ret, (SZ_ret, (PT_ret, (TP_ret, NUM_it_ret)))),
              do NUM_ret <- NUM_ret;
              ret (STK_ret, (SZ_ret, (PT_ret, (TP_ret, NUM_ret)))))
           end
         end;
       init_iter x :=
         do x <- x;
         ret (ab_stk x, (ab_sz x, (ab_pt x, (ab_tp x, init_iter (NotBot (ab_num x))))))
    |}.

  Instance widen_mem_correct:
    widen_op_correct (iter_t +⊥) (t +⊥) concrete_state.
Proof.
    split.
    - intros. eapply botbind_spec, H. intros. destruct H0; constructor; auto.
      destruct H3. split. auto. red in H4.
      destruct a0 as (? & ? & ? & ? & ?). destruct H4. split. apply H4.
      destruct H5 as (ρ' & A & B). eexists. split. eauto.
      apply init_iter_correct. auto.
    - unfold widen_mem. intros x y cs Hcs.
      destruct x as [|(STK & SZ & PT & TP & NUM)]. contradiction.
      destruct Hcs. destruct H2 as [? [? [ρ' []]]]. simpl.
      match goal with
      | |- context [?A ▽ ?B] => pose proof (widen_incr A B _ H5); destruct (AB)
      end.
      unfold ACTree.elt in *. destruct y as [|(STK' & SZ' & PT' & TP' & NUM')].
      + split.
        constructor; auto. split. auto. split. auto. eexists. split. eauto. apply H6.
        eapply botbind_spec. 2:apply H6.
        constructor; auto. split. auto. split. auto. eexists. split. eauto. apply H7.
      + 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, H. intros x y f Hf. apply join_correct. auto. }
        assert (STK'' ≠ Allinvariant cs).
        { intros. apply H0. 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 PT PT') (mem_as_fun ge cs)).
        { apply ACTreeDom.join_tree_correct. refine _. auto. }
        assert (γ (types_widen TP TP') (mem_as_fun ge cs)).
        { apply join_correct. auto. }
        split.
        * constructor. auto. auto. auto. split. auto. split. auto.
          eexists. split. eauto. apply H6.
        * eapply botbind_spec.
          constructor. auto. auto. auto. split. auto. split. auto.
          eexists. split. eauto. eauto. apply H6.
  Qed.

  Instance mem_cshm_dom : mem_dom t (iter_t+⊥) :=
    {| assign := assign
     ; assign_any := assign_any
     ; store := store
     ; assume := assume

     ; noerror := noerror
     ; deref_fun := deref_fun

     ; ab_vload := vload
     ; ab_vstore := vstore

     ; push_frame := push_frame
     ; pop_frame := pop_frame

     ; ab_malloc sz dst ab := do _ <- alarm_am "malloc"; ret (⊤)
     ; ab_free e ab := do _ <- alarm_am "free"; ret (⊤)
     ; ab_memcpy sz al dst src ab :=
         do _ <- alarm_am ("memcpy(" ++ to_string sz ++ ", " ++ to_string al ++ ", " ++ to_string dst ++ ", " ++ to_string src ++ ")");
         ret (⊤)

     ; init_mem := init_mem
    |}.

  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.

  Instance t_join_correct : join_op_correct t (t+⊥) concrete_state.
Proof.
    intros (stk & sz & pt & nm) (stk' & sz' & pt' & nm') cc H.
    assert (mem_as_fun ge cc ∈ γ ((nmnm'):tnum+⊥)).
    { apply join_correct. destruct H as [ () | () ]; hsplit; auto. }
    unfold join in H0.
    unfold join, join_mem, mem_cshm_dom, WProd.join_prod_bot at 1 2 3.
    unfold join. simpl.
    destruct (WProd.join_prod_bot _ _ _ _). easy.
    constructor.
    apply join_correct. destruct H as [ () | () ]; auto.
    intro. destruct H as [ [ X INV ] | [ X INV ] ]; apply INV; intro; destruct stk, stk'; try discriminate; apply H1; auto.
    apply sizes_join_correct. destruct H as [ () | () ]; auto.
    split. apply ACTreeDom.join_tree_correct. refine _. destruct H as [()|()]; hsplit; auto. auto.
  Qed.

  Instance t_leb_correct : leb_op_correct t concrete_state.
Proof.
    intros (stk & sz & pt & tpnm) (stk' & sz' & pt' & tpnm') LE (σ & m) [ STK INV SZ (PT & NUM) ].
    unfold WProd.leb_prod, leb in LE. simpl in *.
    unfold leb in LE. simpl in *.
    unfold leb in LE. simpl in *.
    repeat rewrite andb_true_iff in LE. destruct LE as (LEstk & LEsz & LEpt & LEnm).
    constructor.
    simpl. eapply leb_correct; eauto.
    simpl. destruct stk' as [|stk']. intuition. intros _. apply INV.
    destruct stk. simpl in LEstk. eq_some_inv. discriminate.
    simpl. eapply leb_correct; eauto.
    split. simpl. eapply leb_correct; eauto.
    eapply leb_correct; eauto.
  Qed.

  Instance TToString : ToString t :=
    (λ x,
    "Mem { " ++ "stack : " ++ to_string (ab_stk x) ++ new_line
             ++ "blocks: " ++ to_string (ab_sz x) ++ new_line
             ++ "points: " ++ to_string (ab_pt x) ++ new_line
             ++ "types : " ++ to_string (ab_tp x) ++ new_line
             ++ "num : " ++ to_string (ab_num x) ++ new_line
             ++ "}" ++ new_line ).

  Instance IterTToString : ToString iter_t :=
      (λ x,
       "Mem { " ++ "stack : " ++ to_string (ab_stk_iter x) ++ new_line
                ++ "blocks: " ++ to_string (ab_sz_iter x) ++ new_line
                ++ "points: " ++ to_string (ab_pt_iter x) ++ new_line
                ++ "types : " ++ to_string (ab_tp_iter x) ++ new_line
                ++ "num : " ++ to_string (ab_num_iter x) ++ new_line
                ++ "}" ++ new_line ).

End num.