Module Mconvert


Require DebugAbMachineEnv NoError PointsTo.
Import
  Utf8
  String
  Coqlib
  AST
  Integers
  Floats
  Values
  Globalenvs
  Memory
  Cminor
  Csharpminorannot
  ToString
  Util
  AssocList
  ShareTree
  AdomLib
  AbMachineEnv
  AlarmMon
  PExpr
  NoError
  Cells
  PointsTo.

Section LOG.

Context (L: Type).

Definition nconvert_t A : Type := alarm_mon L (list A).

Section FMA.
  Context {A B} (f: Anconvert_t B).

  Definition flat_map_a (q: nconvert_t A) : nconvert_t B :=
    do q <- (q: alarm_mon L (list A));
    (fix rec (q: list A) :=
       match q with
       | nil => ret nil
       | a :: a' =>
         do b <- (f a : alarm_mon L (list B));
       do b' <- rec a';
       ret (b ++ b')%list
       end
    ) q.

    Inductive flat_map_a_spec : list Alist BProp :=
    | FMA_nil : flat_map_a_spec nil nil
    | FMA_app
        a bs
        `(am_get (f a) = Some bs)
        q r
        `(flat_map_a_spec q r)
        : flat_map_a_spec (a :: q) (bs ++ r)
    .

    Definition flat_map_a_spec_nil_inv (r: list B) :
      flat_map_a_spec nil r
      r = nil :=
      λ H,
      match H in flat_map_a_spec x r
            return match x return Prop
                   with _ :: _ => True
                   | nil => r = nil
                   end
      with
      | FMA_app _ _ _ _ _ _ => I
      | FMA_nil => eq_refl
      end.

    Definition flat_map_a_spec_cons_inv (a: A) (q: list A) (r: list B) :
      flat_map_a_spec (a :: q) r
      ∃ bs r', am_get (f a) = Some bsr = app bs r' ∧ flat_map_a_spec q r'.
Proof.
      refineH,
              match H in flat_map_a_spec x r
              return match x return Prop
                     with nil => True
                     | a :: q => ∃ bs r', am_get (f a) = Some bsr = app bs r' ∧ flat_map_a_spec q r'
                     end
              with
              | FMA_nil => I
              | FMA_app _ _ H _ _ H' => _
              end).
      eauto.
    Qed.

    Lemma flat_map_a_spec_nil_r (r: list A) :
      flat_map_a_spec r nil
      r = nilrnil ∧ ∀ a, In a ram_get (f a) = Some nil.
Proof.
      remember (@nil B) eqn: Hnil.
      induction 1. vauto.
      right. split. intro; eq_some_inv.
      intros x X. eq_some_inv. hsplit. subst.
      destruct X as [ -> | X ]. easy.
      destruct IHflat_map_a_spec. easy.
      subst. destruct X. hsplit.
      eauto.
    Qed.
    
    Lemma flat_map_a_correct (q: nconvert_t A) (bs: list B) :
      am_get (flat_map_a q) = Some bs
      ∃ q' : list A, am_get q = Some q' ∧ flat_map_a_spec q' bs.
Proof.
      revert bs.
      elim q; clear q.
      intros q ([ | ], ?); elim q; clear q.
      - simpl; intros bs X; eq_some_inv; subst. exists nil. split. easy. left.
      - intros a q IH bs.
        unfold flat_map_a. simpl.
        apply am_bind_case. easy. simpl; intros; eq_some_inv.
        intros ba log Hba.
        apply am_bind_case. easy. simpl; intros; eq_some_inv.
        intros bs' log' Hbs'.
        specialize (IH bs').
        refine (let X := IH _ in _).
        unfold flat_map_a. simpl. rewrite Hbs'. reflexivity.
        simpl. intro; eq_some_inv. subst bs.
        clear IH Hbs'.
        eexists. split. reflexivity.
        hsplit. right. rewrite Hba. reflexivity. simpl in *. eq_some_inv. congruence.
      - simpl; intros; eq_some_inv.
      - intros a q IH bs.
        unfold flat_map_a. apply am_bind_case; [ easy | .. ];
          simpl; intros; eq_some_inv.
    Qed.

      Lemma fma_in q bs :
        flat_map_a_spec q bs
        ∀ b, In b bs → ∃ a, In a q ∧ ∃ r, am_get (f a) = Some rIn b r.
Proof.
        intros H; elim H; clear q bs H.
        intros _ ().
        intros a bs Ha q r H IH b Hb.
        apply in_app in Hb. elim Hb; clear Hb; intros Hb.
        exists a. split. now left. eauto.
        elim (IH _ Hb). intros a' (Ha' & X).
        exists a'. split. now right. exact X.
      Qed.

      Lemma fma_in' q bs :
        flat_map_a_spec q bs
        ∀ a, In a q → ∃ b, am_get (f a) = Some b ∧ ∀ x, In x bIn x bs.
Proof.
        intros H; elim H; clear q bs H.
        intros _ ().
        intros a bs Ha q r H IH b [ <- | Hb ].
        exists bs. split. exact Ha. intros x Hx; apply in_app; left; exact Hx.
        elim (IH _ Hb); clear IH Hb.
        intros x [Hx Hx']. exists x. split. exact Hx.
        intros y Hy; apply in_app; right; apply Hx', Hy.
      Qed.

      Lemma fma_app a b c d :
        flat_map_a_spec a b
        flat_map_a_spec c d
        flat_map_a_spec (a ++ c) (b ++ d).
Proof.
        intros H; elim H; clear a b H. exact id.
        intros a bs Ha q r Hqr IH Hcd.
        simpl. rewrite <- app_assoc. vauto.
      Qed.

End FMA.

Global Arguments flat_map_a_correct {A B} [f q bs] _.

Global
Instance nconvert_monad : monad nconvert_t :=
  {
    ret A := λ a, ret (a :: nil);
    bind A B m f := flat_map_a f m
  }.

Definition assert (b: bool) err :=
  if b then ret tt else ((tt :: nil, (err :: nil, nil)): (nconvert_t unit)).

Lemma fma_bind {A B C} (e: nconvert_t B) (f: ABnconvert_t C) a c :
  flat_map_a_specx, do y <- e; f x y) a c
  anil
  ∃ b : list B,
    am_get e = Some b
    flat_map_a_spec (curry f) (pair_list a b) c.
Proof.
  intros H; elim H; clear H.
  intros Ha; elim (Ha eq_refl).
  intros x cs H q r Hqr IH _.
  generalize (flat_map_a_correct H). clear H.
  intros (q' & Hq' & H).
  eexists. split. eassumption.
  inversion Hqr. subst. simpl. rewrite ! app_nil_r.
  clear -H. induction H; vauto.
  subst. specialize (IHK, nil_cons (eq_sym K))).
  destruct IH as (b & Hb & IH). assert (b = q') by congruence. subst q'.
  simpl. apply fma_app; auto. clear -H.
  induction H; vauto.
Qed.

Lemma flat_map_a_spec_assoc {A B C} (f: Anconvert_t B) (g: Bnconvert_t C) (ma: list A) (mc: list C) :
  flat_map_a_speca, flat_map_a g (f a)) ma mc
  ∃ mb,
    flat_map_a_spec f ma mbflat_map_a_spec g mb mc.
Proof.
  intros H; elim H; clear H.
  vauto2.
  intros a cs Hcs q r Hqr (mb & Hqmb & Hmbr).
  apply flat_map_a_correct in Hcs. destruct Hcs as (q' & Hq' & Hq'cs).
  eexists. split. vauto. now apply fma_app.
Qed.

Lemma flat_map_a_spec_assert {A B} (f: Anconvert_t B) (b: Abool) (e: _) ma mb :
  flat_map_a_speca, do _ <- assert (b a) (e a); f a) ma mb
  (∀ a, In a mab a = true) ∧
  flat_map_a_spec f ma mb.
Proof.
  intros H; elim H; clear H.
  split. intros ? (). vauto.
  intros a bs Hbs q r Hqr [IH IHqr].
  unfold bind in Hbs. simpl in Hbs.
  apply flat_map_a_correct in Hbs.
  destruct Hbs as (ttt & Hba' & Hbs).
  destruct (b a) eqn: Hba; simpl in Hba'; eq_some_inv; subst ttt.
  split. intros a' [ -> | Ha' ]; auto.
  constructor; auto.
  apply flat_map_a_spec_cons_inv in Hbs.
  destruct Hbs as ( bs' & nil' & Hbs' & -> & Hnil' ).
  apply flat_map_a_spec_nil_inv in Hnil'. subst.
  rewrite app_nil_r. exact Hbs'.
Qed.

End LOG.

Local Unset Elimination Schemes.

Local Open Scope string_scope.

Module Nconvert(T:SHARETREE).

