llvm.org GIT mirror llvm / bf540e5
[ADT] Make filter_iterator support bidirectional iteration 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 factored out forward iteration functionality into filter_iterator_base, and added a specialization of filter_iterator_impl which supports bidirectional iteration. Thanks to Tim Shen, Zachary Turner, and others for suggesting this design and providing feedback! This version of the patch supersedes the original (https://reviews.llvm.org/D45792). 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 Differential Revision: https://reviews.llvm.org/D45853 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@330875 91177308-0d34-0410-b5e6-96231b3b80d8 Vedant Kumar 1 year, 5 months ago
3 changed file(s) with 137 addition(s) and 38 deletion(s). Raw diff Collapse all Expand all
270270 /// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; });
271271 /// // R contains { 1, 3 }.
272272 /// \endcode
273 template
274 class filter_iterator
273 ///
274 /// Note: filter_iterator_base implements support for forward iteration.
275 /// filter_iterator_impl exists to provide support for bidirectional iteration,
276 /// conditional on whether the wrapped iterator supports it.
277 template
278 class filter_iterator_base
275279 : public iterator_adaptor_base<
276 filter_iterator, WrappedIteratorT,
280 filter_iterator_base,
281 WrappedIteratorT,
277282 typename std::common_type<
278 std::forward_iterator_tag,
279 typename std::iterator_traits<
280 WrappedIteratorT>::iterator_category>::type> {
283 IterTag, typename std::iterator_traits<
284 WrappedIteratorT>::iterator_category>::type> {
281285 using BaseT = iterator_adaptor_base<
282 filter_iterator, WrappedIteratorT,
286 filter_iterator_base,
287 WrappedIteratorT,
283288 typename std::common_type<
284 std::forward_iterator_tag,
285 typename std::iterator_traits::iterator_category>::
286 type>;
287
288 struct PayloadType {
289 WrappedIteratorT End;
290 PredicateT Pred;
291 };
292
293 Optional Payload;
289 IterTag, typename std::iterator_traits<
290 WrappedIteratorT>::iterator_category>::type>;
291
292 protected:
293 WrappedIteratorT End;
294 PredicateT Pred;
294295
295296 void findNextValid() {
296 assert(Payload && "Payload should be engaged when findNextValid is called");
297 while (this->I != Payload->End && !Payload->Pred(*this->I))
297 while (this->I != End && !Pred(*this->I))
298298 BaseT::operator++();
299299 }
300300
301 // Construct the begin iterator. The begin iterator requires to know where end
302 // is, so that it can properly stop when it hits end.
303 filter_iterator(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred)
304 : BaseT(std::move(Begin)),
305 Payload(PayloadType{std::move(End), std::move(Pred)}) {
301 // Construct the iterator. The begin iterator needs to know where the end
302 // is, so that it can properly stop when it gets there. The end iterator only
303 // needs the predicate to support bidirectional iteration.
304 filter_iterator_base(WrappedIteratorT Begin, WrappedIteratorT End,
305 PredicateT Pred)
306 : BaseT(Begin), End(End), Pred(Pred) {
306307 findNextValid();
307308 }
308309
309 // Construct the end iterator. It's not incrementable, so Payload doesn't
310 // have to be engaged.
311 filter_iterator(WrappedIteratorT End) : BaseT(End) {}
312
313310 public:
314311 using BaseT::operator++;
315312
316 filter_iterator &operator++() {
313 filter_iterator_base &operator++() {
317314 BaseT::operator++();
318315 findNextValid();
319316 return *this;
320317 }
321
322 template
323 friend iterator_range, PT>>
324 make_filter_range(RT &&, PT);
325 };
318 };
319
320 /// Specialization of filter_iterator_base for forward iteration only.
321 template
322 typename IterTag = std::forward_iterator_tag>
323 class filter_iterator_impl
324 : public filter_iterator_base {
325 using BaseT = filter_iterator_base;
326
327 public:
328 filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End,
329 PredicateT Pred)
330 : BaseT(Begin, End, Pred) {}
331 };
332
333 /// Specialization of filter_iterator_base for bidirectional iteration.
334 template
335 class filter_iterator_impl
336 std::bidirectional_iterator_tag>
337 : public filter_iterator_base
338 std::bidirectional_iterator_tag> {
339 using BaseT = filter_iterator_base
340 std::bidirectional_iterator_tag>;
341 void findPrevValid() {
342 while (!this->Pred(*this->I))
343 BaseT::operator--();
344 }
345
346 public:
347 using BaseT::operator--;
348
349 filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End,
350 PredicateT Pred)
351 : BaseT(Begin, End, Pred) {}
352
353 filter_iterator_impl &operator--() {
354 BaseT::operator--();
355 findPrevValid();
356 return *this;
357 }
358 };
359
360 namespace detail {
361
362 template struct fwd_or_bidi_tag_impl {
363 using type = std::forward_iterator_tag;
364 };
365
366 template <> struct fwd_or_bidi_tag_impl {
367 using type = std::bidirectional_iterator_tag;
368 };
369
370 /// Helper which sets its type member to forward_iterator_tag if the category
371 /// of \p IterT does not derive from bidirectional_iterator_tag, and to
372 /// bidirectional_iterator_tag otherwise.
373 template struct fwd_or_bidi_tag {
374 using type = typename fwd_or_bidi_tag_impl
375 std::bidirectional_iterator_tag,
376 typename std::iterator_traits::iterator_category>::value>::type;
377 };
378
379 } // namespace detail
380
381 /// Defines filter_iterator to a suitable specialization of
382 /// filter_iterator_impl, based on the underlying iterator's category.
383 template
384 using filter_iterator = filter_iterator_impl<
385 WrappedIteratorT, PredicateT,
386 typename detail::fwd_or_bidi_tag::type>;
326387
327388 /// Convenience function that takes a range of elements and a predicate,
328389 /// and return a new filter_iterator range.
336397 make_filter_range(RangeT &&Range, PredicateT Pred) {
337398 using FilterIteratorT =
338399 filter_iterator, PredicateT>;
339 return make_range(FilterIteratorT(std::begin(std::forward(Range)),
340 std::end(std::forward(Range)),
341 std::move(Pred)),
342 FilterIteratorT(std::end(std::forward(Range))));
400 return make_range(
401 FilterIteratorT(std::begin(std::forward(Range)),
402 std::end(std::forward(Range)), Pred),
403 FilterIteratorT(std::end(std::forward(Range)),
404 std::end(std::forward(Range)), Pred));
343405 }
344406
345407 // forward declarations required by zip_shortest/zip_first
77 //===----------------------------------------------------------------------===//
88
99 #include "llvm/ADT/iterator.h"
10 #include "llvm/ADT/ArrayRef.h"
1011 #include "llvm/ADT/STLExtras.h"
1112 #include "llvm/ADT/SmallVector.h"
1213 #include "gtest/gtest.h"
193194 IsOdd);
194195 SmallVector Actual(Range.begin(), Range.end());
195196 EXPECT_EQ((SmallVector{1, 3, 5}), Actual);
197 }
198
199 TEST(FilterIteratorTest, ReverseFilterRange) {
200 auto IsOdd = [](int N) { return N % 2 == 1; };
201 int A[] = {0, 1, 2, 3, 4, 5, 6};
202
203 // Check basic reversal.
204 auto Range = reverse(make_filter_range(A, IsOdd));
205 SmallVector Actual(Range.begin(), Range.end());
206 EXPECT_EQ((SmallVector{5, 3, 1}), Actual);
207
208 // Check that the reverse of the reverse is the original.
209 auto Range2 = reverse(reverse(make_filter_range(A, IsOdd)));
210 SmallVector Actual2(Range2.begin(), Range2.end());
211 EXPECT_EQ((SmallVector{1, 3, 5}), Actual2);
212
213 // Check empty ranges.
214 auto Range3 = reverse(make_filter_range(ArrayRef(), IsOdd));
215 SmallVector Actual3(Range3.begin(), Range3.end());
216 EXPECT_EQ((SmallVector{}), Actual3);
217
218 // Check that we don't skip the first element, provided it isn't filtered
219 // away.
220 auto IsEven = [](int N) { return N % 2 == 0; };
221 auto Range4 = reverse(make_filter_range(A, IsEven));
222 SmallVector Actual4(Range4.begin(), Range4.end());
223 EXPECT_EQ((SmallVector{6, 4, 2, 0}), Actual4);
196224 }
197225
198226 TEST(PointerIterator, Basic) {
6868 CI = BB->phis().begin();
6969 EXPECT_NE(CI, BB->phis().end());
7070
71 // Test that filtering iterators work with basic blocks.
72 auto isPhi = [](Instruction &I) { return isa(&I); };
73 auto Phis = make_filter_range(*BB, isPhi);
74 auto ReversedPhis = reverse(make_filter_range(*BB, isPhi));
75 EXPECT_EQ(std::distance(Phis.begin(), Phis.end()), 3);
76 EXPECT_EQ(&*Phis.begin(), P1);
77 EXPECT_EQ(std::distance(ReversedPhis.begin(), ReversedPhis.end()), 3);
78 EXPECT_EQ(&*ReversedPhis.begin(), P3);
79
7180 // And iterate a const range.
7281 for (const auto &PN : const_cast(BB.get())->phis()) {
7382 EXPECT_EQ(BB.get(), PN.getIncomingBlock(0));