Module memory_separation

Require Export memory memory_map_separation values_separation.
Require Import natmap.

Section memory.
Context `{EnvSpec K}.
Implicit Types Γ : env K.
Implicit Types Δ : memenv K.
Implicit Types τ : type K.
Implicit Types a : addr K.
Implicit Types w : mtree K.
Implicit Types v : val K.
Implicit Types m : mem K.
Implicit Types Ω : lockset.
Local Arguments union _ _ !_ !_ /.
Hint Immediate cmap_lookup_typed val_typed_type_valid.

Ltac solve_length := repeat first
  [ rewrite take_length | rewrite drop_length | rewrite app_length
  | rewrite zip_with_length | rewrite replicate_length | rewrite resize_length
  | rewrite fmap_length | erewrite ctree_flatten_length by eauto
  | erewrite val_flatten_length by eauto | rewrite to_bools_length
  | rewrite bit_size_of_char ]; lia.
Hint Extern 0 (length _ = _) => solve_length.
Hint Extern 0 (length __) => solve_length.
Hint Extern 0 (length __) => solve_length.

Lemma mem_lookup_subseteq Γ Δ m1 m2 a v1 :
  ✓ Γ → ✓{Γ,Δ} m1m1m2m1 !!{Γ} a = Some v1m2 !!{Γ} a = Some v1.
Proof.
  unfold lookupE, mem_lookup; intros.
  destruct (m1 !!{Γ} a) as [w1|] eqn:?; simplify_option_equality.
  destruct (cmap_lookup_subseteq Γ m1 m2 a w1) as (w2&->&?); simpl; auto.
  { eapply ctree_Forall_not; eauto using cmap_lookup_Some, pbits_mapped. }
  by erewrite option_guard_True, (to_val_subseteq _ _ w1 w2)
    by eauto using cmap_lookup_Some, pbits_mapped,
    pbits_kind_subseteq, @ctree_flatten_subseteq.
Qed.

Lemma mem_alloc_disjoint Γ Δ m1 m2 o1 μ x v τ :
  ✓ Γ → sep_valid x → ¬sep_unmapped x → (Γ,Δ) ⊢ v : τ →
  m1m2o1dom indexset m2mem_alloc Γ o1 μ x v m1m2.
Proof.
  rewrite cmap_dom_alt.
  destruct m1 as [m1], m2 as [m2]; simpl; intros ???? Hm ? o; specialize (Hm o).
  destruct (decide (o = o1)); simplify_map_equality';
    simplify_type_equality; [|by destruct (m1 !! o), (m2 !! o)].
  split; eauto 12 using ctree_typed_sep_valid, of_val_typed, Forall_replicate,
    ctree_Forall_not, Forall_impl, of_val_mapped, Forall_replicate,
    @sep_unmapped_empty_alt.
Qed.

Lemma mem_alloc_union Γ m1 m2 o1 μ x v :
  o1dom indexset m2
  mem_alloc Γ o1 μ x v (m1m2) = mem_alloc Γ o1 μ x v m1m2.
Proof.
  rewrite cmap_dom_alt; destruct m1 as [m1], m2 as [m2];
    intros; sep_unfold; f_equal'; by apply insert_union_with_l.
Qed.

Lemma mem_freeable_perm_subseteq m1 m2 o μ :
  mem_freeable_perm o μ m1m1m2mem_freeable_perm o μ m2.
Proof.
  unfold mem_freeable_perm.
  destruct m1 as [m1], m2 as [m2]; intros (w1&?&?) Hm; specialize (Hm o).
  destruct (m2 !! o) as [[|w2]|] eqn:?; simplify_option_equality;
    naive_solver eauto using @ctree_flatten_subseteq, pbits_subseteq_full.
Qed.

Lemma mem_free_disjoint m1 m2 o1 μ :
  m1m2mem_freeable_perm o1 μ m1mem_free o1 m1m2.
Proof.
  destruct m1 as [m1], m2 as [m2]; intros Hm (w1&?&?) o; specialize (Hm o).
  destruct (decide (o = o1));
    simplify_map_equality'; [|by destruct (m1 !! o), (m2 !! o)].
  destruct (m2 !! o1) as [[|w2]|];
    intuition; eauto using pbits_disjoint_full, @ctree_flatten_disjoint.
Qed.

Lemma mem_free_disjoint_le m ms o μ :
  mem_freeable_perm o μ mm :: ms ⊆⊥ mem_free o m :: ms.
Proof.
  intros. apply sep_disjoint_cons_le_inj; intros m'.
  rewrite !sep_disjoint_list_double, !(symmetry_iff _ m').
  eauto using mem_free_disjoint.
Qed.

Lemma mem_free_union m1 m2 o1 μ :
  m1m2mem_freeable_perm o1 μ m1
  mem_free o1 (m1m2) = mem_free o1 m1m2.
Proof.
  destruct m1 as [m1], m2 as [m2]; intros Hm (w1&?&?); sep_unfold; f_equal'.
  apply alter_union_with_l; [|intros [] ??; naive_solver].
  intros [] [|w2 ?] ??; do 2 f_equal';
    specialize (Hm o1); simplify_map_equality';
    naive_solver eauto using pbits_disjoint_full, @ctree_flatten_disjoint.
Qed.

Lemma mem_force_disjoint Γ Δ1 m1 m2 a1 τ1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2is_Some (m1 !!{Γ} a1) → mem_force Γ a1 m1m2.
Proof.
  unfold lookupE, mem_lookup, mem_force, lookupE, cmap_lookup; intros ??? [??].
  destruct (m1 !!{Γ} _) as [w1|] eqn:?; case_option_guard; simplify_equality'.
  destruct (cmap_lookup_ref_Some Γ Δ1 m1 (addr_index a1) (addr_ref Γ a1) w1)
    as (τ&σ&?&?&?); auto.
  eapply cmap_alter_ref_disjoint; eauto.
  case_decide; simplify_equality'; case_option_guard; simplify_equality'.
  { eapply ctree_Forall_not; eauto using pbits_mapped. }
  destruct (w1 !!{Γ} _) as [w1'|] eqn:?; simplify_option_equality.
  intros ?; eapply (ctree_Forall_not _ _ _ w1');
    eauto using ctree_lookup_byte_Forall, pbit_unmapped_indetify,
    pbits_mapped, ctree_lookup_byte_typed.
Qed.

Lemma mem_force_disjoint_le Γ Δ m ms a v τ :
  ✓ Γ → ✓{Γ,Δ} m → (Γ,Δ) ⊢ a : TType τ → is_Some (m !!{Γ} a) →
  m :: ms ⊆⊥ mem_force Γ a m :: ms.
Proof.
  intros. apply sep_disjoint_cons_le_inj; intros m'.
  rewrite !sep_disjoint_list_double, !(symmetry_iff _ m').
  eauto using mem_force_disjoint.
Qed.

Lemma mem_force_union Γ Δ1 m1 m2 a1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2is_Some (m1 !!{Γ} a1) →
  mem_force Γ a1 (m1m2) = mem_force Γ a1 m1m2.
Proof.
  unfold lookupE, mem_lookup, mem_force, lookupE, cmap_lookup; intros ??? [??].
  destruct (m1 !!{Γ} _) as [w1|] eqn:?; case_option_guard; simplify_equality'.
  destruct (cmap_lookup_ref_Some Γ Δ1 m1 (addr_index a1) (addr_ref Γ a1) w1)
    as (τ&σ&?&?&?); auto.
  eapply cmap_alter_ref_union; eauto.
  case_decide; simplify_equality'; case_option_guard; simplify_equality'.
  { eapply ctree_Forall_not; eauto using pbits_mapped. }
  destruct (w1 !!{Γ} _) as [w1'|] eqn:?; simplify_option_equality.
  intros ?; eapply (ctree_Forall_not _ _ _ w1');
    eauto using ctree_lookup_byte_Forall, pbit_unmapped_indetify,
    pbits_mapped, ctree_lookup_byte_typed.
Qed.

Lemma mem_forced_subseteq Γ Δ m1 m2 a :
  ✓ Γ → ✓{Γ,Δ} m1m1m2mem_forced Γ a m1
  is_Some (m1 !!{Γ} a) → mem_forced Γ a m2.
Proof.
  unfold mem_forced; rewrite sep_subseteq_spec'; intros ?? (m3&->&?) Hforced ?.
  by erewrite mem_force_union, Hforced by eauto.
Qed.

Lemma mem_writable_subseteq Γ Δ m1 m2 a :
  ✓ Γ → ✓{Γ,Δ} m1m1m2mem_writable Γ a m1mem_writable Γ a m2.
Proof.
  intros ??? (w1&?&?).
  destruct (cmap_lookup_subseteq Γ m1 m2 a w1) as (w2&?&?); auto.
  { eauto using ctree_Forall_not,
      cmap_lookup_Some, pbits_mapped, pbits_kind_weaken. }
  exists w2; eauto using pbits_kind_subseteq, @ctree_flatten_subseteq.
Qed.

Lemma mem_insert_disjoint Γ Δ1 m1 m2 a1 v1 τ1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2
  (Γ,Δ1) ⊢ a1 : TType τ1 → mem_writable Γ a1 m1 → (Γ,Δ1) ⊢ v1 : τ1 →
  <[a1:=v1]{Γ}>m1m2.
Proof.
  intros ???? (w1&?&?) ?. assert (ctree_unshared w1).
  { eapply pbits_unshared; eauto using pbits_kind_weaken,
      pbits_valid_sep_valid, ctree_flatten_valid. }
  assert (ctree_Forall (notsep_unmapped) w1).
  { eapply pbits_mapped; eauto using pbits_kind_weaken. }
  eapply cmap_alter_disjoint; eauto using of_val_flatten_typed,
    of_val_flatten_mapped, of_val_flatten_disjoint, ctree_Forall_not.
Qed.

Lemma mem_insert_disjoint_le Γ Δ m ms a v τ :
  ✓ Γ → ✓{Γ,Δ} m → (Γ,Δ) ⊢ a : TType τ → mem_writable Γ a m → (Γ,Δ) ⊢ v : τ →
  m :: ms ⊆⊥ <[a:=v]{Γ}>m :: ms.
Proof.
  intros. apply sep_disjoint_cons_le_inj; intros m'.
  rewrite !sep_disjoint_list_double, !(symmetry_iff _ m').
  eauto using mem_insert_disjoint.
Qed.

Lemma mem_insert_union Γ Δ1 m1 m2 a1 v1 τ1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2
  (Γ,Δ1) ⊢ a1 : TType τ1 → mem_writable Γ a1 m1 → (Γ,Δ1) ⊢ v1 : τ1 →
  <[a1:=v1]{Γ}>(m1m2) = <[a1:=v1]{Γ}>m1m2.
Proof.
  intros ???? (w1&?&?) ?. assert (ctree_unshared w1).
  { eapply pbits_unshared; eauto using pbits_kind_weaken,
      pbits_valid_sep_valid, ctree_flatten_valid. }
  assert (ctree_Forall (notsep_unmapped) w1).
  { eapply pbits_mapped; eauto using pbits_kind_weaken. }
  eapply cmap_alter_union; eauto using of_val_flatten_typed, ctree_Forall_not,
    of_val_flatten_mapped, of_val_flatten_disjoint, of_val_flatten_union.
Qed.

Lemma mem_lock_disjoint Γ Δ1 m1 m2 a1 τ1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2
  (Γ,Δ1) ⊢ a1 : TType τ1 → mem_writable Γ a1 m1mem_lock Γ a1 m1m2.
Proof.
  intros ???? (w1&?&Hw1).
  assert ((Γ,Δ1) ⊢ ctree_map pbit_lock w1 : τ1).
  { eapply ctree_map_typed; eauto using cmap_lookup_Some, pbits_lock_valid,
      pbit_lock_indetified, ctree_flatten_valid, pbit_lock_mapped. }
  eapply cmap_alter_disjoint; eauto.
  { eapply ctree_Forall_not; eauto; rewrite ctree_flatten_map.
    apply Forall_fmap; apply (Forall_impl (notsep_unmapped));
      eauto using pbits_mapped, pbits_kind_weaken, pbit_lock_mapped. }
  eauto 8 using @ctree_map_disjoint, @ctree_flatten_disjoint, Forall_true,
    pbit_lock_mapped, Forall_impl, pbit_lock_unmapped, pbits_lock_disjoint.
Qed.

Lemma mem_lock_disjoint_le Γ Δ m ms a τ :
  ✓ Γ → ✓{Γ,Δ} m → (Γ,Δ) ⊢ a : TType τ → mem_writable Γ a m
  m :: ms ⊆⊥ mem_lock Γ a m :: ms.
Proof.
  intros. apply sep_disjoint_cons_le_inj; intros m'.
  rewrite !sep_disjoint_list_double, !(symmetry_iff _ m').
  eauto using mem_lock_disjoint.
Qed.

Lemma mem_lock_union Γ Δ1 m1 m2 a1 τ1 :
  ✓ Γ → ✓{Γ,Δ1} m1m1m2
  (Γ,Δ1) ⊢ a1 : TType τ1 → mem_writable Γ a1 m1
  mem_lock Γ a1 (m1m2) = mem_lock Γ a1 m1m2.
Proof.
  intros ???? (w1&?&Hw1).
  assert ((Γ,Δ1) ⊢ ctree_map pbit_lock w1 : τ1).
  { eapply ctree_map_typed; eauto using cmap_lookup_Some, pbits_lock_valid,
      pbit_lock_indetified, ctree_flatten_valid, pbit_lock_mapped. }
  eapply cmap_alter_union; eauto.
  * eapply ctree_Forall_not; eauto; rewrite ctree_flatten_map.
    apply Forall_fmap; apply (Forall_impl (notsep_unmapped));
      eauto using pbits_mapped, pbits_kind_weaken, pbit_lock_mapped.
  * eauto 8 using @ctree_map_disjoint, @ctree_flatten_disjoint, Forall_true,
      pbit_lock_mapped, Forall_impl, pbit_lock_unmapped, pbits_lock_disjoint.
  * intros w2 Hw; apply ctree_map_union;
      eauto using pbits_lock_disjoint, @ctree_flatten_disjoint.
    apply ctree_flatten_disjoint in Hw.
    elim Hw; intros; f_equal'; auto; apply pbit_lock_union.
Qed.

Lemma mem_locks_union m1 m2 :
  m1m2mem_locks (m1m2) = mem_locks m1mem_locks m2.
Proof.
  intros Hm. apply elem_of_equiv_L; intros [o i]; rewrite elem_of_union,
    !elem_of_mem_locks; destruct m1 as [m1], m2 as [m2]; simpl.
  rewrite lookup_union_with. specialize (Hm o). assert (∀ w1 w2,
    w1w2pbit_locked <$> ctree_flatten (w1w2) !! i = Some true
      pbit_locked <$> ctree_flatten w1 !! i = Some true
      ∨ pbit_locked <$> ctree_flatten w2 !! i = Some true).
  { intros w1 w2 ?. rewrite ctree_flatten_union, <-!list_lookup_fmap by done.
    assert (ctree_flatten w1 ⊥* ctree_flatten w2)
      by auto using @ctree_flatten_disjoint.
    rewrite pbits_locked_union, lookup_zip_with by done.
    sets1 := pbit_locked <$> ctree_flatten w1).
    sets2 := pbit_locked <$> ctree_flatten w2).
    assert (Forall2_ _, True) βs1 βs2).
    { eapply Forall2_fmap, Forall2_impl; eauto. }
    destructs1 !! i) as [[]|] eqn:?, (βs2 !! i) as [[]|] eqn:?;
      decompose_Forall_hyps; intuition congruence. }
  destruct (m1 !! o) as [[]|], (m2 !! o) as [[]|]; naive_solver eauto 0.
Qed.

Lemma mem_locks_union_list ms : ⊥ msmem_locks (⋃ ms) = ⋃ (mem_locks <$> ms).
Proof.
  induction 1; csimpl; auto using mem_locks_empty.
  by rewrite mem_locks_union; f_equal.
Qed.

Lemma mem_locks_subseteq m1 m2 : m1m2mem_locks m1mem_locks m2.
Proof.
  rewrite !sep_subseteq_spec'; intros (m3&->&?).
  rewrite mem_locks_union by done. esolve_elem_of.
Qed.

Lemma ctree_unlock_disjoint w1 w2 βs :
  w1w2 → βs =.>* pbit_locked <$> ctree_flatten w1
  ctree_merge pbit_unlock_if w1 βsw2.
Proof.
  intros Hw Hβs. apply ctree_merge_disjoint; auto.
  * list.solve_length.
  * rewrite Forall2_fmap_r in Hβs.
    cut (Forall sep_valid (ctree_flatten w1));
      eauto using @ctree_valid_Forall, @ctree_disjoint_valid_l.
    induction Hβs as [|[] ?]; intros;
      decompose_Forall_hyps; auto using pbit_unlock_mapped.
  * rewrite Forall2_fmap_r in Hβs.
    induction Hβs as [|[] ?];
      constructor; csimpl; auto using pbit_unlock_unmapped.
  * eauto using pbits_unlock_disjoint, @ctree_flatten_disjoint.
Qed.

Lemma mem_unlock_disjoint m1 m2 Ω :
  m1m2 → Ω ⊆ mem_locks m1mem_unlock Ω m1m2.
Proof.
  intros Hm HΩm'.
  pose proofo, mem_locks_subseteq_inv _ _ o HΩm') as HΩm; clear HΩm'.
  destruct m1 as [m1], m2 as [m2], Ω as [Ω ?]; intros o; simpl in *.
  specialize (Hm o); specialize (HΩm o). rewrite !lookup_merge by done.
  destruct (m1 !! o) as [[|w1 β1]|], (m2 !! o) as [[|w2 β2]|],
   (Ω !! o) as [ω|]; simplify_equality'; rewrite ?ctree_flatten_merge; try done.
  { intuition eauto using ctree_unlock_disjoint, pbits_unlock_empty_inv,
      to_bools_length, @ctree_disjoint_valid_l, @ctree_valid_Forall. }
  assert (Forall sep_valid (ctree_flatten w1))
    by intuition eauto using @ctree_disjoint_valid_l, @ctree_valid_Forall.
  intuition eauto using ctree_unlock_disjoint,
    pbits_unlock_empty_inv, to_bools_length.
  apply ctree_merge_valid; auto using to_bools_length, pbits_unlock_sep_valid.
  apply Forall2_lookup_2; auto using to_bools_length.
  intros ?? [] ??; decompose_Forall_hyps; eauto using pbit_unlock_mapped.
Qed.

Lemma mem_unlock_disjoint_le m ms Ω :
  Ω ⊆ mem_locks mm :: ms ⊆⊥ mem_unlock Ω m :: ms.
Proof.
  intros. apply sep_disjoint_cons_le_inj; intros m'.
  rewrite !sep_disjoint_list_double, !(symmetry_iff _ m').
  eauto using mem_unlock_disjoint.
Qed.

Lemma ctree_unlock_union w1 w2 βs :
  w1w2 → βs =.>* pbit_locked <$> ctree_flatten w1
  ctree_merge pbit_unlock_if (w1w2) βs
  = ctree_merge pbit_unlock_if w1 βsw2.
Proof.
  intros. apply ctree_merge_union; eauto using pbits_unlock_disjoint,
    pbits_unlock_union, @ctree_flatten_disjoint; list.solve_length.
Qed.

Lemma mem_unlock_union m1 m2 Ω :
  m1m2 → Ω ⊆ mem_locks m1
  mem_unlock Ω (m1m2) = mem_unlock Ω m1m2.
Proof.
  intros Hm HΩm'.
  pose proofo, mem_locks_subseteq_inv _ _ o HΩm') as HΩm; clear HΩm'.
  destruct m1 as [m1], m2 as [m2], Ω as [Ω ?]; sep_unfold; f_equal'.
  apply map_eq; intros o; simpl in *; specialize (Hm o); specialize (HΩm o).
  rewrite lookup_merge, !lookup_union_with, lookup_merge by done.
  destruct (m1 !! o) as [[|w1 β1]|], (m2 !! o) as [[|w2 β2]|],
    (Ω !! o) as [ω|]; do 2 f_equal'; try done.
  rewrite ctree_flatten_union, zip_with_length_l_eq
    by naive_solver eauto using Forall2_length, @ctree_flatten_disjoint.
  intuition auto using ctree_unlock_union.
Qed.

Lemma mem_unlock_all_disjoint_le m ms : m :: ms ⊆⊥ mem_unlock_all m :: ms.
Proof. by apply mem_unlock_disjoint_le. Qed.
Lemma mem_unlock_all_disjoint_list_le ms : ms ⊆⊥ mem_unlock_all <$> ms.
Proof.
  induction ms as [|m ms IH]; csimpl; [done|].
  by rewrite <-mem_unlock_all_disjoint_le, <-IH.
Qed.

Lemma mem_unlock_all_union m1 m2 :
  m1m2mem_unlock_all (m1m2) = mem_unlock_all m1mem_unlock_all m2.
Proof.
  unfold mem_unlock_all; intros. assert (m1mem_unlock_all m2).
  { symmetry. by apply mem_unlock_disjoint. }
  by rewrite mem_locks_union, mem_unlock_union_locks, sep_commutative',
    mem_unlock_union, sep_commutative', mem_unlock_union
    by auto using mem_unlock_disjoint.
Qed.

Lemma mem_unlock_all_union_list ms :
  ⊥ msmem_unlock_all (⋃ ms) = ⋃ (mem_unlock_all <$> ms).
Proof.
  by induction 1; simpl;
    rewrite ?mem_unlock_all_union, ?mem_unlock_all_empty by done; f_equal.
Qed.

End memory.