Module CountingSem_proof

Correctness of the instrumented semantics.

Require Import Coqlib.
Require Import Maps.
Require Import AST.
Require Import Integers.
Require Import Values.
Require Import Events.
Require Import Memory.
Require Import Globalenvs.
Require Import Smallstep.
Require Import Behaviors.
Require Import Op.
Require Import Registers.
Require Import RTL.
Require Import Errors.
Require Import String.
Require Import DLib.
Require Import Scope.
Require Import Counter.
Require Import CountingSem.

Open Scope error_monad_scope.

Definition check_one_single_func (prog:program) : res function :=
  let ge := Genv.globalenv prog in
  do b_main <- get_opt (Genv.find_symbol ge prog.(prog_main)) "main block not found";
  do main <- get_opt (Genv.find_funct_ptr ge b_main) "main func not found";
    match main with
      | External _ => Error (MSG "main is external"::nil)
      | Internal main =>
        do _ <- ptree_forall_error
          (fun pc ins =>
            match ins with
              | Itailcall _ _ _
              | Icall _ _ _ _ _ => Error (MSG "call after inlining ?"::nil)
              | _ => OK tt
            end)
          (fn_code main);
          OK main
    end.

Section ge.

Variable prog : program.
Definition ge := Genv.globalenv prog.
Variable b_main : block.
Variable b_main_eq : Genv.find_symbol ge prog.(prog_main) = Some b_main.
Variable f: function.
Variable f_eq : Genv.find_funct_ptr ge b_main = Some (Internal f).
Variable m0: mem.
Variable m0_eq: Genv.init_mem prog = Some m0.
Variable stk:block.
Variable m1:mem.
Variable m1_stk_eq: Mem.alloc m0 0 (fn_stacksize f) = (m1, stk).
Notation sp := (Vptr stk Int.zero).
Variable scp: Scope.family f.

Hypothesis no_call:
    forall pc ins,
      (fn_code f)!pc = Some ins ->
      match ins with
        | Itailcall _ _ _
        | Icall _ _ _ _ _ => False
        | _ => True
      end.

Variable annots: PMap.t (list node).

