Module Cop


Arithmetic and logical operators for the Compcert C and Clight languages

In this module, we have kept both the old operators on value and the new ones on symbolic values, and we prove for each pair of those operators that our new operations are abstractions of the old ones, i.e. if sem_* = Some v then exists v', sem_*_expr = Some v' /\ v' == v

Require Import Coqlib.
Require Import AST.
Require Import Integers.
Require Import Floats.
Require Import Values.
Require Import Values_symbolictype.
Require Import Ctypes.
Require Import Values_symbolic.
Require Import NormaliseSpec.
Require Import Memory.

Syntax of operators.


Inductive unary_operation : Type :=
  | Onotbool : unary_operation (* boolean negation (! in C) *)
  | Onotint : unary_operation (* integer complement (~ in C) *)
  | Oneg : unary_operation (* opposite (unary -) *)
  | Oabsfloat : unary_operation. (* floating-point absolute value *)

Inductive binary_operation : Type :=
  | Oadd : binary_operation (* addition (binary +) *)
  | Osub : binary_operation (* subtraction (binary -) *)
  | Omul : binary_operation (* multiplication (binary *) *)
  | Odiv : binary_operation (* division (/) *)
  | Omod : binary_operation (* remainder (%) *)
  | Oand : binary_operation (* bitwise and (&) *)
  | Oor : binary_operation (* bitwise or (|) *)
  | Oxor : binary_operation (* bitwise xor (^) *)
  | Oshl : binary_operation (* left shift (<<) *)
  | Oshr : binary_operation (* right shift (>>) *)
  | Oeq: binary_operation (* comparison (==) *)
  | One: binary_operation (* comparison (!=) *)
  | Olt: binary_operation (* comparison (<) *)
  | Ogt: binary_operation (* comparison (>) *)
  | Ole: binary_operation (* comparison (<=) *)
  | Oge: binary_operation. (* comparison (>=) *)

Inductive incr_or_decr : Type := Incr | Decr.

Type classification and semantics of operators.


Most C operators are overloaded (they apply to arguments of various types) and their semantics depend on the types of their arguments. The following classify_* functions take as arguments the types of the arguments of an operation. They return enough information to resolve overloading for this operator applications, such as ``both arguments are floats'', or ``the first is a pointer and the second is an integer''. This classification is used in the compiler (module Cshmgen) to resolve overloading statically. The sem_* functions below compute the result of an operator application. Since operators are overloaded, the result depends both on the static types of the arguments and on their run-time values. The corresponding classify_* function is first called on the types of the arguments to resolve static overloading. It is then followed by a case analysis on the values of the arguments.

Casts and truth values


Inductive classify_cast_cases : Type :=
  | cast_case_neutral (* int|pointer -> int32|pointer *)
  | cast_case_i2i (sz2:intsize) (si2:signedness) (* int -> int *)
  | cast_case_f2f (* double -> double *)
  | cast_case_s2s (* single -> single *)
  | cast_case_f2s (* double -> single *)
  | cast_case_s2f (* single -> double *)
  | cast_case_i2f (si1: signedness) (* int -> double *)
  | cast_case_i2s (si1: signedness) (* int -> single *)
  | cast_case_f2i (sz2:intsize) (si2:signedness) (* double -> int *)
  | cast_case_s2i (sz2:intsize) (si2:signedness) (* single -> int *)
  | cast_case_l2l (* long -> long *)
  | cast_case_i2l (si1: signedness) (* int -> long *)
  | cast_case_l2i (sz2: intsize) (si2: signedness) (* long -> int *)
  | cast_case_l2f (si1: signedness) (* long -> double *)
  | cast_case_l2s (si1: signedness) (* long -> single *)
  | cast_case_f2l (si2:signedness) (* double -> long *)
  | cast_case_s2l (si2:signedness) (* single -> long *)
  | cast_case_f2bool (* double -> bool *)
  | cast_case_s2bool (* single -> bool *)
  | cast_case_l2bool (* long -> bool *)
  | cast_case_p2bool (* pointer -> bool *)
  | cast_case_struct (id1: ident) (fld1: fieldlist) (id2: ident) (fld2: fieldlist) (* struct -> struct *)
  | cast_case_union (id1: ident) (fld1: fieldlist) (id2: ident) (fld2: fieldlist) (* union -> union *)
  | cast_case_void (* any -> void *)
  | cast_case_default.

Definition classify_cast (tfrom tto: type) : classify_cast_cases :=
  match tto, tfrom with
  | Tint I32 si2 _, (Tint _ _ _ | Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _) => cast_case_neutral
  | Tint IBool _ _, Tfloat F64 _ => cast_case_f2bool
  | Tint IBool _ _, Tfloat F32 _ => cast_case_s2bool
  | Tint IBool _ _, (Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _) => cast_case_p2bool
  | Tint sz2 si2 _, Tint sz1 si1 _ => cast_case_i2i sz2 si2
  | Tint sz2 si2 _, Tfloat F64 _ => cast_case_f2i sz2 si2
  | Tint sz2 si2 _, Tfloat F32 _ => cast_case_s2i sz2 si2
  | Tfloat F64 _, Tfloat F64 _ => cast_case_f2f
  | Tfloat F32 _, Tfloat F32 _ => cast_case_s2s
  | Tfloat F64 _, Tfloat F32 _ => cast_case_s2f
  | Tfloat F32 _, Tfloat F64 _ => cast_case_f2s
  | Tfloat F64 _, Tint sz1 si1 _ => cast_case_i2f si1
  | Tfloat F32 _, Tint sz1 si1 _ => cast_case_i2s si1
  | (Tpointer _ _ | Tcomp_ptr _ _), (Tint _ _ _ | Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _) => cast_case_neutral
  | Tlong _ _, Tlong _ _ => cast_case_l2l
  | Tlong _ _, Tint sz1 si1 _ => cast_case_i2l si1
  | Tint IBool _ _, Tlong _ _ => cast_case_l2bool
  | Tint sz2 si2 _, Tlong _ _ => cast_case_l2i sz2 si2
  | Tlong si2 _, Tfloat F64 _ => cast_case_f2l si2
  | Tlong si2 _, Tfloat F32 _ => cast_case_s2l si2
  | Tfloat F64 _, Tlong si1 _ => cast_case_l2f si1
  | Tfloat F32 _, Tlong si1 _ => cast_case_l2s si1
  | (Tpointer _ _ | Tcomp_ptr _ _), Tlong _ _ => cast_case_l2i I32 Unsigned
  | Tlong si2 _, (Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _) => cast_case_i2l si2
  | Tstruct id2 fld2 _, Tstruct id1 fld1 _ => cast_case_struct id1 fld1 id2 fld2
  | Tunion id2 fld2 _, Tunion id1 fld1 _ => cast_case_union id1 fld1 id2 fld2
  | Tvoid, _ => cast_case_void
  | _, _ => cast_case_default
  end.

Semantics of casts. sem_cast v1 t1 t2 = Some v2 if value v1, viewed with static type t1, can be converted to type t2, resulting in value v2.

Definition cast_int_int (sz: intsize) (sg: signedness) (i: int) : int :=
  match sz, sg with
  | I8, Signed => Int.sign_ext 8 i
  | I8, Unsigned => Int.zero_ext 8 i
  | I16, Signed => Int.sign_ext 16 i
  | I16, Unsigned => Int.zero_ext 16 i
  | I32, _ => i
  | IBool, _ => if Int.eq i Int.zero then Int.zero else Int.one
  end.

Definition cast_int_float (si: signedness) (i: int) : float :=
  match si with
  | Signed => Float.of_int i
  | Unsigned => Float.of_intu i
  end.

Definition cast_float_int (si : signedness) (f: float) : option int :=
  match si with
  | Signed => Float.to_int f
  | Unsigned => Float.to_intu f
  end.

Definition cast_int_single (si: signedness) (i: int) : float32 :=
  match si with
  | Signed => Float32.of_int i
  | Unsigned => Float32.of_intu i
  end.

Definition cast_single_int (si : signedness) (f: float32) : option int :=
  match si with
  | Signed => Float32.to_int f
  | Unsigned => Float32.to_intu f
  end.

Definition cast_int_long (si: signedness) (i: int) : int64 :=
  match si with
  | Signed => Int64.repr (Int.signed i)
  | Unsigned => Int64.repr (Int.unsigned i)
  end.

Definition cast_long_float (si: signedness) (i: int64) : float :=
  match si with
  | Signed => Float.of_long i
  | Unsigned => Float.of_longu i
  end.

Definition cast_long_single (si: signedness) (i: int64) : float32 :=
  match si with
  | Signed => Float32.of_long i
  | Unsigned => Float32.of_longu i
  end.

Definition cast_float_long (si : signedness) (f: float) : option int64 :=
  match si with
  | Signed => Float.to_long f
  | Unsigned => Float.to_longu f
  end.

Definition cast_single_long (si : signedness) (f: float32) : option int64 :=
  match si with
  | Signed => Float32.to_long f
  | Unsigned => Float32.to_longu f
  end.

Definition sem_cast (valid_ptr: block -> int -> bool) (v: val) (t1 t2: type) : option val :=
  match classify_cast t1 t2 with
  | cast_case_neutral =>
      match v with
      | Vint _ | Vptr _ _ => Some v
      | _ => None
      end
  | cast_case_i2i sz2 si2 =>
      match v with
      | Vint i => Some (Vint (cast_int_int sz2 si2 i))
      | _ => None
      end
  | cast_case_f2f =>
      match v with
      | Vfloat f => Some (Vfloat f)
      | _ => None
      end
  | cast_case_s2s =>
      match v with
      | Vsingle f => Some (Vsingle f)
      | _ => None
      end
  | cast_case_s2f =>
      match v with
      | Vsingle f => Some (Vfloat (Float.of_single f))
      | _ => None
      end
  | cast_case_f2s =>
      match v with
      | Vfloat f => Some (Vsingle (Float.to_single f))
      | _ => None
      end
  | cast_case_i2f si1 =>
      match v with
      | Vint i => Some (Vfloat (cast_int_float si1 i))
      | _ => None
      end
  | cast_case_i2s si1 =>
      match v with
      | Vint i => Some (Vsingle (cast_int_single si1 i))
      | _ => None
      end
  | cast_case_f2i sz2 si2 =>
      match v with
      | Vfloat f =>
          match cast_float_int si2 f with
          | Some i => Some (Vint (cast_int_int sz2 si2 i))
          | None => None
          end
      | _ => None
      end
  | cast_case_s2i sz2 si2 =>
      match v with
      | Vsingle f =>
          match cast_single_int si2 f with
          | Some i => Some (Vint (cast_int_int sz2 si2 i))
          | None => None
          end
      | _ => None
      end
  | cast_case_f2bool =>
      match v with
      | Vfloat f =>
          Some(Vint(if Float.cmp Ceq f Float.zero then Int.zero else Int.one))
      | _ => None
      end
  | cast_case_s2bool =>
      match v with
      | Vsingle f =>
          Some(Vint(if Float32.cmp Ceq f Float32.zero then Int.zero else Int.one))
      | _ => None
      end
  | cast_case_p2bool =>
      match v with
      | Vint i => Some (Vint (cast_int_int IBool Signed i))
      | Vptr b o => if valid_ptr b o
                    then Some (Vint Int.one)
                    else None
      | _ => None
      end
  | cast_case_l2l =>
      match v with
      | Vlong n => Some (Vlong n)
      | _ => None
      end
  | cast_case_i2l si =>
      match v with
      | Vint n => Some(Vlong (cast_int_long si n))
      | _ => None
      end
  | cast_case_l2i sz si =>
      match v with
      | Vlong n => Some(Vint (cast_int_int sz si (Int.repr (Int64.unsigned n))))
      | _ => None
      end
  | cast_case_l2bool =>
      match v with
      | Vlong n =>
          Some(Vint(if Int64.eq n Int64.zero then Int.zero else Int.one))
      | _ => None
      end
  | cast_case_l2f si1 =>
      match v with
      | Vlong i => Some (Vfloat (cast_long_float si1 i))
      | _ => None
      end
  | cast_case_l2s si1 =>
      match v with
      | Vlong i => Some (Vsingle (cast_long_single si1 i))
      | _ => None
      end
  | cast_case_f2l si2 =>
      match v with
      | Vfloat f =>
          match cast_float_long si2 f with
          | Some i => Some (Vlong i)
          | None => None
          end
      | _ => None
      end
  | cast_case_s2l si2 =>
      match v with
      | Vsingle f =>
          match cast_single_long si2 f with
          | Some i => Some (Vlong i)
          | None => None
          end
      | _ => None
      end
  | cast_case_struct id1 fld1 id2 fld2 =>
      match v with
      | Vptr b ofs =>
          if ident_eq id1 id2 && fieldlist_eq fld1 fld2 then Some v else None
      | _ => None
      end
  | cast_case_union id1 fld1 id2 fld2 =>
      match v with
      | Vptr b ofs =>
          if ident_eq id1 id2 && fieldlist_eq fld1 fld2 then Some v else None
      | _ => None
      end
  | cast_case_void =>
      Some v
  | cast_case_default =>
      None
  end.

Require Import Values_symbolictype.

Definition expr_cast_int_int sz2 si2 (e:expr_sym) : expr_sym :=
  match sz2 with
      IBool => Eunop OpBoolval Tint e
    | I32 => e
    | _ => Eunop (match si2 with
                      Signed => OpSignext
                    | Unsigned => OpZeroext
                  end match sz2 with
                          I8 => 8 | I16 => 16 | I32 => 32 | _ => 1
                      end)
                 Tint e
  end.

Definition sg_conv (s:signedness) :=
  match s with
      Signed => SESigned
    | Unsigned => SEUnsigned
  end.

Definition expr_cast_gen (fromt: styp) (froms:signedness)
           (tot: styp) (tos:signedness) (e:expr_sym) : expr_sym :=
  Eunop (OpConvert (sg_conv froms) (tot,sg_conv tos)) fromt e.

Definition sem_cast_expr (v: expr_sym) (t1 t2: type) : option expr_sym :=
  match classify_cast t1 t2 with
    | cast_case_neutral => Some v
    | cast_case_i2i sz2 si2 => Some (expr_cast_int_int sz2 si2 v)
    | cast_case_f2f => Some v
    | cast_case_s2s => Some v
    | cast_case_s2f => Some (expr_cast_gen Tsingle Signed Tfloat Signed v)
    | cast_case_f2s => Some (expr_cast_gen Tfloat Signed Tsingle Signed v)
    | cast_case_i2f si1 => Some (expr_cast_gen Tint si1 Tfloat Signed v)
    | cast_case_i2s si1 => Some (expr_cast_gen Tint si1 Tsingle Signed v)
    | cast_case_f2i sz2 si2 =>
      Some (expr_cast_int_int sz2 si2 (expr_cast_gen Tfloat Signed Tint si2 v))
    | cast_case_s2i sz2 si2 =>
      Some (expr_cast_int_int sz2 si2 (expr_cast_gen Tsingle Signed Tint si2 v))
    | cast_case_f2bool =>
      Some (
          Eunop OpNotbool Tint
                (Ebinop (OpCmp SESigned Ceq) Tfloat Tfloat v (Eval (Efloat Float.zero))))
    | cast_case_s2bool =>
      Some (Eunop OpNotbool Tint
                  (Ebinop (OpCmp SESigned Ceq) Tsingle Tsingle v (Eval (Esingle Float32.zero))))
    | cast_case_p2bool =>
      Some (Eunop OpNotbool Tint
                  (Ebinop (OpCmp SEUnsigned Ceq) Tint Tint v (Eval (Eint Int.zero))))
    | cast_case_l2l => Some v
    | cast_case_i2l si => Some (expr_cast_gen Tint si Tlong Signed v)
    | cast_case_l2i sz si => Some (expr_cast_int_int sz si (Val.loword v))
    | cast_case_l2bool =>
      Some (Eunop OpNotbool Tint
                  (Ebinop (OpCmp SESigned Ceq) Tlong Tlong v (Eval (Elong Int64.zero))))
    | cast_case_l2f si1 =>
      Some (expr_cast_gen Tlong si1 Tfloat Signed v)
    | cast_case_l2s si1 =>
      Some (expr_cast_gen Tlong si1 Tsingle Signed v)
    | cast_case_f2l si2 =>
      Some (expr_cast_gen Tfloat Signed Tlong si2 v)
    | cast_case_s2l si2 =>
      Some (expr_cast_gen Tsingle Signed Tlong si2 v)
    | cast_case_struct id1 fld1 id2 fld2 =>
      if ident_eq id1 id2 && fieldlist_eq fld1 fld2 then Some v else None
    | cast_case_union id1 fld1 id2 fld2 =>
      if ident_eq id1 id2 && fieldlist_eq fld1 fld2 then Some v else None
    | cast_case_void => Some v
    | cast_case_default => None
  end.

Lemma normalise_boolval:
  forall i,
    Normalise.eq_modulo_normalise
      (Eunop OpBoolval Tint (Eval (Eint i)))
      (Eval (Eint (if Int.eq i Int.zero then Int.zero else Int.one))).
Proof.
  intros; intro; intros.
  Normalise.destr_wt.
  simpl in e; intuition. inv e.
  apply Normalise.norm_eq''; simpl; intuition try congruence. rewrite <- H0; simpl.
  unfold NormaliseSpec.eunop, NormaliseSpec.unop, NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'. simpl.
  destruct (Int.eq i Int.zero); simpl; auto.
Qed.

Lemma normalise_f2s:
  forall f,
    Normalise.eq_modulo_normalise
      (Eval (Esingle (Float.to_single f)))
      (Eunop (OpConvert SESigned (Tsingle, SESigned)) Tfloat (Eval (Efloat f))).
Proof.
  intros; intro; intros.
  Normalise.destr_wt.
  simpl in e; intuition. inv e.
  apply Normalise.norm_eq''; simpl; intuition try congruence.
Qed.

Lemma normalise_s2f:
  forall f,
    Normalise.eq_modulo_normalise
      (Eval (Efloat (Float.of_single f)))
      (Eunop (OpConvert SESigned (Tfloat,SESigned)) Tsingle (Eval (Esingle f))).
Proof.
  unfold expr_cast_gen.
  intros; intro; intros.
  Normalise.destr_wt.
  simpl in e; intuition. inv e.
  apply Normalise.norm_eq''; simpl; intuition try congruence.
Qed.

Definition result_of_val (v:val) : result :=
  match v with
      Vint _ => Int
    | Vptr _ _ => Ptr
    | Vlong _ => Long
    | Vfloat _ => Float
    | Vsingle _ => Single
    | Vundef => Int
  end.

Notation expr_of_val v :=
  (Eval (NormaliseSpec.sval_of_val v)).

Definition norm_eq_compat (m: mem) (e:expr_sym) (v:val) : Prop :=
  Normalise.normalise (Mem.bounds_of_block m) (Mem.nat_mask m) e (result_of_val v) =
  Normalise.normalise (Mem.bounds_of_block m) (Mem.nat_mask m) (expr_of_val v) (result_of_val v).


Definition vptr (m: mem) :=
  fun b i => Mem.valid_pointer m b (Int.unsigned i).

Lemma valid_pointer:
  forall m b i,
    Mem.valid_pointer m b i = true ->
    in_bound i (Mem.bounds_of_block m b).
