diff --git a/algebra/upred_tactics.v b/algebra/upred_tactics.v
index 4e82798c749fe424ff4fafe55cc7e629f8821a0f..d266a33b579ae1969e93bc47d048ae249ea7251b 100644
--- a/algebra/upred_tactics.v
+++ b/algebra/upred_tactics.v
@@ -146,6 +146,74 @@ Tactic Notation "ecancel" open_constr(Ps) :=
        close Ps (@nil (uPred M)) ltac:(fun Qs => cancel Qs)
     end. 
 
+(** [to_front [P1, P2, ..]] rewrites in the premise of ⊑ such that
+    the assumptions P1, P2, ... appear at the front, in that order. *)
+Tactic Notation "to_front" open_constr(Ps) :=
+  let rec tofront Ps :=
+    lazymatch eval hnf in Ps with
+    | [] => idtac
+    | ?P :: ?Ps =>
+      rewrite ?(assoc (★)%I);
+      match goal with
+      | |- (?Q ★ _)%I ⊑ _ => (* test if it is already at front. *)
+        unify P Q with typeclass_instances
+      | |- _ => find_pat P ltac:(fun P => rewrite {1}[(_ ★ P)%I]comm)
+      end;
+      tofront Ps
+    end
+  in
+  rewrite [X in _ ⊑ X]lock;
+  tofront (rev Ps);
+  rewrite -[X in _ ⊑ X]lock.
+
+(** [sep_split] is used to introduce a (★).
+    Use [sep_split left: [P1, P2, ...]] to define which assertions will be
+    taken to the left; the rest will be available on the right.
+    [sep_split right: [P1, P2, ...]] works the other way around. *)
+(* TODO: These tactics fail if the list contains *all* assumptions.
+   However, in these cases using the "other" variant with the emtpy list
+   is much more convenient anyway. *)
+(* TODO: These tactics are pretty slow, can we use the reflection stuff
+   above to speed them up? *)
+Tactic Notation "sep_split" "right:" open_constr(Ps) :=
+  match goal with
+  | |- ?P ⊑ _ =>
+    let iProp := type of P in (* ascribe a type to the list, to prevent evars from appearing *)
+    lazymatch eval hnf in (Ps : list iProp) with
+    | [] => apply sep_intro_True_r
+    | ?P :: ?Ps =>
+      to_front (P::Ps);
+      (* Run assoc length (ps) times *)
+      let rec nassoc Ps :=
+          lazymatch eval hnf in Ps with
+          | [] => idtac
+          | _ :: ?Ps => rewrite assoc; nassoc Ps
+          end in
+      rewrite [X in _ ⊑ X]lock -?(assoc (★)%I);
+      nassoc Ps; rewrite [X in X ⊑ _]comm -[X in _ ⊑ X]lock;
+      apply sep_mono
+   end
+  end.
+Tactic Notation "sep_split" "left:" open_constr(Ps) :=
+  match goal with
+  | |- ?P ⊑ _ =>
+    let iProp := type of P in (* ascribe a type to the list, to prevent evars from appearing *)
+    lazymatch eval hnf in (Ps : list iProp) with
+    | [] => apply sep_intro_True_l
+    | ?P :: ?Ps =>
+      to_front (P::Ps);
+      (* Run assoc length (ps) times *)
+      let rec nassoc Ps :=
+          lazymatch eval hnf in Ps with
+          | [] => idtac
+          | _ :: ?Ps => rewrite assoc; nassoc Ps
+          end in
+      rewrite [X in _ ⊑ X]lock -?(assoc (★)%I);
+      nassoc Ps; rewrite -[X in _ ⊑ X]lock;
+      apply sep_mono
+   end
+  end.
+
 (** Assumes a goal of the shape P ⊑ ▷ Q. Alterantively, if Q
     is built of ★, ∧, ∨ with ▷ in all branches; that will work, too.
     Will turn this goal into P ⊑ Q and strip ▷ in P below ★, ∧, ∨. *)
diff --git a/program_logic/hoare_lifting.v b/program_logic/hoare_lifting.v
index ada18d5bb76a3d5e036b11063fd38423cd1356d1..6e580bf9ee88c25b57ee95b835076a7cfdedb4d8 100644
--- a/program_logic/hoare_lifting.v
+++ b/program_logic/hoare_lifting.v
@@ -1,3 +1,4 @@
+From algebra Require Import upred_tactics.
 From program_logic Require Export hoare lifting.
 From program_logic Require Import ownership.
 Import uPred.
@@ -31,21 +32,20 @@ Proof.
   intros ?? Hsafe Hstep; apply: always_intro. apply impl_intro_l.
   rewrite (assoc _ P) {1}/vs always_elim impl_elim_r pvs_always_r.
   rewrite -(wp_lift_step E1 E2 φ _ e1 σ1) //; apply pvs_mono.
