This is an archive of the discontinued LLVM Phabricator instance.

[ADT] Teach reverse() about filter_iterator ranges
AbandonedPublic

Authored by vsk on Apr 18 2018, 7:07 PM.

Details

Summary

This makes it possible to reverse a filtered range. For example, here's
a way to visit memory accesses in a BasicBlock in reverse order:

auto MemInsts = reverse(make_filter_range(BB, [](Instruction &I) {
  return isa<StoreInst>(&I) || isa<LoadInst>(&I);
}));

for (auto &MI : MemInsts)
  ...

To implement this functionality, I added an overload of reverse() which
is enabled just for filter_iterator ranges. The overload invalidates the
original range and creates a new filter_iterator range using reverse
iterators.

I'd appreciate any comments about how to make this cleaner. The only
alternative approach I considered was to get filter_iterator to adopt
bidirectional_iterator_tag and define an operator-- conditionally, but I
couldn't get this to work out.

This was motivated by a problem we encountered in D45657: we'd like to
visit the non-debug-info instructions in a BasicBlock in reverse order.

Testing: check-llvm, check-clang

Diff Detail

Event Timeline

vsk created this revision.Apr 18 2018, 7:07 PM
vsk edited the summary of this revision. (Show Details)Apr 18 2018, 7:12 PM
vsk updated this revision to Diff 143032.Apr 18 2018, 7:31 PM
  • Avoid some code movement.
vsk updated this revision to Diff 143034.Apr 18 2018, 8:00 PM
  • Add some more tests.
zturner added inline comments.Apr 19 2018, 7:25 AM
include/llvm/ADT/STLExtras.h
235

Can we use std::rbegin() and std::rend() instead of C.rbegin() and C.rend()? This way it works for arrays too.

236–247

Can we put these two functions in the detail namespace?

237

I'd rather overload make_reverse_iterator() instead of reverse(), then have one implementation of reverse()

238–239

Do we need the enable_if? Won't the non-existence of C.rbegin() trigger SFINAE and fall back to the second overload?

aprantl added inline comments.Apr 19 2018, 8:42 AM
include/llvm/ADT/STLExtras.h
290

///

323

///
there's more below...

vsk updated this revision to Diff 143159.Apr 19 2018, 1:52 PM
vsk marked 3 inline comments as done.
  • Simplify the is_filter_iterator predicate.
  • Fix up some doxygen comments.
  • Remove an unnecessary enable_if.
include/llvm/ADT/STLExtras.h
235

I think this overload already works with arrays. Do you have an example of a case that's not supported?

236–247

I can do that in a follow-up. Aside from make_reverse_iterator, which other function did you have in mind?

237

This doesn't seem to be possible. A filter_iterator pointing to the end of the sequence can't meaningfully be reversed, because the Payload would be missing.

238–239

Nope, nice catch, we don't need this enable_if. I'll get rid of it.

My understanding is that, the reason why reverse_iterator<filter_iterator<I, F>> doesn't "just work" is because filter_iterator<I> is only a forward_iterator, while reverse_iterator expects a bidirectional_iterator. Can we make filter_iterator bidirectional instead?

My understanding is that, the reason why reverse_iterator<filter_iterator<I, F>> doesn't "just work" is because filter_iterator<I> is only a forward_iterator, while reverse_iterator expects a bidirectional_iterator. Can we make filter_iterator bidirectional instead?

Yea it seems like filter_iterator should be bidirectional if and only if the underlying range is bidirectional.

dblaikie added a subscriber: vsk.Apr 19 2018, 2:18 PM

+1 - I'd rather not have a special case for filter+reverse if we can help
it. If filter can be made bidi so it "just works" without reverse knowing
anything about it, that seems better.

vsk added a comment.Apr 19 2018, 6:05 PM

My understanding is that, the reason why reverse_iterator<filter_iterator<I, F>> doesn't "just work" is because filter_iterator<I> is only a forward_iterator, while reverse_iterator expects a bidirectional_iterator. Can we make filter_iterator bidirectional instead?

I think it's possible. I've sketched a version of this which passes tests: https://reviews.llvm.org/D45853. I haven't got all of the enable_ifs finalized, but it should be simpler than this patch.

The downside is that it introduces copies of the wrapped begin/end iterators, as well as the predicate functor. That's why I'm leaning towards adding an overload of reverse(), but I'm open to either approach.

vsk abandoned this revision.Apr 20 2018, 3:10 PM
In D45792#1072947, @vsk wrote:

My understanding is that, the reason why reverse_iterator<filter_iterator<I, F>> doesn't "just work" is because filter_iterator<I> is only a forward_iterator, while reverse_iterator expects a bidirectional_iterator. Can we make filter_iterator bidirectional instead?

I think it's possible. I've sketched a version of this which passes tests: https://reviews.llvm.org/D45853. I haven't got all of the enable_ifs finalized, but it should be simpler than this patch.

The downside is that it introduces copies of the wrapped begin/end iterators, as well as the predicate functor. That's why I'm leaning towards adding an overload of reverse(), but I'm open to either approach.

I've realized that the current version of filter_iterator doesn't support predicates which capture move-only types, so I no longer have reservations about just making filter_iterator bidirectional.

I'll abandon this in favor of https://reviews.llvm.org/D45853.