Module IntPromotion


Require Import Coqlib.
Require Import Op.
Require Import Values Integers.
Require Import IntPromotionCommon.
Require Import ZIntervalDomain.
Require Import OptionMonad.
Import ListNotations.

Local Open Scope option_monad_scope.

Lemma int_unsigned_pos n:
  0 <=? Int.unsigned n = true.
Proof.
  apply Z.leb_le; apply Int.unsigned_range.
Qed.

Definition is_Iint (v : ival): bool :=
  match v with
  | Iint false _ => true
  | _ => false
  end.

Lemma inv_is_Iint v:
  is_Iint v = true -> exists i, v = Iint false i.
Proof.
  unfold is_Iint; destruct v; try discriminate.
  destruct b. discriminate. eauto.
Qed.

Definition mk_op_prom1 (op' : operation) (usg sgn : bool) (args : list bool) (res : bool) : op_promotion :=
  mk_op_prom (ASSERT usg IN Some op') (ASSERT sgn IN Some op') args res.

Definition promote_operation (op : operation) (args : list ival) : op_promotion :=
  match op, args with
  | Omove, [x] =>
      mk_op_prom (Some Omove) (Some Omove) [true] true
  | Ointconst n, [] =>
      mk_op_prom (Some (Olongconst (promote false n))) (Some (Olongconst (promote true n))) [] true
  | Oadd, [Iint b1 i1; Iint b2 i2] =>
      mk_op_prom1 Oaddl
        (is_in_unsigned_range (Itv.add (Itv32.mod_itv i1) (Itv32.mod_itv i2)))
        (is_in_signed_range (Itv.add (zinterval_sgn_of_usg i1) (zinterval_sgn_of_usg i2)))
        [true; true] true
  | Oaddimm n, [Iint b1 i1] =>
      mk_op_prom
        (if is_in_unsigned_range (Itv.add (Itv32.mod_itv i1) (Itv.intconst (Int.unsigned n)))
         then Some (Oaddlimm (promote false n))
         else if is_in_unsigned_range (Itv.add (Itv32.mod_itv i1) (Itv.intconst (Int.signed n)))
         then Some (Oaddlimm (promote true n))
         else None)
        (ASSERT (is_in_signed_range (Itv.add (zinterval_sgn_of_usg i1) (Itv.intconst (Int.signed n)))) IN
         Some (Oaddlimm (promote true n)))
        [true] true
  | Osub, [Iint b1 i1; Iint b2 i2] =>
      mk_op_prom1 Osubl
        (is_in_unsigned_range (Itv.sub (Itv32.mod_itv i1) (Itv32.mod_itv i2)))
        (is_in_signed_range (Itv.sub (zinterval_sgn_of_usg i1) (zinterval_sgn_of_usg i2)))
        [true; true] true
  | Oshl, [Iint b1 i1; Iint b2 i2] =>
      mk_op_prom
        (ASSERT (zlt (Itv.itv_hi (Itv32.mod_itv i2)) Int.zwordsize &&
                 is_in_unsigned_range (Itv.shl (Itv32.mod_itv i1) (Itv32.mod_itv i2)
                                 (Itv32.mod_lo i1) (Itv32.mod_lo i2))) IN
         Some Oshll)
        None
        [true; false] true
  | Oshlimm n, [Iint b1 i1] =>
      mk_op_prom
        (ASSERT (zlt (Int.unsigned n) Int.zwordsize &&
                 is_in_unsigned_range (Itv.shl (Itv32.mod_itv i1) (Itv.intconst (Int.unsigned n))
                                 (Itv32.mod_lo i1) (int_unsigned_pos _))) IN
         Some (Oshllimm n))
        None
        [true] true
  | Ocast32signed, [Iint b1 i1] =>
      mk_op_prom None (Some Omove) [true] false
  | Ocast32unsigned, [Iint b1 i1] =>
      mk_op_prom (Some Omove) None [true] false
  | _, _ =>
      op_prom_None
  end.

Definition promote_condition (cond : condition) (args : list ival) : cond_promotion :=
  match cond, args with
  | Ccomp c, [x1; x2] =>
      mk_cond_prom None (Some (Ccompl c))
  | Ccompu c, [x1; x2] =>
      mk_cond_prom
        (ASSERT (Archi.ptr64 || (is_Iint x1 && is_Iint x2)) IN
         Some (Ccomplu c))
        None
  | Ccompimm c n, [x1] =>
      mk_cond_prom None (Some (Ccomplimm c (promote true n)))
  | Ccompuimm c n, [x1] =>
      mk_cond_prom
        (ASSERT (Archi.ptr64 || is_Iint x1) IN
         Some (Ccompluimm c (promote false n)))
        None
  | _,_ =>
      cond_prom_None
  end.

Soundness

Local Ltac inv_hypothesis :=
  match goal with
  | H : list_forall2 _ _ (_ :: _) |- _ => inv H
  | H : list_forall2 _ _ nil |- _ => inv H
  | H : is_Iint _ = true |- _ => apply inv_is_Iint in H as (? & ->)
  | H : vmatch _ (Iint _ _) |- _ => inv H
  | H : _ && _ = true |- _ => apply andb_prop in H as [? ?]
  | H : _ || _ = true |- _ => apply orb_prop in H as [?|?]
  | H : proj_sumbool _ = true |- _ => apply proj_sumbool_true in H
  end.

Local Hint Resolve zinterval_sgn_of_usg_correct Itv.zmatch_intconst
                   Itv.zmatch_add Itv.zmatch_sub Itv.zmatch_shl : itv.

Local Hint Resolve Int.eqm_signed_unsigned Int64.eqm_signed_unsigned Int.eqm_add Int64.eqm_add
                   Int.eqm_sub Int64.eqm_sub : eqm.

Lemma elim_hi_lt itv bd i
  (H : Itv.itv_hi itv < bd)
  (Z : Itv.zmatch i itv):
  i < bd.
Proof.
  case Z as [_ ?]; lia.
Qed.

Local Ltac inv_hypothesis_unsafe :=
  match goal with
  | H : is_in_unsigned_range _ = true |- _ =>
      eapply is_in_unsigned_range_sound in H; [|solve [eauto with itv]]
  | H : is_in_signed_range _ = true |- _ =>
      eapply is_in_signed_range_sound in H; [|solve [eauto with itv]]
  | H : Itv.itv_hi _ < _ |- _ =>
      eapply elim_hi_lt in H; [|solve [eauto with itv]]
  end.

Local Ltac unsigned_to_signed :=
  erewrite <- Int.eqm_samerepr, <- Int64.eqm_samerepr by (eauto with eqm).

Local Ltac slv_casts :=
  simpl; unfold promote; repeat f_equal;
  rewrite ?Int.unsigned_repr, ?Int.signed_repr, ?Int64.unsigned_repr, ?Int64.signed_repr
    by (auto using Int64.int_unsigned_range, int64_int_signed_range);
  reflexivity.

Lemma promote_operation_sound F V ge sp m op args iargs
  (MATCH : list_forall2 vmatch args iargs):
  sound_op_promotion (@promotable_op F V ge sp m) op (promote_operation op iargs) args.
Proof.
  unfold sound_op_promotion.
  destruct op; intros [|]; simpl;
  repeat (autodestruct; simpl; try solve [constructor]);
  intros; subst;
  repeat inv_hypothesis;
  unfold promotable_op; simpl;
    try reflexivity;
  unfold promote, Int.add, Int64.add, Int.sub, Int64.sub, Int.shl, Int64.shl',
         Int.ltu, Int64.iwordsize', Int.iwordsize;
  try solve [repeat inv_hypothesis_unsafe; (idtac + unsigned_to_signed); slv_casts].
  { (* addimm, unsigned promotion of the argument, signed promotion of the immediate *)
    repeat inv_hypothesis_unsafe.
    do 2 f_equal.
    apply Int64.eqm_samerepr.
    eapply Int64.eqm_trans.
    - apply Int64.eqm_sym; apply Int64.eqm_add; apply Int64.eqm_unsigned_repr.
    - apply Int64.eqm_refl2; symmetry.
      erewrite <- Int.eqm_samerepr
        by (apply Int.eqm_add; [apply Int.eqm_refl | apply Int.eqm_signed_unsigned]).
      slv_casts.
  }
  1,2:solve[ (* shl, shlimm *)
    repeat inv_hypothesis_unsafe;
    rewrite !Int.unsigned_repr by (cbn; lia);
    assert (Int.zwordsize <= Int64.zwordsize) by (cbn; lia);
    do 2 (autodestruct; intros _; try solve [exfalso; lia]);
    slv_casts
  ].
Qed.

Lemma promote_condition_sound m cond args iargs
  (MATCH : list_forall2 vmatch args iargs):
  sound_cond_promotion (@promotable_cond m) cond (promote_condition cond iargs) args.
Proof.
  unfold sound_cond_promotion.
  destruct cond; intros [|]; simpl;
  repeat (autodestruct; simpl; try solve [constructor]);
  try solve [constructor];
  intros; subst;
  repeat inv_hypothesis;
  unfold promotable_cond; simpl;
  unfold Val.cmp_bool, Val.cmpl_bool, Val.cmpu_bool, Val.cmplu_bool;
  repeat (autodestruct; simpl; try solve [constructor]);
  intros; subst;
  rewrite ?int_cmp_Z, ?int_cmpu_Z, ?int64_cmp_Z, ?int64_cmpu_Z;
  slv_casts.
Qed.

Global Opaque promote_operation promote_condition.