Module Import PT := PT T.

  Section nconvert.
    Variable L: Type.
    Variable ge: genv.
    Variable m: Mem.mem.
    Variable μ: ident.
    Variable ε: env.
    Variable tmp: temp_env.
    Variable σ : list (temp_env * env).
    Hypothesis Hμ : μ = plength σ.

    Variable var : Type.
    Variable var_dec : EqDec var.
    Variable elt2p: T.elt -> var.
    Variables num iter_num : Type.
    Variable NumDom : ab_machine_env (var := var) num iter_num.

    Definition convert_constant (cst: constant) : mexpr var :=
      match cst with
        | Ointconst n => MEconst _ (MNint n)
        | Olongconst n => MEconst _ (MNint64 n)
        | Ofloatconst f => MEconst _ (MNfloat f)
        | Osingleconst f => MEconst _ (MNsingle f)
        | Oaddrsymbol _ ofs => MEconst _ (MNint ofs)
      end.

    Inductive nconvert_msg_kind : Set :=
    | MSG_ILL_TYPED_UNOP
    | MSG_ILL_TYPED_BINOP
    | MSG_LARGE_PTSET
    | MSG_UNSAFE_NUM
    | MSG_VALID_PTR
    | MSG_EQ_BLOCKS
    | MSG_CMP_BLK_DK
    | MSG_CMP_OP
    | MSG_NULL
    .

    Definition nconvert_msg_type (k: nconvert_msg_kind) : Type :=
      match k with
      | MSG_LARGE_PTSET => Cminor.binary_operationtype+⊤ → type+⊤ → string
      | MSG_NULL
      | MSG_UNSAFE_NUM
        => mexpr varstring
      | MSG_EQ_BLOCKS
      | MSG_CMP_BLK_DK
        => BlockSet.tBlockSet.tstring
      | MSG_ILL_TYPED_UNOP => Cminor.unary_operationtypstring
      | MSG_ILL_TYPED_BINOP => Cminor.binary_operationtyptypstring
      | MSG_VALID_PTR => boolBlockSet.tmexpr varstring
      | MSG_CMP_OP => comparisonstring
      end.

    Section STRING_OF_MEXPR.

    Import DebugAbMachineEnv DebugCminor.
 
    Local Instance AblockToString : ToString ablock := AblockToString ge.
    Local Instance string_of_type : ToString type := string_of_type ge.
    Existing Instances MN_ToString MNI_ToString.

    Context (nm: num).

    Fixpoint string_of_mexpr (e: mexpr var) {struct e} : string :=
      match e with
      | MEvar ty v => to_string (get_itv (MEvar ty v) nm)
      | MEconst c => to_string c
      | MEunop op e' => string_of_unop op ++ string_of_mexpr e'
      | MEbinop op x y => "(" ++ string_of_mexpr x ++ string_of_binop op ++ string_of_mexpr y ++ ")"
      end.

    Definition nconvert_msg (k: nconvert_msg_kind) : nconvert_msg_type k :=
      match k return nconvert_msg_type k with
      | MSG_ILL_TYPED_UNOP => λ op ty, "ill-typed op: " ++ to_string op ++ to_string ty
      | MSG_ILL_TYPED_BINOP => λ op tyty₂, "ill-typed op: " ++ to_string ty₁ ++ to_string op ++ to_string ty
      | MSG_LARGE_PTSET => λ op tyty₂, "too large points-to set in subtraction or comparison: " ++ to_string ty₁ ++ to_string op ++ to_string ty
      | MSG_UNSAFE_NUM=> λ me, "cannot prove safe a numerical expression: " ++ string_of_mexpr me
      | MSG_VALID_PTR => λ weak bs me, "cannot prove pointer " ++ (if weak then "weakly " else "") ++ "valid: " ++ to_string bs ++ " " ++ string_of_mexpr me
      | MSG_EQ_BLOCKS => λ bsbs₂, "cannot prove blocks equal in subtraction " ++ to_string bs₁ ++ " and " ++ to_string bs
      | MSG_CMP_BLK_DK => λ bsbs₂, "cannot compare points-to sets " ++ to_string bs₁ ++ " and " ++ to_string bs
      | MSG_CMP_OP => λ c, "bad comparison operator: " ++ to_string c
      | MSG_NULL => λ me, "cannot prove definitely null: " ++ string_of_mexpr me
      end.
    Global Opaque nconvert_msg.

    End STRING_OF_MEXPR.

    Definition well_typed_unop (op: unary_operation) (ty: typ) : bool :=
      match op with
      | Ocast8unsigned
      | Ocast8signed
      | Ocast16unsigned
      | Ocast16signed
      | Onegint
      | Onotint
      | Ofloatofint
      | Ofloatofintu
      | Osingleofint
      | Osingleofintu
      | Olongofint
      | Olongofintu
          => eq_dec VI ty
      | Onegf
      | Oabsf
      | Osingleoffloat
      | Ointoffloat
      | Ointuoffloat
      | Olongoffloat
      | Olonguoffloat
          => eq_dec VF ty
      | Onegfs
      | Oabsfs
      | Ofloatofsingle
      | Ointofsingle
      | Ointuofsingle
      | Olongofsingle
      | Olonguofsingle
          => eq_dec VS ty
      | Onegl
      | Onotl
      | Ointoflong
      | Ofloatoflong
      | Ofloatoflongu
      | Osingleoflong
      | Osingleoflongu
          => eq_dec VL ty
      end.

    Inductive nconvert_binop_cases : Type :=
    | NBC_normal
    | NBC_ptr_sub
    | NBC_ptr_ptr_cmp `(comparison)
    | NBC_ptr_int_cmp `(bool) `(comparison)
    | NBC_wrong
    .

    Definition nbc_classify (op: binary_operation) (tyty₂: typ) : nconvert_binop_cases :=
      match ty₁, ty₂, op with
      | VI, (VIP | VP), Oadd | (VIP | VP), VI, Oadd
      | (VIP | VP), VI, Osub
      | VI, VI,
        (Oadd | Osub | Omul | Odiv | Odivu | Omod | Omodu | Oand
         | Oor | Oxor | Oshl | Oshr | Oshru | Ocmp _ | Ocmpu _)
      | VL, VL,
        (Oaddl | Osubl | Omull | Odivl | Odivlu | Omodl | Omodlu
         | Oandl | Oorl | Oxorl | Ocmpl _ | Ocmplu _)
      | VL, VI, (Oshll | Oshrl | Oshrlu)
      | VF, VF, (Oaddf | Osubf | Omulf | Odivf | Ocmpf _)
      | VS, VS, (Oaddfs | Osubfs | Omulfs | Odivfs | Ocmpfs _) =>
        NBC_normal
      | VP, VP, Osub => NBC_ptr_sub
      | VP, VP, Ocmpu c => NBC_ptr_ptr_cmp c
      | VP, VI, Ocmpu c => NBC_ptr_int_cmp false c
      | VI, VP, Ocmpu c => NBC_ptr_int_cmp true c
      | _, _, _ => NBC_wrong
      end.

    Lemma nbc_classify_intro :
      forall P : binary_operation -> typ -> typ -> nconvert_binop_cases -> Prop,

        (forall o x y, P o x y NBC_wrong) ->

        P Oadd VI VIP NBC_normal ->
        P Oadd VI VP NBC_normal ->
        P Oadd VIP VI NBC_normal ->
        P Oadd VP VI NBC_normal ->

        P Osub VIP VI NBC_normal ->
        P Osub VP VI NBC_normal ->

        P Oadd VI VI NBC_normal ->
        P Osub VI VI NBC_normal ->
        P Omul VI VI NBC_normal ->
        P Odiv VI VI NBC_normal ->
        P Odivu VI VI NBC_normal ->
        P Omod VI VI NBC_normal ->
        P Omodu VI VI NBC_normal ->
        P Oand VI VI NBC_normal ->
        P Oor VI VI NBC_normal ->
        P Oxor VI VI NBC_normal ->
        P Oshl VI VI NBC_normal ->
        P Oshr VI VI NBC_normal ->
        P Oshru VI VI NBC_normal ->
        (forall c, P (Ocmp c) VI VI NBC_normal) ->
        (forall c, P (Ocmpu c) VI VI NBC_normal) ->

        P Oaddl VL VL NBC_normal ->
        P Osubl VL VL NBC_normal ->
        P Omull VL VL NBC_normal ->
        P Odivl VL VL NBC_normal ->
        P Odivlu VL VL NBC_normal ->
        P Omodl VL VL NBC_normal ->
        P Omodlu VL VL NBC_normal ->
        P Oandl VL VL NBC_normal ->
        P Oorl VL VL NBC_normal ->
        P Oxorl VL VL NBC_normal ->
        (forall c, P (Ocmpl c) VL VL NBC_normal) ->
        (forall c, P (Ocmplu c) VL VL NBC_normal) ->

        P Oshll VL VI NBC_normal ->
        P Oshrl VL VI NBC_normal ->
        P Oshrlu VL VI NBC_normal ->

        P Oaddf VF VF NBC_normal ->
        P Osubf VF VF NBC_normal ->
        P Omulf VF VF NBC_normal ->
        P Odivf VF VF NBC_normal ->
        (forall c, P (Ocmpf c) VF VF NBC_normal) ->

        P Oaddfs VS VS NBC_normal ->
        P Osubfs VS VS NBC_normal ->
        P Omulfs VS VS NBC_normal ->
        P Odivfs VS VS NBC_normal ->
        (forall c, P (Ocmpfs c) VS VS NBC_normal) ->

        P Osub VP VP NBC_ptr_sub ->
        (forall c, P (Ocmpu c) VP VP (NBC_ptr_ptr_cmp c)) ->
        (forall c, P (Ocmpu c) VP VI (NBC_ptr_int_cmp false c)) ->
        (forall c, P (Ocmpu c) VI VP (NBC_ptr_int_cmp true c)) ->

        forall o x y, P o x y (nbc_classify o x y).
Proof.
      intros.
      destruct x, y, o; eauto.
    Qed.

    Context {MD: Type} (sz : ABTree.t (Z * MD)).
    Hypothesis SZ :
      ∀ b hi md b',
        ABTreeDom.get b sz = Just (hi, md) →
        block_of_ablock ge ((tmp, ε) :: σ) b = Some b' →
        Mem.range_perm m b' 0 hi Cur Nonempty.
    
    Variable nm : num.

    Definition is_valid_ptr (weak: bool) (bs: BlockSet.t) (off: mexpr var) : bool :=
      match min_size sz bs MaxSize with
      | MaxSize => false
      | Size (Zpos min) =>
        let i := Int.repr (Zpos (if weak then Pos.succ min else min)) in
        Pos.ltb min (Z.to_pos Int.max_unsigned) &&
                is_bot (assume (MEbinop (Ocmpu Clt) off (me_const_i _ i)) false nm)
      | Size _
      | MinSize => false
      end.

    Let is_null := is_null NumDom nm.

    Notation "'error' e" := (do _ <- alarm_am e; ret nil) (at level 50).
    Local Notation am_assert b err :=
      (assert L b_, err)).

    Fixpoint nconvert (pt: points_to) (e:pexpr T.elt) : nconvert_t L (mexpr var) :=
      match e with
        | PEvar c ty =>
          match ty with
            | VI|VP|VIP => ret (MEvar MNTint (elt2p c))
            | VL => ret (MEvar MNTint64 (elt2p c))
            | VF => ret (MEvar MNTfloat (elt2p c))
            | VS => ret (MEvar MNTsingle (elt2p c))
          end
        | PElocal _ => ret (MEconst _ (MNint Int.zero))
        | PEconst cst _ _ => ret (convert_constant cst)

        | PEunop op e _ _ =>
          let ty := pexpr_get_ty e in
          do _ <- am_assert (well_typed_unop op ty)
             (nconvert_msg nm MSG_ILL_TYPED_UNOP op ty);
          do e' <- nconvert pt e;
            ret (MEunop op e')

        | PEbinop op e1 e2 _ _ =>
          let ty₁ := pexpr_get_ty e1 in
          let ty₂ := pexpr_get_ty e2 in
          match nbc_classify op tytywith

          | NBC_normal =>
            do e1' <- nconvert pt e1;
            do e2' <- nconvert pt e2;
            ret (MEbinop op e1' e2')

          | NBC_ptr_sub =>
            do nx <- nconvert pt e1;
            do ny <- nconvert pt e2;
            match pt_eval_pexpr ge μ pt e1, pt_eval_pexpr ge μ pt e2 with
            | Just (TyPtr (Just xb)), Just (TyPtr (Just yb)) =>
              let res := MEbinop op nx ny in
              match cmp_blockset xb yb with
              | Equal b => ret res
              | _ => error (nconvert_msg nm MSG_EQ_BLOCKS xb yb)
              end
            | ty₁, ty₂ =>
              error (nconvert_msg nm MSG_LARGE_PTSET Osub tyty₂)
            end

          | NBC_ptr_ptr_cmp c =>
            do nx <- nconvert pt e1;
            do ny <- nconvert pt e2;
            match pt_eval_pexpr ge μ pt e1, pt_eval_pexpr ge μ pt e2 with
            | Just (TyPtr (Just xb)), Just (TyPtr (Just yb)) =>
              match cmp_blockset xb yb with
              | Equal b =>
                let res := MEbinop (Ocmpu c) nx ny in
                do _ <- am_assert (is_valid_ptr true xb nx)
                   (nconvert_msg nm MSG_VALID_PTR true xb nx);
                do _ <- am_assert (is_valid_ptr true yb ny)
                   (nconvert_msg nm MSG_VALID_PTR true yb ny);
                 ret res
              | Disjoint =>
                do _ <- am_assert (noerror nx nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM nx);
                do _ <- am_assert (noerror ny nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM ny);
                do _ <- am_assert (is_valid_ptr false xb nx)
                   (nconvert_msg nm MSG_VALID_PTR false xb nx);
                do _ <- am_assert (is_valid_ptr false yb ny)
                   (nconvert_msg nm MSG_VALID_PTR false yb ny);
                match c with
                | Ceq => ret (MEconst _ (MNint Int.zero))
                | Cne => ret (MEconst _ (MNint Int.one))
                | _ => error (nconvert_msg nm MSG_CMP_OP c)
                end
              | DontKnow =>
                do _ <- am_assert (noerror nx nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM nx);
                do _ <- am_assert (noerror ny nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM ny);
                do _ <- am_assert (is_valid_ptr false xb nx)
                   (nconvert_msg nm MSG_VALID_PTR false xb nx);
                do _ <- am_assert (is_valid_ptr false yb ny)
                   (nconvert_msg nm MSG_VALID_PTR false yb ny);
                match c with
                | (Ceq | Cne) =>
                  (MEconst _ (MNint Int.zero) :: MEconst _ (MNint Int.one) :: nil, (nil, nil))
                | _ => error (nconvert_msg nm MSG_CMP_BLK_DK xb yb)
                end
              | Contradiction =>
                do _ <- am_assert (noerror nx nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM nx);
                do _ <- am_assert (noerror ny nm)
                   (nconvert_msg nm MSG_UNSAFE_NUM ny);
                ret (MEconst _ (MNint Int.one))
              end
            | ty₁, ty₂ =>
              error (nconvert_msg nm MSG_LARGE_PTSET (Ocmpu c) tyty₂)
            end

          | NBC_ptr_int_cmp swapped c =>
            do nx <- nconvert pt e1;
            do ny <- nconvert pt e2;
            do _ <- am_assert (noerror nx nm)
               (nconvert_msg nm MSG_UNSAFE_NUM nx);
            do _ <- am_assert (noerror ny nm)
               (nconvert_msg nm MSG_UNSAFE_NUM ny);
            do _ <- am_assert (is_null (if swapped then nx else ny))
               (nconvert_msg nm MSG_NULL (if swapped then nx else ny));
            match pt_eval_pexpr ge μ pt (if swapped then e2 else e1) with
            | Just (TyPtr (Just bs)) =>
              match c with
              | (Ceq | Cne) =>
                let res := MEconst _ (MNint match c with Ceq => Int.zero | _ => Int.one end) in
                do _ <- am_assert (is_valid_ptr true bs (if swapped then ny else nx))
                   (nconvert_msg nm MSG_VALID_PTR true bs (if swapped then ny else nx));
                ret res
              | _ => error (nconvert_msg nm MSG_CMP_OP c)
              end
            | ty =>
              error (nconvert_msg nm MSG_LARGE_PTSET (Ocmpu c)
                                  (if swapped then All else ty)
                                  (if swapped then ty else All))
            end

          | NBC_wrong => error (nconvert_msg nm MSG_ILL_TYPED_BINOP op tyty₂)
          end
      end.

    Local
    Ltac clarify :=
      repeat (eq_some_inv; match goal with
             | NE : ?x ≠ ?y, OR: ?x = ?y_ |- _ =>
               destruct OR as [ OR | OR ]; [ elim (NE OR); fail | ]
             | NEx : ?xnil, NEy : ?ynil, OR: pair_list ?x ?y = nil_ |- _ =>
               destruct OR as [ OR | OR ]; [ elim (pair_list_not_nil NEx NEy OR); fail | ]
             | H : ?x = ?y |- _ => subst y || subst x
             | H : am_get (_, (nil, _)) = Some _ |- _ => apply some_eq_inv in H
             | H : am_get (_, (_ :: _, _)) = Some _ |- _ => exact (None_not_Some H _)
             | H : am_get (flat_map_a _ _ _) = Some _ |- _ =>
               apply flat_map_a_correct in H; hsplit
             | H : am_get (do _ <- _; _) = Some _ |- _ =>
               let K := fresh in
               rename H into K;
               pose proof (flat_map_a_correct _ K) as H; clear K
                                                        
             | H : am_get match ?x with All => _ | _ => _ end = Some _ |- _ =>
               destruct x eqn: ?
             | H : am_get match ?x with TyFloat => _ | _ => _ end = Some _ |- _ =>
               destruct x eqn: ?
             | H : am_get match cmp_blockset ?x ?y with _ => _ end = Some _ |- _ =>
               pose proof (cmp_blockset_correct x y);
                 destruct (cmp_blockset x y)
             | H : am_get match ?x with Ceq => _ | _ => _ end = Some _ |- _ =>
               destruct x
             | H : am_get (am_assert (noerror ?x ?y) _) = Some _, NM : mach_gamma ?y _ |- _ =>
               let K := fresh in
               destruct (noerror x y) eqn: K; [
                   pose proof (noerror_correct y NM K)
                 | ]; simpl in H
             | H : am_get (am_assert (noerror ?x ?y) _) = Some _ |- _ =>
               destruct (noerror x y) eqn: ?; simpl in H
             | H : am_get (am_assert (is_valid_ptr ?x ?y ?z) _) = Some _ |- _ =>
               destruct (is_valid_ptr x y z) eqn: ?; simpl in H
             | H : am_get (am_assert (is_null ?x) _) = Some _ |- _ =>
               destruct (is_null x) eqn: ?; simpl in H

             | H : flat_map_a_spec _ _ (_ :: _) _ |- _ =>
               apply flat_map_a_spec_cons_inv in H; hsplit
             | H : flat_map_a_spec _ _ nil _ |- _ =>
               apply flat_map_a_spec_nil_inv in H; hsplit
             | H : flat_map_a_spec _ _ _ nil |- _ =>
               apply flat_map_a_spec_nil_r in H
             | H : flat_map_a_spec _ _ ?a _, Ha: ?anil |- _ =>
               let K := fresh in
               rename H into K;
               pose proof (fma_bind _ _ _ _ _ K Ha) as H; clear K

             | H : In _ (_ ++ nil) |- _ =>
               apply in_app in H; destruct H as [ H | () ]
             | H : In _ nil |- _ =>
               destruct H as ()
             | H : In _ (_ :: nil) |- _ =>
               destruct H as [ H | () ]
             | H : In _ (_ :: _ :: nil) |- _ =>
               destruct H as [ H | [ H | () ] ]

             | H : ∀ x, In x (_ :: nil) → _ |- _ =>
               specialize (H _ (or_introl _ eq_refl))
             | H : ∀ x, In x (_ ++ nil) → _ |- _ =>
               repeat rewrite app_nil_r in H
             end).

    Local
    Ltac forward :=
      repeat match goal with
             | H : ∀ _, __, K : _ |- _ => specialize (H _ K)
    end.

    Lemma nconvert_fv pt pe :
      ∀ lme, am_get (nconvert pt pe) = Some lme
      ∀ me, In me lme
      ∀ x, xme_free_var me → ∃ y, ype_free_var pex = elt2p y.
Proof.
      clear Hμ.
      induction pe; simpl; intros lme Hlme me Hme cc Hcc.
      - destruct t; simpl in *; eq_some_inv; subst lme;
        destruct Hme as [ <- | () ];
        simpl in Hcc; subst cc; vauto.
      - simpl in *; eq_some_inv; subst lme;
        destruct Hme as [ <- | () ].
        destruct Hcc.
      - destruct c as []; simpl in *; eq_some_inv; subst lme;
        destruct Hme as [ <- | () ];
        simpl in *; destruct Hcc.
      - destruct (well_typed_unop _ _); simpl in *;
        clarify.
        pose proof (fma_in _ _ _ _ H0 _ Hme). hsplit.
        clarify.
        now forward.

      - destruct (nbc_classify _ _ _);
        clarify; hsplit; forward;
        repeat (repeat match goal with
                 | X: flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
                   pose proof (fma_in _ _ _ _ X _ Y); clear X; hsplit
                 | H : me_free_var (MEbinop _ _ _) _ |- _ =>
                   destruct H as [ H | H ]
                 | H : me_free_var (MEconst _ _) _ |- _ =>
                   destruct H
               end; clarify; hsplit; clarify; forward);
        eauto.
    Qed.

    Definition ncompat : val -> mach_num -> Prop :=
      λ v, match v with
             | Vint i | Vptr _ i => eq (MNint i)
             | Vlong l => eq (MNint64 l)
             | Vsingle f => eq (MNsingle f)
             | Vfloat f => eq (MNfloat f)
             | Vundef => λ _, True
           end.

    Lemma ncompat_inv_int v i :
      ncompat v (MNint i) →
      v = Vint i ∨ (∃ b, v = Vptr b i) ∨ v = Vundef.
Proof.
      destruct v; inversion 1; vauto.
    Qed.

    Lemma ncompat_inv_int64 v i :
      ncompat v (MNint64 i) →
      v = Vlong iv = Vundef.
Proof.
      destruct v; inversion 1; vauto.
    Qed.

    Lemma ncompat_inv_float v f :
      ncompat v (MNfloat f) →
      v = Vfloat fv = Vundef.
Proof.
      destruct v; inversion 1; vauto.
    Qed.

    Lemma ncompat_inv_single v f :
      ncompat v (MNsingle f) →
      v = Vsingle fv = Vundef.
Proof.
      destruct v; inversion 1; vauto.
    Qed.

    Definition compat_fun (v:val) : mach_num :=
      match v with
      | Vptr _ i | Vint i => MNint i
      | Vlong l => MNint64 l
      | Vundef => MNint Int.zero
      | Vfloat f => MNfloat f
      | Vsingle f => MNsingle f
      end.

    Lemma ncompat_compat_fun v :
      ncompat v (compat_fun v).
Proof.
      destruct v; simpl; constructor.
    Qed.

    Ltac ncompat_inv H :=
      match type of H with
             | ncompat ?v (MNint ?i) =>
               destruct (ncompat_inv_int v i H) as [?|[(? & ?)|?]];
               clear H
             | ncompat ?v (MNint64 ?i) =>
               destruct (ncompat_inv_int64 v i H) as [?|?];
               clear H
             | ncompat ?v (MNfloat ?i) =>
               destruct (ncompat_inv_float v i H) as [?|?];
               clear H
             | ncompat ?v (MNsingle ?i) =>
               destruct (ncompat_inv_single v i H) as [?|?];
               clear H
             end.

    Definition wtype_num (v:mach_num) (tp: typ) : Prop :=
      match v, tp with
        | MNint _, (VI|VP|VIP)
        | MNint64 _, VL
        | MNfloat _, VF
        | MNsingle _, VS => True
        | _, _ => False
      end.

    Definition compatC:T.elt -> val) (ρ:var -> mach_num) : Prop :=
      forall c, ncompatC c) (ρ (elt2p c)).

    Variable ρC: T.elt -> val.
    Variable ρ: var -> mach_num.

    Variable Hcompat: compat ρC ρ.
    Hint Constructors is_bool.

    Lemma val_cmp_exists_int: forall c i1 i2,
      exists i,
        Val.cmp c (Vint i1) (Vint i2) = Vint i /\
        of_bool (Int.cmp c i1 i2) = (MNint i).
Proof.
      unfold Val.cmp, of_bool, Val.cmp_bool; intros.
      destruct Int.cmp; simpl; repeat econstructor.
    Qed.

    Lemma val_cmpu_exists_int: forall f c i1 i2,
      exists i,
        Val.cmpu f c (Vint i1) (Vint i2) = Vint i /\
        of_bool (Int.cmpu c i1 i2) = MNint i.
Proof.
      unfold Val.cmpu, of_bool, Val.cmpu_bool; intros.
      destruct Int.cmpu; simpl; repeat econstructor.
    Qed.

    Lemma val_cmpf_exists_int: forall c f1 f2,
      exists i,
        Val.cmpf c (Vfloat f1) (Vfloat f2) = Vint i /\
        of_bool (Float.cmp c f1 f2) = MNint i.
Proof.
      unfold Val.cmpf, of_bool, Val.cmpf_bool; intros.
      destruct Float.cmp; simpl; repeat econstructor.
    Qed.

    Lemma val_cmpfs_exists_int: forall c f1 f2,
      exists i,
        Val.cmpfs c (Vsingle f1) (Vsingle f2) = Vint i /\
        of_bool (Float32.cmp c f1 f2) = MNint i.
Proof.
      unfold Val.cmpfs, of_bool, Val.cmpfs_bool; intros.
      destruct Float32.cmp; simpl; repeat econstructor.
    Qed.

    Lemma val_of_bool_exists_int: forall b,
      exists i,
        Val.of_bool b = Vint i /\
        of_bool b = MNint i.
Proof.
      unfold of_bool; intros.
      destruct b; repeat econstructor.
    Qed.

    Lemma ncompat_Vundef: forall n, ncompat Vundef n.
Proof.
      destruct n; constructor.
    Qed.
    Hint Resolve ncompat_Vundef.

    Lemma exist_ncompat_Vundef: forall P,
      (exists n, P n) ->
      exists n : mach_num, ncompat Vundef n /\ P n.
Proof.
      intros P (n & H).
      repeat econstructor; eauto.
    Qed.

    Lemma exist_ncompat_Vint: forall (P:mach_num -> Prop) i,
      P (MNint i) ->
      exists n : mach_num, ncompat (Vint i) n /\ P n.
Proof.
      intros P H.
      repeat econstructor; eauto.
    Qed.

    Lemma exist_ncompat_Vptr: forall (P:mach_num -> Prop) b i,
      P (MNint i) ->
      exists n : mach_num, ncompat (Vptr b i) n /\ P n.
Proof.
      intros P H.
      repeat econstructor; eauto.
    Qed.

    Lemma exist_ncompat_Vlong: forall (P:mach_num -> Prop) l,
      P (MNint64 l) ->
      exists n : mach_num, ncompat (Vlong l) n /\ P n.
Proof.
      intros P H.
      repeat econstructor; eauto.
    Qed.

    Lemma exist_ncompat_Vfloat: forall (P:mach_num -> Prop) f,
      P (MNfloat f) ->
      exists n : mach_num, ncompat (Vfloat f) n /\ P n.
Proof.
      intros P H.
      repeat econstructor; eauto.
    Qed.

    Lemma exist_ncompat_Vsingle: forall (P:mach_num -> Prop) f,
      P (MNsingle f) ->
      exists n : mach_num, ncompat (Vsingle f) n /\ P n.
Proof.
      intros P H.
      repeat econstructor; eauto.
    Qed.

    Lemma of_bool_MNint : forall b,
      exists i, of_bool b = MNint i.
Proof.
      unfold of_bool; destruct b; econstructor; reflexivity.
    Qed.

    Hypothesis block_of_ablock_inj :
      ∀ abbabb₂,
        block_of_ablock ge ((tmp, ε) :: σ) ab₁ = Some b₁ →
        block_of_ablock ge ((tmp, ε) :: σ) ab₂ = Some b₂ →
        ab₁ ≠ ab₂ →
        b₁ ≠ b₂.

    Lemma nconvert_correct: ∀ e,
      ∀ v, eval_pexpr ge m ρC ε e v ->
      ∀ pt (PT: ρCpoints_to_gamma ge ((tmp, ε) :: σ) pt) lme,
        am_get (nconvert pt e) = Some lme
      vVundef
      ∃ e', In e' lme
      exists n, ncompat v n /\
                eval_mexpr ρ e' n /\
                wtype_num n (pexpr_get_ty e).
Proof.
      intros e v Hv pt PT lme Hlme Hvundef.
      cut (∃ e', In e' lme ∧ ∃ n : mach_num, ncompat v neval_mexpr ρ e' n).
      { intros (e' & He' & n & A & B). exists e'. split. auto. exists n. split. auto. split. auto.
        pose proof pexpr_get_ty_ok Hv Hvundef.
        destruct v, n, (pexpr_get_ty e); try contradiction; try congruence; constructor. }
      revert v Hv lme Hlme Hvundef.
      induction e; intros vv Hvv; eval_pexpr_inv; hsplit; clarify; simpl.
      - intros. specialize (Hcompat v).
        destruct t; clarify;
        eexists; (split; [ left; reflexivity | ]);
        exists (ρ (elt2p v)); repeat split; auto;
        apply gamma_typ_inv in H;
        try destruct H as [ H | H ];
        hsplit; try rewrite H in Hcompat; simpl in Hcompat;
        (constructor; [now auto|]); simpl in Hcompat; try (intuition; fail);
        rewrite <- Hcompat; constructor.
      - intros lme Hlme NB; eq_some_inv. clarify.
        eexists; split. left; reflexivity.
        exists (MNint Int.zero).
        repeat (constructor; auto).
        
      - eval_pexpr_inv; hsplit; clarify; intros e' ?; clarify.
        intros Hvundef.
        destruct H. easy.
        destruct c; simpl in *; clarify;
        (econstructor; split; [auto|repeat econstructor]);
        try apply Zle_refl.
        destruct Genv.find_symbol. reflexivity. elim (Hvundef eq_refl).
      - intros lme Hlme HB.
        destruct H1. easy.
        assert (v' ≠ Vundef) as Hv1.
        { contradict HB. clarify. destruct u; simpl in *; eq_some_inv; auto. }
        assert (Hg:= pexpr_get_ty_ok Hvv Hv1).
        clarify. hsplit.
        destruct (well_typed_unop _ _) eqn: Hwt; simpl in *; clarify.
        forward. specialize (IHe Hv1). hsplit.
        repeat match goal with
               | X: flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
                 pose proof (fma_in' _ _ _ _ X _ Y); clear X; hsplit
               end; clarify.
        eexists. split. apply in_app; left; eassumption.
        destruct n;
        ncompat_inv H3; clarify; try congruence;
        destruct u; simpl in *; eq_some_inv; hsplit; apply proj_sumbool_true in Hwt;
        rewrite <- Hwt in Hg; try (destruct Hg; fail); clear Hg; clarify;
        repeat econstructor; simpl; eauto.
      - intros lme Hlme HB.
        destruct H1 as [ H1 | H1 ]. elim (HB H1); fail.
        assert (v₁ ≠ Vundef) as Hv1.
        { contradict HB. clarify. clear -H0.
          destruct b; simpl in *;
          unfold Val.cmpl, Val.cmplu, Val.cmpl_bool, Val.cmplu_bool in *;
          eq_some_inv; hsplit; eq_some_inv; auto. }
        assert (Hg1:= pexpr_get_ty_ok Hvv Hv1). red in Hg1.
        assert (v₂ ≠ Vundef) as Hv2.
        { contradict HB. clarify. clear -H0.
          destruct v₁, b; simpl in *; eq_some_inv; auto;
          unfold Val.cmpl, Val.cmplu, Val.cmpl_bool, Val.cmplu_bool in *;
          simpl in *; eq_some_inv; auto. }
        assert (Hg2:= pexpr_get_ty_ok H Hv2). red in Hg2.
        generalize dependent (pexpr_get_ty e2). intros ty2.
        generalize dependent (pexpr_get_ty e1). intros ty1.
        generalize dependent b; intros b.
        apply nbc_classify_intro;
          [ intros o x y Hty H0 Hg1 Hlme Hg2; simpl in Hlme; eq_some_inv; fail | .. ];
          ( intros c Hty EV Hg1 Hlme Hg2 || intros Hty EV Hg1 Hlme Hg2 );
          clarify; hsplit; forward;

          specialize (IHe1 Hv1); hsplit;

          repeat match goal with
                 | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
                   pose proof (fma_in' _ _ _ _ X _ Y); clear X; hsplit
               end; clarify; hsplit; clarify; forward;
          specialize (IHe2 Hv2); hsplit;
          
          repeat match goal with
                 | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
                   pose proof (fma_in' _ _ _ _ X _ Y); clear X; hsplit
                 end; clarify; hsplit; clarify;

        try ((eexists; split; [ eauto; fail | ]));

        destruct v₁; try (elim (Hv1 eq_refl)); simpl in *; clarify;
        destruct v₂; try (elim (Hv2 eq_refl)); simpl in *; clarify;

        try (elim Hg1; discriminate); try (elim Hg2; discriminate);

      repeat match goal with
      | H : pt_eval_pexpr _ _ _ ?e = Just ?bs, EV : eval_pexpr _ _ _ _ ?pe _, X : _ |- _ =>
        generalize (pt_eval_pexpr_correct eq_refl PT EV); rewrite H; clear H;
        let Hbs := fresh Hbs in
        intros (? & Hbs & ?); eauto;
        (specialize (X _ Hbs) || specializeq, X _ q Hbs))
      end;
      try match goal with
      | X: block_of_ablock _ _ ?x = _,
        Y: block_of_ablock _ _ ?y = _,
        NE: ?x ≠ ?y |- _ =>
        pose proof (block_of_ablock_inj _ _ _ _ X Y NE)
      end;

        try (unfold Val.cmpl, Val.cmplu in *; simpl in * );
        clarify;
        repeat match goal with
           | id: (if ?b then _ else _) = Some _ |- _ => destruct b eqn:?; inversion id; clear id; clarify
           | id: (_ || _) = false |- _ => apply orb_false_elim in id; destruct id
           | id: (_ && _) = false |- _ => apply andb_false_iff in id
           | H: (_ && _) = true |- _ => apply andb_true_iff in H; destruct H
           | H: appcontext[eq_dec _ _] |- _ => apply proj_sumbool_true in H
           | |- appcontext [if ?b then _ else _] => destruct b eqn:?
         end;
        try match goal with
          | |- context[Oshl] => destruct (Int.ltu i0 Int.iwordsize) eqn:?
          | |- context[Oshr] => destruct (Int.ltu i0 Int.iwordsize) eqn:?
          | |- context[Oshru] => destruct (Int.ltu i0 Int.iwordsize) eqn:?
          | |- context[Oshll] => destruct (Int.ltu i0 Int64.iwordsize') eqn:?
          | |- context[Oshrl] => destruct (Int.ltu i0 Int64.iwordsize') eqn:?
          | |- context[Oshrlu] => destruct (Int.ltu i0 Int64.iwordsize') eqn:?
          | |- context[Val.cmp ?c (Vint ?i1) (Vint ?i2)] =>
            destruct (val_cmp_exists_int c i1 i2) as (bi & B1 & B2); rewrite B1
          | |- context[Val.cmpu ?m ?c (Vint ?i1) (Vint ?i2)] =>
            destruct (val_cmpu_exists_int m c i1 i2) as (bi & B1 & B2); rewrite B1
          | |- context[Val.cmpf ?c (Vfloat ?f1) (Vfloat ?f2)] =>
            destruct (val_cmpf_exists_int c f1 f2) as (bi & B1 & B2); rewrite B1
          | |- context[Val.cmpfs ?c (Vsingle ?f1) (Vsingle ?f2)] =>
            destruct (val_cmpfs_exists_int c f1 f2) as (bi & B1 & B2); rewrite B1
          | |- context[Val.of_bool ?b] =>
            destruct (val_of_bool_exists_int b) as (bi & B1 & B2); rewrite B1
        end;
        try (rewrite He_a1, He_a2);
        try (elim (HB eq_refl));
        try (apply exist_ncompat_Vundef; econstructor; split;
             [ econstructor; try eassumption; econstructor; eauto
             | try match goal with
                 | |- context[of_bool ?b] =>
                   destruct (of_bool_MNint b) as (nb & Hb); rewrite Hb
               end; simpl; auto]; fail);
        try (apply exist_ncompat_Vint;
             econstructor; try eassumption; try rewrite <- B2;
             econstructor; eauto; fail);
        try (match goal with b: block |- _ =>
             refine (exist_ncompat_Vptr _ b _ _);
             econstructor; try eassumption;
             (econstructor || rewrite Int.add_commut; econstructor); eauto; fail
             end);
        try (now repeat (econstructor (eauto))).
        idtac.
        hsplit; clarify. unfold Val.cmpu in *.
        simpl in *. destruct (eq_block _ _). 2: congruence. clarify.
        revert HB. bif. 2: intros X; elim (X eq_refl). intros _.
        match goal with |- appcontext[ Int.cmpu ?c ?x ?y] =>
                        exists (of_bool (Int.cmpu c x y))
        end. split. simpl. bif; reflexivity. vauto.
        unfold Val.cmpu in *. simpl in *. revert HB.
        case eq_block. congruence. intros _.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto.
        vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        case eq_block. congruence. intros _.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto.
        vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. bif'. 2: intros X; elim (X eq_refl). intros _.
        destruct (Int.eq _ _) eqn: Heq.
        exists (MEconst _ (MNint Int.one)). split. eauto. vauto2.
        exists (MEconst _ (MNint Int.zero)). split. eauto. vauto2.
        bif'. 2: intros X; elim (X eq_refl). intros _.
        exists (MEconst _ (MNint Int.zero)). split. eauto. vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. bif'. 2: intros X; elim (X eq_refl). intros _.
        destruct (Int.eq _ _) eqn: Heq.
        exists (MEconst _ (MNint Int.zero)). split. eauto. vauto2.
        exists (MEconst _ (MNint Int.one)). split. eauto. vauto2.
        bif'. 2: intros X; elim (X eq_refl). intros _.
        exists (MEconst _ (MNint Int.one)). split. eauto. vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto2.
        unfold Val.cmpu in *. simpl in *. revert HB.
        bif'. 2: intros X; elim (X eq_refl). intros _. vauto2.
    Qed.

    Lemma is_valid_ptr_valid (NM: ρ ∈ γ nm) weak bs off :
      is_valid_ptr weak bs off = true
      ∀ ab, BlockSet.mem ab bs = true
      ∀ b, block_of_ablock ge ((tmp, ε) :: σ) ab = Some b
      ∀ i, eval_mexpr ρ off (MNint i) →
           (if weak
           then Mem.weak_valid_pointer m b (Int.unsigned i)
           else Mem.valid_pointer m b (Int.unsigned i)) = true.
Proof.
      unfold is_valid_ptr.
      generalize (min_size_min sz bs MaxSize). simpl.
      destruct (min_size sz bs MaxSize) as [|z|]; intros [H _] LT; eq_some_inv.
      destruct z as [|min|]; eq_some_inv.
      rewrite !andb_true_iff in LT. destruct LT as [Hz LT].
      rewrite Pos2Z.inj_ltb, Z.ltb_lt in Hz.
      pose proof (assume_bot NumDom _ _ LT NM) as K. clear LT.
      intros ab Hab b Hb i EV.
      specialize (H _ Hab). simpl in H.
      generalizehi md EQ, SZ ab hi md _ EQ Hb).
      unfold ABTreeDom.get. destruct (ABTree.get ab sz) as [(hi, md)|].
      2: exact (False_ind _ H).
      simpl in H.
      intros Hperm. specialize (Hperm _ _ eq_refl).
      unfold Mem.weak_valid_pointer, Mem.valid_pointer.
      destruct weak.
      *
      generalize (Int.unsigned_range i). intros [Hi Hi'].
      assert (0 < Int.unsigned i ∨ 0 = Int.unsigned i) as [Hi''|Hi'']. Psatz.lia.
      destruct (Mem.perm_dec _ _ (_ - 1) _ _) as [|Q]. rewrite orb_true_r. reflexivity.
      destruct (Int.cmpu Clt i (Int.repr (Zpos (Pos.succ min)))) eqn: Hmin.
      2: elim K; rewrite <- Hmin; vauto.
      elim Q; clear Q.
      simpl in Hmin. unfold Int.ltu in Hmin. rewrite Int.unsigned_repr in Hmin.
      2: change Int.max_unsigned with 4294967295; Psatz.lia.
      destruct (zlt (Int.unsigned i) _); eq_some_inv. clear Hmin.
      specialize (Hperm (Int.unsigned i - 1)).
      eapply Mem.perm_implies. eapply Hperm. Psatz.lia. destruct ab; vauto.
      destruct (Mem.perm_dec _ _ (Int.unsigned i) _ _) as [|Q]. reflexivity.
      destruct (Int.cmpu Clt i (Int.repr (Zpos (Pos.succ min)))) eqn: Hmin.
      2: elim K; rewrite <- Hmin; vauto.
      elim Q; clear Q.
      simpl in Hmin. unfold Int.ltu in Hmin. rewrite Int.unsigned_repr in Hmin.
      2: change Int.max_unsigned with 4294967295; Psatz.lia.
      destruct (zlt (Int.unsigned i) _); eq_some_inv. clear Hmin.
      specialize (Hperm (Int.unsigned i)).
      eapply Hperm. Psatz.lia.
       *
      destruct (Mem.perm_dec _ _ _ _ _) as [|Q]. reflexivity.
      elim Q; clear Q.
      destruct (Int.cmpu Clt i (Int.repr (Zpos min))) eqn: Hi.
      2: elim K; rewrite <- Hi; vauto. clear K.
      simpl in Hi. unfold Int.ltu in Hi. rewrite Int.unsigned_repr in Hi.
      2: change Int.max_unsigned with 4294967295; Psatz.lia.
      destruct (zlt (Int.unsigned i)); eq_some_inv. clear Hi.
      specialize (Hperm (Int.unsigned i)).
      eapply Hperm.
      hsplit. split. apply Int.unsigned_range. Psatz.lia.
    Qed.

    Fixpoint nconvert_def_pre (pe: pexpr T.elt) : Prop :=
      match pe with
      | PEvar v ty => ρC(v) ∈ γ ty
      | PElocal i => ε(i) ≠ None
      | PEconst (Oaddrsymbol i _) _ _ => Genv.find_symbol ge iNone
      | PEconst _ _ _ => True
      | PEunop u pe' _ _ =>
        nconvert_def_pre pe'
      | PEbinop b pe1 pe2 _ _ =>
        nconvert_def_pre pe1
        nconvert_def_pre pe2
      end.

    Lemma wtype_num_VI {n} :
      wtype_num n VI → ∃ i, n = MNint i.
Proof.
destruct n; intros (); vauto. Qed.

    Lemma wtype_num_VP {n} :
      wtype_num n VP → ∃ i, n = MNint i.
Proof.
destruct n; intros (); vauto. Qed.

    Lemma wtype_num_VIP {n} :
      wtype_num n VIP → ∃ i, n = MNint i.
Proof.
destruct n; intros (); vauto. Qed.

    Lemma wtype_num_VF {n} :
      wtype_num n VF → ∃ f, n = MNfloat f.
Proof.
destruct n; intros (); vauto. Qed.

    Lemma wtype_num_VS {n} :
      wtype_num n VS → ∃ s, n = MNsingle s.
Proof.
destruct n; intros (); vauto. Qed.

    Lemma wtype_num_VL {n} :
      wtype_num n VL → ∃ l, n = MNint64 l.
Proof.
destruct n; intros (); vauto. Qed.

    Ltac wtype_num_inv :=
      repeat match goal with
             | H : wtype_num _ VI |- _ => apply wtype_num_VI in H
             | H : wtype_num _ VP |- _ => apply wtype_num_VP in H
             | H : wtype_num _ VIP |- _ => apply wtype_num_VIP in H
             | H : wtype_num _ VF |- _ => apply wtype_num_VF in H
             | H : wtype_num _ VS |- _ => apply wtype_num_VS in H
             | H : wtype_num _ VL |- _ => apply wtype_num_VL in H
             end.

    Lemma wtype_of_bool {b} :
      wtype_num (of_bool b) VI.
Proof.
case b; exact I. Qed.
    Local Hint Resolve wtype_of_bool.

    Lemma nconvert_eval_mexpr (NM: ρ ∈ γ nm) pt (PT: ρCpoints_to_gamma ge ((tmp, ε) :: σ) pt) pe :
      nconvert_def_pre pe
      ∀ lme, am_get (nconvert pt pe) = Some lme
      lmenil
      ∀ ne, In ne lme → ¬ error_mexpr ρ ne
      ∃ n, neval_mexpr ρ newtype_num n (pexpr_get_ty pe).
Proof.
      elim pe; clear pe; simpl.
      - intros v () TY; apply gamma_typ_inv in TY; try destruct TY as [ TY | TY ]; hsplit; intros lme ?;
        clarify;
        (split; [intro; clarify|]);
        intros ? ?; clarify;
        generalize (Hcompat v); rewrite TY; simpl; intros K; symmetry in K;
        eexists; split; vauto.
      - intros i Hi lme ?; clarify; split; [ intros ?; clarify | ]. intros ne ? _; clarify; eexists; vauto.
      - intros c t TY Hc lme ?; clarify.
        split. intro; clarify.
        intros ne ?; clarify.
        intros NB.
        destruct c; simpl in *; subst t; eexists; vauto.

      - intros u pe IH ? -> DEF lme ?; clarify; hsplit.
        destruct (well_typed_unop _ _) eqn: Hwt; simpl in *; clarify.
        specialize (IH DEF). forward.
        destruct IH as [ Q' IH ].
        split.
        intro; eq_some_inv; hsplit; clarify; hsplit.
        destruct q' as [ | a q' ]. auto. assert (In a (a::q')) by now left. forward. clarify.
        intros ne ? NB. clarify.
        match goal with
        | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
          pose proof (fma_in _ _ _ _ X _ Y); clear X; hsplit
        end; clarify; hsplit; clarify; forward.
        edestruct IH as (n' & Hn' & Hn'ty).
        intros K; apply NB; vauto.
        destruct u; simpl in *; apply eq_dec_eq in Hwt; rewrite <- Hwt in *;
        wtype_num_inv; hsplit; clarify;
        try (eexists; split; vauto; fail);
        try (match goal with
             | H : eval_mexpr _ ?ne (MNfloat ?f) |- appcontext[ MEunop Ointoffloat ?ne ] => destruct (Float.to_int f) eqn: ?
             | H : eval_mexpr _ ?ne (MNfloat ?f) |- appcontext[ MEunop Ointuoffloat ?ne ] => destruct (Float.to_intu f) eqn: ?
             | H : eval_mexpr _ ?ne (MNfloat ?f) |- appcontext[ MEunop Olongoffloat ?ne ] => destruct (Float.to_long f) eqn: ?
             | H : eval_mexpr _ ?ne (MNfloat ?f) |- appcontext[ MEunop Olonguoffloat ?ne ] => destruct (Float.to_longu f) eqn: ?
             | H : eval_mexpr _ ?ne (MNsingle ?s) |- appcontext[ MEunop Ointofsingle ?ne ] => destruct (Float32.to_int s) eqn: ?
             | H : eval_mexpr _ ?ne (MNsingle ?s) |- appcontext[ MEunop Ointuofsingle ?ne ] => destruct (Float32.to_intu s) eqn: ?
             | H : eval_mexpr _ ?ne (MNsingle ?s) |- appcontext[ MEunop Olongofsingle ?ne ] => destruct (Float32.to_long s) eqn: ?
             | H : eval_mexpr _ ?ne (MNsingle ?s) |- appcontext[ MEunop Olonguofsingle ?ne ] => destruct (Float32.to_longu s) eqn: ?
             end;
             [ eexists; split; vauto | elim NB; vauto ]).

      - intros op pe1 IH1 pe2 IH2 ty Hty [ DEF1 DEF2 ].
        specialize (IH1 DEF1). specialize (IH2 DEF2).
        revert ty Hty IH1 IH2.
        apply nbc_classify_intro; intros;
        simpl in Hty;
        clarify; hsplit; forward;
        destruct IH1 as [ Q1 IH1 ];
        clarify; hsplit; forward;
        destruct IH2 as [ Q2 IH2 ];
        (split; [
           intros ->; clarify; hsplit;
           repeat match goal with
                  | H : ?xnil, K : ∀ a, In a ?x_ |- _ =>
                    destruct x as [ | () ? ]; [ elim (H eq_refl) | specialize (K _ (or_introl _ eq_refl)) ];
                    simpl in K;
                    clarify; hsplit; forward; hsplit; clarify; hsplit
                  end
          |]);
        intros ne Hne;
        repeat match goal with
        | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
          pose proof (fma_in _ _ _ _ X _ Y); clear X; hsplit; clarify
        | X : In ?q (pair_list _ _) |- _ =>
          try (destruct q);
          apply in_pair_list_inv in X; simpl in *; hsplit; clarify
        end;
        hsplit; clarify;
        intros NB;
        forward;

        repeat match goal with
        | IH : ¬ error_mexpr _ __ |- _ =>
          edestruct IH as (? & ? & ?); clear IH;
          [
          try match goal with
              | NE : noerror ?ne ?nm = true |- ¬ error_mexpr _ ?ne =>
                refine (noerror_correct nm _ NE); eassumption
              end;
            intros ?; apply NB; vauto; fail
          | ]
        end;
        wtype_num_inv; hsplit; clarify;
        try (eexists; split; vauto; eauto; fail);

        try match goal with
        | Ha: eval_mexpr _ ?a (MNint ?x), Hb: eval_mexpr _ ?b (MNint ?y) |- appcontext[ MEbinop ?op ?a ?b ] =>
          match op with
          | Odiv => idtac | Odivu => idtac | Omod => idtac | Omodu => idtac
          | _ => fail
          end;
          let Z := fresh in
          destruct (Int.eq y Int.zero) eqn: Z;
            [ apply int_eq in Z; subst y; elim NB; vauto | ];
          try assert (Int.eq x (Int.repr Int.min_signed) = falseInt.eq y Int.mone = false) by (
            let X := fresh in
            destruct (Int.eq x (Int.repr Int.min_signed)) eqn: X;
            [
              apply int_eq in X; subst x;
              let Y := fresh in
              destruct (Int.eq y Int.mone) eqn: Y;[
                  apply int_eq in Y; subst y; elim NB; vauto
                 | now right ]
            | now left ]
          )
        | Ha: eval_mexpr _ ?a (MNint64 ?x), Hb: eval_mexpr _ ?b (MNint64 ?y) |- appcontext[ MEbinop ?op ?a ?b ] =>
          match op with
          | Odivl => idtac | Odivlu => idtac | Omodl => idtac | Omodlu => idtac
          | _ => fail
          end;
          let Z := fresh in
          destruct (Int64.eq y Int64.zero) eqn: Z;
            [ apply int64_eq in Z; subst y; elim NB; vauto | ];
          try assert (Int64.eq x (Int64.repr Int64.min_signed) = falseInt64.eq y Int64.mone = false) by (
            let X := fresh in
            destruct (Int64.eq x (Int64.repr Int64.min_signed)) eqn: X;
            [
              apply int64_eq in X; subst x;
              let Y := fresh in
              destruct (Int64.eq y Int64.mone) eqn: Y;[
                  apply int64_eq in Y; subst y; elim NB; vauto
                 | now right ]
            | now left ]
          )
        | Ha: eval_mexpr _ ?a (MNint ?x), Hb: eval_mexpr _ ?b (MNint ?y) |- appcontext[ MEbinop ?op ?a ?b ] =>
          match op with
          | Oshl => idtac | Oshr => idtac | Oshru => idtac
          | _ => fail
          end;
          destruct (Int.ltu y Int.iwordsize) eqn: ?;
            [ | elim NB; vauto ]
        | Ha: eval_mexpr _ ?a (MNint64 ?x), Hb: eval_mexpr _ ?b (MNint ?y) |- appcontext[ MEbinop ?op ?a ?b ] =>
          match op with
          | Oshll => idtac | Oshrl => idtac | Oshrlu => idtac
          | _ => fail
          end;
          destruct (Int.ltu y Int64.iwordsize') eqn: ?;
            [ | elim NB; vauto ]
        end;
        try (eexists; split; vauto; fail).
    Qed.

    Lemma nconvert_def (NM: ρ ∈ γ nm) pt (PT: ρCpoints_to_gamma ge ((tmp, ε) :: σ) pt) pe :
      nconvert_def_pre pe
      ∀ lme,
        am_get (nconvert pt pe) = Some lme
        (∀ ne, In ne lme → ¬ error_mexpr ρ ne) →
        ¬ error_pexpr ge m ρC ε pe.
Proof.
      elim pe; clear pe; simpl; auto.
    - intros v () Hty lme ?; clarify; apply gamma_typ_inv in Hty;
      try destruct Hty as [ Hty | Hty ]; hsplit;
      intros _; congruence.
    - intros c.
      intros t; destruct c; intros -> DEF lme ?; clarify;
      auto.

    - intros u pe IHpe ty -> PRE.
      specialize (IHpe PRE).
      pose proof (@pexpr_get_ty_ok _ _ _ ge m ρC ε pe) as TY.
      intros lme K. clarify. hsplit.
      destruct (well_typed_unop _ _) eqn: Hwt; simpl in *; clarify.
      forward.
      intros NB.
        assert (∀ ne', In ne' q' → ¬ error_mexpr ρ ne') as NB'.
        {
          intros ne' Hne' B.
          clarify.
          repeat match goal with
                 | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
                   pose proof (fma_in' _ _ _ _ X _ Y); clear X; hsplit
                 end; clarify; hsplit; clarify;
          idtac.
          forward. apply NB. vauto.
        }
        specialize (IHpe NB').
        intros [ B | (? & EV & B) ].
        easy.
        assert (DEF := error_pexpr_def IHpe EV).
        specialize (TY _ EV DEF).
        destruct u; apply proj_sumbool_true in Hwt;
        rewrite <- Hwt in TY;
        match goal with
        | EV : eval_pexpr _ _ _ _ _ ?i,
               B : error_unop _ _ |- _ =>
          destruct i; destruct TY;
          try (inversion B; intuition eauto; discriminate);
          let CPT := fresh in
          edestructx y, nconvert_correct _ _ EV _ PT x y DEF) as (? & ? & ? & CPT & ? & _);
            [ eassumption | ];
            simpl in CPT; clarify;
            inversion B; clear B; intuition eauto;
            eapply NB; vauto;
            idtac end;
        match goal with
        | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
          pose proof (fma_in' _ _ _ _ X _ Y); clear X; hsplit
        end; clarify; eauto.

    - intros b pe1 IHpe1 pe2 IHpe2 ty Hty [ PRE1 PRE2 ] lme K.
      pose proof (nconvert_eval_mexpr NM _ PT _ PRE1) as X1.
      pose proof (nconvert_eval_mexpr NM _ PT _ PRE2) as X2.
      specialize (IHpe1 PRE1). clear PRE1.
      specialize (IHpe2 PRE2). clear PRE2.
      pose proof (@pexpr_get_ty_ok _ _ _ ge m ρC ε pe1) as TY1.
      pose proof (@pexpr_get_ty_ok _ _ _ ge m ρC ε pe2) as TY2.
      generalize dependent (pexpr_get_ty pe2). intros ty2.
      generalize dependent (pexpr_get_ty pe1). intros ty1.
      generalize dependent b; intros b.
      apply nbc_classify_intro;
        [ intros o x y Hty X1 TY1 H; exact (None_not_Some H _) | .. ];
      (intros c Hty X1 TY1 K X2 TY2 H X || intros Hty X1 TY1 K X2 TY2 H X); revert H X;
      clarify; destruct K as ( q1 & K1 & K );
      specialize (IHpe1 _ K1); specialize (X1 _ K1); destruct X1 as [ Q1 X1 ];
      clarify; destruct K as ( q2 & K2 & K );
      specialize (IHpe2 _ K2); specialize (X2 _ K2); destruct X2 as [ Q2 X2 ];
      clarify;
      intros NB;
      (assert (∀ ne : mexpr var, In ne q1 → ¬error_mexpr ρ ne) as NB1; [
        intros ne1 Hne1 E1;
         destruct q2 as [ | ne2 q2 ]; [ elim (Q2 eq_refl) | ];
         assert (In (ne1, ne2) (pair_list q1 (ne2 :: q2))) as IN by (
                                                                     apply in_pair_list; simpl; eauto
                                                                   );
         match goal with
         | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
           pose proof (fma_in' _ _ _ _ X _ Y); clear X
         end; hsplit; simpl in *; clarify; hsplit; clarify; eauto; eapply NB; eauto; vauto
       | ] );
      specialize (IHpe1 NB1);

      (assert (∀ ne : mexpr var, In ne q2 → ¬error_mexpr ρ ne) as NB2; [
        intros ne2 Hne2 E2;
         destruct q1 as [ | ne1 q1 ]; [ elim (Q1 eq_refl) | ];
         destruct (X1 _ (or_introl _ eq_refl) (NB1 _ (or_introl _ eq_refl))) as (? & ? & ?);
         wtype_num_inv; hsplit; clarify;
         assert (In (ne1, ne2) (pair_list (ne1 :: q1) q2)) as IN by (
                                                                     apply in_pair_list; simpl; eauto
                                                                   );
         match goal with
         | X : flat_map_a_spec _ _ _ _, Y : In _ _ |- _ =>
           pose proof (fma_in' _ _ _ _ X _ Y); clear X
         end; hsplit; simpl in *; clarify; hsplit; clarify; eauto;
         eapply NB; eauto; vauto
        |]);
      specialize (IHpe2 NB2);

      intros [ B | ( ? & EV1 & [ B | (? & EV2 & B ) ] ) ];
      eauto;
      match goal with
      | EV1: eval_pexpr _ _ _ _ pe1 ?i1,
        ME1 : am_get (nconvert pt pe1) = Some _ |- _ =>
        assert (DEF1 := error_pexpr_def IHpe1 EV1);
        specialize (TY1 _ EV1 DEF1);
        destruct i1; destruct TY1;
        let T := fresh in
        destruct (nconvert_correct pe1 _ EV1 _ PT _ ME1 DEF1) as (? & ? & ? & T & ? & _);
        simpl in T
      end;
      match goal with
      | EV2: eval_pexpr _ _ _ _ pe2 ?i2,
        ME2 : am_get (nconvert pt pe2) = Some _ |- _ =>
        assert (DEF2 := error_pexpr_def IHpe2 EV2);
        specialize (TY2 _ EV2 DEF2);
        destruct i2; destruct TY2;
        let T := fresh in
        destruct (nconvert_correct pe2 _ EV2 _ PT _ ME2 DEF2) as (? & ? & ? & T & ? & _);
        simpl T
      end;
      try (inversion B; auto; clarify; apply NB; vauto2);

      clarify;
      match goal with
      | H1: In ?x q1, H2: In ?y q2, K : flat_map_a_spec _ _ (pair_list q1 q2) _ |- _ =>
        generalize (fma_in' _ _ _ _ K _ (@in_pair_list _ _ _ _ (x,y) H1 H2));
          let U := fresh in
          let V := fresh in
          intros (? & U & V);
            simpl in U; clarify;
            forward;
            specialize (X2 NB2);
            hsplit; wtype_num_inv; hsplit; clarify; simpl in *; clarify
      end;
      try (inversion B; auto; clarify; apply NB; vauto2).

      Local
      Ltac q NM PT :=
      repeat match goal with
      | H : pt_eval_pexpr _ _ _ ?pe = _, EV: eval_pexpr _ _ _ _ ?pe _ |- _ =>
        generalize (pt_eval_pexpr_correct eq_refl PT EV);
          rewrite H; clear H; intros (? & ? & ?); eauto
      | H : cmp_blockset ?x ?y = _, X : _, Y : _ |- _ =>
        generalize (cmp_blockset_correct x y _ _ X Y); rewrite H; clear H; intro
      | H : is_valid_ptr ?w ?bs ?ne = true, X : _, Y : _, Z : eval_mexpr _ ?ne _ |- _ =>
        generalize (is_valid_ptr_valid NM w bs ne H _ X _ Y _ Z); clear H; intro H
      | H : is_null ?me = true, EV : eval_mexpr _ ?me _ |- _ =>
        generalize (is_null_correct NumDom nm NM H EV); clear H; intro H
      end; hsplit; clarify.
      Local
      Ltac fwd :=
        match goal with H : ∀ _ _, __, K : _ |- _ => specializex, H _ x K) end;
        forward.
      q NM PT. inversion B; clear B; clarify; eauto. fwd. hsplit. clarify. congruence.
      q NM PT. unfold Mem.weak_valid_pointer in *.
      inversion B; clear B; clarify; eauto; fwd; hsplit; clarify; try congruence.
      intuition congruence.
      q NM PT. inversion B; clear B; clarify; eauto; fwd; hsplit; clarify. intuition congruence.
      repeat match goal with X : Mem.valid_pointer _ _ _ = true |- _ => rewrite X in * end.
      simpl in *. intuition clarify.
      q NM PT. inversion B; clear B; clarify; eauto; fwd; hsplit; clarify. intuition congruence.
      repeat match goal with X : Mem.valid_pointer _ _ _ = true |- _ => rewrite X in * end.
      simpl in *. intuition clarify.
      q NM PT. inversion B; clear B; clarify; eauto. intuition congruence.
      repeat match goal with X : Mem.valid_pointer _ _ _ = true |- _ => rewrite X in * end.
      simpl in *. intuition clarify.
      q NM PT. inversion B; clear B; clarify; eauto. intuition congruence.
      repeat match goal with X : Mem.valid_pointer _ _ _ = true |- _ => rewrite X in * end.
      simpl in *. intuition clarify.
      q NM PT.
      q NM PT. unfold Mem.weak_valid_pointer in *.
      inversion B; clear B; clarify; eauto. rewrite Int.eq_true in *. clarify. congruence.
      q NM PT. unfold Mem.weak_valid_pointer in *.
      inversion B; clear B; clarify; eauto. rewrite Int.eq_true in *. clarify. congruence.
      q NM PT. unfold Mem.weak_valid_pointer in *.
      inversion B; clear B; clarify; eauto. rewrite Int.eq_true in *. clarify. congruence.
      q NM PT. unfold Mem.weak_valid_pointer in *.
      inversion B; clear B; clarify; eauto. rewrite Int.eq_true in *. clarify. congruence.
    Qed.

  End nconvert.

  Import Utf8.

  Local Instance TeltEqDec : EqDec T.elt := T.elt_eq.
  Lemma compat_upd var `{EQ: EqDec var} f ρC ρ :
    (∀ x y, xyf xf y) →
    compat var f ρC ρ →
    ∀ x v n ρC',
      ncompat v n
      ρC' ∈ upd_spec ρC x v
      compat var f ρC' (upd ρ (f x) n).
Proof.
    intros INJ H x v n ρC' V UPD c. rewrite (UPD c). clear UPD.
    unfold upd. simpl.
    destruct (eq_dec c x). subst. rewrite dec_eq_true. assumption.
    rewrite dec_eq_false; auto.
  Qed.

End Nconvert.