Module MoreRTL

Require Import RTL.
Require Import MoreSemantics.
Require Import AssocList.
Import Utf8.
Import Coqlib Maps.
Import Globalenvs.
Import Memory.
Import Integers.
Require Import Extra.
Import Annotations.

Lemma semantics_determinate:
  forall (p: program), Smallstep.determinate (semantics p).
Proof.
  intros p. constructor; simpl.
- intros s t1 s1 t2 s2 H H0.
  inversion H; clear H; inversion H0; clear H0; try subst; try discriminate;
  repeat match goal with
  | K : State _ _ _ _ _ _ = State _ _ _ _ _ _ |- _ => inversion K; clear K; try subst
  | K : Callstate _ _ _ _ = Callstate _ _ _ _ |- _ => inversion K; clear K; try subst
  | K : Returnstate _ _ _ = Returnstate _ _ _ |- _ => inversion K; clear K; try subst
  | E : ?a = ?b, E' : ?a' = ?b' |- _ => let K := fresh in assert (b = b') as K by congruence; clear E';
   try (inversion K; clear K); try subst
  | H : Events.eval_builtin_args _ _ _ _ _ _, H' : Events.eval_builtin_args _ _ _ _ _ _ |- _ =>
    pose proof (Events.eval_builtin_args_determ H H'); clear H H'; try subst
  | EF : Events.external_call _ _ _ _ _ _ _, EF' : Events.external_call _ _ _ _ _ _ _ |- _ =>
    destruct (Events.external_call_determ _ _ _ _ _ _ _ _ _ _ EF EF'); intuition congruence
  end; vauto.
- intros s t s' H. inversion H; clear H; subst; simpl; try omega; eapply Events.external_call_trace_length; eauto.
- intros s1 s2 H H0. inversion H; clear H; inversion H0; clear H0; subst.
  subst ge0 ge. congruence.
- intros s r H t s' K. inversion H; clear H; subst; inversion K.
- intros s r1 r2 H H0; inversion H; clear H; inversion H0; congruence.
Qed.

Heuristic detection of loops.

Section BACK_EDGES.
  
  Definition node_set : Type := PTree.t unit.
  Definition empty : node_set := PTree.empty _.
  Definition mem (n: node) (s: node_set) : { s ! n = Some tt } + { s ! n = None } :=
    match s ! n as b return { b = Some tt } + { b = None } with
    | Some x => let 'tt as y := x return { Some y = Some tt } + { _ = None } in left eq_refl
    | None => right eq_refl
    end.

  Definition add (n: node) (s: node_set) : node_set :=
    if mem n s then s else PTree.set n tt s.

  Context (m: code).


  Definition back_edges : node_set :=
    PTree.folds pc i,
                let succ := successors_instr i in
                List.fold_lefts pc',
                                if (pc <=? pc')%positive
                                then add pc' s
                                else s
                               ) succ s
               ) m empty.

End BACK_EDGES.

Section COLLECT_ANNOT.

Variable p: RTL.program.

An annotation is trivial if it can be inferred just by looking at the instruction it belongs to: the annotation is empty (top) or the pointer is a known constant or the range is full 0; sizeof

Definition sizeof (g: AST.ident) : Z :=
  match assoc g (AST.prog_defs p) with
  | Some (AST.Gvar gv) => Genv.init_data_list_size (AST.gvar_init gv)
  | _ => 1
  end.

Definition is_trivial_annotation (α: annotation) (κ: AST.memory_chunk) (addr: Op.addressing) : bool :=
  match snd α with
  | nil => true
  | bs :: nil =>
    match bs with
    | ABglobal g r =>
      match addr with
      | Op.Abased g' _
      | Op.Abasedscaled _ g' _ =>
        if AST.ident_eq g g'
        then range_leb (Int.zero, Int.repr (Z.max 0 (Z.min (sizeof g - size_chunk κ) Int.max_unsigned))) r
        else false
      | Op.Aglobal g' o =>
        if AST.ident_eq g g'
        then range_leb (o, o) r
        else false
      | _ => false
      end
    | ABlocal O _ r =>
      match addr with
      | Op.Ainstack o => range_leb (o, o) r
      | _ => false
      end
    | ABlocal (S _) _ _ => false
    end
  | _ => false
  end.

Collects the set of non-trivial annotation names that appear in a RTL program.

Definition collect_annot_in_fun (f: function) : node_set -> node_set :=
  PTree.fold
    (λ acc _ i,
     match i with
     | Inop _
     | Iop _ _ _ _
     | Icall _ _ _ _ _
     | Itailcall _ _ _
     | Ibuiltin _ _ _ _
     | Icond _ _ _ _
     | Ijumptable _ _
     | Ireturn _
       => acc
     | Iload ((n, _) as α) κ addr _ _ _
     | Istore ((n, _) as α) κ addr _ _ _
       =>
       if is_trivial_annotation α κ addr
       then acc
       else add n acc
     end)
    (fn_code f).

Definition collect_annot : node_set :=
  List.fold_left
    (λ acc def,
     match def with
     | (_, AST.Gfun (AST.Internal f)) => collect_annot_in_fun f acc
     | _ => acc
     end)
    (AST.prog_defs p) empty.

End COLLECT_ANNOT.

RTL programs only call functions that are declared.

Section CALL_DECLARED.

Context (p: program).

Definition declared_fundef (fd: fundef) : Prop :=
  ∃ name, In (name, AST.Gfun fd) (AST.prog_defs p).
Arguments declared_fundef _ /.

Definition declared_in_frame (sf: stackframe) : Prop :=
  let 'Stackframe _ fn _ _ _ := sf in
  declared_fundef (AST.Internal fn).

Definition declared_in_stack (stk: list stackframe) : Prop :=
  Forall declared_in_frame stk.

Definition call_declared (s: state) : Prop :=
  match s with
  | Returnstate stk _ _ => declared_in_stack stk
  | State stk fn _ _ _ _ => declared_in_stack stkdeclared_fundef (AST.Internal fn)
  | Callstate stk fd _ _ => declared_in_stack stkdeclared_fundef fd
  end.

Theorem call_declared_invariant : invariant (semantics p) call_declared.
Proof.
  apply invariant_ind; simpl.
  - intros s INIT; inv INIT.
    split. vauto.
    simpl. eauto using Genv.find_funct_ptr_inversion.
  - intros s REACH INV tr s' STEP; inv STEP; try exact INV;
    try destruct INV as [ INV1 INV2 ];
    repeat constructor; simpl; auto;
    repeat match goal with
    | x : sum Registers.reg AST.ident |- _ => destruct x; simpl in *
    | H : appcontext[ Genv.find_symbol ?ge ?s ] |- _ => destruct (Genv.find_symbol ge s); eq_some_inv
    end;
    eauto using Genv.find_funct_inversion, Genv.find_funct_ptr_inversion.
    now inv INV.
    apply (Forall_inv INV).
Qed.

End CALL_DECLARED.

Section PERMISSION_IN_BOUNDS.

Non-empty permissions (and above) in blocks allocated to global variables are in-bounds, where said bounds can be computed from the variable declaration (and initialisation data).

Context (p: program) (m₀: Mem.mem).
Let ge := Genv.globalenv p.

Hypothesis names_norepet : list_norepet (AST.prog_defs_names p).
Hypothesis initial_memory : Genv.init_mem p = Some m₀.

Lemma mem_perm_nonempty {m b o k pe} :
  Mem.perm m b o k pe
  Mem.perm m b o k Nonempty.
Proof.
  intros H. eapply Mem.perm_implies. eassumption. vauto.
Qed.

Lemma permission_in_bounds :
  ∀ g b,
    Genv.find_symbol ge g = Some b
    ∀ ofs,
      Mem.perm mb ofs Max Nonempty
      0 <= ofsofs < sizeof p g.
Proof.
  intros g b Hgb ofs PERM.
  pose proof (Genv.find_symbol_inversion p _ Hgb) as Hgb'.
  unfold AST.prog_defs_names in Hgb'. rewrite in_map_iff in Hgb'.
  destruct Hgb' as ((g' & gd') & ? & Hgb'). simpl in *. subst g'.
  generalize (assoc_in g (AST.prog_defs p)).
  generalize (proj1 (assoc_None g (AST.prog_defs p))).
  unfold sizeof.
  case assoc. 2: intros K; elim (K eq_refl _ Hgb').
  clear Hgb'.
  intros gd _ Hgd. specialize (Hgd _ eq_refl).
  destruct gd as [ fd | gv ].
  - destruct (Genv.find_funct_ptr_exists p _ _ names_norepet Hgd) as (b' & Hb' & FFP).
    assert (b' = b). fold ge in Hb'. congruence. clear Hb'. subst b'.
    generalize (Genv.init_mem_characterization_2 p b FFP initial_memory).
    intros (_ & H).
    specialize (H _ _ _ PERM). destruct H as [ -> _ ]. Psatz.lia.
  - destruct (Genv.find_var_exists p _ _ names_norepet Hgd) as (b' & Hb' & FVI).
    assert (b' = b). fold ge in Hb'. congruence. clear Hb'. subst b'.
    generalize (Genv.init_mem_characterization p b FVI initial_memory).
    intros (_ & H & _).
    specialize (H _ _ _ PERM). destruct H as (H & _).
    exact H.
Qed.

Definition mem_has_initial_global_max_permission (m: Mem.mem) : Prop :=
  ∀ g b,
    Genv.find_symbol ge g = Some b
    ∀ ofs,
      Mem.perm m b ofs MaxMem.perm mb ofs Max.

Definition initially_valid_blocks_are_still_valid (m: Mem.mem) : Prop :=
  Mem.valid_block m₀ ⊆ Mem.valid_block m.

Definition mem_of_state (s: state) : Mem.mem :=
  match s with
  | State _ _ _ _ _ m
  | Callstate _ _ _ m
  | Returnstate _ _ m
    => m
  end.

Arguments mem_of_state s /.

Definition state_has_initial_global_max_permission (s: state) : Prop :=
  mem_of_state s ∈ (mem_has_initial_global_max_permissioninitially_valid_blocks_are_still_valid).

Theorem permission_in_bounds_invariant : invariant (semantics p) state_has_initial_global_max_permission.
Proof.
  apply invariant_ind.
  - intros s (). clear s.
    intros b f m' ge' H H0 H1 H2. split; red; simpl; congruence.
  - intros s REACH Hs tr s' STEP.
    inv STEP; try exact Hs;
    destruct Hs as [Hs Hs'].
    + (* store *)
      destruct a; simpl in *; eq_some_inv.
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        eapply Mem.perm_store_2; eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Mem.store_valid_block_1; eauto.
    + (* tail-call *)
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        eapply Mem.perm_free_3; eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Mem.valid_block_free_1; eassumption.
    + (* builtin *)
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        refine (Events.external_call_max_perm _ _ _ _ _ _ ofs _ _ (Hs' _ (Genv.find_symbol_not_fresh p _ initial_memory Hg)) Hpe).
        eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Events.external_call_valid_block; eassumption.
    + (* return — same as tail-call *)
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        eapply Mem.perm_free_3; eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Mem.valid_block_free_1; eassumption.
    + (* internal call *)
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        eapply Mem.perm_alloc_4. eassumption. assumption.
        intros <-.
        pose proof (Hs' _ (Genv.find_symbol_not_fresh p _ initial_memory Hg)) as Hbvalid.
        refine (Mem.fresh_block_alloc _ _ _ _ _ _ Hbvalid). eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Mem.valid_block_alloc; eassumption.
    + (* external call — same as built-in *)
      split.
      * intros g ? Hg ofs pe Hpe; apply (Hs g _ Hg ofs pe); clear Hs.
        refine (Events.external_call_max_perm _ _ _ _ _ _ ofs _ _ (Hs' _ (Genv.find_symbol_not_fresh p _ initial_memory Hg)) Hpe).
        eassumption.
      * intros ? Hb; specialize (Hs' _ Hb).
        eapply Events.external_call_valid_block; eassumption.
Qed.

Definition valid_access_in_bounds (m: Mem.mem) : Prop :=
  ∀ g b,
  Genv.find_symbol ge g = Some b
  ∀ κ ofs pe,
  Mem.valid_access m κ b ofs pe
  0 <= ofsofs <= sizeof p g - size_chunk κ.

Local Opaque mem_of_state.

Corollary valid_access_in_bounds_invariant : invariant (semantics p) (valid_access_in_boundsmem_of_state).
Proof.
  intros s REACH.
  destruct (permission_in_bounds_invariant _ REACH) as [ H _ ]. clear REACH.
  intros g b Hgb κ ofs pe [ PERM AL ].
  specializei Hi, H _ _ Hgb i pe (Mem.perm_cur_max _ _ _ _ (PERM i Hi))).
  clear PERM.
  generalizei Hi, permission_in_bounds _ _ Hgb i (mem_perm_nonempty (H i Hi))). clear H.
  intros H.
  pose proof (size_chunk_pos κ).
  split.
  apply H. Psatz.lia.
  specialize (H (ofs + size_chunk κ - 1)).
  Psatz.lia.
Qed.

End PERMISSION_IN_BOUNDS.

Section STACKS.

  Import Values.

  Definition stack : Type := list val.

  Definition stack_of_frames (stk: list stackframe) : stack :=
    List.mapsf, let 'Stackframe _ _ sp _ _ := sf in sp) stk.

  Definition stack_of_state (s: state) : stack :=
    match s with
    | State stk _ sp _ _ _ => sp :: stack_of_frames stk
    | Callstate stk _ _ _
    | Returnstate stk _ _ => stack_of_frames stk
    end.

  Definition pointer_to_block (v: val) (b: block) : Prop :=
    v = Vptr b Int.zero.

  Arguments list_norepet {_} _.

  Definition wf_stack (m: Mem.mem) (stk: stack) : Prop :=
    ∃ ptrs,
      ptrs ∈ (Forall2 pointer_to_block stklist_norepetForall (Mem.valid_block m)).

  Lemma wf_stack_pop m v stk :
    wf_stack m (v :: stk) → wf_stack m stk.
Proof.
    intros (ptrs & (P2B & NR) & Hvalid).
    inv P2B. inv Hvalid. apply list_norepet_cons in NR. hsplit. vauto.
  Qed.

  Lemma wf_stack_m {m m'} :
    Mem.valid_block mMem.valid_block m' →
    wf_stack mwf_stack m'.
Proof.
    intros Hm stk (ptrs & P2Bs & Hvalid).
    exists ptrs. split. exact P2Bs.
    eauto using Forall_impl.
  Qed.

  Theorem wf_stack_invariant (p: program) :
    invariant (RTL.semantics p) (λ s, wf_stack (mem_of_state s) (stack_of_state s)).
Proof.
    apply invariant_ind.
    - intros s (). intros b f m0 ge H H0 H1 H2. exists nil. vauto2.
    - intros s REACH WF tr s' STEP.
      inv STEP; try exact WF.
      + (* Store *)
        destruct a; simpl in *; eq_some_inv.
        refine (wf_stack_m _ _ WF).
        eapply Mem.store_valid_block_1; eassumption.
      + (* Tailcall *)
        eapply wf_stack_pop.
        eapply wf_stack_m. 2: eassumption.
        eapply Mem.valid_block_free_1; eassumption.
      + (* Builtin *)
        refine (wf_stack_m _ _ WF).
        intros b. eapply Events.external_call_valid_block; eassumption.
      + (* Return *)
        eapply wf_stack_m. 2: exact (wf_stack_pop _ _ _ WF).
        eapply Mem.valid_block_free_1; eassumption.
      + (* Function entry *)
        destruct WF as (ptrs & (P2B & NR) & Hvalid).
        eexists (_ :: _). split. split.
        * vauto.
        * destruct ptrs as [ | x ptrs ]. vauto.
          constructor. 2: exact NR.
          apply Mem.alloc_result in H. subst.
          rewrite Forall_forall in Hvalid.
          intros IN; specialize (Hvalid _ IN).
          exact (Plt_strict _ Hvalid).
        * constructor.
          eauto using Mem.valid_new_block.
          eapply Forall_impl. 2: exact Hvalid.
          eapply Mem.valid_block_alloc; eauto.
      + (* External call — same as builtin *)
        refine (wf_stack_m _ _ WF).
        intros b. eapply Events.external_call_valid_block; eassumption.
  Qed.
  
End STACKS.