-  rewrite always_and_sep_r -assoc; apply sep_mono; first done.
+  rewrite always_and_sep_r -assoc; apply sep_mono_r.
   rewrite (later_intro (∀ _, _)) -later_sep; apply later_mono.
   apply forall_intro=>e2; apply forall_intro=>σ2; apply forall_intro=>ef.
   rewrite (forall_elim e2) (forall_elim σ2) (forall_elim ef).
   apply wand_intro_l; rewrite !always_and_sep_l.
+  (* Apply the view shift. *)
   rewrite (assoc _ _ P') -(assoc _ _ _ P') assoc.
   rewrite {1}/vs -always_wand_impl always_elim wand_elim_r.
   rewrite pvs_frame_r; apply pvs_mono.
-  rewrite (comm _ (Φ1 _ _ _)) -assoc (assoc _ (Φ1 _ _ _)).
-  rewrite {1}/ht -always_wand_impl always_elim wand_elim_r.
-  rewrite assoc (comm _ _ (wp _ _ _)) -assoc.
-  apply sep_mono; first done.
-  destruct ef as [e'|]; simpl; [|by apply const_intro].
-  rewrite {1}/ht -always_wand_impl always_elim wand_elim_r; apply wp_mono=>v.
-  by apply const_intro.
+  (* Now we're almost done. *)
+  sep_split left: [Φ1 _ _ _; {{ Φ1 _ _ _ }} e2 @ E2 {{ Ψ }}]%I.
+  - rewrite {1}/ht -always_wand_impl always_elim wand_elim_r //.
+  - destruct ef as [e'|]; simpl; [|by apply const_intro].
+    rewrite {1}/ht -always_wand_impl always_elim wand_elim_r //.
 Qed.
 
 Lemma ht_lift_atomic_step
@@ -90,15 +90,10 @@ Proof.
   apply forall_intro=>e2; apply forall_intro=>ef; apply impl_intro_l.
   rewrite (forall_elim e2) (forall_elim ef).
   rewrite always_and_sep_l !always_and_sep_r {1}(always_sep_dup (â–  _)).
-  rewrite {1}(assoc _ (_ ★ _)%I) -(assoc _ (■ _)%I).
-  rewrite (assoc _ (â–  _)%I P) -{1}(comm _ P) -(assoc _ P).
-  rewrite (assoc _ (■ _)%I) assoc -(assoc _ (■ _ ★ P))%I.
-  rewrite (comm _ (■ _ ★ P'))%I assoc.
-  rewrite {1}/ht -always_wand_impl always_elim wand_elim_r.
-  rewrite -assoc; apply sep_mono; first done.
-  destruct ef as [e'|]; simpl; [|by apply const_intro].
-  rewrite {1}/ht -always_wand_impl always_elim wand_elim_r; apply wp_mono=>v.
-  by apply const_intro.
+  sep_split left: [■ φ _ _; P; {{ ■ φ _ _ ★ P }} e2 @ E {{ Ψ }}]%I.
+  - rewrite {1}/ht -always_wand_impl always_elim wand_elim_r //.
+  - destruct ef as [e'|]; simpl; [|by apply const_intro].
+    rewrite assoc {1}/ht -always_wand_impl always_elim wand_elim_r //.
 Qed.
 
 Lemma ht_lift_pure_det_step
diff --git a/program_logic/sts.v b/program_logic/sts.v
index 101ff16bf7d7cc8a09f9d8485bf9a7e474abf217..ce9c13d0f25cbf4ddc5197ea436d4ed5e110138c 100644
--- a/program_logic/sts.v
+++ b/program_logic/sts.v
@@ -99,12 +99,12 @@ Section sts.
   Proof.
     rewrite /sts_inv later_exist sep_exist_r. apply exist_elim=>s.
     rewrite later_sep pvs_timeless !pvs_frame_r. apply pvs_mono.
-    rewrite -(exist_intro s).
-    rewrite [(_ ★ ▷φ _)%I]comm -!assoc -own_op -[(▷φ _ ★ _)%I]comm.
-    rewrite own_valid_l discrete_valid.
-    rewrite -!assoc. apply const_elim_sep_l=> Hvalid.
+    rewrite -(exist_intro s). ecancel [▷ φ _]%I.
+    to_front [own _ _; sts_ownS _ _ _].
+    rewrite -own_op own_valid_l discrete_valid.
+    apply const_elim_sep_l=> Hvalid.
     assert (s ∈ S) by eauto using sts_auth_frag_valid_inv.
-    rewrite const_equiv // left_id comm sts_op_auth_frag //.
+    rewrite const_equiv // left_id sts_op_auth_frag //.
     by assert (✓ sts_frag S T) as [??] by eauto using cmra_valid_op_r.
   Qed.