Ltac frame_SEP' L :=  (* this should be generalized to permit framing on LOCAL part too *)
grab_indexes_SEP L;
match goal with
| |- @semax _ _ (PROPx _ (LOCALx _ (SEPx ?R))) _ _ =>
rewrite <- (firstn_skipn (length L) R);
simpl length; unfold firstn, skipn;
eapply (semax_frame_PQR nil);
[ unfold closed_wrt_modvars;  auto 50 with closed
| ]
| |- ENTAIL _ , (PROPx _ (LOCALx _ (SEPx ?R))) |-- _ =>
rewrite <- (firstn_skipn (length L) R);
simpl length; unfold firstn, skipn;
apply derives_frame_PQR
end.
Tactic Notation "frame_SEP" constr(a) :=
frame_SEP' (a::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) :=
frame_SEP' (a::b::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) :=
frame_SEP' (a::b::c::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) :=
frame_SEP' (a::b::c::d::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) :=
frame_SEP' (a::b::c::d::e::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) constr(f) :=
frame_SEP' (a::b::c::d::e::f::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) constr(f) constr(g) :=
frame_SEP' (a::b::c::d::e::f::g::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) constr(f) constr(g) constr(h) :=
frame_SEP' (a::b::c::d::e::f::g::h::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) constr(f) constr(g) constr(h) constr(i) :=
frame_SEP' (a::b::c::d::e::f::g::h::i::nil).
Tactic Notation "frame_SEP" constr(a) constr(b) constr(c) constr(d) constr(e) constr(f) constr(g) constr(h) constr(i) constr(j) :=
frame_SEP' (a::b::c::d::e::f::g::h::i::j::nil).

Lemma gather_SEP:
forall R1 R2,
SEPx (R1 ++ R2) = SEPx (fold_right sepcon emp R1 :: R2).