Hypothesis valid_annotation1:
    forall pc id,
      In pc (annots # id) ->
      match (fn_code f)!pc with
        | Some (Ibuiltin (EF_annot id' _) _ _ _)
        | Some (Ibuiltin (EF_annot_val id' _) _ _ _) => id = id'
        | _ => False
      end.

Hypothesis valid_annotation2:
    forall pc ins,
      (fn_code f)!pc = Some ins ->
      match ins with
        | Ibuiltin (EF_annot id _) _ _ _
        | Ibuiltin (EF_annot_val id _) _ _ _ => In pc (annots # id)
        | _ => True
      end.

Open Scope nat_scope.

Fixpoint count_annot (id0:ident) (t:trace) : nat :=
  match t with
      | nil => 0
      | (Event_annot id _)::q =>
        if peq id id0
        then S (count_annot id0 q)
        else count_annot id0 q
      | _::q => count_annot id0 q
  end.

Fixpoint sum (f:node->nat) (l:list node) : nat :=
  match l with
      | nil => 0
      | x::q => (f x) + (sum f q)
  end.

Variable id0:ident.

Lemma count_annot_app : forall t1 t2,
  count_annot id0 (t1++t2) = count_annot id0 t1 + count_annot id0 t2.
Proof.
  Opaque Zplus.
  induction t1; simpl; auto.
  destruct a; auto.
  destruct peq; auto.
  intros; rewrite IHt1; omega.
Qed.

Lemma sum_incr: forall cs pc l,
   sum (fun n : node => cs # n) l <=
   sum (fun n : node => (cs # pc <- (cs # pc + 1)) # n) l.
Proof.
  induction l; simpl; intros; auto.
  rewrite PMap.gsspec; destruct peq; simpl.
  > subst; try omega.
  > auto with arith.
Qed.

Lemma sum_incr_not_In: forall cs pc l,
   ~ In pc l ->
   sum (fun n : node => (cs # pc <- (cs # pc + 1)) # n) l =
   sum (fun n : node => cs # n) l.
Proof.
  induction l; simpl; intros; auto.
  rewrite IHl; auto.
  rewrite PMap.gso; auto.
Qed.

Lemma sum_incr_In: forall cs pc l,
   In pc l ->
   S (sum (fun n : node => cs # n) l) <=
    sum (fun n : node => (cs # pc <- (cs # pc + 1)) # n) l.
Proof.
  induction l; simpl; intuition.
  > subst; rewrite PMap.gss; auto.
    generalize (sum_incr cs pc l).
    omega.
  > rewrite PMap.gsspec; destruct peq; auto.
    > generalize (sum_incr cs pc l).
      subst; omega.
    > auto with arith.
Qed.

Inductive match_states (tr:trace) : RTL.state -> cstate -> Prop :=
| match_states_st: forall pc rs m cs rcs,
  count_annot id0 tr <= sum (fun n => cs # n) (annots # id0) ->
  match_states
   tr (RTL.State nil f sp pc rs m) (CST (State pc rs m) cs rcs)
| match_states_ret: forall pc rs m cs rcs r mr or,
  count_annot id0 tr <= sum (fun n => cs # n) (annots # id0) ->
  (fn_code f) ! pc = Some (Ireturn or) ->
  match_states
   tr (RTL.Returnstate nil r mr) (CST (State pc rs m) cs rcs)
.

Lemma forward_simulation: forall tr s1 t s2,
  RTL.step ge s1 t s2 ->
  forall s1' (MS: match_states tr s1 s1'),
    (exists s2', cstep ge f scp sp s1' s2' /\ match_states (app tr t) s2 s2')
    \/ match_states (app tr t) s2 s1'.
Proof.
  intros tr s1 t s2 Hstep s1' MS.
  inv Hstep; inversion MS; subst f0; try subst sp0; try subst;
  try (left; econstructor; split; [econstructor; eauto; econstructor (solve [eauto])|econstructor]);
  try (rewrite count_annot_app; unfold inc_counter; simpl;
       rewrite sum_incr_not_In; [rewrite plus_0_r; assumption|
                                 intro I; generalize (valid_annotation1 _ _ I);
                                 rewrite H; auto]; fail).
  > left; exploit no_call; eauto; simpl; intuition.
  > left; exploit no_call; eauto; simpl; intuition.
  > rewrite count_annot_app; unfold inc_counter; simpl.
    destruct ef; inv H0; simpl;
    try (invh volatile_load);
    try (invh volatile_store);
    try (rewrite sum_incr_not_In; [rewrite plus_0_r; assumption|
                                 intro I; generalize (valid_annotation1 _ _ I);
                                 rewrite H; auto]; fail).
    destruct peq; subst.
    > eapply le_trans; [idtac|eapply sum_incr_In].
      > omega.
      > apply (valid_annotation2 _ _ H).
    > rewrite sum_incr_not_In; [rewrite plus_0_r; assumption|
                                intro I; generalize (valid_annotation1 _ _ I);
                                rewrite H; auto].
    destruct peq; subst.
    > eapply le_trans; [idtac|eapply sum_incr_In].
      > omega.
      > apply (valid_annotation2 _ _ H).
    > rewrite sum_incr_not_In; [rewrite plus_0_r; assumption|
                                intro I; generalize (valid_annotation1 _ _ I);
                                rewrite H; auto].
  > right; econstructor.
    rewrite count_annot_app;
    rewrite plus_0_r; simpl; auto.
    eauto.
Qed.

Lemma match_initial_states: forall st0 t st1,
   initial_state prog st0 ->
   RTL.step ge st0 t st1 ->
   exists rs, exists m,
     match_states t st1 (init_st f rs m).
Proof.
  intros.
  inv H.
  assert (b=b_main) by (unfold ge, ge0 in *; congruence); subst.
  assert (f0=Internal f) by (unfold ge, ge0 in *; congruence); subst.
  inv H0.
  assert (stk0=stk) by (unfold ge, ge0 in *; congruence); subst.
  unfold init_st.
  repeat (econstructor; simpl).
  omega.
Qed.

Definition L := semantics prog.

Definition star := (star (Smallstep.step L) (globalenv L)).

Lemma reach_match_states: forall s1 tr2 s2,
  star s1 tr2 s2 ->
  forall tr1 s1' rs m,
    match_states tr1 s1 s1' ->
    Reach ge f scp sp rs m s1' ->
    exists s2',
      match_states (app tr1 tr2) s2 s2' /\
      Reach ge f scp sp rs m s2'.
Proof.
  clear no_call valid_annotation1 valid_annotation2.
  induction 1; intros.
  > exists s1'; inv H; split; auto; econstructor; eauto.
    > rewrite count_annot_app; rewrite plus_0_r; auto.
    > rewrite count_annot_app; rewrite plus_0_r; auto.
  > destruct (forward_simulation tr1 s1 t1 s2 H _ H2).
    > destruct H4 as (s1'' & S1 & S2).
      destruct (IHstar _ _ rs m S2) as (s2''' & S3).
      econstructor; eauto.
      econstructor; rewrite app_ass in S3.
      rewrite H1; eauto.
    > destruct (IHstar _ _ rs m H4) as (s2''' & S3 & S4).
      auto.
      econstructor; rewrite app_ass in S3.
      rewrite H1; eauto.
Qed.

Lemma rtl_terminates_counter: forall t r,
  program_behaves L (Behaviors.Terminates t r) ->
  exists rs, exists m, exists s,
    Reaches_final ge f scp sp rs m /\
    Reach ge f scp sp rs m s /\
    count_annot id0 t <= sum (fun n => (get_cs s) # n) (annots # id0).
Proof.
  clear no_call valid_annotation1 valid_annotation2.
  intros.
  inv H.
  inv H1.
  inv H3.
  > inv H4; inv H0.
  > exploit match_initial_states; eauto.
    intros (rs & m & M).
    exploit reach_match_states; eauto.
    constructor.
    intros (s'' & S1 & S2).
    inv H4; inv S1.
    repeat (econstructor; eauto).
Qed.

Variable bound : node -> option Z.
Variable correct_bound :
  forall rs m, Reaches_final ge f scp sp rs m ->
  forall s, Reach ge f scp sp rs m s ->
  forall n, (fn_code f)!n <> None ->
  forall b, bound n = Some b ->
    ((Z_of_nat (get_cs s) # n) <= b)%Z.

Fixpoint bound_from_list (l:list node) : option Z :=
  match l with
    | nil => Some 0%Z
    | n::q =>
      match bound_from_list q with
        | None => None
        | Some b =>
          match bound n with
              | None => None
              | Some bn => Some (bn+b)%Z
          end
      end
  end.

Lemma bound_from_list_correct: forall f l b,
  (forall n b, In n l -> bound n = Some b -> (Z_of_nat (f n) <= b)%Z) ->
  bound_from_list l = Some b ->
  (Z_of_nat (sum f l) <= b)%Z.
Proof.
  induction l; simpl; intros.
  > inv H0; omega.
  > case_eq (bound_from_list l); intros; rewrite H1 in H0; try congruence.
    case_eq (bound a); intros; rewrite H2 in H0; inv H0.
    rewrite inj_plus.
    apply Zplus_le_compat; auto.
Qed.

Lemma rtl_terminates_bound: forall t r b,
  program_behaves L (Behaviors.Terminates t r) ->
  bound_from_list (annots # id0) = Some b ->
  (Z_of_nat (count_annot id0 t) <= b)%Z.
Proof.
  intros.
  destruct (rtl_terminates_counter t r) as (rs & m & s & S1 & S2 & S3); auto.
  eapply Zle_trans.
  apply inj_le.
  apply S3.
  eapply bound_from_list_correct; eauto.
  intros; eapply correct_bound; eauto.
  generalize (valid_annotation1 _ _ H1); destruct ((fn_code f)!n); congruence.
Qed.

End ge.

Section get_annots.
Variable f: function.

Definition annots : PMap.t (list node) :=
  PTree.fold
    (fun m pc ins =>
       match ins with
        | Ibuiltin (EF_annot id _) _ _ _
        | Ibuiltin (EF_annot_val id _) _ _ _ =>
          PMap.set id (pc::PMap.get id m) m
        | _ => m
       end)
    (fn_code f) (PMap.init nil).

Lemma valid_annotation1:
    forall pc id,
      In pc (annots # id) ->
      match (fn_code f)!pc with
        | Some (Ibuiltin (EF_annot id' _) _ _ _)
        | Some (Ibuiltin (EF_annot_val id' _) _ _ _) => id = id'
        | _ => False
      end.
Proof.
  unfold annots.
  apply PTree_Properties.fold_rec
    with (P:=fun code m =>
               forall pc id, In pc (m#id) ->
                             match code!pc with
                               | Some (Ibuiltin (EF_annot id' _) _ _ _)
                               | Some (Ibuiltin (EF_annot_val id' _) _ _ _) => id = id'
                               | _ => False
                             end).
  > intros.
    rewrite <- H.
    apply H0; auto.
  > intros pc id; rewrite PMap.gi; simpl; intuition.
  > intros.
    rewrite PTree.gsspec; destruct peq; subst.
    > destruct v; try (generalize (H1 _ _ H2); rewrite H; destruct 1; fail).
      destruct e; try (generalize (H1 _ _ H2); rewrite H; destruct 1; fail).
      > rewrite PMap.gsspec in H2; destruct peq; auto.
        generalize (H1 _ _ H2); rewrite H; intuition.
      > rewrite PMap.gsspec in H2; destruct peq; auto.
        generalize (H1 _ _ H2); rewrite H; intuition.
  > destruct v; try (apply H1; auto; fail).
    destruct e; try (apply H1; auto; fail).
    > rewrite PMap.gsspec in H2; destruct peq.
      > apply H1; subst; simpl in *; intuition.
      > apply H1; auto.
    > rewrite PMap.gsspec in H2; destruct peq.
      > apply H1; subst; simpl in *; intuition.
      > apply H1; auto.
Qed.

Lemma valid_annotation2:
    forall pc ins,
      (fn_code f)!pc = Some ins ->
      match ins with
        | Ibuiltin (EF_annot id _) _ _ _
        | Ibuiltin (EF_annot_val id _) _ _ _ => In pc (annots # id)
        | _ => True
      end.
Proof.
  unfold annots.
  apply PTree_Properties.fold_rec
    with (P:=fun code m =>
               forall pc ins, code!pc = Some ins ->
                             match ins with
                               | Ibuiltin (EF_annot id' _) _ _ _
                               | Ibuiltin (EF_annot_val id' _) _ _ _ => In pc (m#id')
                               | _ => True
                             end).
  > intros.
    rewrite <- H in H1.
    apply H0; auto.
  > intros pc ins; rewrite PTree.gempty; congruence.
  > intros.
    rewrite PTree.gsspec in H2; destruct peq; subst.
    > inv H2; destruct ins; auto.
      destruct e; auto.
      rewrite PMap.gss; left; auto.
      rewrite PMap.gss; left; auto.
    > generalize (H1 _ _ H2); destruct ins; auto; clear H1.
      destruct e; auto.
      > destruct v; auto.
        destruct e; auto.
        rewrite PMap.gsspec; destruct peq; auto.
        subst; auto with datatypes.
        rewrite PMap.gsspec; destruct peq; auto.
        subst; auto with datatypes.
      > destruct v; auto.
        destruct e; auto.
        rewrite PMap.gsspec; destruct peq; auto.
        subst; auto with datatypes.
        rewrite PMap.gsspec; destruct peq; auto.
        subst; auto with datatypes.
Qed.

End get_annots.