Module ZIntervalAnalysis


Require Import Coqlib ZArith.
Require Import AnalysisDomain.
Require Import ZIntervalDomain ZIntervalAOp.
Require Import Linking AST Op Registers Builtins.
Require Import Values Memory Integers Globalenvs Events.
Require List.


Definition areg (ae: aenv) (r: reg) : ival := AE.get r ae.
Definition aregs (ae: aenv) (rl: list reg) : list ival := List.map (areg ae) rl.

Lemma areg_sound:
  forall e ae r, ematch e ae -> vmatch (e#r) (areg ae r).
Proof.
  intros. apply H.
Qed.

Lemma aregs_sound:
  forall e ae rl, ematch e ae -> list_forall2 vmatch (e##rl) (aregs ae rl).
Proof.
  induction rl; simpl; intros. constructor. constructor; auto; apply areg_sound; auto.
Qed.

Fixpoint ae_set_multiple (regs : list reg) (avals : list ival) (ae : aenv): aenv :=
  match regs, avals with
  | (r::regs0), (av::avals0) =>
      ae_set_multiple regs0 avals0 (AE.set r av ae)
  | nil, _ | _, nil => ae
  end.

Lemma ematch_restrict:
  forall e ae (av : ival) r
         (VMATCH : vmatch (e # r) av)
         (EMATCH : ematch e ae),
    ematch e (AE.set r av ae).
Proof.
  unfold ematch.
  intros.
  assert (ae <> AE.bot) as NOT_BOT.
  { intro BOT.
    pose proof (EMATCH r) as EMATCH_r.
    rewrite BOT in EMATCH_r.
    rewrite AE.get_bot in EMATCH_r.
    inv EMATCH_r.
  }
  assert ( ~ AVal.eq av AVal.bot) as NOT_BOT2.
  { intro BOT.
    rewrite BOT in VMATCH.
    inv VMATCH.
  }
  rewrite (AE.gsspec r r0 NOT_BOT NOT_BOT2).
  destruct (peq r0 r).
  { subst r0.
    assumption.
  }
  apply EMATCH.
Qed.

Lemma ematch_restrict_multiple:
  forall regs avals e ae
         (ARG_MATCH : list_forall2 vmatch (e ## regs) avals)
         (EMATCH : ematch e ae),
    (ematch e (ae_set_multiple regs avals ae)).
Proof.
  induction regs; cbn; intros.
  assumption.
  inv ARG_MATCH.
  apply IHregs.
  assumption.
  apply ematch_restrict; assumption.
Qed.


Module ZIntervalAADomain(IR : IRspec) <: AADomain IR.
  Module VA := Interval_Analysis.
  
  Definition astate : Type := AE.t.
  Definition Astate : astate -> VA.t := Some.
  Definition filter_astate (s : VA.t) : option astate := s.
  Lemma filter_astate_bot: filter_astate VA.bot = None.
  Proof.
reflexivity. Qed.
  Lemma filter_iff_Astate s s': filter_astate s = Some s' <-> s = Astate s'.
  Proof.
reflexivity. Qed.
  Lemma filter_ge s s' (GE : VA.ge s s') (BOT : filter_astate s = None): filter_astate s' = None.
  Proof.
unfold filter_astate in BOT. rewrite BOT in GE. destruct s'; [case GE | reflexivity ]. Qed.

  Definition default_top : VA.t := Some AE.top.
  Lemma ge_default_top s': VA.ge default_top s'.
  Proof.
case s' as [s'|]; simpl; auto using AE.ge_top. Qed.

  Definition entry_state (f : IR.function) : VA.t :=
    Some (einit_regs (IR.fn_params f)).

  Definition prog_info := unit.
  Definition get_prog_info (p : program (fundef IR.function) unit): prog_info := tt.
  
  Local Hint Resolve areg_sound aregs_sound : ival.
  
Transfer functions

  Definition transfer_op (_ : prog_info) (op : operation) (args : list reg) (res : reg)
                         (ae : astate): VA.t :=
    let a := eval_static_operation op (aregs ae args) in
    Some (AE.set res a ae).

  Program Definition uint32_full_range := Int_Modulus_Interval.mkmod_interval (Interval.mkinterval 0 4294967295 _) _ _.
  Program Definition uint16_full_range := Int_Modulus_Interval.mkmod_interval (Interval.mkinterval 0 65535 _) _ _.
  Program Definition uint8_full_range := Int_Modulus_Interval.mkmod_interval (Interval.mkinterval 0 255 _) _ _.

  Definition transfer_load (_ : prog_info) (trap : trapping_mode) (chk : memory_chunk) (addr : addressing)
             (args : list reg) (res : reg) (ae : astate): VA.t :=
    let itv :=
      match chk with
      | Mint32 => if Archi.ptr64 then Iint true uint32_full_range else Itop
      | Mint16unsigned => Iint true uint16_full_range
      | Mint8unsigned => Iint true uint8_full_range
      | _ => Itop
      end in
    Some (AE.set res itv ae).

  Definition transfer_store (_ : prog_info) (chk : memory_chunk) (addr : addressing) (args : list reg)
                            (src : reg) (ae : astate): VA.t :=
    Some ae.

  Definition filter_condition (cond: condition) (regs: list reg) (ae : aenv) : aenv :=
    ae_set_multiple regs (filter_static_condition cond (aregs ae regs)) ae.

  Definition transfer_condition (_ : prog_info) (cond : condition) (args : list reg) (ae : astate): VA.t * VA.t :=
    match eval_static_condition cond (aregs ae args) with
    | Bnone => (None, None)
    | Just true => (Some ae, None)
    | Just false => (None, Some ae)
    | Btop | Any_bool =>
        (Some (filter_condition cond args ae),
         Some (filter_condition (negate_condition cond) args ae))
    end.

  Definition transfer_call (_ : prog_info) (args : list reg) (res : reg) (ae : astate): VA.t :=
      Some (AE.set res Itop ae).

  Fixpoint abuiltin_arg (ae: aenv) (ba: builtin_arg reg) : ival :=
    match ba with
    | BA r => areg ae r
    | BA_int n => intconst n
    | BA_long n => longconst n
    | BA_float _
    | BA_single _
    | BA_loadstack _ _
    | BA_addrstack _
    | BA_loadglobal _ _ _
    | BA_addrglobal _ _ => Itop
    | BA_splitlong hi lo => longofwords (abuiltin_arg ae hi) (abuiltin_arg ae lo)
    | BA_addptr ba1 ba2 =>
        let v1 := abuiltin_arg ae ba1 in
        let v2 := abuiltin_arg ae ba2 in
        if Archi.ptr64 then addl v1 v2 else add v1 v2
    end.

  Definition set_builtin_res (br: builtin_res reg) (av: ival) (ae: aenv) : aenv :=
    match br with
    | BR r => AE.set r av ae
    | _ => ae
    end.

  Definition transfer_builtin_default
                (ae: aenv)
                (args: list (builtin_arg reg)) (res: builtin_res reg) :=
    Some (set_builtin_res res Itop ae).

  Definition eval_static_builtin_function
                (ae: aenv)
                (bf: builtin_function) (args: list (builtin_arg reg)) :=
    match builtin_function_sem bf
                   (map val_of_ival (map (abuiltin_arg ae) args)) with
    | Some v => ival_of_val v
    | None => None
    end.

  Definition transfer_builtin (_ : prog_info) (ef : external_function)
                (args: list (builtin_arg reg)) (res: builtin_res reg) (ae : aenv): VA.t :=
    match ef, args with
    | EF_vload chunk, addr :: nil =>
        Some (set_builtin_res res Itop ae)
    | EF_vstore chunk, addr :: v :: nil =>
        Some (set_builtin_res res Itop ae)
    | EF_memcpy sz al, dst :: src :: nil =>
        Some (set_builtin_res res Itop ae)
    | (EF_annot _ _ _ | EF_debug _ _ _), _ =>
        Some (set_builtin_res res Itop ae)
    | EF_annot_val _ _ _, v :: nil =>
        let av := abuiltin_arg ae v in
        Some (set_builtin_res res av ae)
    | EF_builtin name sg, _ =>
        match lookup_builtin_function name sg with
        | Some bf =>
            match eval_static_builtin_function ae bf args with
            | Some av => Some (set_builtin_res res av ae)
            | None => transfer_builtin_default ae args res
            end
        | None => transfer_builtin_default ae args res
        end
    | _, _ =>
        transfer_builtin_default ae args res
    end.

  Definition transfer_jumpi (_ : prog_info) (arg : reg) (i : int) (ae : aenv) : VA.t :=
    Some (AE.set arg (intconst i) ae).

  Definition widen_node (_ : IR.function) (_ : IR.node) := true.

  Local Notation genv := (Genv.t (fundef IR.function) unit).
  
  Definition asmatch (_ : prog_info) (_ : genv) (_ : ghost_env) (_ : block) (rs : RTL.regset) (_ : mem)
                     (ae : astate): Prop :=
    ematch rs ae.

  Definition valid_callstate (_ : prog_info) (_ : genv) (_ : ghost_env) (_ : mem) (_ : list val): Prop :=
    True.
  Definition valid_returnstate (_ : prog_info) (_ : genv) (_ : ghost_env) (_ : mem) (_ : val) : Prop :=
    True.
  Definition succ_state (_ : prog_info) (_ : genv) (_ : block)
                        (_ : ghost_env) (_ : mem) (_ : ghost_env) (_ : mem) : Prop :=
    True.

  Lemma asmatch_ge: forall pi ge gh sp rs m s0 s1,
    VA.ge (Astate s1) (Astate s0) ->
    asmatch pi ge gh sp rs m s0 ->
    asmatch pi ge gh sp rs m s1.
  Proof.
    unfold asmatch; simpl; intros. eapply ematch_ge; eauto.
  Qed.
  
  Lemma succ_state_refl pi ge bd gh m:
    succ_state pi ge bd gh m gh m.
  Proof.
constructor. Qed.

  Lemma succ_state_trans pi ge bd gh0 m0 gh1 m1 gh2 m2:
    succ_state pi ge bd gh0 m0 gh1 m1 ->
    succ_state pi ge bd gh1 m1 gh2 m2 ->
    succ_state pi ge bd gh0 m0 gh2 m2.
  Proof.
constructor. Qed.

  Lemma initial_valid_callstate prog b f m cu:
    let ge := Genv.globalenv prog in
    Genv.init_mem prog = Some m ->
    Genv.find_symbol ge prog.(prog_main) = Some b ->
    Genv.find_funct_ptr ge b = Some f ->
    IR.funsig f = signature_main ->
    forall (LINK : linkorder cu prog),
    exists gh, valid_callstate (get_prog_info cu) (Genv.globalenv prog) gh m nil.
  Proof.
exists dummy_ghost_env; constructor. Qed.

  Lemma sound_tailcall pi ge gh sp m lo hi m' rs aa args
    (MATCH : asmatch pi ge gh sp rs m aa)
    (MEM : Mem.free m sp lo hi = Some m'):
    exists gh',
      valid_callstate pi ge gh' m' (rs ## args) /\
      (forall gh1 m1, succ_state pi ge (Mem.nextblock m') gh' m' gh1 m1 ->
                      succ_state pi ge sp gh m gh1 m1).
  Proof.
    exists dummy_ghost_env; split; constructor.
  Qed.
  
  Lemma sound_return pi ge gh sp m lo hi m' rs aa or
    (MATCH : asmatch pi ge gh sp rs m aa)
    (MEM : Mem.free m sp lo hi = Some m'):
    exists gh',
      valid_returnstate pi ge gh' m' (regmap_optget or Vundef rs) /\
      succ_state pi ge sp gh m gh' m'.
  Proof.
    exists dummy_ghost_env; split; constructor.
  Qed.

  Lemma sound_external_call pi ge gh m args ef t res m'
    (VCS : valid_callstate pi ge gh m args)
    (EXTCALL : external_call ef ge args m t res m'):
    exists gh',
      valid_returnstate pi ge gh' m' res /\
      succ_state pi ge (Mem.nextblock m) gh m gh' m'.
  Proof.
    exists dummy_ghost_env; split; constructor.
  Qed.

  Lemma function_init pi ge gh m args f m' sp
    (VCS : valid_callstate pi ge gh m args)
    (ARGTYPE: Val.has_argtype_list args (IR.fn_sig f).(sig_args))
    (ALLOC: Mem.alloc m 0 (IR.fn_stacksize f) = (m', sp)):
    exists aa gh',
      entry_state f = Astate aa /\
      asmatch pi ge gh' sp (RTL.init_regs args (IR.fn_params f)) m' aa /\
      succ_state pi ge sp gh m gh' m'.
  Proof.
    eexists _, dummy_ghost_env; split. reflexivity.
    split. apply ematch_init. constructor.
    constructor.
  Qed.

  Section BUILTIN_SOUNDNESS.
  Lemma abuiltin_arg_sound:
    forall {F V : Type} (ge : Genv.t F V) rs m ae sp
    (EMATCH : ematch rs ae)
    a v
    (EVAL : eval_builtin_arg ge (fun r => rs#r) (Vptr sp Ptrofs.zero) m a v),
    vmatch v (abuiltin_arg ae a).
  Proof.
    induction a; intros; inv EVAL; simpl; eauto with ival.
    destruct Archi.ptr64; auto with ival.
  Qed.

  Lemma abuiltin_args_sound:
    forall {F V : Type} (ge : Genv.t F V) rs sp m ae
    (EMATCH : ematch rs ae)
    al vl
    (EVAL : eval_builtin_args ge (fun r => rs#r) (Vptr sp Ptrofs.zero) m al vl),
    list_forall2 vmatch vl (map (abuiltin_arg ae) al).
  Proof.
    intros until vl. induction 1; simpl.
  - constructor.
  - constructor; auto. eapply abuiltin_arg_sound; eauto.
  Qed.

  Lemma set_builtin_res_sound:
    forall rs ae v av res,
    ematch rs ae ->
    vmatch v av ->
    ematch (regmap_setres res v rs) (set_builtin_res res av ae).
  Proof.
    intros. destruct res; simpl; auto. apply ematch_update; auto.
  Qed.

  Lemma eval_static_builtin_function_sound:
    forall {F V : Type} (ge : Genv.t F V)
           rs sp m ae (bf: builtin_function) al vl v va,
    ematch rs ae ->
    eval_builtin_args ge (fun r => rs#r) (Vptr sp Ptrofs.zero) m al vl ->
    eval_static_builtin_function ae bf al = Some va ->
    builtin_function_sem bf vl = Some v ->
    vmatch v va.
  Proof.
    unfold eval_static_builtin_function; intros.
    exploit (abuiltin_args_sound ge); eauto.
    set (vla := map (abuiltin_arg ae) al) in *. intros VMA.
    destruct (builtin_function_sem bf (map val_of_ival vla)) as [v0|] eqn:A; try discriminate.
    assert (LD: Val.lessdef v0 v).
    { apply val_inject_lessdef.
      exploit (bs_inject _ (builtin_function_sem bf)).
      apply val_inject_list_lessdef. eapply list_val_of_ival_sound; eauto.
      rewrite A, H2; simpl. auto.
    }
    inv LD. apply ival_of_val_sound; auto. discriminate.
  Qed.
  End BUILTIN_SOUNDNESS.

  Section TRANSFER_SOUNDNDESS.

    Variables (pi : prog_info) (ge : genv) (gh : ghost_env) (sp : block) (rs : RTL.regset) (m : mem)
              (s0 : astate).

  Lemma transfer_op_correct op args res v
    (MATCH : asmatch pi ge gh sp rs m s0)
    (EVAL : eval_operation ge (Vptr sp Ptrofs.zero) op rs ## args m = Some v):
    exists s1,
      transfer_op pi op args res s0 = Astate s1 /\
      asmatch pi ge gh sp (rs#res <- v) m s1.
  Proof.
    eexists; split. reflexivity.
    apply ematch_update. assumption.
    eapply eval_static_operation_sound; eauto with ival.
  Qed.

  Lemma transfer_load_correct trap chunk addr args res v
    (MATCH : asmatch pi ge gh sp rs m s0)
    (LOAD : RTL.has_loaded ge (Vptr sp Ptrofs.zero) rs m chunk addr args v trap):
    exists s1,
      transfer_load pi trap chunk addr args res s0 = Astate s1 /\
      asmatch pi ge gh sp (rs#res <- v) m s1.
  Proof.
    eexists; split. reflexivity.
    apply ematch_update. { eauto with ival. }
    inv LOAD; cycle 1.
    { destruct chunk; try constructor;
      destruct Archi.ptr64; constructor. }
    unfold Mem.loadv in *; destruct a; try discriminate.
    pose proof (Mem.load_xtype _ _ _ _ _ LOAD0) as XTYPE.
    destruct chunk; cbn in XTYPE; destruct v.
    all: try contradiction.
    all: try (constructor ; fail).
    all: try (destruct Archi.ptr64; constructor; fail).
    all: try (cbn in RETTYPE; rewrite RETTYPE; constructor; fail).
    { constructor.
      assert (0 <= 8 < Int.zwordsize) as WSIZE.
      { change Int.zwordsize with 32. lia. }
      pose proof (Int.zero_ext_range 8 i0 WSIZE) as RANGE.
      rewrite XTYPE.
      change (two_p 8) with 256 in *.
      unfold uint8_full_range. constructor; cbn; lia.
    }
    { constructor.
      assert (0 <= 16 < Int.zwordsize) as WSIZE.
      { change Int.zwordsize with 32. lia. }
      pose proof (Int.zero_ext_range 16 i0 WSIZE) as RANGE.
      rewrite XTYPE.
      change (two_p 16) with 65536 in *.
      unfold uint16_full_range. constructor; cbn; lia.
    }
    all: destruct Archi.ptr64; try discriminate; try (apply vmatch_top; fail);
       constructor;
         pose proof (Int.unsigned_range i0);
         change Int_Modulus_Interval.modulus with 4294967296 in *;
         unfold uint32_full_range; constructor; cbn; lia.
  Qed.

  Lemma transfer_store_correct chunk addr args a src m'
    (MATCH : asmatch pi ge gh sp rs m s0)
    (EVAL : eval_addressing ge (Vptr sp Ptrofs.zero) addr rs##args = Some a)
    (STORE : Mem.storev chunk m a rs # src = Some m'):
    exists s1,
      transfer_store pi chunk addr args src s0 = Astate s1 /\
      asmatch pi ge gh sp rs m' s1 /\
      succ_state pi ge sp gh m gh m'.
  Proof.
    eexists; repeat split. assumption.
  Qed.

  Lemma filter_condition_sound cond regs
    (EMATCH : ematch rs s0)
    (COND : eval_condition cond (rs ## regs) m = Some true):
    ematch rs (filter_condition cond regs s0).
  Proof.
    intros.
    unfold filter_condition.
    apply ematch_restrict_multiple. 2:assumption.
    apply filter_static_condition_sound with (m := m). 2:assumption.
    apply aregs_sound.
    assumption.
  Qed.

  Lemma transfer_condition_correct cond args b
    (MATCH : asmatch pi ge gh sp rs m s0)
    (EVAL : eval_condition cond rs ## args m = Some b):
    exists s1,
      (let (sso, snot) := transfer_condition pi cond args s0 in
       if b then sso else snot) = Astate s1 /\
      asmatch pi ge gh sp rs m s1.
  Proof.
    assert (EVALN : eval_condition (negate_condition cond) rs ## args m = Some (negb b))
      by (rewrite eval_negate_condition, EVAL; reflexivity).
    assert (ARGS : list_forall2 (vmatch) rs ## args (aregs s0 args))
      by auto with ival.
    pose proof (eval_static_condition_sound cond (rs ## args) m (aregs s0 args) ARGS) as COND.
    unfold transfer_condition; rewrite EVAL in COND.
    set (sc := eval_static_condition cond (aregs s0 args)) in *; clearbody sc.
    inversion COND; subst;
      (destruct b; eexists; (split; [reflexivity|try assumption]));
      apply filter_condition_sound; eauto.
  Qed.

  Lemma transfer_call_correct args res
    (MATCH : asmatch pi ge gh sp rs m s0):
    exists gh_ce0,
      valid_callstate pi ge gh_ce0 m (rs ## args) /\
    forall gh_ce1 m' vres,
      valid_returnstate pi ge gh_ce1 m' vres ->
      succ_state pi ge (Mem.nextblock m) gh_ce0 m gh_ce1 m' ->
    exists s1 gh_cr1,
      transfer_call pi args res s0 = Astate s1 /\
      asmatch pi ge gh_cr1 sp (rs # res <- vres) m' s1 /\
      succ_state pi ge sp gh m gh_cr1 m'.
  Proof.
    eexists gh; split. constructor.
    intros ? m' vres _ _.
    eexists _, gh; repeat split.
    apply ematch_update; eauto with ival.
  Qed.

  Lemma transfer_builtin_correct args vargs ef t vres m' res
    (MATCH : asmatch pi ge gh sp rs m s0)
    (EVAL : eval_builtin_args ge (fun r : positive => rs # r) (Vptr sp Ptrofs.zero) m args vargs)
    (CALL : external_call ef ge vargs m t vres m'):
    exists s1 gh',
      transfer_builtin pi ef args res s0 = Astate s1 /\
      asmatch pi ge gh' sp (regmap_setres res vres rs) m' s1 /\
      succ_state pi ge sp gh m gh' m'.
  Proof.
    Local Ltac intro_goal :=
      eexists _, gh; split; [|split];
        [reflexivity|apply set_builtin_res_sound; auto with ival|constructor].
    set (tr := transfer_builtin pi ef args res s0).
    set (Goal := _).
    assert (DEFAULT: tr = transfer_builtin_default s0 args res -> Goal); subst Goal. {
      intros ->; intro_goal.
    }
    set (aargs := map (abuiltin_arg s0) args) in *.
    assert (ARGS: list_forall2 vmatch vargs aargs)
      by (eapply abuiltin_args_sound; eauto).
    destruct ef; simpl in tr; auto; simpl in CALL.
    - (* EF_builtin *)
      unfold builtin_or_external_sem in CALL.
      destruct (lookup_builtin_function name sg) as [bf|] eqn:LK; auto.
      destruct (eval_static_builtin_function s0 bf args) as [av|] eqn:ES; auto.
      inv CALL.
      intro_goal.
      eapply eval_static_builtin_function_sound; eauto.
    - (* EF_vload *)
      destruct args as [|addr [|]]; auto.
    - (* EF_vstore *)
      destruct args as [|arg0 [|arg1 [|]]]; auto.
    - (* EF_memcpy *)
      destruct args as [|arg0 [|arg1 [|]]]; auto.
    - (* EF_annot_val *)
      destruct args as [|arg0 [|]]; auto.
      inv CALL.
      intro_goal.
      inv ARGS; assumption.
  Qed.

  Lemma transfer_jumpi_correct arg i
    (MATCH : asmatch pi ge gh sp rs m s0)
    (R_EQ : rs # arg = Vint i):
    exists s1,
      transfer_jumpi pi arg i s0 = Astate s1 /\
      asmatch pi ge gh sp rs m s1.
  Proof.
    eexists; split. reflexivity.
    apply ematch_restrict; eauto.
    rewrite R_EQ.
    constructor; apply zmatch_intconst.
  Qed.

    Definition eforget := ZIntervalDomain.eforget.
    Definition eforget_ge := ZIntervalDomain.eforget_ge.
  End TRANSFER_SOUNDNDESS.
End ZIntervalAADomain.