Proof.
  intros.
  unfold Mem.valid_pointer in H.
  destruct (Mem.perm_dec m b i Cur Nonempty); try discriminate.
  eapply Mem.perm_bounds; eauto.
Qed.

Ltac rew_tt H0 :=
  match type of H0 with
      context[Normalise.normalise ?sz ?msk (Eval ?v) ?t] =>
      first [
          erewrite Normalise.normalise_int_int in H0; eauto
        | erewrite Normalise.normalise_long_long in H0; eauto
        | erewrite Normalise.normalise_float_float in H0; eauto
        | erewrite Normalise.normalise_single_single in H0; eauto
        ]
  end.

Ltac norm_eval_wt :=
  match goal with
      H: Normalise.normalise _ _ ?v ?t = _ |- _ =>
      match goal with
          H1: tcheck_expr v = _ |- _ => fail 1
        | _ => destruct (tcheck_expr_dec v (NormaliseSpec.typ_of_result t));
              [| rewrite Normalise.expr_wt in H; try congruence; auto; fail]
      end
  end.

Ltac unfold_eval :=
    repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
            NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop',
            NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl).

Ltac rewrite_norm :=
  repeat match goal with
      |- context[NormaliseSpec.eSexpr ?al ?em _ _ ?t ?v] =>
      repeat match goal with
          H: Normalise.normalise _ _ v ?tt = _ ,
          H1 : NormaliseSpec.compat ?al ?sz ?msk |- _ =>
          let x := fresh "H" in
          let A := fresh "A" in
          let B := fresh "B" in
          generalize (Normalise.norm_correct2 sz msk v tt);
            try rewrite H; intro x;
            edestruct (NormaliseSpec.IsNorm2.eval_ok eu eb sz msk v tt _ x)
                      as [|[A B]]
            ; eauto; try congruence; simpl in *; rewrite B; simpl
      end
  end.

Lemma sem_cast_expr_ok:
  forall v t1 t2 v' m,
    sem_cast (vptr m) v t1 t2 = Some v' ->
    exists v'',
      sem_cast_expr (expr_of_val v) t1 t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_cast, sem_cast_expr.
  intros.
  destruct (classify_cast t1 t2) eqn:?;
  destruct v; simpl; try congruence; inv H; eexists; simpl; split; eauto;
  try red; intros; simpl; symmetry; try reflexivity.
  - destruct sz2; destruct si2; simpl; symmetry;
    try solve [apply Normalise.normalise_sign_ext_int
              |apply Normalise.normalise_zero_ext_int
              |apply Normalise.normalize_sign_ext_32; simpl; auto
              |apply Normalise.normalize_zero_ext_32; simpl; auto
              | apply normalise_boolval
              ];
    reflexivity.
  - apply normalise_f2s.
  - apply normalise_s2f.
  - unfold expr_cast_gen, cast_int_float.
    Normalise.destr_wt.
    simpl in e; intuition.
    destruct si1; apply Normalise.norm_eq''; simpl; intuition try congruence.
  - unfold expr_cast_gen, cast_int_single.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct si1; apply Normalise.norm_eq''; simpl; intuition try congruence.
    compute.
    rewrite Float32.of_int_double. auto.
    compute.
    rewrite Float32.of_intu_double. auto.
  - revert H1; destr_cond_match; try congruence.
    intro A; inv A. unfold expr_cast_gen.
    destruct sz2; destruct si2; simpl in *;
    Normalise.destr_wt;
    simpl in e; intuition; inv e;
    apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat unfold_eval;
    unfold NormaliseSpec.convert_t;
    unfold Values.Val.intoffloat, Values.Val.intuoffloat;
    try rewrite Heqo; simpl; try reflexivity.
    destruct (Int.eq i Int.zero); simpl; auto.
    destruct (Int.eq i Int.zero); simpl; auto.
  - revert H1; destr_cond_match; try congruence.
    intro A; inv A. unfold expr_cast_gen.
    destruct sz2; destruct si2; simpl in *;
    Normalise.destr_wt;
    simpl in e; intuition; inv e;
    apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
    NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
    unfold NormaliseSpec.convert_t;
    unfold Values.Val.intofsingle, Values.Val.intuofsingle;
    rewrite Heqo; simpl; try reflexivity.
    destruct (Int.eq i Int.zero); simpl; auto.
    destruct (Int.eq i Int.zero); simpl; auto.
  - unfold expr_cast_gen, cast_int_long.
    Normalise.destr_wt.
    simpl in e; intuition.
    destruct si1; apply Normalise.norm_eq''; simpl; intuition try congruence.
  - unfold expr_cast_gen.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct sz2; destruct si2; apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl).
    destruct (Int.eq _ Int.zero); simpl; auto.
    destruct (Int.eq _ Int.zero); simpl; auto.
  - unfold expr_cast_gen.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct si1; apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl).
  - unfold expr_cast_gen.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct si1; apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
  - unfold expr_cast_gen.
    revert H1; destruct (cast_float_long si2 f) eqn:?; try congruence.
    unfold cast_float_long in Heqo.
    intro A; inv A.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct si2; apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
    unfold NormaliseSpec.convert_t, Values.Val.longoffloat, Values.Val.longuoffloat; rewrite Heqo;
    simpl; auto.
  - unfold expr_cast_gen.
    revert H1; destruct (cast_single_long si2 f) eqn:?; try congruence.
    unfold cast_single_long in Heqo.
    intro A; inv A.
    Normalise.destr_wt.
    simpl in e; intuition. inv e.
    destruct si2; apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
    unfold NormaliseSpec.convert_t, Values.Val.longofsingle, Values.Val.longuofsingle; rewrite Heqo;
    simpl; auto.
  - Normalise.destr_wt.
     simpl in e. inv e.
     apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
     repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
             NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop',
             NormaliseSpec.ebinop, NormaliseSpec.binop,
             NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl).
     destruct (Float.cmp Ceq f Float.zero); simpl.
     rewrite Int.eq_false; simpl; auto. discriminate.
     rewrite Int.eq_true; simpl; auto.
  - Normalise.destr_wt.
     simpl in e. inv e.
     apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
     repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
             NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop',
             NormaliseSpec.ebinop, NormaliseSpec.binop,
             NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl).
     destruct (Float32.cmp Ceq f Float32.zero); simpl.
     rewrite Int.eq_false; simpl; auto. discriminate.
     rewrite Int.eq_true; simpl; auto.
  - Normalise.destr_wt.
     simpl in e. inv e.
     apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
     repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
             NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop',
             NormaliseSpec.ebinop, NormaliseSpec.binop,
             NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl).
     destruct (Int64.eq i Int64.zero); simpl.
     rewrite Int.eq_false; simpl; auto. discriminate.
     rewrite Int.eq_true; simpl; auto.
  - Normalise.destr_wt.
     simpl in e. inv e.
     apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
     repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
             NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop',
             NormaliseSpec.ebinop, NormaliseSpec.binop,
             NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl).
     destruct (Int.eq i Int.zero); simpl.
     rewrite Int.eq_false; simpl; auto. discriminate.
     rewrite Int.eq_true; simpl; auto.
  - destruct (vptr m b i) eqn:?; try congruence. inv H1.
    Normalise.destr_wt.
     simpl in e. inv e.
     apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl; intuition try congruence.
     repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
             NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop',
             NormaliseSpec.ebinop, NormaliseSpec.binop,
             NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl).
     destruct (Int.eq _ Int.zero) eqn:?; simpl.
     rewrite Int.eq_false; simpl; auto.
     apply valid_pointer in Heqb0.
     generalize (Int.eq_spec (Int.add (alloc b) i) Int.zero).
     rewrite Heqb1; intro A.
     destruct H.
     specialize (addr_space b _ Heqb0).
     rewrite A in addr_space.
     change (Int.unsigned Int.zero) with 0 in addr_space.
     omega.
     discriminate.
     rewrite Int.eq_true; simpl; auto.
  - destr_cond_match; try congruence. inv H1. auto.
  - destr_cond_match; try congruence. inv H1. auto.
Qed.

The following describes types that can be interpreted as a boolean: integers, floats, pointers. It is used for the semantics of the ! and ? operators, as well as the if, while, and for statements.

Inductive classify_bool_cases : Type :=
  | bool_case_i (* integer *)
  | bool_case_f (* double float *)
  | bool_case_s (* single float *)
  | bool_case_p (* pointer *)
  | bool_case_l (* long *)
  | bool_default.

Definition classify_bool (ty: type) : classify_bool_cases :=
  match typeconv ty with
  | Ctypes.Tint _ _ _ => bool_case_i
  | Tpointer _ _ | Tcomp_ptr _ _ => bool_case_p
  | Ctypes.Tfloat F64 _ => bool_case_f
  | Ctypes.Tfloat F32 _ => bool_case_s
  | Ctypes.Tlong _ _ => bool_case_l
  | _ => bool_default
  end.

Interpretation of values as truth values. Non-zero integers, non-zero floats and non-null pointers are considered as true. The integer zero (which also represents the null pointer) and the float 0.0 are false.

Definition bool_val (vptr: block -> int -> bool) (v: val) (t: type) : option bool :=
  match classify_bool t with
  | bool_case_i =>
      match v with
      | Vint n => Some (negb (Int.eq n Int.zero))
      | _ => None
      end
  | bool_case_f =>
      match v with
      | Vfloat f => Some (negb (Float.cmp Ceq f Float.zero))
      | _ => None
      end
  | bool_case_s =>
      match v with
      | Vsingle f => Some (negb (Float32.cmp Ceq f Float32.zero))
      | _ => None
      end
  | bool_case_p =>
      match v with
      | Vint n => Some (negb (Int.eq n Int.zero))
      | Vptr b ofs => if vptr b ofs then Some true else None
      | _ => None
      end
  | bool_case_l =>
      match v with
      | Vlong n => Some (negb (Int64.eq n Int64.zero))
      | _ => None
      end
  | bool_default => None
  end.

Definition bool_expr (v: expr_sym) (t: type) : expr_sym :=
  match classify_bool t with
  | bool_case_i =>
    Ebinop (OpCmp SESigned Cne) Tint Tint v (Eval (Eint Int.zero))
  | bool_case_f =>
    Ebinop (OpCmp SESigned Cne) Tfloat Tfloat v (Eval (Efloat Float.zero))
  | bool_case_s =>
    Ebinop (OpCmp SESigned Cne) Tsingle Tsingle v (Eval (Esingle Float32.zero))
  | bool_case_p =>
    Ebinop (OpCmp SEUnsigned Cne) Tint Tint v (Eval (Eint Int.zero))
  | bool_case_l =>
    Ebinop (OpCmp SESigned Cne) Tlong Tlong v (Eval (Elong Int64.zero))
  | bool_default =>
    Ebinop (OpCmp SESigned Cne) Tint Tint v (Eval (Eint Int.zero))
  end.

Lemma bool_val_expr_ok:
  forall m v t v',
    bool_val (vptr m) v t = Some v' ->
    exists v'',
      bool_expr (expr_of_val v) t = v'' /\
      norm_eq_compat m v'' (Values.Val.of_bool v').
Proof.
  unfold bool_val, bool_expr.
  intros.
  assert (classify_bool t = bool_case_p \/ classify_bool t <> bool_case_p).
  clear. destruct classify_bool; intuition try congruence.
  destruct H0.
  - rewrite H0 in *.
    destruct v; try congruence.
    + inv H.
      eexists; split; eauto;
      unfold Val.of_bool;
      try destruct (negb _) eqn:?;
      red; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
      inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
      simpl; intuition try congruence;
      repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
              NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl);
      try rewrite Float.cmp_ne_eq;
      try rewrite Float32.cmp_ne_eq;
      try rewrite Heqb; simpl; auto.
    + destruct (vptr m b i) eqn:?; try discriminate.
      inv H. eexists; split; eauto.
      red; simpl; intros.
      apply valid_pointer in Heqb0.
      Normalise.destr_wt; simpl in *; intuition try congruence;
      inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
      simpl; intuition try congruence;
      unfold_eval.
      destruct (Int.eq _ _) eqn:?; simpl; auto.
      generalize (Int.eq_spec (Int.add (alloc b) i) Int.zero).
      inv H.
      specialize (addr_space _ _ Heqb0).
      rewrite Heqb1; intro A.
      rewrite A in addr_space.
      change (Int.unsigned Int.zero) with 0 in addr_space.
      omega.
  - destruct (classify_bool t) eqn:?; simpl;
    destruct v; try congruence; inv H;
    eexists; split; eauto;
    unfold Val.of_bool; try destruct (negb _) eqn:?;
                            red; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
    inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
    simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
            NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop'; simpl);
    try rewrite Float.cmp_ne_eq;
    try rewrite Float32.cmp_ne_eq;
    try rewrite Heqb; simpl; auto.
Qed.

Unary operators


Boolean negation


Definition sem_notbool (vptr: block -> int -> bool) (v: val) (ty: type) : option val :=
  match classify_bool ty with
  | bool_case_i =>
      match v with
      | Vint n => Some (Values.Val.of_bool (Int.eq n Int.zero))
      | _ => None
      end
  | bool_case_f =>
      match v with
      | Vfloat f => Some (Values.Val.of_bool (Float.cmp Ceq f Float.zero))
      | _ => None
      end
  | bool_case_s =>
      match v with
      | Vsingle f => Some (Values.Val.of_bool (Float32.cmp Ceq f Float32.zero))
      | _ => None
      end
  | bool_case_p =>
      match v with
      | Vint n => Some (Values.Val.of_bool (Int.eq n Int.zero))
      | Vptr b ofs => if vptr b ofs then Some Vfalse else None
      | _ => None
      end
  | bool_case_l =>
      match v with
      | Vlong n => Some (Values.Val.of_bool (Int64.eq n Int64.zero))
      | _ => None
      end
  | bool_default => None
  end.

Definition sem_notbool_expr (v: expr_sym) (ty: type) : expr_sym :=
  match classify_bool ty with
  | bool_case_i =>
    Eunop OpNotbool Tint v
  | bool_case_f =>
    Ebinop (OpCmp SESigned Ceq) Tfloat Tfloat v (Eval (Efloat Float.zero))
  | bool_case_s =>
    Ebinop (OpCmp SESigned Ceq) Tsingle Tsingle v (Eval (Esingle Float32.zero))
  | bool_case_p =>
    Eunop OpNotbool Tint v
  | bool_case_l =>
    Ebinop (OpCmp SESigned Ceq) Tlong Tlong v (Eval (Elong Int64.zero))
  | bool_default =>
    Eunop OpNotbool Tint v
  end.


