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
  Csharpminorannot
  Util
  AssocList
  ToString
  Sets
  AdomLib
  AlarmMon
  ListAdom
  AbMemSignatureCsharpminor
  AbMachineEnv
  PExpr
  MemChunkTree
  Cells
  Pun
  NoError
  PointsTo
  Mconvert
  FastArith.

Require Import Annotagen.

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 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.

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.
  intros (b' & Hb' & K).
  exists b'. split. exact Hb'.
  erewrite <- block_of_ablock_tmp; eassumption.
Qed.

Lemma type_gamma_covariant :
  ∀ (G G': gamma_op BlockSet.t block),
  (∀ bs, G bsG' bs) →
  ∀ ty, type_gamma G tytype_gamma G' ty.
Proof.
  intros G G' M ty v.
  case ty; try exact id;
  (intros [ | bs ]; [ exact id |]);
  destruct v; try exact id;
  apply M.
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) as [ | ty ]. exact id.
  apply type_gamma_covariant, ptset_gamma_tmp, 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 := (points_to * num)%type.
  Definition tnum_iter : Type := (points_to * num_iter)%type.

  Section TNUM_GAMMA.

  Context (stk: list (temp_env * env)).

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

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

  Instance tnum_leb_correct : leb_op_correct (points_to * num) (cellval).
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 ((points_to * num)+⊥) (cellval).
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 (points_to * num) ((points_to * num)+⊥) (cellval).
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.
      apply points_to_join_correct; eauto.
      exists ρ'. split. assumption. eauto.
      eapply join_correct. eauto.
    - eapply botbind_spec. intros. split.
      apply points_to_join_correct; eauto.
      exists ρ'. split. assumption. eauto.
      eapply join_correct. eauto.
  Qed.

  Section REALIZATION.


    Definition is_int (tp: points_to) (c: cell) : bool :=
      ACTreeDom.get c tpJust TyInt.

    Lemma is_int_intro (tp: points_to) (ρ: cellval) (TP: ρ ∈ points_to_gamma ge stk tp) (c: cell) (P: boolProp):
      (P false) →
      ((∃ i, ρ c = Vint i) → P true) →
      P (is_int tp c).
Proof.
      intros F T.
      generalize (TP c).
      unfold is_int.
      case (ACTreeDom.get). intros _; exact F.
      intros (); try (intros _; exact F);
      try intros [ | bs ];
      try (intros _; exact F);
      destructc); try (intros (); fail);
      intros Hi; apply T; vauto.
    Qed.
    Global Opaque is_int.

    Definition realize_u8_from_κ (κ: typed_memory_chunk) (ab: ablock) (base: Z) (shift: Z) (tp: points_to) (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 TyInt), nm').

    Definition tnum_mono (x y: tnum) : Prop :=
      ∀ ρ, Pun.pun_u8 ρ →
           ρ ∈ γ x
           ρ ∈ points_to_gamma ge stk (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 =>
             ρ ∈ points_to_gamma ge stk (fst y) ∧
             ∀ ρ',
               compat ρ ρ' →
               ρ' ∈ γ (snd x) → ρ' ∈ γ (snd y)
           end.

    Lemma realize_u8_from_κ_sound (κ: typed_memory_chunk) ab base shift (tp: points_to) (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 _.
        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 Mint8signed _ =>
             let c := cell_from ab (ofs, κ) in
             let c8u := cell_from ab (ofs, exist _ Mint8unsigned I) in
             if is_int tp c8u then
               do nm' <- assign c (MEunop Ocast8signed (MEvar MNTint c8u)) nm;
               NotBot (ACTreeDom.set tp c (Just TyInt), nm')
             else NotBot tn
           | exist Mint16signed _ =>
             if ArithLib.divide ofs F2 then
             let c := cell_from ab (ofs, κ) in
             let c16u := cell_from ab (ofs, exist _ Mint16unsigned I) in
             if is_int tp c16u then
               do nm' <- assign c (MEunop Ocast16signed (MEvar MNTint c16u)) nm;
               NotBot (ACTreeDom.set tp c (Just TyInt), nm')
             else NotBot tn
             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 TyInt), 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.
      - set (c8u := cell_from ab (ofs, exist _ Mint8unsigned I)).
        apply (is_int_intro TP). auto. intros (i & Hi).
        destruct NM as (ρ' & CPT & NM).
        match goal with
        | |- appcontext[ assign ?cx ?mex _ ] =>
          set (c := cx); set (me := mex)
        end.
        assert (∀ ρ', compat ρ ρ' → eval_mexpr ρ' me (MNint (Int.sign_ext 8 i))) as MEV.
        {
          clear ρ' CPT NM. intros ρ' CPT.
          generalize (CPT c8u). rewrite Hi. simpl. intros Hi'.
          unfold me. econstructor.
          constructor. symmetry. exact Hi'. constructor. vauto.
        }
        generalize (@assign_correct _ _ _ _ NumDom c me ρ' _ nm NM (MEV _ CPT)).
        destruct (assign _ _ _) as [ | nm' ] eqn: Hassn; simpl. exact id.
        intros NM'.
        assertc = Vint (Int.sign_ext 8 i)) as Hc.
        {
          destruct (Hpun ab (exist _ Mint8signed I) ofs) as (bytes & Hnof & Hbytes & Hlen & Huc); simpl in *.
          exists ofs; Psatz.lia.
          etransitivity.
          exact Hbytes. clear Hbytes.
          destruct bytes as [ | b₀ [ | ? ? ] ]; try discriminate.
          clear Hlen.
          generalize (Huc 0%nat eq_refl). simpl. rewrite Z.add_0_r. fold c8u. rewrite Hi.
          destruct bas [ | i₀ | ]; try discriminate. intros X; inv X.
          unfold decode_val. simpl.
          rewrite ! decode_int_1.
          rewrite Int.sign_ext_zero_ext. reflexivity. reflexivity.
        }
        split.
        * intros c'.
          unfold ACTreeDom.get. rewrite ACTree.gsspec. case (ACTree.elt_eq).
          intros ->. rewrite Hc. exact I.
          intros _. exact (TP c').
        * clear ρ' CPT NM NM'.
          intros ρ' CPT NM.
          generalize (@assign_correct _ _ _ _ NumDom c me ρ' (MNint (Int.sign_ext 8 i)) nm NM).
          rewrite Hassn.
          assert (ρ' +[ c => MNint (Int.sign_ext 8 i) ] = ρ') 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.
      - 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 F2). destruct ArithLib.divide. 2: auto.
        set (c16u := cell_from ab (ofs, exist _ Mint16unsigned I)).
        apply (is_int_intro TP). auto. intros (i & Hi).
        destruct NM as (ρ' & CPT & NM).
        match goal with
        | |- appcontext[ assign ?cx ?mex _ ] =>
          set (c := cx); set (me := mex)
        end.
        assert (∀ ρ', compat ρ ρ' → eval_mexpr ρ' me (MNint (Int.sign_ext 16 i))) as MEV.
        {
          clear ρ' CPT NM. intros ρ' CPT.
          generalize (CPT c16u). rewrite Hi. simpl. intros Hi'.
          unfold me. econstructor.
          constructor. symmetry. exact Hi'. constructor. vauto.
        }
        generalize (@assign_correct _ _ _ _ NumDom c me ρ' _ nm NM (MEV _ CPT)).
        destruct (assign _ _ _) as [ | nm' ] eqn: Hassn; simpl. exact id.
        intros NM'.
        assertc = Vint (Int.sign_ext 16 i)) as Hc.
        {
          destruct (Hpun ab (exist _ Mint16signed I) ofs) as (bytes & Hnof & Hbytes & Hlen & Huc); simpl in *.
          intuition.
          etransitivity.
          exact Hbytes. clear Hbytes.
          destruct bytes as [ | b₀ [ | b₁ [ | ? ? ] ] ]; try discriminate.
          clear Hlen.
          destruct (Hpun ab (exist _ Mint16unsigned I) ofs) as (bytes' & Hnof' & Hbytes' & Hlen' & Huc'); simpl in *.
          intuition.
          destruct bytes' as [ | b'₀ [ | b'₁ [ | ? ? ] ] ]; try discriminate.
          clear Hlen'.
          unfold typed_memory_chunk in Hbytes'.
          fold c16u in Hbytes'.
          rewrite Hi in Hbytes'.
          destruct b'₀ as [ | i'₀ | ]; try discriminate.
          destruct b'₁ as [ | i'₁ | ]; try discriminate.
          unfold decode_val in Hbytes'. simpl in Hbytes'. inv Hbytes'.
          generalize (Huc 0%nat eq_refl).
          rewrite (Huc' 0%nat eq_refl). simpl.
          destruct bas [ | i₀ | ]; try discriminate.
          generalize (Huc 1%nat eq_refl).
          rewrite (Huc' 1%nat eq_refl). simpl.
          destruct bas [ | i₁ | ]; try discriminate.
          unfold decode_val. simpl.
          rewrite ! decode_int_1, ! decode_int_LE_2; auto.
          assert (∀ x y, Vint x = Vint yx = y) as vint_inj.
          exactx y H, let 'eq_refl := H in eq_refl).
          assert (∀ x y, eqb (Int.eq (Int.zero_ext 8 (Int.repr (Byte.unsigned x))) (Int.zero_ext 8 (Int.repr (Byte.unsigned y)))) (Byte.eq x y)) as ze8rbu_inj'.
          intros x. apply (for_all_byte_correct).
          revert x. apply (for_all_byte_correct).
          vm_compute. reflexivity.
          assert (∀ x y, (Int.zero_ext 8 (Int.repr (Byte.unsigned x))) = Int.zero_ext 8 (Int.repr (Byte.unsigned y)) → x = y) as ze8rbu_inj.
          { intros x y H. generalize (ze8rbu_inj' x y). rewrite H, Int.eq_true.
            generalize (Byte.eq_spec x y). simpl.
            case Byte.eq; easy. }
          intros Hi₁. apply vint_inj, ze8rbu_inj in Hi₁.
          intros Hi₀. apply vint_inj, ze8rbu_inj in Hi₀.
          f_equal.
          rewrite Int.sign_ext_zero_ext. congruence. reflexivity.
        }
        split.
        * intros c'.
          unfold ACTreeDom.get. rewrite ACTree.gsspec. case (ACTree.elt_eq).
          intros ->. rewrite Hc. exact I.
          intros _. exact (TP c').
        * clear ρ' CPT NM NM'.
          intros ρ' CPT NM.
          generalize (@assign_correct _ _ _ _ NumDom c me ρ' (MNint (Int.sign_ext 16 i)) nm NM).
          rewrite Hassn.
          assert (ρ' +[ c => MNint (Int.sign_ext 16 i) ] = ρ') 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.
      - 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).
        hsplit.
        match goal with
        | |- appcontext[ assign ?cx ?mex _ ] =>
          set (c := cx); set (me := mex)
        end.
        set (n := Int.add i2 (Int.add (Int.shl i1 (Int.repr 8))
           (Int.add (Int.shl i0 (Int.repr 16)) (Int.shl i (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 i2 (Int.add (Int.shl i1 (Int.repr 8)) (Int.add (Int.shl i0 (Int.repr 16)) (Int.shl i (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.

  End TNUM_GAMMA.

  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.

  Definition me_of_pe (tn: tnum) (pe: pexpr cell) : nconvert_t annotation_log (mexpr cell) :=
    let '(tp, nm) := tn in
    do me <- N.nconvert _ ge μ _ _x, x) _ _ NumDom sz nm tp pe;
    do _ <- assert _ (AbMachineEnv.noerror me nm)
       (λ _, "me_of_pe(" ++ to_string pe ++ "): error " ++ to_string me);
    ret me.

  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 join_cellsets (cs: list (CellSet.t+⊤)) : CellSet.t+⊤ :=
    List.fold_left
      (λ acc cells,
       match acc with
       | All => All
       | Just acc => fmap (CellSet.union acc) cells
       end)
      cs
      (Just CellSet.empty).

  Lemma join_cellsets_correct cs :
    match join_cellsets cs with
    | All => In All cs
    | Just cells =>
      (∀ s, In s cs → ∃ s', s = Just s') ∧
      (∀ s, In (Just s) cs → ∀ c, CellSet.mem c sCellSet.mem c cells) ∧
      (∀ c, CellSet.mem c cells → ∃ s, CellSet.mem c sIn (Just s) cs)
    end.
Proof.
    elim cs using rev_ind; clear cs.
    - split. intros _ (). split. intros _ (). intros c X; elim (CellSet.mem_empty X).
    - intros s cs IH. unfold join_cellsets. rewrite fold_left_app.
      fold (join_cellsets cs). simpl. destruct (join_cellsets _).
      apply in_app; left; exact IH.
      destruct IH as (IHall & IH & IH').
      destruct s as [ | s ].
      apply in_app; right; left; reflexivity.
      split; [ | split ].
      + intros s' H. apply in_app in H.
        destruct H as [ H | [ <- | () ] ]; eauto.
      + intros s' H c Hc. apply in_app in H.
        rewrite CellSet.mem_union.
        destruct H as [ H | [ H | () ] ].
        specialize (IH _ H _ Hc). bleft. exact IH.
        bright. congruence.
      + intros c H. rewrite CellSet.mem_union in H.
        apply orb_prop in H. destruct H as [ H | H ].
        destruct (IH' _ H) as (s' & Hs' & H').
        exists s'. split. exact Hs'. apply in_app; left; exact H'.
        exists s. split. exact H. apply in_app; right; left; reflexivity.
  Qed.

  Definition has_type_VP (pe: pexpr cell) : bool :=
    match pexpr_get_ty pe with
    | VP => true
    | _ => false
    end.

  Definition itv_of_me (nm: num) (me: mexpr cell) : mach_num_itv+⊥ :=
    get_itv me nm.

  Definition int_pair_of_nvi (nvi: mach_num_itv+⊥) : (int * int)+⊤ :=
    match nvi with
    | Bot => All
    | (NotBot r) =>
      match r with
      | MNIint (Just (ZIntervals.ZITV lo hi)) => Just (Int.repr lo, Int.repr hi)
      | _ => All
      end
    end.

  Definition depth (f: ident) : nat :=
    (Pos.to_nat μ - Pos.to_nat f)%nat.

  Definition mk_log (α: Annotations.annotation) (nm: num) (ptr: BlockSet.t) (me: mexpr cell) : Annotations.annotation :=
    (fst α,
     match int_pair_of_nvi (itv_of_me nm me) with
     | All => nil
     | Just rng =>
       List.map
         (λ b,
          match b with
          | ABlocal f x => Annotations.ABlocal (depth f) x rng
          | ABglobal b => Annotations.ABglobal match Genv.invert_symbol ge b with Some g => g | None => xH end rng
          end
         )
         (BlockSet.elements ptr)
     end).

  Definition am_log (a: annotation_log) : nconvert_t annotation_log unit :=
    (tt :: nil, (nil, a :: nil)).

  Definition deref_pexpr (α: option _) (tn: tnum) (κ: typed_memory_chunk) (lpe: list (pexpr cell)) : alarm_mon annotation_log (CellSet.t+⊤) :=
    let al := align_chunk (proj1_sig κ) in
    do cells <- ((
    do pe <- (ret lpe : nconvert_t _ (pexpr cell));
    do _ <- assert _ (has_type_VP pe)
       (λ _, "deref_pexpr: maybe not a pointer (" ++ to_string pe ++ ")");
    let aptr := pt_eval_pexpr ge μ (fst tn) pe in
    do me <- me_of_pe tn pe;
    do _ <- assert _ (check_align (snd tn) al me)
       (λ _, "bad align (" ++ to_string (proj1_sig κ) ++ "): " ++ to_string me);
    let offs := concretize_int max_concretize me (snd tn) in
    match aptr with
    | Just (TyPtr (Just ptr) | TyZPtr (Just ptr)) =>
      do _ <- match α with None => ret tt | Some α' => am_log (mk_log α' (snd tn) ptr me) end;
      ret (set_product ptr κ offs)
    | _ => ret All
    end) : alarm_mon _ (list (CellSet.t+⊤)));
    ret (join_cellsets cells).

  Lemma deref_pexpr_inv α tn κ lpe cells :
    am_get (deref_pexpr α tn κ lpe) = Some cells
    let al := align_chunk (proj1_sig κ) in
    ∀ pe, In pe lpe
          pexpr_get_ty pe = VP
          ∃ lme,
            am_get (me_of_pe tn pe) = Some lme
            ∀ me, In me lme
            check_align (snd tn) al me = true
            let ofs := concretize_int max_concretize me (snd tn) in
            match cells with All => True | Just cells =>
            ofsNotBot All
            ∃ ptr,
            (pt_eval_pexpr ge μ (fst tn) pe = Just (TyPtr (Just ptr)) ∨
             pt_eval_pexpr ge μ (fst tn) pe = Just (TyZPtr (Just ptr))) ∧
            ∀ b i,
              BlockSet.mem b ptr
              i ∈ γ(ofs) →
              CellSet.mem (cell_from b (Int.unsigned i, κ)) cells
            end.
Proof.
    unfold deref_pexpr.
    set (al := align_chunk (proj1_sig κ)).
    intros H. apply am_get_bind in H. destruct H as (lme & H & Hcells).
    rewrite am_get_ret in Hcells; apply some_eq_inv in Hcells; subst cells.
    simpl in H.
    apply flat_map_a_correct in H.
    destruct H as (lpe' & Hlpe' & H).
    apply some_eq_inv in Hlpe'. subst lpe'.
    apply (flat_map_a_spec_assert) in H.
    destruct H as (Hty & H).
    intros pe Hpe. split.
    generalize (Hty _ Hpe). clear.
    unfold has_type_VP. case pexpr_get_ty; auto; intro; eq_some_inv.
    generalize (fma_in' _ _ _ _ H _ Hpe). clear H.
    intros (cells & Hcells & Hcells_lme).
    unfold bind in Hcells. simpl in Hcells.
    apply flat_map_a_correct in Hcells.
    destruct Hcells as (lme1 & Hlme1 & Hcells).
    exists lme1. split. exact Hlme1.
    intros me Hme.
    generalize (fma_in' _ _ _ _ Hcells _ Hme). clear Hcells.
    intros (cells' & Hcells & Hcc).
    apply flat_map_a_correct in Hcells.
    destruct Hcells as (ttt & Httt & Hcells).
    destruct (check_align _ _ _); simpl in Httt; eq_some_inv; subst.
    split. reflexivity.
    apply flat_map_a_spec_cons_inv in Hcells.
    destruct Hcells as (cells'' & nil' & Hcells & -> & Hnil).
    apply flat_map_a_spec_nil_inv in Hnil. subst nil'.
    generalize (join_cellsets_correct lme).
    destruct join_cellsets. easy.
    intros (Hall & Hj & Hj').
    destruct (pt_eval_pexpr _ _ _ _) as [ | [ | | | | | [ | ptr ] | [ | ptr ] | ] ] eqn: Hpt;
      try (
    apply flat_map_a_correct in Hcells;
    destruct Hcells as (ttl & Hlog & Hcells);
    assert (ttl = tt :: nil) by (destruct α; simpl in *; eq_some_inv; eauto); subst ttl; clear Hlog;
    apply flat_map_a_spec_cons_inv in Hcells;
    destruct Hcells as (cells''' & nil' & Hcells & -> & Hnil);
    apply flat_map_a_spec_nil_inv in Hnil; subst nil');
    simpl in Hcells; eq_some_inv; subst;
    specialize (Hcc _ (or_introl _ eq_refl));
    specialize (Hcells_lme _ Hcc);
    specialize (Hall _ Hcells_lme);
    try (hsplit; discriminate);
    destruct Hall as (s' & Hs');
    (split; [
       destruct concretize_int; [ easy | ];
       intros X; inv X;
       simpl in Hs'; discriminate
     |
     exists ptr; split; [ vauto | ];
     intros b i H H0;
     generalize (set_product_correct ptr κ (concretize_int max_concretize me (snd tn)) b i H H0);
    rewrite Hs' in *; eauto
    ]).
  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 annotation_log (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 annotation_log A) : convert_t A :=
    λ tn, fmapx, (tn, x)) m.

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

  Definition typ_of_type (ty: type) : typ :=
    match ty with
    | TyFloat => VF
    | TySingle => VS
    | TyLong => VL
    | TyZero | TyInt => VI
    | TyPtr _ => VP
    | TyZPtr _ | TyIntPtr => VIP
    end.

  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 (typ_of_type 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.

  Definition typ_eval_constant (cst: constant) : typ :=
    match cst with
      | Ointconst n => VI
      | Ofloatconst n => VF
      | Osingleconst n => VS
      | Olongconst n => VL
      | Oaddrsymbol s ofs => VP
    end.

  Definition typ_eval_binop (op: binary_operation) (arg1 arg2: typ): typ :=
    match op with
    | Oadd =>
      match arg1, arg2 with
      | VP, VI
      | VI, VP => VP
      | VI, VI => VI
      | _, _ => VIP
      end
    | Osub =>
      match arg1, arg2 with
      | VP, VI => VP
      | VP, VP
      | VI, VI => VI
      | _, _ => VIP
      end
    | Omul | Odiv | Odivu | Omod | Omodu | Oand | Oor | Oxor | Oshl
    | Oshr | Oshru | Ocmp _ | Ocmpu _ | Ocmpf _ | Ocmpfs _ | Ocmpl _ | Ocmplu _ => VI
    | Oaddf | Osubf | Omulf | Odivf => VF
    | Oaddfs | Osubfs | Omulfs | Odivfs => VS
    | Oaddl | Osubl | Omull | Odivl | Odivlu | Omodl | Omodlu | Oandl
    | Oorl | Oxorl | Oshll | Oshrl | Oshrlu => VL
    end.

  Remark eval_constant_wt {c} :
    type_of_constant c (typ_eval_constant c).
Proof.
case c; intros; exact eq_refl. Qed.

  Remark eval_binop_wt {op ty ty'} :
    type_of_binop op (typ_eval_binop op ty ty').
Proof.
    destruct op; try reflexivity;
    destruct ty; try exact I; destruct ty'; exact I.
  Qed.

  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 _ (Oaddrsymbol i Int.zero) VP eq_refl :: nil)
           | None => do _ <- alarm (err "bad global variable");
                     ret nil
           end
    | Econst cst =>
      let cst := constant_of_constant cst in
      ret (PEconst _ cst (typ_eval_constant cst) eval_constant_wt :: nil)
    | Eunop op e1 =>
      do e1' <- convert e1;
      ret (rev_mape, PEunop op e (t := type_of_unop op) eq_refl) e1')
    | Ebinop op e1 e2 =>
      do e1' <- convert e1;
      do e2' <- convert e2;
      ret (map2e1 e2,
                 PEbinop op e1 e2
                         (typ_eval_binop op (pexpr_get_ty e1) (pexpr_get_ty e2))
                         eval_binop_wt
                ) 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 tn <- get_tnum;
      do cells <- match κ with Some κ => lift (deref_pexpr (Some α) tn κ 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' annotation_logc, match ACTreeDom.get c tp with
                    | All => Error None (err ("bad cell: " ++ to_string c))
                    | Just ty => Result (PEvar c (typ_of_type ty))
                    end)
              cells
          )
             tn
      end
    end.

  Definition me_of_pe_list (tn: tnum) (lpe: list (pexpr cell)) : alarm_mon annotation_log (list (mexpr cell)) :=
    flat_map_a annotation_log (me_of_pe tn) (ret lpe).

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

  Definition deref_expr_err (tn: tnum) (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,
      lift (
      do cells <- deref_pexpr α tn κ lpe;
      match cells with
      | All => do _ <- alarm_am (deref_expr_err tn 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).
    Hypothesis E: ∀ i, is_local i = truee ! iNone.
    Hypothesis INV : invariant ((tmp, e) :: stk, m).
    Hypothesis SZ: msizes_gamma ge ((tmp, e) :: stk) sz.

    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 (tp: points_to) (nm: num) (ρn: cellmach_num) (NM: ρn ∈ γ(nm)) pe lme :
      am_get (me_of_pe (tp, nm) pe) = Some lme
      am_get (N.nconvert annotation_log ge μ _ _x, x) _ _ NumDom sz nm tp pe) = Some lme
      ∀ me, In me lme → ¬ error_mexpr ρn me.
Proof.
      unfold me_of_pe.
      intros H. unfold bind in H. simpl in H.
      apply flat_map_a_correct in H.
      destruct H as (lme' & Hlme' & H).
      assert (lme' = lme).
      { clear -H. elim H; clear H. easy.
        intros a bs H q r Hqr ->.
        apply flat_map_a_correct in H.
        destruct H as (q' & H & H').
        destruct (noerror _ _); simpl in H; eq_some_inv; subst.
        apply flat_map_a_spec_cons_inv in H'.
        destruct H' as (? & ? & H' & -> & H'').
        apply flat_map_a_spec_nil_inv in H''. subst.
        simpl in H'. eq_some_inv. subst. reflexivity.
      }
      subst lme'.
      split. exact Hlme'.
      intros me Hme.
      assert (noerror me nm = true) as NB.
      generalize (fma_in _ _ _ _ H _ Hme). clear.
      intros (me' & Hme' & r & Hr & Hme).
      apply flat_map_a_correct in Hr. destruct Hr as (q' & Hq' & Hr).
      simpl in Hr.
      destruct (noerror me' _) eqn: X; simpl in Hq'; eq_some_inv; subst.
      apply flat_map_a_spec_cons_inv in Hr.
      destruct Hr as (? & ? & ? & -> & Hr).
      simpl in *. eq_some_inv. subst.
      apply flat_map_a_spec_nil_inv in Hr. subst.
      destruct Hme as [ <- | () ].
      exact X.
      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_correct nm lpe lme log :
      me_of_pe_list nm lpe = (lme, (nil, log)) →
      flat_map_a_spec _ (me_of_pe nm) lpe lme.
Proof.
      intros Hlme.
      generalize (@flat_map_a_correct _ _ _ (me_of_pe nm) (ret lpe) lme (f_equal _ Hlme)).
      intros (lpe' & Hlpe' & H).
      simpl in Hlpe'. apply some_eq_inv in Hlpe'. subst lpe'.
      exact H.
    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 typ_of_type_gamma G ty :
      type_gamma G tygamma_typ (typ_of_type ty).
Proof.
      intros x TY.
      apply type_gamma_inv in TY.
      destruct ty; hsplit;
      repeat match goal with H : __ |- _ => destruct H; hsplit | H : x = _ |- _ => rewrite H end; exact I.
    Qed.

    Lemma convert_wf (q: expr):
      ∀ (tn: tnum) (TN: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn),
      match am_get (convert q tn) with
      | Some (tn', lp) =>
        tnum_mono ((tmp, e) :: stk) 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.
      revert TY.
      apply typ_of_type_gamma.
    - 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. easy. intros; exact I.
      intros ((tp', nm'), lp) log 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. easy. intros; exact I.
      intros ((tp1, nm1), lp1) log1 LP1.
      specialize (IHq1 (tp, nm) TN). rewrite LP1 in IHq1.
      destruct IHq1 as ( MONO1 & IHq1 ).
      apply am_bind_case. easy. intros; exact I.
      intros ((tp2, nm2), lp2) log2 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. easy. intros; exact I.
      intros ((tp1, nm1), lp) log1 LP.
      apply am_bind_case. easy. intros; exact I.
      intros ((tp2, nm2), κ') log2 K.
      assert (Hκ: log2 = niltp2 = tp1nm2 = nm1 ∧ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; subs; eauto).
      destruct Hκ as (-> & -> & -> & [Hκ ->]). clear K.
      apply am_bind_case. easy. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case.
      intros x y l l' H l0 H0. eq_some_inv. subs. specialize (H _ eq_refl). auto.
      intros; eq_some_inv.
      intros x log Hcells log' Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv (Some a) (tp1, nm1) (exist _ κ Hκ) lp).
      rewrite Hcells.
      intros K.
      specialize (IHq (tp, nm) TN). rewrite LP in IHq.
      destruct IHq as ( MONO1 & IHq ).
      assert (ρ ∈ tnum_gamma ((tmp, e) :: stk) (tp1, nm1)) as TN1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound ((tmp, e) :: stk) cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun TN1).
      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. apply typ_of_type_gamma.
    Qed.

    Lemma convert_free_def (q: expr) :
      ∀ (tn: tnum) (TN: ρ ∈ tnum_gamma ((tmp, e) :: stk) 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.
      apply type_gamma_inv' in H. 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. easy. intros; exact I.
      intros ((tp1, nm1), lp) log 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. easy. intros; exact I.
      intros ((tp1, nm1), lp1) log1 LP1.
      apply am_bind_case. easy. intros; exact I.
      intros ((tp2, nm2), lp2) log2 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. easy. intros; exact I.
      intros ((tp1, nm1), lp) log LP.
      generalize (convert_wf q _ TN). rewrite LP. intros [MONO1 _].
      apply am_bind_case. easy. intros; exact I.
      intros ((tp2, nm2), κ') log2 K. assert (Hκ: log2 = niltp2 = tp1nm2 = nm1 ∧ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as (-> & -> & -> & [Hκ ->]). clear K.
      apply am_bind_case. easy. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case.
      intros x y l l' H ? ?; eq_some_inv; subs. specialize (H _ eq_refl). auto.
      intros; eq_some_inv.
      intros x log'' Hcells log' Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv (Some a) (tp1, nm1) (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros K.
      assert (ρ ∈ tnum_gamma ((tmp, e) :: stk) (tp1, nm1)) as TN1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound ((tmp, e) :: stk) cells (tp1, nm1)).
      destruct (optimistic_realization _ _) as [ | (tp', nm') ]; simpl;
      intros Hreal; elim (Hreal ρ Hpun TN1).
      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.
      now intros H; apply type_gamma_inv' in H.
    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 type_of_unop_correct op arg v :
    Cminor.eval_unop op arg = Some v
    vVundef
    γ (type_of_unop op) v.
Proof.
    clear.
    destruct op; destruct arg; simpl; intros Hv U;
    try (destruct (Float.to_int f));
    try (destruct (Float.to_intu f));
    try (destruct (Float.to_long f));
    try (destruct (Float.to_longu f));
    try (destruct (Float32.to_int f));
    try (destruct (Float32.to_intu f));
    try (destruct (Float32.to_long f));
    try (destruct (Float32.to_longu f));
    simpl in Hv;
    eq_some_inv; subst;
    try exact I;
    congruence.
  Qed.

  Lemma typ_eval_binop_correct op arg1 arg2 p t1 t2 v :
    Cminor.eval_binop op arg1 arg2 p = Some v
    vVundef
    γ t1 arg1
    γ t2 arg2
    γ (typ_eval_binop op t1 t2) v.
Proof.
    clear.
    assert (∀ b, Val.of_optbool bVundefgamma_typ VI (Val.of_optbool b)) as Hob.
    { intros [ () | ]; exact_, I) || exactK, K eq_refl). }
    assert (∀ v w, option_map Val.of_bool v = (Some w) → gamma_typ VI w) as Hob'.
    { intros [ () | ] w H; apply option_map_some in H; hsplit; eq_some_inv; subst; vauto. }
    intros EV Hv Ht1 Ht2.
    destruct op; simpl in *;
    eq_some_inv; subst;
    try exact (Hob _ Hv);
    try exact (Hob' _ _ EV);
    destruct arg1; try (specialize (Hv eq_refl); exact Hv || elim Hv);
    apply gamma_typ_inv' in Ht1; try subst t1; try easy;
    destruct arg2; try (specialize (Hv eq_refl); exact Hv || elim Hv);
    apply gamma_typ_inv' in Ht2; try subst t2; try easy;
    try exact I;
    simpl in *;
    try (
    repeat (destruct Ht1 as [ Ht1 | Ht1 ]); subst t1; try exact I;
    repeat (destruct Ht2 as [ Ht2 | Ht2 ]); subst t2; try exact I
      );
    try (revert EV); try (bif; intros; eq_some_inv; subst; vauto)
    .
  Qed.

    Lemma convert_correct (q: expr) :
      ∀ (tn: tnum) (TN: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn),
      match am_get (convert q tn) with
      | Some (tn', lp) =>
        ∀ v, eval_expr ge e tmp m (expr_erase 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.
      eexists. split. left. reflexivity.
      econstructor; eauto.
      right. revert TP. apply typ_of_type_gamma.
    - 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. easy. intros; exact I.
      intros ((tp', nm'), lp) log 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 (t := type_of_unop u) eq_refl). 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 type_of_unop_correct; eassumption.

    - unfold bind. simpl.
      apply am_bind_case. easy. intros; exact I. intros ((tp1, nm1), lp1) log1 Hq1.
      apply am_bind_case. easy. intros; exact I. intros ((tp2, nm2), lp2) log2 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 typ_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. easy. intros; exact I.
      intros ((tp1, nm1), lp) log1 Hlp. specialize (IHq _ TN). rewrite Hlp in IHq.
      generalize (convert_wf q (tp, nm) TN). rewrite Hlp. intros [MONO1 _].
      apply am_bind_case. easy. intros; exact I.
      intros ((tp2, nm2), κ') log2 K. assert (Hκ: log2 = nil /\ tp2 = tp1 /\ nm2 = nm1 /\ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as ( -> & -> & -> & [Hκ ->]). clear K.
      apply am_bind_case. easy. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case.
      intros x y l l' H ? ?; eq_some_inv; subs; specialize (H _ eq_refl); auto.
      intros; eq_some_inv.
      intros x log Hcells log' Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv (Some a) (tp1, nm1) (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros Q.
      assert (ρ ∈ tnum_gamma ((tmp, e) :: stk) (tp1, nm1)) as Hρ1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound ((tmp, e) :: stk) 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 _ H0).
      destruct IHq as (a' & A & B).
      specialize (Q _ eq_refl _ A).
      destruct Q as (Hty & lme & Hlme & Q).
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      generalize (me_of_pe_correct _ _ ρn NM _ Hlme).
      intros (NC & NB).
      destructK, N.nconvert_correct _ ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz nm1 ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ B _ K _ NC) as (me & Hme & n & Z & EV & ?).
      apply Hρ1.
      discriminate.
      simpl in Z. subst n.
      specialize (Q me Hme).
      destruct Q as (CA & CI & CS).
      destruct CS as (ptr & Hptr & CS).
      assert (∃ ab, BlockSet.mem ab ptrblock_of_ablock ge ((tmp, e) :: stk) ab = Some b) as K.
      generalize (pt_eval_pexpr_correct Hstk (proj1 Hρ1) B I).
      simpl in Hptr.
      destruct Hptr as [ Hptr | Hptr ];
      rewrite Hptr; exact id.
      clear Hptr.
      destruct K as (ab & IN & BA).
      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 (typ_of_type 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. revert TP'. apply typ_of_type_gamma.
    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: ρ ∈ tnum_gamma ((tmp, e) :: stk) 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 (expr_erase 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. easy. intros; exact I.
      intros ((tp', nm'), lp) log 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. easy. intros; exact I.
      intros ((tp1, nm1), lp1) log1 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. easy. intros; exact I.
      intros ((tp2, nm2), lp2) log2 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. easy. intros; exact I.
      intros ((tp1, nm1), lp) log1 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. easy. intros; exact I.
      intros ((tp2, nm2), κ') log2 K. assert (Hκ: log2 = nil /\ tp2 = tp1 /\ nm2 = nm1 /\ ∃ Hκ, κ' = Some (exist _ κ Hκ)) by (destruct κ; eq_some_inv; eauto).
      destruct Hκ as (-> & -> & -> & [Hκ ->]). clear K.
      apply am_bind_case. easy. intros; exact I.
      intros (nm3, [|cells]). intros; exact I.
      unfold lift, fmap.
      apply am_bind_case.
      intros x y l l' H ? ?; eq_some_inv; subs; specialize (H _ eq_refl); auto.
      intros; eq_some_inv.
      intros x log Hcells log' Q. simpl in Q. eq_some_inv; subs.
      generalize (deref_pexpr_inv (Some a) (tp1, nm1) (exist _ κ Hκ) lp).
      rewrite Hcells. clear Hcells.
      intros Q.
      simpl.
      assert (ρ ∈ tnum_gamma ((tmp, e) :: stk) (tp1, nm1)) as Hρ1.
      apply (tnum_m_stronger MONO1); auto.
      unfold realize.
      generalize (@optimistic_realization_sound ((tmp, e) :: stk) 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 _ eq_refl _ PE). destruct Q as (Q & lme & Hlme & LME).
        generalize (me_of_pe_correct tp1 nm1 ρn NM pe Hlme). intros (NC & MEB).
        applyP, N.nconvert_def _ ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ (proj1 Hρ1) _ P _ 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 _ eq_refl pe PE). destruct Q as (TY & lme & Hlme & Q).
      generalize (me_of_pe_correct _ _ _ NM pe Hlme). 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 _ (proj1 Hρ1) _ P _ ME MEB) EV).
      intros Z. specialize (Z (WF _ PE)).
      generalize (pexpr_get_ty_ok EV Z).
      rewrite TY. intros TY'. apply gamma_typ_inv in TY'. hsplit. subst v'.
      clear Z TY.
      destruct (N.nconvert_correct _ _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV _ (proj1 Hρ1) _ ME) as (me & Hme & n & Z & EV' & ?).
      discriminate.
      simpl in Z. subst n.
      specialize (Q _ Hme).
      destruct Q as (CA & CI & CS).
      destruct CS as (ptr & Hptr & OFS).
      assert (∃ bs, BlockSet.mem bs ptrblock_of_ablock ge ((tmp, e) :: stk) bs = Some b) as K.
      generalize (pt_eval_pexpr_correct Hstk (proj1 Hρ1) EV I).
      simpl in Hptr.
      destruct Hptr as [ Hptr | Hptr ];
      rewrite Hptr; exact id.
      clear Hptr.
      destruct K 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: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn) κ q :
      match am_get (deref_expr α κ q tn) with
      | Some (tn', cells) =>
        tnum_mono ((tmp, e) :: stk) tn tn' ∧
        ¬ error_expr ge e tmp m (expr_erase q) ∧
        ∀ v, eval_expr ge e tmp m (expr_erase 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. easy. intros; exact I.
      intros ((tp1, nm1), lpe) log1 LPE.
      unfold lift, fmap. simpl.
      apply am_bind_case. easy. intros; exact I.
      intros cells'. simpl.
      apply am_bind_case. intros; eq_some_inv; subs; eauto. intros; eq_some_inv.
      intros [|cells]. intros; eq_some_inv.
      intros log Hcells log' X; eq_some_inv; subst cells'.
      generalize (deref_pexpr_inv α (tp1, 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.
      specialize (Q _ eq_refl _ PE). destruct Q as (TY & lme & Hlme & Q).
      generalize (me_of_pe_correct _ _ _ NM pe Hlme). intros (ME & MEB).
      generalizeP, N.nconvert_def _ ge m _ _ _ _ Hstk _ _x, x) _ _ NumDom sz SZ' _ ρ ρn Nc (@invariant_block_of_ablock_inj _ _ INV) NM _ (proj1 (tnum_m_stronger MONO1 (conj Hpun TN))) _ P _ ME MEB).
      intros Y. specialize (Y (WF _ PE)).
      exact Y.
    - intros v EV.
      specialize (CC _ EV).
      destruct CC as (pe & PE & EV').
      specialize (Q _ eq_refl pe PE). destruct Q as (TY & lme & Hlme & Q).
      generalize (me_of_pe_correct tp1 nm1 _ NM pe Hlme). intros (ME & MEB).
      set (PT := proj1 (tnum_m_stronger MONO1 (conj Hpun TN))).
      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 MEB) EV').
      intros Y. specialize (Y (WF _ PE)).
      generalize (pexpr_get_ty_ok EV' Y).
      rewrite TY. clear Y TY. intros H.
      apply gamma_typ_inv in H. hsplit. subst v.
      destruct (N.nconvert_correct _ _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV' _ PT _ ME) as (me & Hme & n & Z & EVn & ?).
      discriminate.
      specialize (Q _ Hme).
      destruct Q as (CA & ? & ptr & Hptr & OFS).
      simpl in Z. subst n.
      assert (∃ bs, BlockSet.mem bs ptrblock_of_ablock ge ((tmp, e) :: stk) bs = Some b) as K.
      generalize (pt_eval_pexpr_correct Hstk PT EV' I).
      simpl in Hptr.
      destruct Hptr as [ Hptr | Hptr ];
      rewrite Hptr; exact id.
      clear Hptr.
      destruct K as (ab & AB & Hab).
      eexists ab, b, i.
      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: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn) (q: expr) :
      match am_get (nconvert q tn) with
      | Some (tn', _) =>
        tnum_mono ((tmp, e) :: stk) tn tn' ∧
        ¬ error_expr ge e tmp m (expr_erase q)
      | None => True
      end.
Proof.
      unfold nconvert. unfold bind. simpl. apply am_bind_case. easy. intros; exact I.
      intros ((tp1, nm1), lpe) log1 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. easy. intros; exact I.
      intros lme log2 Hlme.
      destruct (proj2 (tnum_m_stronger MONO1 (conj Hpun TN))) asn & Nc & NM).
      simpl. split. exact MONO1.
      apply H. clear H.
      intros pe PE.
      apply me_of_pe_list_correct in Hlme.
      generalize (fma_in' _ _ _ _ Hlme _ PE).
      intros (me & ME & X).
      generalize (me_of_pe_correct _ _ _ NM _ ME).
      intros [ME' NBE].
      refine (N.nconvert_def _ _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ SZ' _ _ _ Nc
                            (@invariant_block_of_ablock_inj _ _ INV)
                            NM
                            _ (proj1 (tnum_m_stronger MONO1 (conj Hpun TN)))
                            _
                            (WF _ PE)
                            _
                            ME'
                            NBE).
    Qed.

    Lemma nconvert_ex (tn: tnum) (TN: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn) (q: expr) :
      match am_get (nconvert q tn) with
      | Some ((tp', nm'), lme) =>
        tnum_mono ((tmp, e) :: stk) tn (tp', nm') ∧
        ∃ lpe, am_get (convert q tn) = Some ((tp', nm'), lpe) ∧
        ∀ v, eval_expr ge e tmp m (expr_erase q) v
             vVundef
        ∀ (TP: ρ ∈ points_to_gamma ge ((tmp, e) :: stk) tp') (ρn: cellmach_num) (Nc: compat ρ ρn) (NM: ρn ∈ γ nm'),
             ∃ pe lme' me n,
               In pe lpe
               eval_pexpr ge m ρ e pe v
               am_get (N.nconvert annotation_log ge μ _ _x, x) _ _ NumDom sz nm' tp' pe) = Some lme' ∧
               (∀ me', In me' lme' → In me' lme) ∧
               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. easy. intros; exact I.
      intros ((tp', nm'), lpe) log 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').
      destruct (me_of_pe_list _ _) as (nle & ([|],?)) eqn: LNE. 2: exact I.
      split. exact MONO.
      apply me_of_pe_list_correct in LNE.
      exists lpe. split. reflexivity.
      intros v EV Def.
      intros TP ρn Nc NM.
      destruct (H v EV) as (pe & IN & EV'); clear H.
      generalize (fma_in' _ _ _ _ LNE _ IN).
      intros (lme & LME & NLE).
      generalize (me_of_pe_correct _ _ _ NM _ LME).
      intros [ME NB].
      destruct (N.nconvert_correct _ _ _ _ _ _ _ Hstk _ _ _ _ _ _ _ _ _ _ Nc (@invariant_block_of_ablock_inj _ _ INV) _ _ EV' _ TP _ ME Def) as (me & Hme & n & N).
      exists pe, lme, me, n. intuition.
    Qed.

    Lemma convert_to_me (tn: tnum) (TN: ρ ∈ tnum_gamma ((tmp, e) :: stk) tn) (q: expr) :
      match am_get (convert q tn) with
      | Some ((tp', nm'), lpe) =>
        match am_get (me_of_pe_list (tp', nm') lpe) with
        | Some lme =>
          tnum_mono ((tmp, e) :: stk) tn (tp', nm') ∧
          ∃ v, eval_expr ge e tmp m (expr_erase q) vvVundef
          ∧ ∀ (TP: ρ ∈ points_to_gamma ge ((tmp, e) :: stk) tp') (ρn: cellmach_num) (Nc: compat ρ ρn) (NM: ρn ∈ γ nm'),
            ∃ pe lme' me n,
                 In pe lpe
                 eval_pexpr ge m ρ e pe v
                 am_get (N.nconvert annotation_log ge μ _ _x, x) _ _ NumDom sz nm' tp' pe) = Some lme' ∧
                 (∀ me', In me' lme' → In me' lme) ∧
                 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 & lme' & me & n & Hn & Hpn & Hvn & Htn & Vn).
      exists pe, lme', me, n. intuition.
    Qed.

  End CONVERT_CORRECT.

  End CONVERT.

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

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

  Arguments ab_stk ab /.
  Arguments ab_sz 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_nm_iter (ab: iter_t) : tnum_iter := snd (snd ab).
  Definition ab_tp_iter (ab: iter_t) : points_to := fst (snd (snd ab)).
  Definition ab_num_iter (ab: iter_t) : num_iter := snd (snd (snd ab)).

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

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

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

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

  Definition with_tnum (f: tnumtnum) (ab: t) : t :=
    (ab_stk ab, (ab_sz 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 ∈ (tnum_gamma (fst cs) (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 ∈ (tnum_iter_gamma (fst cs) (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 * tnum) :=
    match ab_stk ab with
    | All | Just nil => None
    | Just ((ε, τ) :: σ) =>
      let '(sz, tn) := (snd ab) in
      Some (plength σ, ε, τ, σ, sz, tn)
    end.

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

  Definition ab_eval_expr (ab: t) (a: expr)
  : alarm_mon _ ((tnum * type+⊤ * list (mexpr cell)) +⊥) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, tn) =>
      do (tn', lp) <- convert μ ε sz a tn;
      let '(tp', nm') := tn' in
      do ln <- me_of_pe_list μ sz tn' lp;
      match lp with nil => ret Bot | _ =>
      let ty := union_list_map (pt_eval_pexpr ge μ tp') All lp in
      ret (NotBot (tn', 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 (type+⊤ * 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, ty, me) =>
        do m <- ab_eval_exprlist (with_tnum_, tn) ab) e';
        match m with
        | Bot => ret Bot
        | NotBot (ab, y) => ret (NotBot (ab, (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 tp_upd_cells (tp: points_to) (l: list cell) (tt: type+⊤) : points_to :=
    union_list_mapc, ct_upd tp c tt) tp l.


  Definition chunk_type (κ: typed_memory_chunk) (ty: typ+⊤) : 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.

  Local Instance string_of_type : ToString type := string_of_type ge.

  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), ty, ln) =>
        do _ <- am_assert match κ with Some κ => chunk_type κ (fmap typ_of_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) 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.

  Let type_of_typ (ty: typ) : type+⊤ :=
    match ty with
    | VI => Just TyInt
    | VF => Just TyFloat
    | VS => Just TySingle
    | VL => Just TyLong
    | _ => All
    end.

  Definition assign_any (x: ident) (ty: typ) (ab: t) : alarm_mon annotation_log (t+⊥) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, 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_tptp, ACTreeDom.set tp c (type_of_typ 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 annotation_log 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 (α: Annotations.annotation) (κ: 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, 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 (Some α) κ 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, tn) =>
      let b := bool_expr b in
      do (tn', b') <- nconvert μ ε sz b tn;
      let '(tp, nm) := tn' in
      ret
        (do nz <- nassume true nm id b'; ret (AbMem (Just ((ε,τ)::σ)) sz tp nz),
         do z <- nassume false nm id b'; ret (AbMem (Just ((ε,τ)::σ)) sz tp z))
    | None => do _ <- alarm_am "assume failed"; ret (Bot, Bot)
    end.

  Definition deref_fun (e: expr) (ab: t) : alarm_mon annotation_log (list (ident * fundef)) :=
    match split_t ab with
    | Some (μ, ε, τ, σ, sz, tn) =>
      do (tn, cells) <- deref_expr μ ε sz None (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 (type+⊤ * list (mexpr cell)))
             : alarm_mon annotation_log (t+⊥) → alarm_mon _ (t+⊥) :=
    fold_left2
      (λ acc id v,
       do ab' <- acc;
       ret (
           do ab' <- ab';
           let c := ACtemp μ id in
           let '(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_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.

Invalidate pointers to cells local to μ.
  Definition pop_local_pt (μ: fname) : points_topoints_to :=
    ACTreeDom.SP.filter
      (λ c ty,
       if is_local_cell μ c
       then false
       else match ty with
            | (TyPtr (Just bs) | TyZPtr (Just bs)) =>
              negb (BSO.existsb (is_local_block μ) bs)
            | TyPtr All | TyZPtr All
            | TyFloat | TySingle | TyLong | TyInt | TyZero | TyIntPtr => true
            end).

  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_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, _) =>
        do _ <- am_assert match ty with Just (TyInt | TyZero) => true | _ => false end "main ret not an int";
          ret (NotBot (with_sz (clear_local_sizes μ ε)
                           (with_tp (pop_local_pt μ)
                           (with_stk_, Just nil) ab))))
      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_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_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) : alarm_mon annotation_log _ :=
    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 zero_type_of_chunk (κ: typed_memory_chunk) : type :=
    match κ with
    | exist (Mint8signed | Mint8unsigned |
             Mint16signed | Mint16unsigned |
             Mint32) _
      => TyZero
    | exist Mint64 _ => TyLong
    | exist Mfloat64 _ => TyFloat
    | exist Mfloat32 _ => TySingle
    | 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 (zero_type_of_chunk κ))
                (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) (type_of_int n))
                      (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) TyLong)
                      (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) (TyPtr (Just (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) TyFloat)
                 (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) TySingle)
                 (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) (type_of_int i))
                     (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) (type_of_int i))
                     (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 (TyPtr (Just ptr)), 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_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, lne) =>
      do _ <- am_assert
            match ptr with
            | Just ty =>
              match ty, κ with
              | TyLong, Mint64
              | TySingle, Mfloat32
              | TyFloat, Mfloat64
              | (TyInt | TyZero), ( Mint32 | Mint8signed | Mint8unsigned | Mint16signed | Mint16unsigned )
                => true
              | (TyPtr bs | TyZPtr bs), Mint32 => only_global_pointer bs
              | _, _ => false
              end
            | All => false
            end
            ("vstore: cannot prove that the argument " ++ to_string arg ++ " is well-typed " ++ to_string ptr);
      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, _))) =>
             (NotBot (STK, (SZ, (PT, NUM_it_ret))),
              do NUM_ret <- NUM_ret;
              ret (STK, (SZ, (PT, NUM_ret))))
           end
         | NotBot (STK', (SZ', (PT', _))) =>
           match A with
           | Bot =>
             (NotBot (STK', (SZ', (PT', NUM_it_ret))),
              do NUM_ret <- NUM_ret;
              ret (STK', (SZ', (PT', NUM_ret))))
           | NotBot (STK, (SZ, (PT, 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
             (NotBot (STK_ret, (SZ_ret, (PT_ret, NUM_it_ret))),
              do NUM_ret <- NUM_ret;
              ret (STK_ret, (SZ_ret, (PT_ret, NUM_ret))))
           end
         end;
       init_iter x :=
         do x <- x;
         ret (ab_stk x, (ab_sz x, (ab_tp x, init_iter (NotBot (ab_num x)))))
    |}.

  Instance mem_cshm_dom : mem_dom annotation_log 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
    |}.

  Instance t_join_correct : join_op_correct t (t+⊥) concrete_state.
Proof.
    intros (stk & sz & nm) (stk' & sz' & nm') cc H.
    set (G := gamma_bot (tnum_gamma (fst cc))).
    assert (mem_as_fun ge cc ∈ γ ((nmnm'):tnum+⊥)) as H0.
    { apply (join_correct (join_op_correct := tnum_join_correct (fst cc))).
      destruct H as [ () | () ]; hsplit; auto. }
    unfold join in H0.
    unfold join, join_mem, mem_cshm_dom, WProd.join_prod_bot at 1 2.
    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.
    exact H0.
  Qed.

  Instance t_leb_correct : leb_op_correct t concrete_state.
Proof.
    intros (stk & sz & tpnm) (stk' & sz' & tpnm') LE (σ & m) [ STK INV SZ NUM ].
    unfold WProd.leb_prod, leb in LE. simpl in *.
    unfold leb in LE. simpl in *.
    repeat rewrite andb_true_iff in LE. destruct LE as (LEstk & LEsz & 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.
    eapply (leb_correct (leb_op_correct := tnum_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
             ++ "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
                ++ "types : " ++ to_string (ab_tp_iter x) ++ new_line
                ++ "num : " ++ to_string (ab_num_iter x) ++ new_line
                ++ "}" ++ new_line ).

End num.