struct GraphTraits {
// Elements to provide:
+ // NOTICE: We are in a transition from migration interfaces that require
+ // NodeType *, to NodeRef. NodeRef is required to be cheap to copy, but does
+ // not have to be a raw pointer. In the transition, user should define
+ // NodeType, and NodeRef = NodeType *.
+ //
// typedef NodeType - Type of Node in the graph
+ // typedef NodeRef - NodeType *
// typedef ChildIteratorType - Type used to iterate over children in graph
- // static NodeType *getEntryNode(const GraphType &)
+ // static NodeRef getEntryNode(const GraphType &)
// Return the entry node of the graph
- // static ChildIteratorType child_begin(NodeType *)
- // static ChildIteratorType child_end (NodeType *)
+ // static ChildIteratorType child_begin(NodeRef)
+ // static ChildIteratorType child_end (NodeRef)
// Return iterators that point to the beginning and ending of the child
// node list for the specified node.
//
-
// typedef ...iterator nodes_iterator;
// static nodes_iterator nodes_begin(GraphType *G)
// static nodes_iterator nodes_end (GraphType *G)
// your argument to XXX_begin(...) is unknown or needs to have the proper .h
// file #include'd.
//
- typedef typename GraphType::UnknownGraphTypeError NodeType;
+ typedef typename GraphType::UnknownGraphTypeError NodeRef;
};
/// build up a vector of nodes in a particular SCC. Note that it is a forward
/// iterator and thus you cannot backtrack or re-visit nodes.
template <class GraphT, class GT = GraphTraits<GraphT>>
-class scc_iterator
- : public iterator_facade_base<
- scc_iterator<GraphT, GT>, std::forward_iterator_tag,
- const std::vector<typename GT::NodeType *>, ptrdiff_t> {
- typedef typename GT::NodeType NodeType;
+class scc_iterator : public iterator_facade_base<
+ scc_iterator<GraphT, GT>, std::forward_iterator_tag,
+ const std::vector<typename GT::NodeRef>, ptrdiff_t> {
+ typedef typename GT::NodeRef NodeRef;
typedef typename GT::ChildIteratorType ChildItTy;
- typedef std::vector<NodeType *> SccTy;
+ typedef std::vector<NodeRef> SccTy;
typedef typename scc_iterator::reference reference;
/// Element of VisitStack during DFS.
struct StackElement {
- NodeType *Node; ///< The current node pointer.
+ NodeRef Node; ///< The current node pointer.
ChildItTy NextChild; ///< The next child, modified inplace during DFS.
unsigned MinVisited; ///< Minimum uplink value of all children of Node.
- StackElement(NodeType *Node, const ChildItTy &Child, unsigned Min)
- : Node(Node), NextChild(Child), MinVisited(Min) {}
+ StackElement(NodeRef Node, const ChildItTy &Child, unsigned Min)
+ : Node(Node), NextChild(Child), MinVisited(Min) {}
bool operator==(const StackElement &Other) const {
return Node == Other.Node &&
///
/// nodeVisitNumbers are per-node visit numbers, also used as DFS flags.
unsigned visitNum;
- DenseMap<NodeType *, unsigned> nodeVisitNumbers;
+ DenseMap<NodeRef, unsigned> nodeVisitNumbers;
/// Stack holding nodes of the SCC.
- std::vector<NodeType *> SCCNodeStack;
+ std::vector<NodeRef> SCCNodeStack;
/// The current SCC, retrieved using operator*().
SccTy CurrentSCC;
std::vector<StackElement> VisitStack;
/// A single "visit" within the non-recursive DFS traversal.
- void DFSVisitOne(NodeType *N);
+ void DFSVisitOne(NodeRef N);
/// The stack-based DFS traversal; defined below.
void DFSVisitChildren();
/// Compute the next SCC using the DFS traversal.
void GetNextSCC();
- scc_iterator(NodeType *entryN) : visitNum(0) {
+ scc_iterator(NodeRef entryN) : visitNum(0) {
DFSVisitOne(entryN);
GetNextSCC();
}
/// This informs the \c scc_iterator that the specified \c Old node
/// has been deleted, and \c New is to be used in its place.
- void ReplaceNode(NodeType *Old, NodeType *New) {
+ void ReplaceNode(NodeRef Old, NodeRef New) {
assert(nodeVisitNumbers.count(Old) && "Old not in scc_iterator?");
nodeVisitNumbers[New] = nodeVisitNumbers[Old];
nodeVisitNumbers.erase(Old);
};
template <class GraphT, class GT>
-void scc_iterator<GraphT, GT>::DFSVisitOne(NodeType *N) {
+void scc_iterator<GraphT, GT>::DFSVisitOne(NodeRef N) {
++visitNum;
nodeVisitNumbers[N] = visitNum;
SCCNodeStack.push_back(N);
assert(!VisitStack.empty());
while (VisitStack.back().NextChild != GT::child_end(VisitStack.back().Node)) {
// TOS has at least one more child so continue DFS
- NodeType *childN = *VisitStack.back().NextChild++;
- typename DenseMap<NodeType *, unsigned>::iterator Visited =
+ NodeRef childN = *VisitStack.back().NextChild++;
+ typename DenseMap<NodeRef, unsigned>::iterator Visited =
nodeVisitNumbers.find(childN);
if (Visited == nodeVisitNumbers.end()) {
// this node has never been seen.
DFSVisitChildren();
// Pop the leaf on top of the VisitStack.
- NodeType *visitingN = VisitStack.back().Node;
+ NodeRef visitingN = VisitStack.back().Node;
unsigned minVisitNum = VisitStack.back().MinVisited;
assert(VisitStack.back().NextChild == GT::child_end(visitingN));
VisitStack.pop_back();
assert(!CurrentSCC.empty() && "Dereferencing END SCC iterator!");
if (CurrentSCC.size() > 1)
return true;
- NodeType *N = CurrentSCC.front();
+ NodeRef N = CurrentSCC.front();
for (ChildItTy CI = GT::child_begin(N), CE = GT::child_end(N); CI != CE;
++CI)
if (*CI == N)
#include <memory>
#include <utility> // for std::pair
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
#include "llvm/Support/Compiler.h"
namespace llvm {
+namespace detail {
+
+template <typename RangeT>
+using IterOfRange = decltype(std::begin(std::declval<RangeT>()));
+
+} // End detail namespace
//===----------------------------------------------------------------------===//
// Extra additions to <functional>
llvm::make_reverse_iterator(std::begin(C)));
}
+/// An iterator adaptor that filters the elements of given inner iterators.
+///
+/// The predicate parameter should be a callable object that accepts the wrapped
+/// iterator's reference type and returns a bool. When incrementing or
+/// decrementing the iterator, it will call the predicate on each element and
+/// skip any where it returns false.
+///
+/// \code
+/// int A[] = { 1, 2, 3, 4 };
+/// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; });
+/// // R contains { 1, 3 }.
+/// \endcode
+template <typename WrappedIteratorT, typename PredicateT>
+class filter_iterator
+ : public iterator_adaptor_base<
+ filter_iterator<WrappedIteratorT, PredicateT>, WrappedIteratorT,
+ typename std::common_type<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<
+ WrappedIteratorT>::iterator_category>::type> {
+ using BaseT = iterator_adaptor_base<
+ filter_iterator<WrappedIteratorT, PredicateT>, WrappedIteratorT,
+ typename std::common_type<
+ std::forward_iterator_tag,
+ typename std::iterator_traits<WrappedIteratorT>::iterator_category>::
+ type>;
+
+ struct PayloadType {
+ WrappedIteratorT End;
+ PredicateT Pred;
+ };
+
+ Optional<PayloadType> Payload;
+
+ void findNextValid() {
+ assert(Payload && "Payload should be engaged when findNextValid is called");
+ while (this->I != Payload->End && !Payload->Pred(*this->I))
+ BaseT::operator++();
+ }
+
+ // Construct the begin iterator. The begin iterator requires to know where end
+ // is, so that it can properly stop when it hits end.
+ filter_iterator(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred)
+ : BaseT(std::move(Begin)),
+ Payload(PayloadType{std::move(End), std::move(Pred)}) {
+ findNextValid();
+ }
+
+ // Construct the end iterator. It's not incrementable, so Payload doesn't
+ // have to be engaged.
+ filter_iterator(WrappedIteratorT End) : BaseT(End) {}
+
+public:
+ using BaseT::operator++;
+
+ filter_iterator &operator++() {
+ BaseT::operator++();
+ findNextValid();
+ return *this;
+ }
+
+ template <typename RT, typename PT>
+ friend iterator_range<filter_iterator<detail::IterOfRange<RT>, PT>>
+ make_filter_range(RT &&, PT);
+};
+
+/// Convenience function that takes a range of elements and a predicate,
+/// and return a new filter_iterator range.
+///
+/// FIXME: Currently if RangeT && is a rvalue reference to a temporary, the
+/// lifetime of that temporary is not kept by the returned range object, and the
+/// temporary is going to be dropped on the floor after the make_iterator_range
+/// full expression that contains this function call.
+template <typename RangeT, typename PredicateT>
+iterator_range<filter_iterator<detail::IterOfRange<RangeT>, PredicateT>>
+make_filter_range(RangeT &&Range, PredicateT Pred) {
+ using FilterIteratorT =
+ filter_iterator<detail::IterOfRange<RangeT>, PredicateT>;
+ return make_range(FilterIteratorT(std::begin(std::forward<RangeT>(Range)),
+ std::end(std::forward<RangeT>(Range)),
+ std::move(Pred)),
+ FilterIteratorT(std::end(std::forward<RangeT>(Range))));
+}
+
//===----------------------------------------------------------------------===//
// Extra additions to <utility>
//===----------------------------------------------------------------------===//
typename T = typename std::iterator_traits<WrappedIteratorT>::value_type,
typename DifferenceTypeT =
typename std::iterator_traits<WrappedIteratorT>::difference_type,
- typename PointerT = T *, typename ReferenceT = T &,
+ typename PointerT = typename std::conditional<
+ std::is_same<T, typename std::iterator_traits<
+ WrappedIteratorT>::value_type>::value,
+ typename std::iterator_traits<WrappedIteratorT>::pointer, T *>::type,
+ typename ReferenceT = typename std::conditional<
+ std::is_same<T, typename std::iterator_traits<
+ WrappedIteratorT>::value_type>::value,
+ typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type,
// Don't provide these, they are mostly to act as aliases below.
typename WrappedTraitsT = std::iterator_traits<WrappedIteratorT>>
class iterator_adaptor_base
iterator_adaptor_base() = default;
- template <typename U>
- explicit iterator_adaptor_base(
- U &&u,
- typename std::enable_if<
- !std::is_base_of<typename std::remove_cv<
- typename std::remove_reference<U>::type>::type,
- DerivedT>::value,
- int>::type = 0)
- : I(std::forward<U &&>(u)) {}
+ explicit iterator_adaptor_base(WrappedIteratorT u) : I(std::move(u)) {}
const WrappedIteratorT &wrapped() const { return I; }
// traversals.
template <> struct GraphTraits<CallGraphNode *> {
typedef CallGraphNode NodeType;
+ typedef CallGraphNode *NodeRef;
typedef CallGraphNode::CallRecord CGNPairTy;
typedef std::pointer_to_unary_function<CGNPairTy, CallGraphNode *>
template <> struct GraphTraits<const CallGraphNode *> {
typedef const CallGraphNode NodeType;
+ typedef const CallGraphNode *NodeRef;
typedef CallGraphNode::CallRecord CGNPairTy;
typedef std::pointer_to_unary_function<CGNPairTy, const CallGraphNode *>
template <> struct GraphTraits<MachineBasicBlock *> {
typedef MachineBasicBlock NodeType;
+ typedef MachineBasicBlock *NodeRef;
typedef MachineBasicBlock::succ_iterator ChildIteratorType;
static NodeType *getEntryNode(MachineBasicBlock *BB) { return BB; }
template <> struct GraphTraits<const MachineBasicBlock *> {
typedef const MachineBasicBlock NodeType;
+ typedef const MachineBasicBlock *NodeRef;
typedef MachineBasicBlock::const_succ_iterator ChildIteratorType;
static NodeType *getEntryNode(const MachineBasicBlock *BB) { return BB; }
//
template <> struct GraphTraits<Inverse<MachineBasicBlock*> > {
typedef MachineBasicBlock NodeType;
+ typedef MachineBasicBlock *NodeRef;
typedef MachineBasicBlock::pred_iterator ChildIteratorType;
static NodeType *getEntryNode(Inverse<MachineBasicBlock *> G) {
return G.Graph;
template <> struct GraphTraits<Inverse<const MachineBasicBlock*> > {
typedef const MachineBasicBlock NodeType;
+ typedef const MachineBasicBlock *NodeRef;
typedef MachineBasicBlock::const_pred_iterator ChildIteratorType;
static NodeType *getEntryNode(Inverse<const MachineBasicBlock*> G) {
return G.Graph;
template <> struct GraphTraits<BasicBlock*> {
typedef BasicBlock NodeType;
+ typedef BasicBlock *NodeRef;
typedef succ_iterator ChildIteratorType;
static NodeType *getEntryNode(BasicBlock *BB) { return BB; }
template <> struct GraphTraits<const BasicBlock*> {
typedef const BasicBlock NodeType;
+ typedef const BasicBlock *NodeRef;
typedef succ_const_iterator ChildIteratorType;
static NodeType *getEntryNode(const BasicBlock *BB) { return BB; }
//
template <> struct GraphTraits<Inverse<BasicBlock*> > {
typedef BasicBlock NodeType;
+ typedef BasicBlock *NodeRef;
typedef pred_iterator ChildIteratorType;
static NodeType *getEntryNode(Inverse<BasicBlock *> G) { return G.Graph; }
static inline ChildIteratorType child_begin(NodeType *N) {
template <> struct GraphTraits<Inverse<const BasicBlock*> > {
typedef const BasicBlock NodeType;
+ typedef const BasicBlock *NodeRef;
typedef const_pred_iterator ChildIteratorType;
static NodeType *getEntryNode(Inverse<const BasicBlock*> G) {
return G.Graph;
typedef bfi_detail::IrreducibleGraph GraphT;
typedef const GraphT::IrrNode NodeType;
+ typedef const GraphT::IrrNode *NodeRef;
typedef GraphT::IrrNode::iterator ChildIteratorType;
static const NodeType *getEntryNode(const GraphT &G) {
namespace llvm {
template <> struct GraphTraits<ArgumentGraphNode *> {
typedef ArgumentGraphNode NodeType;
+ typedef ArgumentGraphNode *NodeRef;
typedef SmallVectorImpl<ArgumentGraphNode *>::iterator ChildIteratorType;
static inline NodeType *getEntryNode(NodeType *A) { return A; }
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
class LoopVectorizationCostModel;
class LoopVectorizationRequirements;
+// A traits type that is intended to be used in graph algorithms. The graph it
+// models starts at the loop header, and traverses the BasicBlocks that are in
+// the loop body, but not the loop header. Since the loop header is skipped,
+// the back edges are excluded.
+struct LoopBodyTraits {
+ using NodeRef = std::pair<const Loop *, BasicBlock *>;
+
+ // This wraps a const Loop * into the iterator, so we know which edges to
+ // filter out.
+ class WrappedSuccIterator
+ : public iterator_adaptor_base<
+ WrappedSuccIterator, succ_iterator,
+ typename std::iterator_traits<succ_iterator>::iterator_category,
+ NodeRef, std::ptrdiff_t, NodeRef *, NodeRef> {
+ using BaseT = iterator_adaptor_base<
+ WrappedSuccIterator, succ_iterator,
+ typename std::iterator_traits<succ_iterator>::iterator_category,
+ NodeRef, std::ptrdiff_t, NodeRef *, NodeRef>;
+
+ const Loop *L;
+
+ public:
+ WrappedSuccIterator(succ_iterator Begin, const Loop *L)
+ : BaseT(Begin), L(L) {}
+
+ NodeRef operator*() const { return {L, *I}; }
+ };
+
+ struct LoopBodyFilter {
+ bool operator()(NodeRef N) const {
+ const Loop *L = N.first;
+ return N.second != L->getHeader() && L->contains(N.second);
+ }
+ };
+
+ using ChildIteratorType =
+ filter_iterator<WrappedSuccIterator, LoopBodyFilter>;
+
+ static NodeRef getEntryNode(const Loop &G) { return {&G, G.getHeader()}; }
+
+ static ChildIteratorType child_begin(NodeRef Node) {
+ return make_filter_range(make_range<WrappedSuccIterator>(
+ {succ_begin(Node.second), Node.first},
+ {succ_end(Node.second), Node.first}),
+ LoopBodyFilter{})
+ .begin();
+ }
+
+ static ChildIteratorType child_end(NodeRef Node) {
+ return make_filter_range(make_range<WrappedSuccIterator>(
+ {succ_begin(Node.second), Node.first},
+ {succ_end(Node.second), Node.first}),
+ LoopBodyFilter{})
+ .end();
+ }
+};
+
+/// Returns true if the given loop body has a cycle, excluding the loop
+/// itself.
+static bool hasCyclesInLoopBody(const Loop &L) {
+ if (!L.empty())
+ return true;
+
+ for (const auto SCC :
+ make_range(scc_iterator<Loop, LoopBodyTraits>::begin(L),
+ scc_iterator<Loop, LoopBodyTraits>::end(L))) {
+ if (SCC.size() > 1) {
+ DEBUG(dbgs() << "LVL: Detected a cycle in the loop body:\n");
+ DEBUG(L.dump());
+ return true;
+ }
+ }
+ return false;
+}
+
/// \brief This modifies LoopAccessReport to initialize message with
/// loop-vectorizer-specific part.
class VectorizationReport : public LoopAccessReport {
Instruction *UnsafeAlgebraInst;
};
-static void addInnerLoop(Loop &L, SmallVectorImpl<Loop *> &V) {
- if (L.empty())
- return V.push_back(&L);
-
+static void addAcyclicInnerLoop(Loop &L, SmallVectorImpl<Loop *> &V) {
+ if (L.empty()) {
+ if (!hasCyclesInLoopBody(L))
+ V.push_back(&L);
+ return;
+ }
for (Loop *InnerL : L)
- addInnerLoop(*InnerL, V);
+ addAcyclicInnerLoop(*InnerL, V);
}
/// The LoopVectorize Pass.
return false;
}
+ // FIXME: The code is currently dead, since the loop gets sent to
+ // LoopVectorizationLegality is already an innermost loop.
+ //
// We can only vectorize innermost loops.
if (!TheLoop->empty()) {
emitAnalysis(VectorizationReport() << "loop is not the innermost loop");
SmallVector<Loop *, 8> Worklist;
for (Loop *L : *LI)
- addInnerLoop(*L, Worklist);
+ addAcyclicInnerLoop(*L, Worklist);
LoopsAnalyzed += Worklist.size();
--- /dev/null
+; RUN: opt -loop-vectorize -pass-remarks=loop-vectorize -S < %s 2>&1 | FileCheck %s
+
+; FIXME: Check for -pass-remarks-missed and -pass-remarks-analysis output when
+; addAcyclicInnerLoop emits analysis.
+
+; Check that opt does not crash on such input:
+;
+; a, b, c;
+; fn1() {
+; while (b--) {
+; c = a;
+; switch (a & 3)
+; case 0:
+; do
+; case 3:
+; case 2:
+; case 1:
+; ;
+; while (--c)
+; ;
+; }
+; }
+
+@b = common global i32 0, align 4
+@a = common global i32 0, align 4
+@c = common global i32 0, align 4
+
+; CHECK-NOT: vectorized loop
+; CHECK-LABEL: fn1
+
+define i32 @fn1() {
+entry:
+ %tmp2 = load i32, i32* @b, align 4
+ %dec3 = add nsw i32 %tmp2, -1
+ store i32 %dec3, i32* @b, align 4
+ %tobool4 = icmp eq i32 %tmp2, 0
+ br i1 %tobool4, label %while.end, label %while.body.lr.ph
+
+while.body.lr.ph: ; preds = %entry
+ %tmp1 = load i32, i32* @a, align 4
+ %and = and i32 %tmp1, 3
+ %switch = icmp eq i32 %and, 0
+ br label %while.body
+
+while.cond: ; preds = %do.cond
+ %dec = add nsw i32 %dec7, -1
+ %tobool = icmp eq i32 %dec7, 0
+ br i1 %tobool, label %while.cond.while.end_crit_edge, label %while.body
+
+while.body: ; preds = %while.body.lr.ph, %while.cond
+ %dec7 = phi i32 [ %dec3, %while.body.lr.ph ], [ %dec, %while.cond ]
+ br i1 %switch, label %do.body, label %do.cond
+
+do.body: ; preds = %do.cond, %while.body
+ %dec25 = phi i32 [ %dec2, %do.cond ], [ %tmp1, %while.body ]
+ br label %do.cond
+
+do.cond: ; preds = %do.body, %while.body
+ %dec26 = phi i32 [ %dec25, %do.body ], [ %tmp1, %while.body ]
+ %dec2 = add nsw i32 %dec26, -1
+ %tobool3 = icmp eq i32 %dec2, 0
+ br i1 %tobool3, label %while.cond, label %do.body
+
+while.cond.while.end_crit_edge: ; preds = %while.cond
+ store i32 0, i32* @c, align 4
+ store i32 -1, i32* @b, align 4
+ br label %while.end
+
+while.end: ; preds = %while.cond.while.end_crit_edge, %entry
+ ret i32 undef
+}
template <unsigned N>
struct GraphTraits<Graph<N> > {
typedef typename Graph<N>::NodeType NodeType;
+ typedef typename Graph<N>::NodeType *NodeRef;
typedef typename Graph<N>::ChildIterator ChildIteratorType;
static inline NodeType *getEntryNode(const Graph<N> &G) { return G.AccessNode(0); }
namespace {
+template <int> struct Shadow;
+
+struct WeirdIter : std::iterator<std::input_iterator_tag, Shadow<0>, Shadow<1>,
+ Shadow<2>, Shadow<3>> {};
+
+struct AdaptedIter : iterator_adaptor_base<AdaptedIter, WeirdIter> {};
+
+// Test that iterator_adaptor_base forwards typedefs, if value_type is
+// unchanged.
+static_assert(std::is_same<typename AdaptedIter::value_type, Shadow<0>>::value,
+ "");
+static_assert(
+ std::is_same<typename AdaptedIter::difference_type, Shadow<1>>::value, "");
+static_assert(std::is_same<typename AdaptedIter::pointer, Shadow<2>>::value,
+ "");
+static_assert(std::is_same<typename AdaptedIter::reference, Shadow<3>>::value,
+ "");
+
TEST(PointeeIteratorTest, Basic) {
int arr[4] = { 1, 2, 3, 4 };
SmallVector<int *, 4> V;
EXPECT_EQ(End, I);
}
+TEST(FilterIteratorTest, Lambda) {
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, CallableObject) {
+ int Counter = 0;
+ struct Callable {
+ int &Counter;
+
+ Callable(int &Counter) : Counter(Counter) {}
+
+ bool operator()(int N) {
+ Counter++;
+ return N % 2 == 1;
+ }
+ };
+ Callable IsOdd(Counter);
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ EXPECT_EQ(2, Counter);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_GE(Counter, 7);
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, FunctionPointer) {
+ bool (*IsOdd)(int) = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(A, IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, Composition) {
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ std::unique_ptr<int> A[] = {make_unique<int>(0), make_unique<int>(1),
+ make_unique<int>(2), make_unique<int>(3),
+ make_unique<int>(4), make_unique<int>(5),
+ make_unique<int>(6)};
+ using PointeeIterator = pointee_iterator<std::unique_ptr<int> *>;
+ auto Range = make_filter_range(
+ make_range(PointeeIterator(std::begin(A)), PointeeIterator(std::end(A))),
+ IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
+TEST(FilterIteratorTest, InputIterator) {
+ struct InputIterator
+ : iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag> {
+ using BaseT =
+ iterator_adaptor_base<InputIterator, int *, std::input_iterator_tag>;
+
+ InputIterator(int *It) : BaseT(It) {}
+ };
+
+ auto IsOdd = [](int N) { return N % 2 == 1; };
+ int A[] = {0, 1, 2, 3, 4, 5, 6};
+ auto Range = make_filter_range(
+ make_range(InputIterator(std::begin(A)), InputIterator(std::end(A))),
+ IsOdd);
+ SmallVector<int, 3> Actual(Range.begin(), Range.end());
+ EXPECT_EQ((SmallVector<int, 3>{1, 3, 5}), Actual);
+}
+
} // anonymous namespace