Lemma notbool_expr_ok:
  forall m v t v',
    sem_notbool (vptr m) v t = Some v' ->
    exists v'',
      sem_notbool_expr (expr_of_val v) t = v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_notbool, sem_notbool_expr.
  intros.
  assert (classify_bool t = bool_case_p \/ classify_bool t <> bool_case_p).
  clear. destruct classify_bool; intuition try congruence.
  destruct H0.
  - rewrite H0 in *.
    destruct v; try congruence.
    + inv H.
      eexists; split; eauto;
      unfold Values.Val.of_bool.
      destruct (Int.eq i Int.zero) eqn:?;
      red; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
      inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
      simpl; intuition try congruence;
      repeat (unfold NormaliseSpec.eunop, NormaliseSpec.unop,
              NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
      try rewrite Float.cmp_ne_eq;
      try rewrite Float32.cmp_ne_eq;
      try rewrite Heqb; simpl; auto.
    + destruct (vptr m b i) eqn:?; try discriminate.
      inv H. eexists; split; eauto.
      red; simpl; intros.
      apply valid_pointer in Heqb0.
      Normalise.destr_wt; simpl in *; intuition try congruence;
      inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
      simpl; intuition try congruence;
      unfold_eval.
      destruct (Int.eq _ _) eqn:?; simpl; auto.
      generalize (Int.eq_spec (Int.add (alloc b) i) Int.zero).
      inv H.
      specialize (addr_space _ _ Heqb0).
      rewrite Heqb1; intro A.
      rewrite A in addr_space.
      change (Int.unsigned Int.zero) with 0 in addr_space.
      omega.
  - destruct (classify_bool t) eqn:?; simpl;
    destruct v; try congruence; inv H;
    eexists; split; eauto;
    unfold Values.Val.of_bool; destr_cond_match;
    red; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
    inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
    simpl; intuition try congruence;
    repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
            NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop',
            NormaliseSpec.eunop, NormaliseSpec.unop,
            NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
    try rewrite Float.cmp_ne_eq;
    try rewrite Float32.cmp_ne_eq;
    try rewrite Heqb; simpl; auto.
Qed.

Opposite and absolute value


Inductive classify_neg_cases : Type :=
  | neg_case_i(s: signedness) (* int *)
  | neg_case_f (* double float *)
  | neg_case_s (* single float *)
  | neg_case_l(s: signedness) (* long *)
  | neg_default.

Definition classify_neg (ty: type) : classify_neg_cases :=
  match ty with
  | Ctypes.Tint I32 Unsigned _ => neg_case_i Unsigned
  | Ctypes.Tint _ _ _ => neg_case_i Signed
  | Ctypes.Tfloat F64 _ => neg_case_f
  | Ctypes.Tfloat F32 _ => neg_case_s
  | Ctypes.Tlong si _ => neg_case_l si
  | _ => neg_default
  end.

Definition sem_neg (v: val) (ty: type) : option val :=
  match classify_neg ty with
  | neg_case_i sg =>
      match v with
      | Vint n => Some (Vint (Int.neg n))
      | _ => None
      end
  | neg_case_f =>
      match v with
      | Vfloat f => Some (Vfloat (Float.neg f))
      | _ => None
      end
  | neg_case_s =>
      match v with
      | Vsingle f => Some (Vsingle (Float32.neg f))
      | _ => None
      end
  | neg_case_l sg =>
      match v with
      | Vlong n => Some (Vlong (Int64.neg n))
      | _ => None
      end
  | neg_default => None
  end.


Definition sem_neg_expr (v: expr_sym) (ty: type) : expr_sym :=
  match classify_neg ty with
  | neg_case_i sg => Eunop OpNeg Tint v
  | neg_case_f => Eunop OpNeg Tfloat v
  | neg_case_s => Eunop OpNeg Tsingle v
  | neg_case_l sg => Eunop OpNeg Tlong v
  | neg_default => Eunop OpNeg Tint v
  end.

Lemma neg_expr_ok:
  forall v t v',
    sem_neg v t = Some v' ->
    exists v'',
      sem_neg_expr (expr_of_val v) t = v'' /\
      Normalise.eq_modulo_normalise (expr_of_val v') v''.
Proof.
  unfold sem_neg, sem_neg_expr.
  intros.
  destruct (classify_neg t) eqn:?; simpl;
    destruct v; try congruence; inv H;
    eexists; split; simpl; eauto;
    intro; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
    inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
    simpl; intuition try congruence.
Qed.

Definition sem_absfloat (v: val) (ty: type) : option val :=
  match classify_neg ty with
  | neg_case_i sg =>
      match v with
      | Vint n => Some (Vfloat (Float.abs (cast_int_float sg n)))
      | _ => None
      end
  | neg_case_f =>
      match v with
      | Vfloat f => Some (Vfloat (Float.abs f))
      | _ => None
      end
  | neg_case_s =>
      match v with
      | Vsingle f => Some (Vfloat (Float.abs (Float.of_single f)))
      | _ => None
      end
  | neg_case_l sg =>
      match v with
      | Vlong n => Some (Vfloat (Float.abs (cast_long_float sg n)))
      | _ => None
      end
  | neg_default => None
  end.

Definition sem_absfloat_expr (v: expr_sym) (ty: type) : expr_sym :=
  match classify_neg ty with
  | neg_case_i sg =>
    Eunop OpAbsf Tfloat (expr_cast_gen Tint sg Tfloat Signed v)
  | neg_case_f =>
    Eunop OpAbsf Tfloat v
  | neg_case_s =>
    Eunop OpAbsf Tfloat (expr_cast_gen Tsingle Signed Tfloat Signed v)
  | neg_case_l sg =>
    Eunop OpAbsf Tfloat (expr_cast_gen Tlong sg Tfloat Signed v)
  | neg_default =>
    Eunop OpAbsf Tfloat (expr_cast_gen Tint Signed Tfloat Signed v)
  end.


Lemma absfloat_expr_ok:
  forall v t v',
    sem_absfloat v t = Some v' ->
    exists v'',
      sem_absfloat_expr (expr_of_val v) t = v'' /\
      Normalise.eq_modulo_normalise (expr_of_val v') v''.
Proof.
  unfold sem_absfloat, sem_absfloat_expr.
  intros. unfold expr_cast_gen.
  destruct (classify_neg t) eqn:?; simpl;
    try destruct s; destruct v; try congruence; inv H;
    eexists; split; simpl; eauto;
    intro; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
    inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
    simpl; intuition try congruence.
Qed.


Bitwise complement


Inductive classify_notint_cases : Type :=
  | notint_case_i(s: signedness) (* int *)
  | notint_case_l(s: signedness) (* long *)
  | notint_default.

Definition classify_notint (ty: type) : classify_notint_cases :=
  match ty with
  | Ctypes.Tint I32 Unsigned _ => notint_case_i Unsigned
  | Ctypes.Tint _ _ _ => notint_case_i Signed
  | Ctypes.Tlong si _ => notint_case_l si
  | _ => notint_default
  end.

Definition sem_notint (v: val) (ty: type): option val :=
  match classify_notint ty with
  | notint_case_i sg =>
      match v with
      | Vint n => Some (Vint (Int.not n))
      | _ => None
      end
  | notint_case_l sg =>
      match v with
      | Vlong n => Some (Vlong (Int64.not n))
      | _ => None
      end
  | notint_default => None
  end.


Definition sem_notint_expr (v: expr_sym) (ty: type): expr_sym :=
  match classify_notint ty with
  | notint_case_i sg =>
    Eunop OpNot Tint v
  | notint_case_l sg =>
    Eunop OpNot Tlong v
  | notint_default =>
    Eunop OpNot Tint v
  end.


Lemma notint_expr_ok:
  forall v t v',
    sem_notint v t = Some v' ->
    exists v'',
      sem_notint_expr (expr_of_val v) t = v'' /\
      Normalise.eq_modulo_normalise (expr_of_val v') v''.
Proof.
  unfold sem_notint, sem_notint_expr.
  intros.
  destruct (classify_notint t) eqn:?; simpl;
    try destruct s; destruct v; try congruence; inv H;
    eexists; split; simpl; eauto;
    intro; intros; Normalise.destr_wt; simpl in *; intuition try congruence;
    inv e; apply Normalise.norm_eq''; simpl; try rewrite <- H0;
    simpl; intuition try congruence.
Qed.

Binary operators


For binary operations, the "usual binary conversions" consist in

Inductive binarith_cases: Type :=
  | bin_case_i (s: signedness) (* at int type *)
  | bin_case_l (s: signedness) (* at long int type *)
  | bin_case_f (* at double float type *)
  | bin_case_s (* at single float type *)
  | bin_default. (* error *)

Definition classify_binarith (ty1: type) (ty2: type) : binarith_cases :=
  match ty1, ty2 with
  | Ctypes.Tint I32 Unsigned _, Ctypes.Tint _ _ _ => bin_case_i Unsigned
  | Ctypes.Tint _ _ _, Ctypes.Tint I32 Unsigned _ => bin_case_i Unsigned
  | Ctypes.Tint _ _ _, Ctypes.Tint _ _ _ => bin_case_i Signed
  | Ctypes.Tlong Signed _, Ctypes.Tlong Signed _ => bin_case_l Signed
  | Ctypes.Tlong _ _, Ctypes.Tlong _ _ => bin_case_l Unsigned
  | Ctypes.Tlong sg _, Ctypes.Tint _ _ _ => bin_case_l sg
  | Ctypes.Tint _ _ _, Ctypes.Tlong sg _ => bin_case_l sg
  | Ctypes.Tfloat F32 _, Ctypes.Tfloat F32 _ => bin_case_s
  | Ctypes.Tfloat _ _, Ctypes.Tfloat _ _ => bin_case_f
  | Ctypes.Tfloat F64 _, (Ctypes.Tint _ _ _ | Ctypes.Tlong _ _) => bin_case_f
  | (Ctypes.Tint _ _ _ | Ctypes.Tlong _ _), Ctypes.Tfloat F64 _ => bin_case_f
  | Ctypes.Tfloat F32 _, (Ctypes.Tint _ _ _ | Ctypes.Tlong _ _) => bin_case_s
  | (Ctypes.Tint _ _ _ | Ctypes.Tlong _ _), Ctypes.Tfloat F32 _ => bin_case_s
  | _, _ => bin_default
  end.

The static type of the result. Both arguments are converted to this type before the actual computation.

Definition binarith_type (c: binarith_cases) : type :=
  match c with
  | bin_case_i sg => Ctypes.Tint I32 sg noattr
  | bin_case_l sg => Ctypes.Tlong sg noattr
  | bin_case_f => Ctypes.Tfloat F64 noattr
  | bin_case_s => Ctypes.Tfloat F32 noattr
  | bin_default => Tvoid
  end.

Definition sem_binarith
           (vptr: block -> int -> bool)
    (sem_int: signedness -> int -> int -> option val)
    (sem_long: signedness -> int64 -> int64 -> option val)
    (sem_float: float -> float -> option val)
    (sem_single: float32 -> float32 -> option val)
    (v1: val) (t1: type) (v2: val) (t2: type) : option val :=
  let c := classify_binarith t1 t2 in
  let t := binarith_type c in
  match sem_cast vptr v1 t1 t with
  | None => None
  | Some v1' =>
  match sem_cast vptr v2 t2 t with
  | None => None
  | Some v2' =>
  match c with
  | bin_case_i sg =>
      match v1', v2' with
      | Vint n1, Vint n2 => sem_int sg n1 n2
      | _, _ => None
      end
  | bin_case_f =>
      match v1', v2' with
      | Vfloat n1, Vfloat n2 => sem_float n1 n2
      | _, _ => None
      end
  | bin_case_s =>
      match v1', v2' with
      | Vsingle n1, Vsingle n2 => sem_single n1 n2
      | _, _ => None
      end
  | bin_case_l sg =>
      match v1', v2' with
      | Vlong n1, Vlong n2 => sem_long sg n1 n2
      | _, _ => None
      end
  | bin_default => None
  end end end.


Definition sem_binarith_expr
           (sem_int: signedness -> expr_sym -> expr_sym -> option expr_sym)
           (sem_long: signedness -> expr_sym -> expr_sym -> option expr_sym)
           (sem_float: expr_sym -> expr_sym -> option expr_sym)
           (sem_single: expr_sym -> expr_sym -> option expr_sym)
           (v1: expr_sym) (t1: type) (v2: expr_sym) (t2: type) : option expr_sym :=
  let c := classify_binarith t1 t2 in
  let t := binarith_type c in
  match sem_cast_expr v1 t1 t with
    | None => None
    | Some v1' =>
      match sem_cast_expr v2 t2 t with
        | None => None
        | Some v2' =>
          match c with
            | bin_case_i sg =>
              sem_int sg v1' v2'
            | bin_case_f =>
              sem_float v1' v2'
            | bin_case_s =>
              sem_single v1' v2'
            | bin_case_l sg =>
              sem_long sg v1' v2'
            | bin_default => None
          end
      end
  end.

Lemma expr_binarith_ok:
  forall m si sl sf ss si' sl' sf' ss' v1 t1 v2 t2 v'
         (si_same: forall sg n1 n2 v,
                     si sg n1 n2 = Some v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vint n1) ->
                       norm_eq_compat m v2 (Vint n2) ->
                     exists v',
                       si' sg v1 v2 = Some v' /\
                       norm_eq_compat m v' v)
         (sl_same: forall sg n1 n2 v,
                     sl sg n1 n2 = Some v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vlong n1) ->
                       norm_eq_compat m v2 (Vlong n2) ->
                     exists v',
                       sl' sg v1 v2 = Some v' /\
                       norm_eq_compat m v' v)
         (sf_same: forall n1 n2 v,
                     sf n1 n2 = Some v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vfloat n1) ->
                       norm_eq_compat m v2 (Vfloat n2) ->
                     exists v',
                       sf' v1 v2 = Some v' /\
                       norm_eq_compat m v' v)
         (ss_same: forall n1 n2 v,
                     ss n1 n2 = Some v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vsingle n1) ->
                       norm_eq_compat m v2 (Vsingle n2) ->
                     exists v',
                       ss' v1 v2 = Some v' /\
                       norm_eq_compat m v' v),
    sem_binarith (vptr m) si sl sf ss v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_binarith_expr si' sl' sf' ss'
                        (expr_of_val v1) t1
                        (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  intros m si sl sf ss si' sl' sf' ss' v1 t1 v2 t2 v' si_same sl_same sf_same ss_same.
  unfold sem_binarith, sem_binarith_expr.
  destruct (sem_cast (vptr m) v1 t1 _) eqn:?; try congruence.
  destruct (sem_cast (vptr m) v2 t2 _) eqn:?; try congruence.
  apply sem_cast_expr_ok in Heqo.
  apply sem_cast_expr_ok in Heqo0.
  destruct Heqo as [v'' [SC EMM]].
  destruct Heqo0 as [v''1 [SC1 EMM1]].
  rewrite SC. rewrite SC1.
  destruct (classify_binarith t1 t2);
    destruct v eqn:?; destruct v0 eqn:?; try congruence; eauto.
Qed.

Addition


Inductive classify_add_cases : Type :=
  | add_case_pi(ty: type) (* pointer, int *)
  | add_case_ip(ty: type) (* int, pointer *)
  | add_case_pl(ty: type) (* pointer, long *)
  | add_case_lp(ty: type) (* long, pointer *)
  | add_default. (* numerical type, numerical type *)

Definition classify_add (ty1: type) (ty2: type) :=
  match typeconv ty1, typeconv ty2 with
  | Tpointer ty _, Ctypes.Tint _ _ _ => add_case_pi ty
  | Ctypes.Tint _ _ _, Tpointer ty _ => add_case_ip ty
  | Tpointer ty _, Ctypes.Tlong _ _ => add_case_pl ty
  | Ctypes.Tlong _ _, Tpointer ty _ => add_case_lp ty
  | _, _ => add_default
  end.

Definition sem_add vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  match classify_add t1 t2 with
  | add_case_pi ty => (* pointer plus integer *)
      match v1,v2 with
      | Vptr b1 ofs1, Vint n2 =>
        Some (Vptr b1 (Int.add ofs1 (Int.mul (Int.repr (sizeof ty)) n2)))
      | _, _ => None
      end
  | add_case_ip ty => (* integer plus pointer *)
      match v1,v2 with
      | Vint n1, Vptr b2 ofs2 =>
        Some (Vptr b2 (Int.add ofs2 (Int.mul (Int.repr (sizeof ty)) n1)))
      | _, _ => None
      end
  | add_case_pl ty => (* pointer plus long *)
      match v1,v2 with
      | Vptr b1 ofs1, Vlong n2 =>
        let n2 := Int.repr (Int64.unsigned n2) in
        Some (Vptr b1 (Int.add ofs1 (Int.mul (Int.repr (sizeof ty)) n2)))
      | _, _ => None
      end
  | add_case_lp ty => (* long plus pointer *)
      match v1,v2 with
      | Vlong n1, Vptr b2 ofs2 =>
        let n1 := Int.repr (Int64.unsigned n1) in
        Some (Vptr b2 (Int.add ofs2 (Int.mul (Int.repr (sizeof ty)) n1)))
      | _, _ => None
      end
  | add_default =>
      sem_binarith vptr
        (fun sg n1 n2 => Some(Vint(Int.add n1 n2)))
        (fun sg n1 n2 => Some(Vlong(Int64.add n1 n2)))
        (fun n1 n2 => Some(Vfloat(Float.add n1 n2)))
        (fun n1 n2 => Some(Vsingle(Float32.add n1 n2)))
        v1 t1 v2 t2
  end.

Definition sem_add_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  match classify_add t1 t2 with
  | add_case_pi ty => (* pointer plus integer *)
    Some
      (Ebinop OpAdd Tint Tint v1 (Ebinop OpMul Tint Tint v2 (Eval (Eint (Int.repr (sizeof ty))))))
  | add_case_ip ty => (* integer plus pointer *)
    Some (Ebinop OpAdd Tint Tint v2 (Ebinop OpMul Tint Tint v1 (Eval (Eint (Int.repr (sizeof ty))))))
  | add_case_pl ty => (* pointer plus long *)
    Some (
        Ebinop OpAdd Tint Tint v1 (Ebinop OpMul Tint Tint
                                      (Eunop (OpConvert SEUnsigned (Tint,SEUnsigned)) Tlong v2)
                                      (Eval (Eint (Int.repr (sizeof ty))))))
  | add_case_lp ty => (* long plus pointer *)
    Some (Ebinop OpAdd Tint Tint v2 (Ebinop OpMul Tint Tint
                                      (Eunop (OpConvert SEUnsigned (Tint,SEUnsigned)) Tlong v1)
                                      (Eval (Eint (Int.repr (sizeof ty))))))
  | add_default =>
      sem_binarith_expr
        (fun sg n1 n2 => Some(Ebinop OpAdd Tint Tint n1 n2))
        (fun sg n1 n2 => Some(Ebinop OpAdd Tlong Tlong n1 n2))
        (fun n1 n2 => Some(Ebinop OpAdd Tfloat Tfloat n1 n2))
        (fun n1 n2 => Some(Ebinop OpAdd Tsingle Tsingle n1 n2))
        v1 t1 v2 t2
  end.


Lemma add_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_add (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_add_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_add, sem_add_expr.
  intros.
  assert (classify_add t1 t2 = add_default \/ classify_add t1 t2 <> add_default).
  destruct (classify_add t1 t2) eqn:?; intuition try congruence.
  destruct H0.
  - rewrite H0 in *.
    revert H.
    destruct (Mem.concrete_mem m) as [al cpt].
    apply expr_binarith_ok; intros; eauto; inv H;
    eexists; split; simpl; eauto;
    red in H1; red in H2; red; intros;
    simpl in *;
    rew_tt H1; rew_tt H2; repeat norm_eval_wt;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm.
  - destruct (classify_add t1 t2) eqn:?;
             destruct v1; destruct v2; try congruence;
    inv H; try ((eexists; split; simpl; eauto);[idtac];
                red; intros);
    intros; Normalise.destr_wt; simpl in e; inv e;
    apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl;
    unfold_eval;
    intuition try congruence;
    rewrite Int.add_assoc; rewrite Int.mul_commut;
    reflexivity.
Qed.

Subtraction


Inductive classify_sub_cases : Type :=
  | sub_case_pi(ty: type) (* pointer, int *)
  | sub_case_pp(ty: type) (* pointer, pointer *)
  | sub_case_pl(ty: type) (* pointer, long *)
  | sub_default. (* numerical type, numerical type *)

Definition classify_sub (ty1: type) (ty2: type) :=
  match typeconv ty1, typeconv ty2 with
  | Tpointer ty _, Ctypes.Tint _ _ _ => sub_case_pi ty
  | Tpointer ty _ , Tpointer _ _ => sub_case_pp ty
  | Tpointer ty _, Ctypes.Tlong _ _ => sub_case_pl ty
  | _, _ => sub_default
  end.

Definition sem_sub vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  match classify_sub t1 t2 with
  | sub_case_pi ty => (* pointer minus integer *)
      match v1,v2 with
      | Vptr b1 ofs1, Vint n2 =>
          Some (Vptr b1 (Int.sub ofs1 (Int.mul (Int.repr (sizeof ty)) n2)))
      | _, _ => None
      end
  | sub_case_pl ty => (* pointer minus long *)
      match v1,v2 with
      | Vptr b1 ofs1, Vlong n2 =>
          let n2 := Int.repr (Int64.unsigned n2) in
          Some (Vptr b1 (Int.sub ofs1 (Int.mul (Int.repr (sizeof ty)) n2)))
      | _, _ => None
      end
  | sub_case_pp ty => (* pointer minus pointer *)
      match v1,v2 with
      | Vptr b1 ofs1, Vptr b2 ofs2 =>
          if eq_block b1 b2 then
            if Int.eq (Int.repr (sizeof ty)) Int.zero then None
            else Some (Vint (Int.divu (Int.sub ofs1 ofs2) (Int.repr (sizeof ty))))
          else None
      | _, _ => None
      end
  | sub_default =>
      sem_binarith vptr
        (fun sg n1 n2 => Some(Vint(Int.sub n1 n2)))
        (fun sg n1 n2 => Some(Vlong(Int64.sub n1 n2)))
        (fun n1 n2 => Some(Vfloat(Float.sub n1 n2)))
        (fun n1 n2 => Some(Vsingle(Float32.sub n1 n2)))
        v1 t1 v2 t2
  end.


Definition sem_sub_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  match classify_sub t1 t2 with
  | sub_case_pi ty => (* pointer minus integer *)
    Some (Ebinop OpSub Tint Tint v1
                 (Ebinop OpMul Tint Tint v2
                         (Eval (Eint (Int.repr (sizeof ty))))))
  | sub_case_pl ty => (* pointer minus long *)
    Some (Ebinop OpSub Tint Tint v1
                 (Ebinop OpMul Tint Tint
                         (expr_cast_gen Tlong Unsigned Tint Unsigned v2 )
                         (Eval (Eint (Int.repr (sizeof ty))))))
  | sub_case_pp ty => (* pointer minus pointer *)
    Some (Ebinop (OpDiv SEUnsigned) Tint Tint
                 (Ebinop OpSub Tint Tint v1 v2)
                 (Eval (Eint (Int.repr (sizeof ty)))))
  | sub_default =>
      sem_binarith_expr
        (fun sg n1 n2 => Some(Ebinop OpSub Tint Tint n1 n2))
        (fun sg n1 n2 => Some(Ebinop OpSub Tlong Tlong n1 n2))
        (fun n1 n2 => Some(Ebinop OpSub Tfloat Tfloat n1 n2))
        (fun n1 n2 => Some(Ebinop OpSub Tsingle Tsingle n1 n2))
        v1 t1 v2 t2
  end.


Lemma sub_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_sub (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_sub_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_sub, sem_sub_expr.
  intros.
  assert (classify_sub t1 t2 = sub_default \/ classify_sub t1 t2 <> sub_default).
  destruct (classify_sub t1 t2) eqn:?; intuition try congruence.
  destruct H0.
  - rewrite H0 in *. unfold norm_eq_compat.
    revert H.
    destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; intros; eauto; inv H;
    eexists; split; simpl; eauto;
    red in H1; red in H2; red; intros;
    specialize (H1 );
    specialize (H2 ); simpl in *;
    rew_tt H1; rew_tt H2; repeat norm_eval_wt;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm.
  - destruct (classify_sub t1 t2) eqn:?;
             destruct v1; destruct v2; try congruence;
    inv H; try ((eexists; split; simpl; eauto);[idtac];
                red; intros).
    + intros; Normalise.destr_wt; simpl in e; inv e;
      apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl;
      repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
              NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop',
              NormaliseSpec.eunop, NormaliseSpec.unop,
              NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
      intuition try congruence.
      rewrite ! Int.sub_add_opp. rewrite Int.add_assoc.
      rewrite Int.mul_commut. reflexivity.
    + destruct (eq_block b b0); try congruence.
      destruct (Int.eq (Int.repr (sizeof ty)) Int.zero) eqn:?; try congruence.
      subst.
      inv H2. simpl.
      intros; Normalise.destr_wt; simpl in e; inv e;
      apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl;
      repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
              NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop',
              NormaliseSpec.eunop, NormaliseSpec.unop,
              NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
      intuition try congruence.
      rewrite Heqb1. simpl.
      rewrite ! (Int.add_commut (alloc b0)).
      rewrite Int.sub_shifted. auto.
    + intros; Normalise.destr_wt; simpl in e; inv e;
          apply Normalise.norm_eq''; simpl; try rewrite <- H0; simpl;
          repeat (unfold NormaliseSpec.ebinop, NormaliseSpec.binop,
                  NormaliseSpec.fun_of_binop, NormaliseSpec.fun_of_binop',
                  NormaliseSpec.eunop, NormaliseSpec.unop,
                  NormaliseSpec.fun_of_unop, NormaliseSpec.fun_of_unop'; simpl);
          intuition try congruence.
          rewrite ! Int.sub_add_opp. rewrite Int.add_assoc.
          rewrite Int.mul_commut. reflexivity.
Qed.
 

Multiplication, division, modulus


Definition sem_mul vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 => Some(Vint(Int.mul n1 n2)))
    (fun sg n1 n2 => Some(Vlong(Int64.mul n1 n2)))
    (fun n1 n2 => Some(Vfloat(Float.mul n1 n2)))
    (fun n1 n2 => Some(Vsingle(Float32.mul n1 n2)))
    v1 t1 v2 t2.


Definition sem_mul_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some( Ebinop OpMul Tint Tint n1 n2))
    (fun sg n1 n2 => Some( Ebinop OpMul Tlong Tlong n1 n2))
    (fun n1 n2 => Some( Ebinop OpMul Tfloat Tfloat n1 n2))
    (fun n1 n2 => Some( Ebinop OpMul Tsingle Tsingle n1 n2))
    v1 t1 v2 t2.



Lemma mul_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_mul (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_mul_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_mul, sem_mul_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros;
  eexists; split; simpl; eauto; red; intros; inv H; simpl in *;
  red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt;
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
  try rewrite e; try rewrite e0; simpl; auto;
  unfold_eval;
  intuition try congruence;
  rewrite_norm.
Qed.

Definition sem_div vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 =>
      match sg with
      | Signed =>
          if Int.eq n2 Int.zero
          || Int.eq n1 (Int.repr Int.min_signed) && Int.eq n2 Int.mone
          then None else Some(Vint(Int.divs n1 n2))
      | Unsigned =>
          if Int.eq n2 Int.zero
          then None else Some(Vint(Int.divu n1 n2))
      end)
    (fun sg n1 n2 =>
      match sg with
      | Signed =>
          if Int64.eq n2 Int64.zero
          || Int64.eq n1 (Int64.repr Int64.min_signed) && Int64.eq n2 Int64.mone
          then None else Some(Vlong(Int64.divs n1 n2))
      | Unsigned =>
          if Int64.eq n2 Int64.zero
          then None else Some(Vlong(Int64.divu n1 n2))
      end)
    (fun n1 n2 => Some(Vfloat(Float.div n1 n2)))
    (fun n1 n2 => Some(Vsingle(Float32.div n1 n2)))
    v1 t1 v2 t2.

Definition sem_div_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some (Ebinop (OpDiv (match sg with
                                        Signed => SESigned
                                      | Unsigned => SEUnsigned
                                    end))
                            Tint Tint n1 n2))
    (fun sg n1 n2 => Some (Ebinop (OpDiv (match sg with
                                        Signed => SESigned
                                      | Unsigned => SEUnsigned
                                    end))
                            Tlong Tlong n1 n2))
    (fun n1 n2 => Some(Ebinop (OpDiv SESigned) Tfloat Tfloat n1 n2))
    (fun n1 n2 => Some(Ebinop (OpDiv SESigned) Tsingle Tsingle n1 n2))
    v1 t1 v2 t2.

Lemma div_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_div (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_div_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_div, sem_div_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros;
  eexists; split; simpl; eauto; red; intros; inv H; simpl in *;
  red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt.
  - repeat (revert H3; destr_cond_match); try congruence;
    destr_cond_match; try congruence; intro A; inv A;
    simpl;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
  - repeat (revert H3; destr_cond_match); try congruence;
    destr_cond_match; try congruence; intro A; inv A;
    simpl;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
  - apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
  - apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
Qed.

Definition sem_mod vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 =>
      match sg with
      | Signed =>
          if Int.eq n2 Int.zero
          || Int.eq n1 (Int.repr Int.min_signed) && Int.eq n2 Int.mone
          then None else Some(Vint(Int.mods n1 n2))
      | Unsigned =>
          if Int.eq n2 Int.zero
          then None else Some(Vint(Int.modu n1 n2))
      end)
    (fun sg n1 n2 =>
      match sg with
      | Signed =>
          if Int64.eq n2 Int64.zero
          || Int64.eq n1 (Int64.repr Int64.min_signed) && Int64.eq n2 Int64.mone
          then None else Some(Vlong(Int64.mods n1 n2))
      | Unsigned =>
          if Int64.eq n2 Int64.zero
          then None else Some(Vlong(Int64.modu n1 n2))
      end)
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Definition sem_mod_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some (Ebinop (OpMod (match sg with
                                        Signed => SESigned
                                      | Unsigned => SEUnsigned
                                    end))
                            Tint Tint n1 n2))
    (fun sg n1 n2 => Some (Ebinop (OpMod (match sg with
                                        Signed => SESigned
                                      | Unsigned => SEUnsigned
                                    end))
                            Tlong Tlong n1 n2))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.

Lemma mod_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_mod (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_mod_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_mod, sem_mod_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros; try congruence;
  try (eexists; split; simpl; eauto; red; intros; inv H;
       simpl in *;
         red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt).
  - repeat (revert H3; destr_cond_match); try congruence;
    destr_cond_match; try congruence; intro A; inv A;
    simpl;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
  - repeat (revert H3; destr_cond_match); try congruence;
    destr_cond_match; try congruence; intro A; inv A;
    simpl;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
Qed.
 
Definition sem_and vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 => Some(Vint(Int.and n1 n2)))
    (fun sg n1 n2 => Some(Vlong(Int64.and n1 n2)))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.

Definition sem_and_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some(Ebinop OpAnd Tint Tint n1 n2))
    (fun sg n1 n2 => Some(Ebinop OpAnd Tlong Tlong n1 n2))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Lemma and_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_and (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_and_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_and, sem_and_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros; try congruence;
  try (eexists; split; simpl; eauto; red; intros;
       inv H;
       simpl in *;
         red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt);
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
Qed.

Definition sem_or vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 => Some(Vint(Int.or n1 n2)))
    (fun sg n1 n2 => Some(Vlong(Int64.or n1 n2)))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Definition sem_or_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some(Ebinop OpOr Tint Tint n1 n2))
    (fun sg n1 n2 => Some(Ebinop OpOr Tlong Tlong n1 n2))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Lemma or_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_or (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_or_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_or, sem_or_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros; try congruence;
  try (eexists; split; simpl; eauto; red; intros; inv H;
       simpl in *;
         red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt);
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
Qed.


Definition sem_xor vptr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_binarith vptr
    (fun sg n1 n2 => Some(Vint(Int.xor n1 n2)))
    (fun sg n1 n2 => Some(Vlong(Int64.xor n1 n2)))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Definition sem_xor_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_binarith_expr
    (fun sg n1 n2 => Some(Ebinop OpXor Tint Tint n1 n2))
    (fun sg n1 n2 => Some(Ebinop OpXor Tlong Tlong n1 n2))
    (fun n1 n2 => None)
    (fun n1 n2 => None)
    v1 t1 v2 t2.


Lemma xor_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_xor (vptr m) v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_xor_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_xor, sem_xor_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_binarith_ok; auto; intros; try congruence;
  try (eexists; split; simpl; eauto; red; intros; inv H;
       simpl in *;
         red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt);
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    rewrite Heqb; simpl; auto.
Qed.

Shifts


Shifts do not perform the usual binary conversions. Instead, each argument is converted independently, and the signedness of the result is always that of the first argument.

Inductive classify_shift_cases : Type:=
  | shift_case_ii(s: signedness) (* int , int *)
  | shift_case_ll(s: signedness) (* long, long *)
  | shift_case_il(s: signedness) (* int, long *)
  | shift_case_li(s: signedness) (* long, int *)
  | shift_default.

Definition classify_shift (ty1: type) (ty2: type) :=
  match typeconv ty1, typeconv ty2 with
  | Ctypes.Tint I32 Unsigned _, Ctypes.Tint _ _ _ => shift_case_ii Unsigned
  | Ctypes.Tint _ _ _, Ctypes.Tint _ _ _ => shift_case_ii Signed
  | Ctypes.Tint I32 Unsigned _, Ctypes.Tlong _ _ => shift_case_il Unsigned
  | Ctypes.Tint _ _ _, Ctypes.Tlong _ _ => shift_case_il Signed
  | Ctypes.Tlong s _, Ctypes.Tint _ _ _ => shift_case_li s
  | Ctypes.Tlong s _, Ctypes.Tlong _ _ => shift_case_ll s
  | _,_ => shift_default
  end.

Definition sem_shift
    (sem_int: signedness -> int -> int -> int)
    (sem_long: signedness -> int64 -> int64 -> int64)
    (v1: val) (t1: type) (v2: val) (t2: type) : option val :=
  match classify_shift t1 t2 with
  | shift_case_ii sg =>
      match v1, v2 with
      | Vint n1, Vint n2 =>
          if Int.ltu n2 Int.iwordsize
          then Some(Vint(sem_int sg n1 n2)) else None
      | _, _ => None
      end
  | shift_case_il sg =>
      match v1, v2 with
      | Vint n1, Vlong n2 =>
          if Int64.ltu n2 (Int64.repr 32)
          then Some(Vint(sem_int sg n1 (Int64.loword n2))) else None
      | _, _ => None
      end
  | shift_case_li sg =>
      match v1, v2 with
      | Vlong n1, Vint n2 =>
          if Int.ltu n2 Int64.iwordsize'
          then Some(Vlong(sem_long sg n1 (Int64.repr (Int.unsigned n2)))) else None
      | _, _ => None
      end
  | shift_case_ll sg =>
      match v1, v2 with
      | Vlong n1, Vlong n2 =>
          if Int64.ltu n2 Int64.iwordsize
          then Some(Vlong(sem_long sg n1 n2)) else None
      | _, _ => None
      end
  | shift_default => None
  end.


Definition sem_shift_expr
    (sem_int: signedness -> expr_sym -> expr_sym -> expr_sym)
    (sem_long: signedness -> expr_sym -> expr_sym -> expr_sym)
    (v1: expr_sym) (t1: type) (v2: expr_sym) (t2: type) : option expr_sym :=
  match classify_shift t1 t2 with
  | shift_case_ii sg => Some (sem_int sg v1 v2)
  | shift_case_il sg => Some (sem_int sg v1 (expr_cast_gen Tlong sg Tint sg v2))
  | shift_case_li sg => Some (sem_long sg v1 (expr_cast_gen Tint Unsigned Tlong Unsigned v2))
  | shift_case_ll sg => Some (sem_long sg v1 v2)
  | shift_default => None
  end.

Lemma expr_shift_sem_ok:
  forall m si sl si' sl' v1 t1 v2 t2 v'
         (si_expr: forall sg n1 n2 n2',
                   (forall alloc em,
                      NormaliseSpec.eSexpr alloc em eb eu Tint n2 =
                      NormaliseSpec.eSexpr alloc em eb eu Tint n2') ->
                   forall alloc em,
                      NormaliseSpec.eSexpr alloc em eb eu Tint (si' sg n1 n2) =
                      NormaliseSpec.eSexpr alloc em eb eu Tint (si' sg n1 n2'))
         (sl_expr: forall sg n1 n2 n2',
                   (forall alloc em,
                      NormaliseSpec.eSexpr alloc em eb eu Tlong n2 =
                      NormaliseSpec.eSexpr alloc em eb eu Tlong n2') ->
                   forall alloc em,
                     NormaliseSpec.eSexpr alloc em eb eu Tlong (sl' sg n1 n2) =
                     NormaliseSpec.eSexpr alloc em eb eu Tlong (sl' sg n1 n2'))
         (si_wt: forall sg n1 n2,
                   tcheck_expr n1 = Some Tint ->
                   tcheck_expr n2 = Some Tint ->
                   tcheck_expr (si' sg n1 n2) = Some Tint)
         (sl_wt: forall sg n1 n2,
                   tcheck_expr n1 = Some Tlong ->
                   tcheck_expr n2 = Some Tlong ->
                   tcheck_expr (sl' sg n1 n2) = Some Tlong)
         (si_same: forall sg n1 n2 v,
                     si sg n1 n2 = v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vint n1) ->
                       norm_eq_compat m v2 (Vint n2) ->
                       Int.ltu n2 Int.iwordsize = true ->
                       exists v',
                         si' sg v1 v2 = v' /\
                         norm_eq_compat m v' (Vint v))
         (sl_same: forall sg n1 n2 v,
                     sl sg n1 n2 = v ->
                     forall v1 v2,
                       norm_eq_compat m v1 (Vlong n1) ->
                       norm_eq_compat m v2 (Vlong n2) ->
                       Int64.ltu n2 Int64.iwordsize = true ->
                       exists v',
                         sl' sg v1 v2 = v' /\
                         norm_eq_compat m v' (Vlong v)),
    sem_shift si sl v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_shift_expr si' sl'
                     (expr_of_val v1) t1
                     (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  intros m si sl si' sl' v1 t1 v2 t2 v' si_ex sl_ex si_wt sl_wt si_same sl_same.
  unfold sem_shift, sem_shift_expr.
  destruct (classify_shift t1 t2);
    destruct v1; destruct v2; try congruence;
  destr_cond_match; try congruence; intro A; inv A;
  eexists; split; simpl; eauto.
  - edestruct (si_same s i i0 _ eq_refl (Eval (Eint i)) (Eval (Eint i0))); try red; intros;simpl in *; eauto.
    destruct H.
    rewrite <- H0; subst; eauto.
  - edestruct (sl_same s i i0 _ eq_refl (Eval (Elong i)) (Eval (Elong i0))); try red; intros;simpl in *; eauto.
    destruct H.
    rewrite <- H0; subst; eauto.
  - edestruct (si_same s i (Int64.loword i0) _ eq_refl (Eval (Eint i)) (Eval (Eint (Int64.loword i0)))); try red; intros;simpl in *; eauto.
    + unfold Int64.ltu in Heqb; unfold Int.ltu.
      change (Int64.unsigned (Int64.repr 32)) with 32 in Heqb.
      change (Int.unsigned Int.iwordsize) with 32.
      revert Heqb; destr_cond_match; try congruence.
      destr_cond_match; auto. clear Heqs0 Heqs1.
      unfold Int64.loword in g.
      rewrite Int.unsigned_repr in g. intros; omega.
      split. generalize (Int64.unsigned_range i0). omega.
      apply Z.le_trans with 32. omega. vm_compute; congruence.
    + destruct H.
      rewrite <- H0; subst; eauto.
      unfold expr_cast_gen.
      Normalise.destr_wt.
      destruct s;
        apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
        try rewrite e; try rewrite e0; simpl; auto;
        unfold_eval;
        intuition try congruence;
        rewrite_norm.
  - edestruct (sl_same s i (Int64.repr (Int.unsigned i0)) _ eq_refl (Eval (Elong i)) (Eval (Elong (Int64.repr (Int.unsigned i0))))); try red; intros;simpl in *; eauto.
    + unfold Int64.ltu, Int.ltu in *.
      rewrite Int64.unsigned_repr. unfold Int64.iwordsize' in Heqb.
      rewrite Int.unsigned_repr in Heqb. auto.
      vm_compute; intuition congruence.
      revert Heqb; destr_cond_match; try congruence. clear Heqs0.
      generalize (Int.unsigned_range_2 i0). intros; split. omega.
      destruct H; etransitivity; eauto.
      vm_compute; congruence.
    + destruct H.
      rewrite <- H0; subst; eauto.
      unfold expr_cast_gen.
      Normalise.destr_wt.
      destruct s;
        apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
        try rewrite e; try rewrite e0; simpl; auto;
        unfold_eval;
        intuition try congruence;
        rewrite_norm.
Qed.

Definition sem_shl (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_shift
    (fun sg n1 n2 => Int.shl n1 n2)
    (fun sg n1 n2 => Int64.shl n1 n2)
    v1 t1 v2 t2.

Definition sem_shl_expr(v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_shift_expr
    (fun sg n1 n2 => Ebinop OpShl Tint Tint n1 n2)
    (fun sg n1 n2 => Ebinop OpShl Tlong Tint n1
                            (expr_cast_gen Tlong Unsigned Tint Unsigned n2))
    v1 t1 v2 t2.


Lemma shl_expr_ok:
  forall vptr v1 v2 t1 t2 v',
    sem_shl v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_shl_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat vptr v'' v'.
Proof.
  unfold sem_shl, sem_shl_expr.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_shift_sem_ok; auto; intros; try congruence.
  - simpl. unfold NormaliseSpec.ebinop.
    rewrite H. reflexivity.
  - simpl. unfold NormaliseSpec.ebinop. unfold expr_cast_gen.
    simpl. unfold NormaliseSpec.eunop.
    rewrite H. reflexivity.
  - simpl. rewrite H; rewrite H0; simpl; auto.
  - simpl. rewrite H; rewrite H0; simpl; auto.
  - eexists; split; simpl; eauto; red; intros; inv H;
    simpl in *;
    red in H0; red in H1;
    simpl in *;
    rew_tt H0; rew_tt H1;
    repeat norm_eval_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence.
    try rewrite e; try rewrite e0; simpl; auto.
    unfold_eval.
    rewrite_norm. rewrite H2; simpl; auto.
  -
    eexists; split; simpl; eauto; red; intros; inv H;
    simpl in *;
    red in H0; red in H1;
    simpl in *;
    rew_tt H0; rew_tt H1;
    repeat norm_eval_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence.
    try rewrite e; try rewrite e0; simpl; auto.
    unfold_eval.
    rewrite_norm.
    destr_cond_match; simpl; auto.
    unfold Int64.shl, Int64.shl'.
    repeat f_equal.
    apply Int.unsigned_repr.
    unfold Int64.ltu in H2.
    revert H2; destr_cond_match; try congruence.
    generalize (Int64.unsigned_range n2). split. omega.
    transitivity (Int64.unsigned Int64.iwordsize); try omega. vm_compute. congruence.
    exfalso; clear - Heqb H2.
    unfold Int64.ltu, Int.ltu in *.
    revert Heqb; destr_cond_match; try congruence.
    revert H2; destr_cond_match; try congruence.
    clear Heqs Heqs0.
    change (Int.unsigned Int64.iwordsize') with 64 in g.
    change (Int64.unsigned Int64.iwordsize) with 64 in l.
    unfold Int64.loword in g.
    rewrite Int.unsigned_repr in g. intros.
    omega.
    split. generalize (Int64.unsigned_range n2); omega.
    transitivity 64; try omega.
    vm_compute; congruence.
Qed.

Definition sem_shr (v1:val) (t1:type) (v2: val) (t2:type) : option val :=
  sem_shift
    (fun sg n1 n2 => match sg with Signed => Int.shr n1 n2 | Unsigned => Int.shru n1 n2 end)
    (fun sg n1 n2 => match sg with Signed => Int64.shr n1 n2 | Unsigned => Int64.shru n1 n2 end)
    v1 t1 v2 t2.


Definition sem_shr_expr (v1:expr_sym) (t1:type) (v2: expr_sym) (t2:type) : option expr_sym :=
  sem_shift_expr
    (fun sg n1 n2 => Ebinop (OpShr match sg with Signed => SESigned
                                              | Unsigned => SEUnsigned
                                   end)
                            Tint Tint n1 n2)
    (fun sg n1 n2 => Ebinop (OpShr match sg with Signed => SESigned
                                              | Unsigned => SEUnsigned
                                   end)
                            Tlong Tint n1 (expr_cast_gen Tlong Unsigned Tint Unsigned n2))
    v1 t1 v2 t2.


Lemma shr_expr_ok:
  forall m v1 v2 t1 t2 v',
    sem_shr v1 t1 v2 t2 = Some v' ->
    exists v'',
      sem_shr_expr (expr_of_val v1) t1
                   (expr_of_val v2) t2 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  intros m v1 v2 t1 t2 v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  apply expr_shift_sem_ok; auto; intros; try congruence.
  - simpl. unfold NormaliseSpec.ebinop.
    rewrite H. reflexivity.
  - simpl. unfold NormaliseSpec.ebinop. unfold expr_cast_gen.
    simpl. unfold NormaliseSpec.eunop.
    rewrite H. reflexivity.
  - simpl. rewrite H; rewrite H0; simpl; auto.
  - simpl. rewrite H; rewrite H0; simpl; auto.
  -
    eexists; split; simpl; eauto; red; intros; inv H;
    simpl in *;
    red in H0; red in H1;
    simpl in *;
    rew_tt H0; rew_tt H1;
    repeat norm_eval_wt.
    destruct sg; apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    rewrite_norm; rewrite H2; simpl; auto.
  -
    eexists; split; simpl; eauto; red; intros; inv H;
    simpl in *;
    red in H0; red in H1;
    simpl in *;
    rew_tt H0; rew_tt H1;
    repeat norm_eval_wt;
    destruct sg;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    rewrite_norm;
    destr_cond_match; simpl; auto.
    + unfold Int64.shr, Int64.shr'.
      repeat f_equal.
      apply Int.unsigned_repr.
      unfold Int64.ltu in H2.
      revert H2; destr_cond_match; try congruence.
      generalize (Int64.unsigned_range n2). split. omega.
      transitivity (Int64.unsigned Int64.iwordsize); try omega. vm_compute. congruence.
    + exfalso; clear - Heqb H2.
      unfold Int64.ltu, Int.ltu in *.
      revert Heqb; destr_cond_match; try congruence.
      revert H2; destr_cond_match; try congruence.
      clear Heqs Heqs0.
      change (Int.unsigned Int64.iwordsize') with 64 in g.
      change (Int64.unsigned Int64.iwordsize) with 64 in l.
      unfold Int64.loword in g.
      rewrite Int.unsigned_repr in g. intros.
      omega.
      split. generalize (Int64.unsigned_range n2); omega.
      transitivity 64; try omega.
      vm_compute; congruence.
    + unfold Int64.shru, Int64.shru'.
      repeat f_equal.
      apply Int.unsigned_repr.
      unfold Int64.ltu in H2.
      revert H2; destr_cond_match; try congruence.
      generalize (Int64.unsigned_range n2). split. omega.
      transitivity (Int64.unsigned Int64.iwordsize); try omega. vm_compute. congruence.
    + exfalso; clear - Heqb H2.
      unfold Int64.ltu, Int.ltu in *.
      revert Heqb; destr_cond_match; try congruence.
      revert H2; destr_cond_match; try congruence.
      clear Heqs Heqs0.
      change (Int.unsigned Int64.iwordsize') with 64 in g.
      change (Int64.unsigned Int64.iwordsize) with 64 in l.
      unfold Int64.loword in g.
      rewrite Int.unsigned_repr in g. intros.
      omega.
      split. generalize (Int64.unsigned_range n2); omega.
      transitivity 64; try omega.
      vm_compute; congruence.
Qed.

Comparisons


Inductive classify_cmp_cases : Type :=
  | cmp_case_pp (* pointer, pointer *)
  | cmp_case_pl (* pointer, long *)
  | cmp_case_lp (* long, pointer *)
  | cmp_default. (* numerical, numerical *)

Definition classify_cmp (ty1: type) (ty2: type) :=
  match typeconv ty1, typeconv ty2 with
  | Tpointer _ _ , Tpointer _ _ => cmp_case_pp
  | Tpointer _ _ , Ctypes.Tint _ _ _ => cmp_case_pp
  | Ctypes.Tint _ _ _, Tpointer _ _ => cmp_case_pp
  | Tpointer _ _ , Ctypes.Tlong _ _ => cmp_case_pl
  | Ctypes.Tlong _ _ , Tpointer _ _ => cmp_case_lp
  | _, _ => cmp_default
  end.

Definition sem_cmp (c:comparison)
                  (v1: val) (t1: type) (v2: val) (t2: type)
                  (m: mem): option val :=
  match classify_cmp t1 t2 with
  | cmp_case_pp =>
      option_map Values.Val.of_bool (Values.Val.cmpu_bool (Mem.valid_pointer m) c v1 v2)
  | cmp_case_pl =>
      match v2 with
      | Vlong n2 =>
          let n2 := Int.repr (Int64.unsigned n2) in
          option_map Values.Val.of_bool (Values.Val.cmpu_bool (Mem.valid_pointer m) c v1 (Vint n2))
      | _ => None
      end
  | cmp_case_lp =>
      match v1 with
      | Vlong n1 =>
          let n1 := Int.repr (Int64.unsigned n1) in
          option_map Values.Val.of_bool (Values.Val.cmpu_bool (Mem.valid_pointer m) c (Vint n1) v2)
      | _ => None
      end
  | cmp_default =>
      sem_binarith (fun b i => Mem.valid_pointer m b (Int.unsigned i))
        (fun sg n1 n2 =>
            Some(Values.Val.of_bool(match sg with Signed => Int.cmp c n1 n2 | Unsigned => Int.cmpu c n1 n2 end)))
        (fun sg n1 n2 =>
            Some(Values.Val.of_bool(match sg with Signed => Int64.cmp c n1 n2 | Unsigned => Int64.cmpu c n1 n2 end)))
        (fun n1 n2 =>
            Some(Values.Val.of_bool(Float.cmp c n1 n2)))
        (fun n1 n2 =>
            Some(Values.Val.of_bool(Float32.cmp c n1 n2)))
        v1 t1 v2 t2
  end.


Definition sem_cmp_expr (c:comparison)
                  (v1: expr_sym) (t1: type) (v2: expr_sym) (t2: type)
                  (m: mem): option expr_sym :=
  match classify_cmp t1 t2 with
  | cmp_case_pp =>
    Some (Ebinop (OpCmp SEUnsigned c) Tint Tint v1 v2)
  | cmp_case_pl =>
    Some (Ebinop (OpCmp SEUnsigned c) Tint Tint v1 (expr_cast_gen Tlong Unsigned Tint Unsigned v2))
  | cmp_case_lp =>
    Some (Ebinop (OpCmp SEUnsigned c) Tint Tint (expr_cast_gen Tlong Unsigned Tint Unsigned v1) v2)
  | cmp_default =>
    sem_binarith_expr
      (fun sg n1 n2 =>
         Some(
             Ebinop (OpCmp (match sg with
                                Signed => SESigned
                              | Unsigned => SEUnsigned
                            end) c) Tint Tint n1 n2))
        (fun sg n1 n2 =>
         Some(
             Ebinop (OpCmp (match sg with
                                Signed => SESigned
                              | Unsigned => SEUnsigned
                            end) c) Tlong Tlong n1 n2))
        (fun n1 n2 =>
            Some( Ebinop (OpCmp SESigned c) Tfloat Tfloat n1 n2))
        (fun n1 n2 =>
            Some( Ebinop (OpCmp SESigned c) Tsingle Tsingle n1 n2))
        v1 t1 v2 t2
  end.

Lemma int_eq_true:
  forall i1 i2,
    Int.eq i1 i2 = true ->
    i1 = i2.
Proof.
  intros i1 i2 A.
  generalize (Int.eq_spec i1 i2).
  rewrite A. tauto.
Qed.

Lemma compat_conserve_cmp:
  forall c al sz msk b i i0
         (COMPAT: compat al sz msk)
         (bnd0: forall bb, fst (sz bb) = 0)
         (IB1: in_bound (Int.unsigned i) (sz b))
         (IB2: in_bound (Int.unsigned i0) (sz b)),
    Int.cmpu c (Int.add i (al b)) (Int.add i0 (al b)) = Int.cmpu c i i0.
Proof.
  intros. inv COMPAT.
  clear overlap alignment.
  rewrite Int.translate_cmpu; auto.
  generalize (addr_space _ _ IB1)
             (addr_space _ _ IB2)
             (addr_space b Int.zero)
             (addr_space b (Int.repr (snd (sz b) -1))).
  destruct (sz b) eqn:?.
  generalize (bnd0 b); rewrite Heqp. simpl; intro; subst.
  unfold in_bound in *; simpl in *.
  intros A B C D.
  Alloc.trim C. change (Int.unsigned Int.zero) with 0. omega.
  Alloc.trim D. destruct (zle (z0 - 1) Int.max_unsigned).
  rewrite Int.unsigned_repr. omega. omega.
  generalize (Int.unsigned_range_2 (Int.repr (z0-1))). omega.
  clear bnd0.
  rewrite Int.add_zero in C.
  unfold Int.add in *.
  clear B D.
  split. omega.
  destruct (zle (Int.unsigned i + Int.unsigned (al b)) Int.max_unsigned); auto.
  assert (exists j, 0 <= j <= Int.unsigned i /\ Int.unsigned (al b) + j = Int.max_unsigned + 1).
  (exists (Int.max_unsigned + 1 - Int.unsigned (al b)); split; try omega).
  destruct H as [j [E F]].
  generalize (addr_space b (Int.repr j)). rewrite Heqp; simpl.
  rewrite Int.unsigned_repr.
  rewrite F.
  change (Int.unsigned (Int.repr (Int.max_unsigned + 1))) with 0.
  intro G; Alloc.trim G. omega. omega.
  generalize (Int.unsigned_range i). omega.
  generalize (addr_space _ _ IB1)
             (addr_space _ _ IB2)
             (addr_space b Int.zero)
             (addr_space b (Int.repr (snd (sz b) -1))).
  destruct (sz b) eqn:?.
  generalize (bnd0 b); rewrite Heqp. simpl; intro; subst.
  unfold in_bound in *; simpl in *.
  intros A B C D.
  Alloc.trim C. change (Int.unsigned Int.zero) with 0. omega.
  Alloc.trim D. destruct (zle (z0 - 1) Int.max_unsigned).
  rewrite Int.unsigned_repr. omega. omega.
  generalize (Int.unsigned_range_2 (Int.repr (z0-1))). omega.
  clear bnd0.
  rewrite Int.add_zero in C.
  unfold Int.add in *.
  clear A D.
  split. omega.
  destruct (zle (Int.unsigned i0 + Int.unsigned (al b)) Int.max_unsigned); auto.
  assert (exists j, 0 <= j <= Int.unsigned i0 /\ Int.unsigned (al b) + j = Int.max_unsigned + 1).
  (exists (Int.max_unsigned + 1 - Int.unsigned (al b)); split; try omega).
  destruct H as [j [E F]].
  generalize (addr_space b (Int.repr j)). rewrite Heqp; simpl.
  rewrite Int.unsigned_repr.
  rewrite F.
  change (Int.unsigned (Int.repr (Int.max_unsigned + 1))) with 0.
  intro G; Alloc.trim G. omega. omega.
  generalize (Int.unsigned_range i). omega.
Qed.


Lemma compat_conserve_cmp':
  forall c al sz msk b i i0
         (COMPAT: compat al sz msk)
         (bnd0: forall bb, fst (sz bb) = 0)
         (IB1: in_bound (Int.unsigned i) (sz b))
         (IB2: (Int.unsigned i0) = snd (sz b)),
    Int.cmpu c (Int.add i (al b)) (Int.add i0 (al b)) = Int.cmpu c i i0.
Proof.
  intros. inv COMPAT.
  clear overlap alignment.
  rewrite Int.translate_cmpu; auto.
  generalize (addr_space _ _ IB1)
             (addr_space b Int.zero)
             (addr_space b (Int.repr (snd (sz b) - 1))).
  destruct (sz b) eqn:?.
  generalize (bnd0 b); rewrite Heqp. simpl; intro; subst.
  unfold in_bound in *; simpl in *.
  intros A C D.
  Alloc.trim C. change (Int.unsigned Int.zero) with 0. omega.
  Alloc.trim D. destruct (zle (z0 - 1) Int.max_unsigned).
  rewrite Int.unsigned_repr. omega. omega.
  generalize (Int.unsigned_range_2 (Int.repr (z0-1))). omega.
  clear bnd0.
  rewrite Int.add_zero in C.
  unfold Int.add in *.
  clear D.
  split. omega.
  destruct (zle (Int.unsigned i + Int.unsigned (al b)) Int.max_unsigned); auto.
  assert (exists j, 0 <= j <= Int.unsigned i /\ Int.unsigned (al b) + j = Int.max_unsigned + 1).
  (exists (Int.max_unsigned + 1 - Int.unsigned (al b)); split; try omega).
  destruct H as [j [E F]].
  generalize (addr_space b (Int.repr j)). rewrite Heqp; simpl.
  rewrite Int.unsigned_repr.
  rewrite F.
  change (Int.unsigned (Int.repr (Int.max_unsigned + 1))) with 0.
  intro G; Alloc.trim G. omega. omega.
  generalize (Int.unsigned_range i). omega.
  generalize (addr_space _ _ IB1)
             (addr_space b Int.zero)
             (addr_space b (Int.repr (snd (sz b) -1))).
  destruct (sz b) eqn:?.
  generalize (bnd0 b); rewrite Heqp. simpl; intro; subst.
  unfold in_bound in *; simpl in *.
  intros A C D.
  Alloc.trim C. change (Int.unsigned Int.zero) with 0. omega.
  Alloc.trim D. destruct (zle (z0 - 1) Int.max_unsigned).
  rewrite Int.unsigned_repr. omega. omega.
  generalize (Int.unsigned_range_2 (Int.repr (z0-1))). omega.
  clear bnd0.
  rewrite Int.add_zero in C.
  unfold Int.add in *.
  clear A D.
  split. omega.
  destruct (zle (Int.unsigned i0 + Int.unsigned (al b)) Int.max_unsigned); auto.
  assert (exists j, 0 < j < Int.unsigned i0 /\ Int.unsigned (al b) + j = Int.max_unsigned).
  (exists (Int.max_unsigned - Int.unsigned (al b)); split; try omega).
  destruct H as [j [E F]].
  generalize (addr_space b (Int.repr j)). rewrite Heqp; simpl.
  rewrite Int.unsigned_repr.
  rewrite F.
  change (Int.unsigned (Int.repr (Int.max_unsigned))) with Int.max_unsigned.
  intro G; Alloc.trim G. subst. omega.
  generalize (Int.unsigned_range i). omega.
  omega.
Qed.

Require Import Psatz.

Lemma weak_valid_pointer:
  forall m b0 i0 sz
         (bnds: forall b, sz b = Mem.bounds_of_block m b)
         (bnd0: forall b, fst (sz b) = 0)
         (Hib : forall (b : block) i,
                Mem.valid_pointer m b i = true ->
                in_bound i (sz b))
         (Hnib : forall (b : block) i,
                ~ in_bound i (sz b) ->
                  Mem.valid_pointer m b i = false
         )
         (NVP: Mem.valid_pointer m b0 (Int.unsigned i0) = false)
         (NVP': Mem.valid_pointer m b0 (Int.unsigned i0 - 1) = true),
    Int.unsigned i0 = snd (sz b0) \/ in_bound (Int.unsigned i0) (sz b0).
Proof.
  intros.
  assert (in_bound (Int.unsigned i0 - 1) (sz b0)).
  {
    destruct (in_bound_dec (sz b0) (Int.unsigned i0 - 1)); auto.
  } auto.
  apply Hib in NVP'.
  assert (Int.unsigned i0 - 1 = Int.unsigned (Int.sub i0 Int.one)).
  {
    unfold in_bound in *; destruct (sz b0) eqn:?; simpl in *.
    unfold Int.sub.
    rewrite Int.unsigned_repr. change (Int.unsigned Int.one) with 1.
    omega.
    generalize (bnd0 b0); rewrite Heqp. simpl; intro; subst.
    change (Int.unsigned Int.one) with 1.
    destruct (zle z0 0).
    subst.
    unfold Mem.valid_pointer in NVP'.
    destruct (Mem.perm_dec m b0 (Int.unsigned i0 - 1) Cur Nonempty); try discriminate.
    clear NVP'. apply Mem.perm_bounds in p.
    rewrite <- bnds in p. rewrite Heqp in p. unfold in_bound in p; simpl in p.
    omega.
    generalize (Int.unsigned_range_2 i0). intros. omega.
    generalize (Int.unsigned_range_2 i0). intros. omega.
  }
  destruct (in_bound_dec (sz b0) (Int.unsigned i0)).
  auto.
  unfold in_bound in *; destruct (sz b0) eqn:?; simpl in *.
  omega.
Qed.

Lemma cmp_expr_ok:
  forall c v1 v2 t1 t2 m v',
    sem_cmp c v1 t1 v2 t2 m = Some v' ->
    exists v'',
      sem_cmp_expr c (expr_of_val v1) t1
                   (expr_of_val v2) t2 m = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_cmp, sem_cmp_expr.
  intros c v1 v2 t1 t2 m v'.
  destruct (Mem.concrete_mem m) as [al cpt].
  destruct (classify_cmp t1 t2).
  - destruct v1; destruct v2; simpl; try congruence;
    intro A; inv A;
    eexists; split; simpl; eauto; red; intros; simpl.
    + destruct (Int.cmpu c i i0) eqn:?; try congruence;
      apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
      try rewrite e; try rewrite e0; simpl; auto;
      unfold_eval;
      intuition try congruence;
      rewrite_norm;
      rewrite Heqb; simpl; auto.
    + replace (Val.result_of_typ (tcheck_sval (NormaliseSpec.sval_of_val v')))
            with Int.
      * destruct (Int.eq i Int.zero) eqn:?; simpl in H0;
        destruct (Values.Val.cmp_different_blocks c) eqn:?; simpl in H0; inv H0;
        destruct (Mem.valid_pointer m b (Int.unsigned i0)) eqn:?; simpl in *; try discriminate.
        inv H1. apply valid_pointer in Heqb1.
        destruct b0; simpl in *;
        apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
        try rewrite e; try rewrite e0; simpl; auto;
        unfold_eval;
        intuition try congruence;
        rewrite_norm; auto;
        inv H; specialize (addr_space _ _ Heqb1);
        destruct (Int.cmpu c i (Int.add (alloc b) i0)) eqn:?; simpl; auto;
        destruct c; simpl in *; try discriminate;
        generalize (Int.eq_spec i Int.zero); rewrite Heqb0;
        generalize (Int.eq_spec i (Int.add (alloc b) i0));
          rewrite <- (negb_involutive (Int.eq _ _)); rewrite Heqb2; simpl;
        intros A B; subst; rewrite B in addr_space; subst;
        change (Int.unsigned Int.zero) with 0 in addr_space; omega.
      * revert H0; destr_cond_match; try discriminate.
        destr_cond_match; try discriminate.
        unfold option_map.
        destr_cond_match; try discriminate. intro A; inv A.
        destruct b0; simpl; auto.
    + destruct (Int.eq i0 Int.zero) eqn:?; simpl in H0;
      destruct (Values.Val.cmp_different_blocks c) eqn:?; simpl in H0; inv H0;
      destruct (Mem.valid_pointer m b (Int.unsigned i)) eqn:?; simpl in *; try discriminate.
      inv H1.
      apply valid_pointer in Heqb1.
      destruct b0; simpl in *;
      apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
      try rewrite e; try rewrite e0; simpl; auto;
      unfold_eval;
      intuition try congruence;
      rewrite_norm; auto;
      inv H; specialize (addr_space _ _ Heqb1);
      destruct (Int.cmpu c (Int.add (alloc b) i) i0) eqn:?; simpl; auto;
      destruct c; simpl in *; try discriminate;
      generalize (Int.eq_spec i0 Int.zero); rewrite Heqb0;
      generalize (Int.eq_spec (Int.add (alloc b) i) i0);
      rewrite <- (negb_involutive (Int.eq _ _)); rewrite Heqb2; simpl;
      intros A B; subst; rewrite B in addr_space; subst;
      change (Int.unsigned Int.zero) with 0 in addr_space; omega.
    + revert H0; repeat (destr_cond_match; simpl; try congruence).
      * subst. intro A; inv A.
        apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
        try rewrite e; try rewrite e0; simpl; auto;
        unfold_eval;
        intuition try congruence;
        rewrite_norm; auto.
        destruct (Int.cmpu c i i0); simpl in *; congruence.
        destruct (Int.cmpu c i i0); simpl in *; congruence.
        assert (forall b, fst (Mem.bounds_of_block m b) = 0).
        {
          unfold Mem.bounds_of_block. intro; destr_cond_match; simpl; auto.
          destruct p. apply Mem.bounds_lo_inf0 in Heqo. subst; auto.
        }
        {
          assert ((in_bound (Int.unsigned i) (Mem.bounds_of_block m b0) \/
                   Int.unsigned i = snd (Mem.bounds_of_block m b0)) /\
                  (in_bound (Int.unsigned i0) (Mem.bounds_of_block m b0) \/
                   Int.unsigned i0 = snd (Mem.bounds_of_block m b0))).
          {
            clear - Heqb1 H0.
            rewrite andb_true_iff in Heqb1; destruct Heqb1 as [A B].
            rewrite orb_true_iff in A, B.
            split.
            - destruct (Mem.valid_pointer m b0 (Int.unsigned i)) eqn:?.
              + left; apply valid_pointer; auto.
              + destruct A. discriminate.
                eapply weak_valid_pointer in H; eauto.
                * destruct H; auto.
                * intros b i1; rewrite Mem.valid_pointer_nonempty_perm.
                  apply Mem.perm_bounds.
                * intros b i1; destruct (Mem.valid_pointer m b i1) eqn:?; auto.
                  rewrite Mem.valid_pointer_nonempty_perm in Heqb1.
                  apply Mem.perm_bounds in Heqb1. congruence.
            - destruct (Mem.valid_pointer m b0 (Int.unsigned i0)) eqn:?.
              + left; apply valid_pointer; auto.
              + destruct B. discriminate.
                eapply weak_valid_pointer in H; eauto.
                * destruct H; auto.
                * intros b i1; rewrite Mem.valid_pointer_nonempty_perm.
                  apply Mem.perm_bounds.
                * intros b i1; destruct (Mem.valid_pointer m b i1) eqn:?; auto.
                  rewrite Mem.valid_pointer_nonempty_perm in Heqb1.
                  apply Mem.perm_bounds in Heqb1. congruence.
          } clear Heqb1.
          destruct H1 as [[A|A] [B|B]].
          - rewrite ! (Int.add_commut (alloc b0));
            erewrite compat_conserve_cmp; eauto; try congruence.
            destruct (Int.cmpu c i i0); simpl; auto.
          - rewrite ! (Int.add_commut (alloc b0)).
            erewrite compat_conserve_cmp'; eauto.
            destruct (Int.cmpu c i i0) eqn:?; simpl; auto.
          - rewrite ! (Int.add_commut (alloc b0)).
            rewrite <- (Int.swap_cmpu c (Int.add _ _)).
            erewrite compat_conserve_cmp'; eauto.
            rewrite Int.swap_cmpu.
            destruct (Int.cmpu c i i0) eqn:?; simpl; auto.
          - unfold Int.add. unfold Int.cmpu, Int.eq, Int.ltu.
            rewrite A, B.
            rewrite ! zeq_true.
            rewrite ! zlt_false; try omega.
            simpl.
            destruct c; simpl; auto.
        }
      * unfold option_map; destr_cond_match; try discriminate.
        intro A; inv A.
        apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
        try rewrite e; try rewrite e0; simpl; auto;
        unfold_eval;
        intuition try congruence;
        rewrite_norm; auto.
        destruct b1; simpl in *; congruence.
        destruct b1; simpl in *; congruence.
        destruct (Int.cmpu c (Int.add (alloc b) i)
                           (Int.add (alloc b0) i0)) eqn:?; simpl;
        destruct b1 eqn:?; simpl; auto;
        destruct c; simpl in *; try discriminate.
        rewrite andb_true_iff in Heqb1.
        destruct Heqb1 as [A B].
        apply valid_pointer in A.
        apply valid_pointer in B.
        inv H. specialize (overlap _ _ _ _ n A B).
        rewrite (int_eq_true _ _ Heqb2) in overlap. congruence.
        rewrite andb_true_iff in Heqb1.
        destruct Heqb1 as [A B].
        apply valid_pointer in A.
        apply valid_pointer in B.
        inv H. specialize (overlap _ _ _ _ n A B).
        rewrite negb_false_iff in Heqb2.
        rewrite (int_eq_true _ _ Heqb2) in overlap. congruence.
  - destruct v2; simpl; try congruence.
    destruct v1; simpl; try congruence; intro A; inv A;
    eexists; split; simpl; eauto; red; intros.
    destruct (Int.cmpu c i0 (Int.repr (Int64.unsigned i))) eqn:?;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    setoid_rewrite Heqb; simpl; auto.
    destruct (Mem.valid_pointer m b (Int.unsigned i0)) eqn:?; try discriminate.
    destruct (Int.eq (Int.repr (Int64.unsigned i)) Int.zero) eqn:?; simpl in *; try congruence.
    destruct (Values.Val.cmp_different_blocks c) eqn:?; simpl in H0; inv H0.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    try (destruct b0; simpl in *; try congruence; fail);
    setoid_rewrite (int_eq_true _ _ Heqb1).
    inv H.
    apply valid_pointer in Heqb0.
    specialize (addr_space _ _ Heqb0).
    destruct (Int.cmpu c (Int.add (alloc b) i0) Int.zero) eqn:?; simpl;
    destruct c; simpl in Heqo; try congruence; inv Heqo; simpl in *; auto.
    rewrite (int_eq_true _ _ Heqb2) in addr_space.
    change (Int.unsigned Int.zero) with 0 in addr_space; omega.
    rewrite negb_false_iff in Heqb2.
    rewrite (int_eq_true _ _ Heqb2) in addr_space.
    change (Int.unsigned Int.zero) with 0 in addr_space; omega.
  - destruct v1; simpl; try congruence.
    destruct v2; simpl; try congruence; intro A; inv A;
    eexists; split; simpl; eauto; red; intros.
    destruct (Int.cmpu c (Int.repr (Int64.unsigned i)) i0) eqn:?;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    setoid_rewrite Heqb; simpl; auto.
    destruct (Mem.valid_pointer m b (Int.unsigned i0)) eqn:?; try discriminate.
    destruct (Int.eq (Int.repr (Int64.unsigned i)) Int.zero) eqn:?; try discriminate.
    destruct (Values.Val.cmp_different_blocks c) eqn:?; try discriminate.
    destruct c; simpl in *; inv Heqo; inv H0;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm; destr_cond_match; simpl; try discriminate; auto;
    setoid_rewrite (int_eq_true _ _ Heqb1) in Heqb2;
    inv H; apply valid_pointer in Heqb0; specialize (addr_space _ _ Heqb0);
    try rewrite negb_false_iff in Heqb2;
    rewrite <- (int_eq_true _ _ Heqb2) in addr_space;
    change (Int.unsigned Int.zero) with 0 in addr_space; omega.
-
  apply expr_binarith_ok; auto; intros; try congruence;
  try (eexists; split; simpl; eauto; red; intros; inv H;
       simpl in *;
         red in H0; red in H1;
  simpl in *;
  rew_tt H0; rew_tt H1;
  repeat norm_eval_wt);
  try destruct sg;
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    rewrite_norm;
    try (destruct (Int.cmp c n1 n2); simpl in *; congruence);
    try (destruct (Int.cmpu c n1 n2); simpl in *; congruence);
    try (destruct (Int64.cmp c n1 n2); simpl in *; congruence);
    try (destruct (Int64.cmpu c n1 n2); simpl in *; congruence);
    try (destruct (Float.cmp c n1 n2); simpl in *; congruence);
    try (destruct (Float32.cmp c n1 n2); simpl in *; congruence).
Qed.

Function applications


Inductive classify_fun_cases : Type :=
  | fun_case_f (targs: typelist) (tres: type) (cc: calling_convention) (* (pointer to) function *)
  | fun_default.

Definition classify_fun (ty: type) :=
  match ty with
  | Tfunction args res cc => fun_case_f args res cc
  | Tpointer (Tfunction args res cc) _ => fun_case_f args res cc
  | _ => fun_default
  end.

Argument of a switch statement


Inductive classify_switch_cases : Type :=
  | switch_case_i
  | switch_case_l
  | switch_default.

Definition classify_switch (ty: type) :=
  match ty with
  | Ctypes.Tint _ _ _ => switch_case_i
  | Ctypes.Tlong _ _ => switch_case_l
  | _ => switch_default
  end.

Definition sem_switch_arg (m: mem) (v: expr_sym) (ty: type): option Z :=
  match classify_switch ty with
  | switch_case_i =>
      match Mem.mem_norm m v Int with Vint n => Some(Int.unsigned n) | _ => None end
  | switch_case_l =>
      match Mem.mem_norm m v Long with Vlong n => Some(Int64.unsigned n) | _ => None end
  | switch_default =>
      None
  end.

Combined semantics of unary and binary operators


Definition sem_unary_operation vptr
            (op: unary_operation) (v: val) (ty: type): option val :=
  match op with
  | Onotbool => sem_notbool vptr v ty
  | Onotint => sem_notint v ty
  | Oneg => sem_neg v ty
  | Oabsfloat => sem_absfloat v ty
  end.

Definition sem_unary_operation_expr
            (op: unary_operation) (v: expr_sym) (ty: type): expr_sym :=
  match op with
  | Onotbool => sem_notbool_expr v ty
  | Onotint => sem_notint_expr v ty
  | Oneg => sem_neg_expr v ty
  | Oabsfloat => sem_absfloat_expr v ty
  end.

Lemma expr_unop_ok:
  forall m op v ty v',
    sem_unary_operation (vptr m) op v ty = Some v' ->
    exists v'',
      sem_unary_operation_expr op (expr_of_val v) ty = v'' /\
      norm_eq_compat m v'' v'.
Proof.
  destruct op; simpl; intros x ty v' A.
  - destruct (notbool_expr_ok _ _ _ _ A) as [v'' [B C]];
    exists v''; split; auto.
  - destruct (notint_expr_ok _ _ _ A) as [v'' [B C]];
    exists v''; split; auto.
    red; intros. eauto.
  - destruct (neg_expr_ok _ _ _ A) as [v'' [B C]];
    exists v''; split; auto.
    red; intros. eauto.
  - destruct (absfloat_expr_ok _ _ _ A) as [v'' [B C]];
    exists v''; split; auto.
    red; intros. eauto.
Qed.

Definition sem_binary_operation
    (op: binary_operation)
    (v1: val) (t1: type) (v2: val) (t2:type)
    (m: mem): option val :=
  let vptr := fun b i => Mem.valid_pointer m b (Int.unsigned i) in
  match op with
  | Oadd => sem_add vptr v1 t1 v2 t2
  | Osub => sem_sub vptr v1 t1 v2 t2
  | Omul => sem_mul vptr v1 t1 v2 t2
  | Omod => sem_mod vptr v1 t1 v2 t2
  | Odiv => sem_div vptr v1 t1 v2 t2
  | Oand => sem_and vptr v1 t1 v2 t2
  | Oor => sem_or vptr v1 t1 v2 t2
  | Oxor => sem_xor vptr v1 t1 v2 t2
  | Oshl => sem_shl v1 t1 v2 t2
  | Oshr => sem_shr v1 t1 v2 t2
  | Oeq => sem_cmp Ceq v1 t1 v2 t2 m
  | One => sem_cmp Cne v1 t1 v2 t2 m
  | Olt => sem_cmp Clt v1 t1 v2 t2 m
  | Ogt => sem_cmp Cgt v1 t1 v2 t2 m
  | Ole => sem_cmp Cle v1 t1 v2 t2 m
  | Oge => sem_cmp Cge v1 t1 v2 t2 m
  end.

Definition sem_binary_operation_expr
    (op: binary_operation)
    (v1: expr_sym) (t1: type) (v2: expr_sym) (t2:type)
    (m: mem): option expr_sym :=
  match op with
  | Oadd => sem_add_expr v1 t1 v2 t2
  | Osub => sem_sub_expr v1 t1 v2 t2
  | Omul => sem_mul_expr v1 t1 v2 t2
  | Omod => sem_mod_expr v1 t1 v2 t2
  | Odiv => sem_div_expr v1 t1 v2 t2
  | Oand => sem_and_expr v1 t1 v2 t2
  | Oor => sem_or_expr v1 t1 v2 t2
  | Oxor => sem_xor_expr v1 t1 v2 t2
  | Oshl => sem_shl_expr v1 t1 v2 t2
  | Oshr => sem_shr_expr v1 t1 v2 t2
  | Oeq => sem_cmp_expr Ceq v1 t1 v2 t2 m
  | One => sem_cmp_expr Cne v1 t1 v2 t2 m
  | Olt => sem_cmp_expr Clt v1 t1 v2 t2 m
  | Ogt => sem_cmp_expr Cgt v1 t1 v2 t2 m
  | Ole => sem_cmp_expr Cle v1 t1 v2 t2 m
  | Oge => sem_cmp_expr Cge v1 t1 v2 t2 m
  end.

Definition is_valid_ptr (m:mem) (v: val) : Prop :=
  match v with
      Vptr b o => in_bound (Int.unsigned o) (Mem.bounds_of_block m b)
    | _ => True
  end.

Lemma expr_binop_ok:
  forall op v1 t1 v2 t2 m v',
    sem_binary_operation op v1 t1 v2 t2 m = Some v' ->
    exists v'',
      sem_binary_operation_expr op (expr_of_val v1) t1
                               (expr_of_val v2) t2 m = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  destruct op; simpl; intros v1 t1 v2 t2 m v'.
  - apply add_expr_ok.
  - apply sub_expr_ok.
  - apply mul_expr_ok.
  - apply div_expr_ok.
  - apply mod_expr_ok.
  - apply and_expr_ok.
  - apply or_expr_ok.
  - apply xor_expr_ok.
  - apply shl_expr_ok.
  - apply shr_expr_ok.
  - apply cmp_expr_ok.
  - apply cmp_expr_ok.
  - apply cmp_expr_ok.
  - apply cmp_expr_ok.
  - apply cmp_expr_ok.
  - apply cmp_expr_ok.
Qed.

Lemma valid_ptr_norm_val:
  forall m v
         (IVP : is_valid_ptr m v),
    Normalise.normalise (Mem.bounds_of_block m) (Mem.nat_mask m)
                        (expr_of_val v) (result_of_val v) = v.
Proof.
  intros.
  destruct (Mem.concrete_mem m).
  destruct v; simpl.
  apply Normalise.normalise_undef.
  eapply Normalise.normalise_int_int; eauto.
  eapply Normalise.normalise_long_long; eauto.
  eapply Normalise.normalise_float_float; eauto.
  eapply Normalise.normalise_single_single; eauto.
  eapply (Normalise.normalise_ptr_ptr _ _ _ H b i).
  simpl in IVP; auto.
Qed.

Lemma norm_eq_compat_norm:
  forall m e v,
    norm_eq_compat m e v ->
    is_valid_ptr m v ->
    Mem.mem_norm m e (result_of_val v) = v.
Proof.
  intros m e v NEC IVP.
  red in NEC. unfold Mem.mem_norm.
  rewrite NEC.
  apply valid_ptr_norm_val; auto.
Qed.

Lemma expr_binop_ok':
  forall op v1 t1 v2 t2 m v',
    sem_binary_operation op v1 t1 v2 t2 m = Some v' ->
    is_valid_ptr m v' ->
    exists v'',
      sem_binary_operation_expr op (expr_of_val v1) t1
                                (expr_of_val v2) t2 m = Some v'' /\
      Mem.mem_norm m v'' (result_of_val v') = v'.
Proof.
  intros op v1 t1 v2 t2 m v' SBO IVP.
  apply expr_binop_ok in SBO.
  destruct SBO as [v'' [A B]].
  eexists; split; eauto.
  apply norm_eq_compat_norm; auto.
Qed.


Definition sem_incrdecr vptr (id: incr_or_decr) (v: val) (ty: type) :=
  match id with
  | Incr => sem_add vptr v ty (Vint Int.one) type_int32s
  | Decr => sem_sub vptr v ty (Vint Int.one) type_int32s
  end.

Definition sem_incrdecr_expr (id: incr_or_decr) (v: expr_sym) (ty: type) :=
  match id with
  | Incr => sem_add_expr v ty (Eval (Eint Int.one)) type_int32s
  | Decr => sem_sub_expr v ty (Eval (Eint Int.one)) type_int32s
  end.

Lemma incrdecr_expr_ok:
  forall m id v1 t1 v',
    sem_incrdecr (vptr m) id v1 t1 = Some v' ->
    exists v'',
      sem_incrdecr_expr id (expr_of_val v1) t1 = Some v'' /\
      norm_eq_compat m v'' v'.
Proof.
  unfold sem_incrdecr, sem_incrdecr_expr.
  intros m id v1 t1 v'.
  destruct id.
  apply add_expr_ok.
  apply sub_expr_ok.
Qed.

Definition incrdecr_type (ty: type) :=
  match typeconv ty with
  | Tpointer ty a => Tpointer ty a
  | Ctypes.Tint sz sg a => Ctypes.Tint sz sg noattr
  | Ctypes.Tlong sg a => Ctypes.Tlong sg noattr
  | Ctypes.Tfloat sz a => Ctypes.Tfloat sz noattr
  | _ => Tvoid
  end.

Compatibility with extensions and injections


Section GENERIC_INJECTION.

Variable f: meminj.
Variable p: Val.partial_undef_allocation.
Variables m m': mem.

Hypothesis valid_pointer_inj:
  forall b1 ofs b2 delta,
  f b1 = Some(b2, delta) ->
  Mem.valid_pointer m b1 (Int.unsigned ofs) = true ->
  Mem.valid_pointer m' b2 (Int.unsigned (Int.add ofs (Int.repr delta))) = true.

Hypothesis weak_valid_pointer_inj:
  forall b1 ofs b2 delta,
  f b1 = Some(b2, delta) ->
  Mem.weak_valid_pointer m b1 (Int.unsigned ofs) = true ->
  Mem.weak_valid_pointer m' b2 (Int.unsigned (Int.add ofs (Int.repr delta))) = true.

Hypothesis weak_valid_pointer_no_overflow:
  forall b1 ofs b2 delta,
  f b1 = Some(b2, delta) ->
  Mem.weak_valid_pointer m b1 (Int.unsigned ofs) = true ->
  0 <= Int.unsigned ofs + Int.unsigned (Int.repr delta) <= Int.max_unsigned.

Hypothesis valid_different_pointers_inj:
  forall b1 ofs1 b2 ofs2 b1' delta1 b2' delta2,
  b1 <> b2 ->
  Mem.valid_pointer m b1 (Int.unsigned ofs1) = true ->
  Mem.valid_pointer m b2 (Int.unsigned ofs2) = true ->
  f b1 = Some (b1', delta1) ->
  f b2 = Some (b2', delta2) ->
  b1' <> b2' \/
  Int.unsigned (Int.add ofs1 (Int.repr delta1)) <> Int.unsigned (Int.add ofs2 (Int.repr delta2)).

Remark val_inject_vtrue: forall f, val_inject f Vtrue Vtrue.
Proof.
unfold Vtrue; auto. Qed.

Remark val_inject_vfalse: forall f, val_inject f Vfalse Vfalse.
Proof.
unfold Vfalse; auto. Qed.

Remark val_inject_of_bool: forall f b, val_inject f (Values.Val.of_bool b) (Values.Val.of_bool b).
Proof.
  intros. unfold Values.Val.of_bool.
  destruct b; [apply val_inject_vtrue|apply val_inject_vfalse].
Qed.

Hint Resolve val_inject_vtrue val_inject_vfalse val_inject_of_bool.

Ltac TrivialInject :=
  match goal with
  | |- exists v', Some ?v = Some v' /\ _ => (exists v; split; auto)
  | _ => idtac
  end.

Definition vptr1 := (fun b i => Mem.valid_pointer m b (Int.unsigned i)).
Definition vptr2 := (fun b i => Mem.valid_pointer m' b (Int.unsigned i)).

Lemma sem_cast_inject:
  forall v1 ty1 ty v tv1,
    sem_cast vptr1 v1 ty1 ty = Some v ->
    val_inject f v1 tv1 ->
    exists tv, sem_cast vptr2 tv1 ty1 ty = Some tv /\
               val_inject f v tv.
Proof.
  unfold sem_cast; intros; destruct (classify_cast ty1 ty);
  inv H0; inv H; TrivialInject.
  - econstructor; eauto.
  - destruct (cast_float_int si2 f0); inv H1; TrivialInject.
  - destruct (cast_single_int si2 f0); inv H1; TrivialInject.
  - destruct (cast_float_long si2 f0); inv H1; TrivialInject.
  - destruct (cast_single_long si2 f0); inv H1; TrivialInject.
  - revert H2; destr_cond_match; try discriminate.
    intro A; inv A.
    unfold vptr1, vptr2 in *; erewrite valid_pointer_inj; eauto.
  - destruct (ident_eq id1 id2 && fieldlist_eq fld1 fld2); inv H2; TrivialInject. econstructor; eauto.
  - destruct (ident_eq id1 id2 && fieldlist_eq fld1 fld2); inv H2; TrivialInject. econstructor; eauto.
  - econstructor; eauto.
Qed.


Ltac inject_auto :=
  match goal with
      |- Val.expr_inject _ _ (Eunop _ _ _) (Eunop _ _ _) =>
      apply expr_inject_unop; auto
    | |- Val.expr_inject _ _ (Ebinop _ _ _ _ _) (Ebinop _ _ _ _ _) =>
      apply expr_inject_binop; auto
  end.

Lemma sem_cast_expr_inject:
  forall v1 ty1 ty v tv1,
    sem_cast_expr v1 ty1 ty = Some v ->
    Val.expr_inject p f v1 tv1 ->
    exists tv, sem_cast_expr tv1 ty1 ty = Some tv /\ Val.expr_inject p f v tv.
Proof.
  unfold sem_cast_expr; intros; destruct (classify_cast ty1 ty);
  inv H; try (eexists; split; eauto);
  try (try destruct si2, sz2; simpl; unfold expr_cast_gen; repeat inject_auto; auto; fail);
  try (repeat inject_auto; constructor; simpl; auto; fail).
  unfold Val.loword, expr_cast_int_int;
  destruct si2, sz2; repeat inject_auto.
  destr_cond_match; try congruence; eauto.
  revert H2; destr_cond_match; try congruence; eauto.
  destr_cond_match; try congruence; eauto.
  revert H2; destr_cond_match; try congruence; eauto.
Qed.

Lemma sem_unary_operation_inject:
  forall op v1 ty v tv1,
  sem_unary_operation vptr1 op v1 ty = Some v ->
  val_inject f v1 tv1 ->
  exists tv, sem_unary_operation vptr2 op tv1 ty = Some tv /\ val_inject f v tv.
Proof.
  unfold sem_unary_operation; intros. destruct op.
  - unfold sem_notbool in *; destruct (classify_bool ty); inv H0; inv H; TrivialInject.
    unfold vptr1, vptr2 in *.
    revert H2; destr_cond_match; try discriminate. intro A; inv A.
    erewrite valid_pointer_inj; eauto.
  - unfold sem_notint in *; destruct (classify_notint ty); inv H0; inv H; TrivialInject.
  - unfold sem_neg in *; destruct (classify_neg ty); inv H0; inv H; TrivialInject.
  - unfold sem_absfloat in *; destruct (classify_neg ty); inv H0; inv H; TrivialInject.
Qed.



Lemma sem_unary_operation_expr_inject:
  forall op v1 ty v tv1,
  sem_unary_operation_expr op v1 ty = v ->
  Val.expr_inject p f v1 tv1 ->
  exists tv, sem_unary_operation_expr op tv1 ty = tv /\ Val.expr_inject p f v tv.
Proof.
  unfold sem_unary_operation; intros. destruct op; simpl in *.
  - unfold sem_notbool_expr in *.
    destruct (classify_bool ty); inv H; eexists; split; eauto;
    inject_auto; constructor; simpl; auto.
  - unfold sem_notint_expr in *.
    destruct (classify_notint ty); inv H; eexists; split; eauto;
    inject_auto; constructor; simpl; auto.
  - unfold sem_neg_expr in *.
    destruct (classify_neg ty); inv H; eexists; split; eauto;
    inject_auto; constructor; simpl; auto.
  - unfold sem_absfloat_expr in *. subst.
    destruct (classify_neg ty); eexists; split; eauto;
    unfold expr_cast_gen; try destruct s; repeat inject_auto.
Qed.


Definition optval_self_injects (ov: option val) : Prop :=
  match ov with
  | Some (Vptr b ofs) => False
  | _ => True
  end.


Definition optval_self_injects_expr (ov: option expr_sym) : Prop :=
  match ov with
  | Some e => forall p f, Val.expr_inject p f e e
  | _ => True
  end.

Remark sem_binarith_inject:
  forall sem_int sem_long sem_float sem_single v1 t1 v2 t2 v v1' v2',
  sem_binarith vptr1 sem_int sem_long sem_float sem_single v1 t1 v2 t2 = Some v ->
  val_inject f v1 v1' -> val_inject f v2 v2' ->
  (forall sg n1 n2, optval_self_injects (sem_int sg n1 n2)) ->
  (forall sg n1 n2, optval_self_injects (sem_long sg n1 n2)) ->
  (forall n1 n2, optval_self_injects (sem_float n1 n2)) ->
  (forall n1 n2, optval_self_injects (sem_single n1 n2)) ->
  exists v', sem_binarith vptr2 sem_int sem_long sem_float sem_single v1' t1 v2' t2 = Some v' /\ val_inject f v v'.
Proof.
  intros.
  assert (SELF: forall ov v, ov = Some v -> optval_self_injects ov -> val_inject f v v).
  {
    intros. subst ov; simpl in H7. destruct v0; contradiction || constructor.
  }
  unfold sem_binarith in *.
  set (c := classify_binarith t1 t2) in *.
  set (t := binarith_type c) in *.
  destruct (sem_cast vptr1 v1 t1 t) as [w1|] eqn:C1; try discriminate.
  destruct (sem_cast vptr1 v2 t2 t) as [w2|] eqn:C2; try discriminate.
  exploit (sem_cast_inject v1); eauto. intros (w1' & C1' & INJ1).
  exploit (sem_cast_inject v2); eauto. intros (w2' & C2' & INJ2).
  rewrite C1'; rewrite C2'.
  destruct c; inv INJ1; inv INJ2; discriminate || eauto.
Qed.


Definition sem_conserves_inject (sem: expr_sym -> expr_sym -> option expr_sym) : Prop :=
  forall p f v1 v2 v1' v2' v',
    Val.expr_inject p f v1 v1' ->
    Val.expr_inject p f v2 v2' ->
    sem v1 v2 = Some v' ->
    exists v'',
      sem v1' v2' = Some v'' /\
      Val.expr_inject p f v' v''.

Remark sem_binarith_expr_inject:
  forall sem_int sem_long sem_float sem_single v1 t1 v2 t2 v v1' v2'
         (SBE: sem_binarith_expr sem_int sem_long sem_float sem_single v1 t1 v2 t2 = Some v)
         (EI1: Val.expr_inject p f v1 v1')
         (EI2: Val.expr_inject p f v2 v2')
         (SCIi: forall sg, sem_conserves_inject (sem_int sg))
         (SCIl: forall sg, sem_conserves_inject (sem_long sg))
         (SCIf: sem_conserves_inject sem_float)
         (SCIs: sem_conserves_inject sem_single),
  exists v', sem_binarith_expr sem_int sem_long sem_float sem_single v1' t1 v2' t2 = Some v' /\
             Val.expr_inject p f v v'.
Proof.
  intros.
  assert (SELF: forall ov v, ov = Some v -> optval_self_injects_expr ov -> Val.expr_inject p f v v).
  {
    intros. subst. apply H0.
  }
  unfold sem_binarith_expr in *.
  set (c := classify_binarith t1 t2) in *.
  set (t := binarith_type c) in *.
  destruct (sem_cast_expr v1 t1 t) as [w1|] eqn:C1; try discriminate.
  destruct (sem_cast_expr v2 t2 t) as [w2|] eqn:C2; try discriminate.
  exploit (sem_cast_expr_inject v1); eauto. intros (w1' & C1' & INJ1).
  exploit (sem_cast_expr_inject v2); eauto. intros (w2' & C2' & INJ2).
  rewrite C1'; rewrite C2'.
  destruct c; try discriminate.
  eapply SCIi; eauto.
  eapply SCIl; eauto.
  eapply SCIf; eauto.
  eapply SCIs; eauto.
Qed.

Remark sem_shift_inject:
  forall sem_int sem_long v1 t1 v2 t2 v v1' v2',
  sem_shift sem_int sem_long v1 t1 v2 t2 = Some v ->
  val_inject f v1 v1' -> val_inject f v2 v2' ->
  exists v', sem_shift sem_int sem_long v1' t1 v2' t2 = Some v' /\ val_inject f v v'.
Proof.
  intros. exists v.
  unfold sem_shift in *; destruct (classify_shift t1 t2); inv H0; inv H1; try discriminate.
  destruct (Int.ltu i0 Int.iwordsize); inv H; auto.
  destruct (Int64.ltu i0 Int64.iwordsize); inv H; auto.
  destruct (Int64.ltu i0 (Int64.repr 32)); inv H; auto.
  destruct (Int.ltu i0 Int64.iwordsize'); inv H; auto.
Qed.



Definition sem_conserves_inject2 (sem: expr_sym -> expr_sym -> expr_sym) : Prop :=
  forall p f v1 v2 v1' v2',
    Val.expr_inject p f v1 v1' ->
    Val.expr_inject p f v2 v2' ->
    Val.expr_inject p f (sem v1 v2) (sem v1' v2').

Remark sem_shift_expr_inject:
  forall sem_int sem_long v1 t1 v2 t2 v v1' v2'
         (SSE:sem_shift_expr sem_int sem_long v1 t1 v2 t2 = Some v)
         (EI1: Val.expr_inject p f v1 v1')
         (EI2:Val.expr_inject p f v2 v2')
         (SCIi: forall sg, sem_conserves_inject2 (sem_int sg))
         (SCIl: forall sg, sem_conserves_inject2 (sem_long sg)),
  exists v', sem_shift_expr sem_int sem_long v1' t1 v2' t2 = Some v' /\ Val.expr_inject p f v v'.
Proof.
  intros.
  unfold sem_shift_expr in *.
  destruct (classify_shift t1 t2); try discriminate; inv SSE;
  eexists; split; simpl; eauto.
  eapply SCIi; eauto.
  eapply SCIl; eauto.
  eapply SCIi; eauto. unfold expr_cast_gen. destruct s; repeat inject_auto.
  eapply SCIl; eauto. unfold expr_cast_gen. repeat inject_auto.
Qed.

Remark sem_cmp_inj:
  forall cmp v1 tv1 ty1 v2 tv2 ty2 v,
  sem_cmp cmp v1 ty1 v2 ty2 m = Some v ->
  val_inject f v1 tv1 ->
  val_inject f v2 tv2 ->
  exists tv, sem_cmp cmp tv1 ty1 tv2 ty2 m' = Some tv /\ val_inject f v tv.
Proof.
  intros.
  unfold sem_cmp in *; destruct (classify_cmp ty1 ty2).
-
  destruct (Values.Val.cmpu_bool (Mem.valid_pointer m) cmp v1 v2) as [b|] eqn:E; simpl in H; inv H.
  replace (Values.Val.cmpu_bool (Mem.valid_pointer m') cmp tv1 tv2) with (Some b).
  simpl. TrivialInject.
  symmetry. eapply val_cmpu_bool_inject; eauto.
-
  destruct v2; try discriminate. inv H1.
  set (v2 := Vint (Int.repr (Int64.unsigned i))) in *.
  destruct (Values.Val.cmpu_bool (Mem.valid_pointer m) cmp v1 v2) as [b|] eqn:E; simpl in H; inv H.
  replace (Values.Val.cmpu_bool (Mem.valid_pointer m') cmp tv1 v2) with (Some b).
  simpl. TrivialInject.
  symmetry. eapply val_cmpu_bool_inject with (v2 := v2); eauto. constructor.
-
  destruct v1; try discriminate. inv H0.
  set (v1 := Vint (Int.repr (Int64.unsigned i))) in *.
  destruct (Values.Val.cmpu_bool (Mem.valid_pointer m) cmp v1 v2) as [b|] eqn:E; simpl in H; inv H.
  replace (Values.Val.cmpu_bool (Mem.valid_pointer m') cmp v1 tv2) with (Some b).
  simpl. TrivialInject.
  symmetry. eapply val_cmpu_bool_inject with (v1 := v1); eauto. constructor.
-
  assert (SELF: forall b, optval_self_injects (Some (Values.Val.of_bool b))).
  {
    destruct b; exact I.
  }
  eapply sem_binarith_inject; eauto.
Qed.


Remark sem_cmp_expr_inj:
  forall cmp v1 tv1 ty1 v2 tv2 ty2 v,
  sem_cmp_expr cmp v1 ty1 v2 ty2 m = Some v ->
  Val.expr_inject p f v1 tv1 ->
  Val.expr_inject p f v2 tv2 ->
  exists tv, sem_cmp_expr cmp tv1 ty1 tv2 ty2 m' = Some tv /\ Val.expr_inject p f v tv.
Proof.
  intros.
  unfold sem_cmp_expr in *; destruct (classify_cmp ty1 ty2); inv H;
  try (eexists; split; eauto; [idtac];
  unfold expr_cast_gen; repeat inject_auto).

  eapply sem_binarith_expr_inject; eauto;
   red; intros; eexists; split; eauto; inv H4; inject_auto.
Qed.

Lemma sem_binary_operation_inj:
  forall op v1 ty1 v2 ty2 v tv1 tv2,
  sem_binary_operation op v1 ty1 v2 ty2 m = Some v ->
  val_inject f v1 tv1 -> val_inject f v2 tv2 ->
  exists tv, sem_binary_operation op tv1 ty1 tv2 ty2 m' = Some tv /\ val_inject f v tv.
Proof.
  unfold sem_binary_operation; intros; destruct op.
-
  unfold sem_add in *; destruct (classify_add ty1 ty2).
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. repeat rewrite Int.add_assoc. decEq. apply Int.add_commut.
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. repeat rewrite Int.add_assoc. decEq. apply Int.add_commut.
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. repeat rewrite Int.add_assoc. decEq. apply Int.add_commut.
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. repeat rewrite Int.add_assoc. decEq. apply Int.add_commut.
  + eapply sem_binarith_inject; eauto; intros; exact I.
-
  unfold sem_sub in *; destruct (classify_sub ty1 ty2).
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. rewrite Int.sub_add_l. auto.
  + inv H0; inv H1; inv H. TrivialInject.
    destruct (eq_block b1 b0); try discriminate. subst b1.
    rewrite H0 in H2; inv H2. rewrite dec_eq_true.
    destruct (Int.eq (Int.repr (sizeof ty)) Int.zero); inv H3.
    rewrite Int.sub_shifted. TrivialInject.
  + inv H0; inv H1; inv H. TrivialInject.
    econstructor. eauto. rewrite Int.sub_add_l. auto.
  + eapply sem_binarith_inject; eauto; intros; exact I.
-
  eapply sem_binarith_inject; eauto; intros; exact I.
-
  eapply sem_binarith_inject; eauto; intros.
  destruct sg.
  destruct (Int.eq n2 Int.zero
            || Int.eq n1 (Int.repr Int.min_signed) && Int.eq n2 Int.mone); exact I.
  destruct (Int.eq n2 Int.zero); exact I.
  destruct sg.
  destruct (Int64.eq n2 Int64.zero
            || Int64.eq n1 (Int64.repr Int64.min_signed) && Int64.eq n2 Int64.mone); exact I.
  destruct (Int64.eq n2 Int64.zero); exact I.
  exact I.
  exact I.
-
  eapply sem_binarith_inject; eauto; intros.
  destruct sg.
  destruct (Int.eq n2 Int.zero
            || Int.eq n1 (Int.repr Int.min_signed) && Int.eq n2 Int.mone); exact I.
  destruct (Int.eq n2 Int.zero); exact I.
  destruct sg.
  destruct (Int64.eq n2 Int64.zero
            || Int64.eq n1 (Int64.repr Int64.min_signed) && Int64.eq n2 Int64.mone); exact I.
  destruct (Int64.eq n2 Int64.zero); exact I.
  exact I.
  exact I.
-
  eapply sem_binarith_inject; eauto; intros; exact I.
-
  eapply sem_binarith_inject; eauto; intros; exact I.
-
  eapply sem_binarith_inject; eauto; intros; exact I.
-
  eapply sem_shift_inject; eauto.
-
  eapply sem_shift_inject; eauto.
- eapply sem_cmp_inj; eauto.
- eapply sem_cmp_inj; eauto.
- eapply sem_cmp_inj; eauto.
- eapply sem_cmp_inj; eauto.
- eapply sem_cmp_inj; eauto.
- eapply sem_cmp_inj; eauto.
Qed.

Lemma sem_binary_operation_expr_inj:
  forall op v1 ty1 v2 ty2 v tv1 tv2,
  sem_binary_operation_expr op v1 ty1 v2 ty2 m = Some v ->
    Val.expr_inject p f v1 tv1 -> Val.expr_inject p f v2 tv2 ->
  exists tv, sem_binary_operation_expr op tv1 ty1 tv2 ty2 m' = Some tv /\ Val.expr_inject p f v tv.
Proof.
  intros.
  unfold sem_binary_operation_expr in *.
  destruct op; simpl in *;
  try (eapply sem_binarith_expr_inject; eauto;
    red; intros; eexists; split; eauto; inv H4; inject_auto; fail);
  try ( eapply sem_shift_expr_inject; eauto;
    red; intros; unfold expr_cast_gen; repeat inject_auto; fail);
  try (eapply sem_cmp_expr_inj; eauto; fail).
  - unfold sem_add_expr in *.
    destruct (classify_add ty1 ty2); try discriminate; inv H;
    try (eexists; split; eauto; repeat inject_auto;
         constructor; simpl; auto).
    eapply sem_binarith_expr_inject; eauto;
    red; intros; eexists; split; eauto; inv H4; inject_auto.
  - unfold sem_sub_expr in *.
    destruct (classify_sub ty1 ty2); try discriminate; inv H;
    try (eexists; split; eauto; repeat inject_auto;
         constructor; simpl; auto).
    inv H1. rewrite inj_ok; simpl; auto.
    eapply sem_binarith_expr_inject; eauto;
    red; intros; eexists; split; eauto; inv H4; inject_auto.
Qed.

Lemma bool_val_inject:
  forall v ty b tv,
  bool_val vptr1 v ty = Some b ->
  val_inject f v tv ->
  bool_val vptr2 tv ty = Some b.
Proof.
  unfold bool_val; intros.
  destruct (classify_bool ty); inv H0; try congruence.
  unfold vptr1, vptr2 in *.
  revert H; destr_cond_match; try discriminate. intro A; inv A.
  erewrite valid_pointer_inj; eauto.
Qed.

End GENERIC_INJECTION.

Lemma sem_binary_operation_inject:
  forall p f m m' op v1 ty1 v2 ty2 v tv1 tv2,
  sem_binary_operation op v1 ty1 v2 ty2 m = Some v ->
  val_inject f v1 tv1 -> val_inject f v2 tv2 ->
  Mem.inject p f m m' ->
  exists tv, sem_binary_operation op tv1 ty1 tv2 ty2 m' = Some tv /\ val_inject f v tv.
Proof.
  intros. eapply sem_binary_operation_inj; eauto.
  intros; eapply Mem.valid_pointer_inject_val; eauto.
  constructor; simpl; unfold Val.inj_ptr; rewrite H3; auto.
  intros; eapply Mem.weak_valid_pointer_inject_val; eauto.
  constructor; simpl; unfold Val.inj_ptr; rewrite H3; auto.
  intros; eapply Mem.weak_valid_pointer_inject_no_overflow; eauto.
  intros; eapply Mem.different_pointers_inject; eauto.
Qed.


Lemma sem_binary_operation_expr_inject:
  forall p f m m' op v1 ty1 v2 ty2 v tv1 tv2,
  sem_binary_operation_expr op v1 ty1 v2 ty2 m = Some v ->
  Val.expr_inject p f v1 tv1 -> Val.expr_inject p f v2 tv2 ->
  Mem.inject p f m m' ->
  exists tv, sem_binary_operation_expr op tv1 ty1 tv2 ty2 m' = Some tv /\ Val.expr_inject p f v tv.
Proof.
  intros. eapply sem_binary_operation_expr_inj; eauto.
Qed.


Some properties of operator semantics


This section collects some common-sense properties about the type classification and semantic functions above. These properties are not used in the CompCert semantics preservation proofs, but increase confidence in the specification and its relation with the ISO C99 standard.

Relation between Boolean value and casting to _Bool type.

Lemma cast_bool_bool_val:
  forall m v t,
  sem_cast (vptr m) v t (Ctypes.Tint IBool Signed noattr) =
  match bool_val (vptr m) v t with None => None | Some b => Some(Values.Val.of_bool b) end.
Proof.
  intros.
  assert (A: classify_bool t =
    match t with
    | Ctypes.Tint _ _ _ => bool_case_i
    | Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _ => bool_case_p
    | Ctypes.Tfloat F64 _ => bool_case_f
    | Ctypes.Tfloat F32 _ => bool_case_s
    | Ctypes.Tlong _ _ => bool_case_l
    | _ => bool_default
    end).
  {
    unfold classify_bool; destruct t; simpl; auto. destruct i; auto.
  }
  unfold bool_val. rewrite A. unfold sem_cast. destruct t; simpl; auto; destruct v; auto.
  destruct (Int.eq i0 Int.zero); auto.
  destruct (Int64.eq i Int64.zero); auto.
  destruct f; auto.
  destruct f; auto.
  destruct f; auto.
  destruct f; auto.
  destruct (Float.cmp Ceq f0 Float.zero); auto.
  destruct f; auto.
  destruct (Float32.cmp Ceq f0 Float32.zero); auto.
  destruct f; auto.
  destruct (Int.eq i Int.zero); auto.
  destruct (vptr m b i); simpl; auto.
  destruct (Int.eq i Int.zero); auto.
  destruct (vptr m b i); simpl; auto.
  destruct (Int.eq i Int.zero); auto.
  destruct (vptr m b i); simpl; auto.
  destruct (Int.eq i0 Int.zero); auto.
  destruct (vptr m b i0); simpl; auto.
Qed.

Lemma cast_bool_bool_val_expr:
  forall v t b,
    sem_cast_expr v t (Ctypes.Tint IBool Signed noattr) = Some b ->
    Normalise.eq_modulo_normalise b (bool_expr v t).
Proof.
  intros.
  assert (A: classify_bool t =
    match t with
    | Ctypes.Tint _ _ _ => bool_case_i
    | Tpointer _ _ | Tcomp_ptr _ _ | Tarray _ _ _ | Tfunction _ _ _ => bool_case_p
    | Ctypes.Tfloat F64 _ => bool_case_f
    | Ctypes.Tfloat F32 _ => bool_case_s
    | Ctypes.Tlong _ _ => bool_case_l
    | _ => bool_default
    end).
  {
    unfold classify_bool; destruct t; simpl; auto. destruct i; auto.
  }
  unfold bool_expr. rewrite A. unfold sem_cast_expr in H.
  destruct t; simpl in *; auto; inv H.
  - intro; intros;
    Normalise.destr_wt;
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    destruct (Int.eq i0 Int.zero); auto.
  - intro; intros;
    Normalise.destr_wt. simpl in *.
    repeat (revert e; destr_cond_match; simpl in *; try congruence).
    repeat (revert Heqb; destr_cond_match; simpl in *; try congruence). intros.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    rewrite Heqb. rewrite <- e. simpl; auto.
    rewrite Heqb. auto.
    destruct (Int64.eq i Int64.zero); auto.
    apply Normalise.norm_eq_not_tc; auto.
    simpl in *.
    destr_cond_match; simpl in *; try congruence.
  - intro; intros;
    destruct f; inv H1;
    Normalise.destr_wt; simpl in *;
    repeat (revert e; destr_cond_match; simpl in *; try congruence);
    repeat (revert Heqb; destr_cond_match; simpl in *; try congruence); intros.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    + rewrite Heqb. rewrite <- e. simpl; auto.
    + rewrite Heqb. auto.
    + rewrite Float32.cmp_ne_eq.
      destruct (Float32.cmp Ceq f Float32.zero); simpl; auto.
    + apply Normalise.norm_eq_not_tc; auto.
      simpl in *.
      destr_cond_match; simpl in *; try congruence.
    + apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
      rewrite Heqb. rewrite <- e. simpl; auto.
      rewrite Heqb. auto.
      rewrite Float.cmp_ne_eq.
      destruct (Float.cmp Ceq f Float.zero); simpl; auto.
    + apply Normalise.norm_eq_not_tc; auto.
      simpl in *.
      destr_cond_match; simpl in *; try congruence.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
   simpl in e. destr_cond_match; simpl in *; try congruence.
   destruct (Int.eq i Int.zero); auto.
   apply Normalise.norm_eq_not_tc; simpl; auto.
   destr_cond_match; simpl in *; try congruence. rewrite Heqb in n; simpl in n. auto.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
   simpl in e. destr_cond_match; simpl in *; try congruence.
   destruct (Int.eq i Int.zero); auto.
   apply Normalise.norm_eq_not_tc; simpl; auto.
   destr_cond_match; simpl in *; try congruence. rewrite Heqb in n; simpl in n. auto.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
   simpl in e. destr_cond_match; simpl in *; try congruence.
   destruct (Int.eq i Int.zero); auto.
   apply Normalise.norm_eq_not_tc; simpl; auto.
   destr_cond_match; simpl in *; try congruence. rewrite Heqb in n; simpl in n. auto.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
   simpl in e. destr_cond_match; simpl in *; try congruence.
   destruct (Int.eq i0 Int.zero); auto.
   apply Normalise.norm_eq_not_tc; simpl; auto.
   destr_cond_match; simpl in *; try congruence. rewrite Heqb in n; simpl in n. auto.
Qed.


Relation between Boolean value and Boolean negation.

Lemma notbool_bool_val:
  forall vptr v t,
  sem_notbool vptr v t =
  match bool_val vptr v t with None => None | Some b => Some(Values.Val.of_bool (negb b)) end.
Proof.
  intros. unfold sem_notbool, bool_val.
  destruct (classify_bool t); auto; destruct v; auto; try rewrite negb_involutive; auto.
  destruct (vptr0 b i); simpl; auto.
Qed.

Lemma notbool_bool_expr:
  forall v t,
    Normalise.eq_modulo_normalise (sem_notbool_expr v t) (Eunop OpNotbool Tint (bool_expr v t)).
Proof.
  intros. unfold sem_notbool_expr, bool_expr.
  destruct (classify_bool t); auto.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    simpl in e. destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    destruct (Int.eq i Int.zero); auto.
    apply Normalise.norm_eq_not_tc; simpl; auto.
    destr_cond_match; simpl in *; try congruence.
    revert n; destr_cond_match; simpl in *; try congruence.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    simpl in e. destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    rewrite Float.cmp_ne_eq.
    destruct (Float.cmp Ceq f Float.zero); auto.
    apply Normalise.norm_eq_not_tc; simpl; auto.
    destr_cond_match; simpl in *; try congruence.
    revert n; destr_cond_match; simpl in *; try congruence.
 - intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    simpl in e. destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    rewrite Float32.cmp_ne_eq.
    destruct (Float32.cmp Ceq f Float32.zero); auto.
    apply Normalise.norm_eq_not_tc; simpl; auto.
    destr_cond_match; simpl in *; try congruence.
    revert n; destr_cond_match; simpl in *; try congruence.
- intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    simpl in e. destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    destruct (Int.eq i Int.zero); auto.
    apply Normalise.norm_eq_not_tc; simpl; auto.
    destr_cond_match; simpl in *; try congruence.
    revert n; destr_cond_match; simpl in *; try congruence.
- intro; intros;
    Normalise.destr_wt.
    apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
    try rewrite e; try rewrite e0; simpl; auto;
    unfold_eval;
    intuition try congruence;
    NormaliseSpec.simpl_eval'.
    simpl in e. destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    revert e; destr_cond_match; simpl in *; try congruence.
    destruct (Int64.eq i Int64.zero); auto.
    apply Normalise.norm_eq_not_tc; simpl; auto.
    destr_cond_match; simpl in *; try congruence.
    revert n; destr_cond_match; simpl in *; try congruence.
- intro; intros;
  Normalise.destr_wt.
  simpl in *. destruct (tcheck_expr v) eqn:?; simpl in *; try discriminate.
  destruct s; simpl in *; try discriminate.
  apply Normalise.norm_eq''; simpl; intros; intuition try congruence;
  try rewrite e; try rewrite e0; simpl; auto;
  unfold_eval;
  intuition try congruence;
  NormaliseSpec.simpl_eval'.
  rewrite Heqo. simpl; auto.
  rewrite Heqo; simpl; auto.
  inv e. auto.
  destruct ( (Int.eq i Int.zero)) eqn:?; simpl.
  rewrite Int.eq_true; simpl; auto.
  rewrite Int.eq_false by apply Int.one_not_zero.
  simpl; auto.
  rewrite Normalise.expr_wt; auto.
  rewrite Normalise.expr_wt; auto.
  simpl in *.
  destruct (tcheck_expr v) eqn:?; simpl in *; try discriminate.
  destruct s; try discriminate.
  simpl; auto.
Qed.


Relation with the arithmetic conversions of ISO C99, section 6.3.1

Module ArithConv.

This is the ISO C algebra of arithmetic types, without qualifiers. S stands for "signed" and U for "unsigned".

Inductive int_type : Type :=
  | _Bool
  | Char | SChar | UChar
  | Short | UShort
  | Int | UInt
  | Long | ULong
  | Longlong | ULonglong.

Inductive arith_type : Type :=
  | I (it: int_type)
  | Float
  | Double
  | Longdouble.

Definition eq_int_type: forall (x y: int_type), {x=y} + {x<>y}.
Proof.
decide equality. Defined.

Definition is_unsigned (t: int_type) : bool :=
  match t with
  | _Bool => true
  | Char => false (* as in most of CompCert's target ABIs *)
  | SChar => false
  | UChar => true
  | Short => false
  | UShort => true
  | Int => false
  | UInt => true
  | Long => false
  | ULong => true
  | Longlong => false
  | ULonglong => true
  end.

Definition unsigned_type (t: int_type) : int_type :=
  match t with
  | Char => UChar
  | SChar => UChar
  | Short => UShort
  | Int => UInt
  | Long => ULong
  | Longlong => ULonglong
  | _ => t
  end.

Definition int_sizeof (t: int_type) : Z :=
  match t with
  | _Bool | Char | SChar | UChar => 1
  | Short | UShort => 2
  | Int | UInt | Long | ULong => 4
  | Longlong | ULonglong => 8
  end.

6.3.1.1 para 1: integer conversion rank

Definition rank (t: int_type) : Z :=
  match t with
  | _Bool => 1
  | Char | SChar | UChar => 2
  | Short | UShort => 3
  | Int | UInt => 4
  | Long | ULong => 5
  | Longlong | ULonglong => 6
  end.

6.3.1.1 para 2: integer promotions, a.k.a. usual unary conversions

Definition integer_promotion (t: int_type) : int_type :=
  if zlt (rank t) (rank Int) then Int else t.

6.3.1.8: Usual arithmetic conversions, a.k.a. binary conversions. This function returns the type to which the two operands must be converted.

Definition usual_arithmetic_conversion (t1 t2: arith_type) : arith_type :=
  match t1, t2 with
  | Longdouble, _ | _, Longdouble => Longdouble
  | Double, _ | _, Double => Double
  | Float, _ | _, Float => Float
  | I i1, I i2 =>
    let j1 := integer_promotion i1 in
    let j2 := integer_promotion i2 in
    if eq_int_type j1 j2 then I j1 else
    match is_unsigned j1, is_unsigned j2 with
    | true, true | false, false =>
        if zlt (rank j1) (rank j2) then I j2 else I j1
    | true, false =>
        if zle (rank j2) (rank j1) then I j1 else
        if zlt (int_sizeof j1) (int_sizeof j2) then I j2 else
        I (unsigned_type j2)
    | false, true =>
        if zle (rank j1) (rank j2) then I j2 else
        if zlt (int_sizeof j2) (int_sizeof j1) then I j1 else
        I (unsigned_type j1)
    end
  end.

Mapping ISO arithmetic types to CompCert types

Definition proj_type (t: arith_type) : type :=
  match t with
  | I _Bool => Ctypes.Tint IBool Unsigned noattr
  | I Char => Ctypes.Tint I8 Unsigned noattr
  | I SChar => Ctypes.Tint I8 Signed noattr
  | I UChar => Ctypes.Tint I8 Unsigned noattr
  | I Short => Ctypes.Tint I16 Signed noattr
  | I UShort => Ctypes.Tint I16 Unsigned noattr
  | I Int => Ctypes.Tint I32 Signed noattr
  | I UInt => Ctypes.Tint I32 Unsigned noattr
  | I Long => Ctypes.Tint I32 Signed noattr
  | I ULong => Ctypes.Tint I32 Unsigned noattr
  | I Longlong => Ctypes.Tlong Signed noattr
  | I ULonglong => Ctypes.Tlong Unsigned noattr
  | Float => Ctypes.Tfloat F32 noattr
  | Double => Ctypes.Tfloat F64 noattr
  | Longdouble => Ctypes.Tfloat F64 noattr
  end.

Relation between typeconv and integer promotion.

Lemma typeconv_integer_promotion:
  forall i, typeconv (proj_type (I i)) = proj_type (I (integer_promotion i)).
Proof.
  destruct i; reflexivity.
Qed.

Relation between classify_binarith and arithmetic conversion.

Lemma classify_binarith_arithmetic_conversion:
  forall t1 t2,
  binarith_type (classify_binarith (proj_type t1) (proj_type t2)) =
  proj_type (usual_arithmetic_conversion t1 t2).
Proof.
  destruct t1; destruct t2; try reflexivity.
- destruct it; destruct it0; reflexivity.
- destruct it; reflexivity.
- destruct it; reflexivity.
- destruct it; reflexivity.
- destruct it; reflexivity.
- destruct it; reflexivity.
- destruct it; reflexivity.
Qed.

End ArithConv.