From 3e3f1602c5b13e6c861abb4044f9cefccca04e94 Mon Sep 17 00:00:00 2001 From: Hans Wennborg Date: Mon, 15 Aug 2016 16:06:31 +0000 Subject: [PATCH] Merging r278573 (and r277399, r278157, r278569): ------------------------------------------------------------------------ r278573 | timshen | 2016-08-12 15:47:13 -0700 (Fri, 12 Aug 2016) | 8 lines [LoopVectorize] Detect loops in the innermost loop before creating InnerLoopVectorizer InnerLoopVectorizer shouldn't handle a loop with cycles inside the loop body, even if that cycle isn't a natural loop. Fixes PR28541. Differential Revision: https://reviews.llvm.org/D22952 ------------------------------------------------------------------------ ------------------------------------------------------------------------ r277399 | timshen | 2016-08-01 15:32:20 -0700 (Mon, 01 Aug 2016) | 9 lines [ADT] NFC: Generalize GraphTraits requirement of "NodeType *" in interfaces to "NodeRef", and migrate SCCIterator.h to use NodeRef Summary: By generalize the interface, users are able to inject more flexible Node token into the algorithm, for example, a pair of vector* and index integer. Currently I only migrated SCCIterator to use NodeRef, but more is coming. It's a NFC. Reviewers: dblaikie, chandlerc Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D22937 ------------------------------------------------------------------------ ------------------------------------------------------------------------ r278157 | timshen | 2016-08-09 13:23:13 -0700 (Tue, 09 Aug 2016) | 7 lines [ADT] Change iterator_adaptor_base's default template arguments to forward more underlying typedefs Reviewers: chandlerc Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D23217 ------------------------------------------------------------------------ ------------------------------------------------------------------------ r278569 | timshen | 2016-08-12 15:03:28 -0700 (Fri, 12 Aug 2016) | 3 lines [ADT] Add filter_iterator for filtering elements Differential Revision: https://reviews.llvm.org/D22951 ------------------------------------------------------------------------ git-svn-id: https://llvm.org/svn/llvm-project/llvm/branches/release_39@278674 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ADT/GraphTraits.h | 15 ++-- include/llvm/ADT/SCCIterator.h | 37 +++++---- include/llvm/ADT/STLExtras.h | 92 +++++++++++++++++++++ include/llvm/ADT/iterator.h | 19 +++-- include/llvm/Analysis/CallGraph.h | 2 + include/llvm/CodeGen/MachineBasicBlock.h | 4 + include/llvm/IR/CFG.h | 4 + lib/Analysis/BlockFrequencyInfoImpl.cpp | 1 + lib/Transforms/IPO/FunctionAttrs.cpp | 1 + lib/Transforms/Vectorize/LoopVectorize.cpp | 93 ++++++++++++++++++++-- test/Transforms/LoopVectorize/pr28541.ll | 71 +++++++++++++++++ unittests/ADT/SCCIteratorTest.cpp | 1 + unittests/Support/IteratorTest.cpp | 87 ++++++++++++++++++++ 13 files changed, 387 insertions(+), 40 deletions(-) create mode 100644 test/Transforms/LoopVectorize/pr28541.ll diff --git a/include/llvm/ADT/GraphTraits.h b/include/llvm/ADT/GraphTraits.h index 823caef7647..eb67b7c8365 100644 --- a/include/llvm/ADT/GraphTraits.h +++ b/include/llvm/ADT/GraphTraits.h @@ -27,19 +27,24 @@ template 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) @@ -57,7 +62,7 @@ struct GraphTraits { // 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; }; diff --git a/include/llvm/ADT/SCCIterator.h b/include/llvm/ADT/SCCIterator.h index bc74416ac88..e89345c0f34 100644 --- a/include/llvm/ADT/SCCIterator.h +++ b/include/llvm/ADT/SCCIterator.h @@ -37,23 +37,22 @@ namespace llvm { /// 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 scc_iterator - : public iterator_facade_base< - scc_iterator, std::forward_iterator_tag, - const std::vector, ptrdiff_t> { - typedef typename GT::NodeType NodeType; +class scc_iterator : public iterator_facade_base< + scc_iterator, std::forward_iterator_tag, + const std::vector, ptrdiff_t> { + typedef typename GT::NodeRef NodeRef; typedef typename GT::ChildIteratorType ChildItTy; - typedef std::vector SccTy; + typedef std::vector 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 && @@ -67,10 +66,10 @@ class scc_iterator /// /// nodeVisitNumbers are per-node visit numbers, also used as DFS flags. unsigned visitNum; - DenseMap nodeVisitNumbers; + DenseMap nodeVisitNumbers; /// Stack holding nodes of the SCC. - std::vector SCCNodeStack; + std::vector SCCNodeStack; /// The current SCC, retrieved using operator*(). SccTy CurrentSCC; @@ -80,7 +79,7 @@ class scc_iterator std::vector 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(); @@ -88,7 +87,7 @@ class scc_iterator /// 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(); } @@ -131,7 +130,7 @@ public: /// 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); @@ -139,7 +138,7 @@ public: }; template -void scc_iterator::DFSVisitOne(NodeType *N) { +void scc_iterator::DFSVisitOne(NodeRef N) { ++visitNum; nodeVisitNumbers[N] = visitNum; SCCNodeStack.push_back(N); @@ -155,8 +154,8 @@ void scc_iterator::DFSVisitChildren() { 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::iterator Visited = + NodeRef childN = *VisitStack.back().NextChild++; + typename DenseMap::iterator Visited = nodeVisitNumbers.find(childN); if (Visited == nodeVisitNumbers.end()) { // this node has never been seen. @@ -176,7 +175,7 @@ template void scc_iterator::GetNextSCC() { 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(); @@ -212,7 +211,7 @@ bool scc_iterator::hasLoop() const { 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) diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h index abd39dacc67..00b796f6381 100644 --- a/include/llvm/ADT/STLExtras.h +++ b/include/llvm/ADT/STLExtras.h @@ -26,10 +26,18 @@ #include #include // 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 +using IterOfRange = decltype(std::begin(std::declval())); + +} // End detail namespace //===----------------------------------------------------------------------===// // Extra additions to @@ -235,6 +243,90 @@ auto reverse( 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 +class filter_iterator + : public iterator_adaptor_base< + filter_iterator, 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, + typename std::common_type< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category>:: + type>; + + struct PayloadType { + WrappedIteratorT End; + PredicateT Pred; + }; + + Optional 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 + friend iterator_range, 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 +iterator_range, PredicateT>> +make_filter_range(RangeT &&Range, PredicateT Pred) { + using FilterIteratorT = + filter_iterator, PredicateT>; + return make_range(FilterIteratorT(std::begin(std::forward(Range)), + std::end(std::forward(Range)), + std::move(Pred)), + FilterIteratorT(std::end(std::forward(Range)))); +} + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// diff --git a/include/llvm/ADT/iterator.h b/include/llvm/ADT/iterator.h index 2898a677db3..0bd28d5c6cd 100644 --- a/include/llvm/ADT/iterator.h +++ b/include/llvm/ADT/iterator.h @@ -155,7 +155,14 @@ template < typename T = typename std::iterator_traits::value_type, typename DifferenceTypeT = typename std::iterator_traits::difference_type, - typename PointerT = T *, typename ReferenceT = T &, + typename PointerT = typename std::conditional< + std::is_same::value_type>::value, + typename std::iterator_traits::pointer, T *>::type, + typename ReferenceT = typename std::conditional< + std::is_same::value_type>::value, + typename std::iterator_traits::reference, T &>::type, // Don't provide these, they are mostly to act as aliases below. typename WrappedTraitsT = std::iterator_traits> class iterator_adaptor_base @@ -168,15 +175,7 @@ protected: iterator_adaptor_base() = default; - template - explicit iterator_adaptor_base( - U &&u, - typename std::enable_if< - !std::is_base_of::type>::type, - DerivedT>::value, - int>::type = 0) - : I(std::forward(u)) {} + explicit iterator_adaptor_base(WrappedIteratorT u) : I(std::move(u)) {} const WrappedIteratorT &wrapped() const { return I; } diff --git a/include/llvm/Analysis/CallGraph.h b/include/llvm/Analysis/CallGraph.h index 4ecacb0f0be..f37e843fed5 100644 --- a/include/llvm/Analysis/CallGraph.h +++ b/include/llvm/Analysis/CallGraph.h @@ -410,6 +410,7 @@ public: // traversals. template <> struct GraphTraits { typedef CallGraphNode NodeType; + typedef CallGraphNode *NodeRef; typedef CallGraphNode::CallRecord CGNPairTy; typedef std::pointer_to_unary_function @@ -431,6 +432,7 @@ template <> struct GraphTraits { template <> struct GraphTraits { typedef const CallGraphNode NodeType; + typedef const CallGraphNode *NodeRef; typedef CallGraphNode::CallRecord CGNPairTy; typedef std::pointer_to_unary_function diff --git a/include/llvm/CodeGen/MachineBasicBlock.h b/include/llvm/CodeGen/MachineBasicBlock.h index d5f918eb2bb..2923371c100 100644 --- a/include/llvm/CodeGen/MachineBasicBlock.h +++ b/include/llvm/CodeGen/MachineBasicBlock.h @@ -740,6 +740,7 @@ struct MBB2NumberFunctor : template <> struct GraphTraits { typedef MachineBasicBlock NodeType; + typedef MachineBasicBlock *NodeRef; typedef MachineBasicBlock::succ_iterator ChildIteratorType; static NodeType *getEntryNode(MachineBasicBlock *BB) { return BB; } @@ -753,6 +754,7 @@ template <> struct GraphTraits { template <> struct GraphTraits { typedef const MachineBasicBlock NodeType; + typedef const MachineBasicBlock *NodeRef; typedef MachineBasicBlock::const_succ_iterator ChildIteratorType; static NodeType *getEntryNode(const MachineBasicBlock *BB) { return BB; } @@ -772,6 +774,7 @@ template <> struct GraphTraits { // template <> struct GraphTraits > { typedef MachineBasicBlock NodeType; + typedef MachineBasicBlock *NodeRef; typedef MachineBasicBlock::pred_iterator ChildIteratorType; static NodeType *getEntryNode(Inverse G) { return G.Graph; @@ -786,6 +789,7 @@ template <> struct GraphTraits > { template <> struct GraphTraits > { typedef const MachineBasicBlock NodeType; + typedef const MachineBasicBlock *NodeRef; typedef MachineBasicBlock::const_pred_iterator ChildIteratorType; static NodeType *getEntryNode(Inverse G) { return G.Graph; diff --git a/include/llvm/IR/CFG.h b/include/llvm/IR/CFG.h index e9bf09333a2..a256b5960bb 100644 --- a/include/llvm/IR/CFG.h +++ b/include/llvm/IR/CFG.h @@ -155,6 +155,7 @@ struct isPodLike> { template <> struct GraphTraits { typedef BasicBlock NodeType; + typedef BasicBlock *NodeRef; typedef succ_iterator ChildIteratorType; static NodeType *getEntryNode(BasicBlock *BB) { return BB; } @@ -168,6 +169,7 @@ template <> struct GraphTraits { template <> struct GraphTraits { typedef const BasicBlock NodeType; + typedef const BasicBlock *NodeRef; typedef succ_const_iterator ChildIteratorType; static NodeType *getEntryNode(const BasicBlock *BB) { return BB; } @@ -187,6 +189,7 @@ template <> struct GraphTraits { // template <> struct GraphTraits > { typedef BasicBlock NodeType; + typedef BasicBlock *NodeRef; typedef pred_iterator ChildIteratorType; static NodeType *getEntryNode(Inverse G) { return G.Graph; } static inline ChildIteratorType child_begin(NodeType *N) { @@ -199,6 +202,7 @@ template <> struct GraphTraits > { template <> struct GraphTraits > { typedef const BasicBlock NodeType; + typedef const BasicBlock *NodeRef; typedef const_pred_iterator ChildIteratorType; static NodeType *getEntryNode(Inverse G) { return G.Graph; diff --git a/lib/Analysis/BlockFrequencyInfoImpl.cpp b/lib/Analysis/BlockFrequencyInfoImpl.cpp index 90bc249bcb3..c2039e1dec2 100644 --- a/lib/Analysis/BlockFrequencyInfoImpl.cpp +++ b/lib/Analysis/BlockFrequencyInfoImpl.cpp @@ -623,6 +623,7 @@ template <> struct GraphTraits { 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) { diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp index fff54408541..787f4342831 100644 --- a/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/lib/Transforms/IPO/FunctionAttrs.cpp @@ -332,6 +332,7 @@ struct ArgumentUsesTracker : public CaptureTracker { namespace llvm { template <> struct GraphTraits { typedef ArgumentGraphNode NodeType; + typedef ArgumentGraphNode *NodeRef; typedef SmallVectorImpl::iterator ChildIteratorType; static inline NodeType *getEntryNode(NodeType *A) { return A; } diff --git a/lib/Transforms/Vectorize/LoopVectorize.cpp b/lib/Transforms/Vectorize/LoopVectorize.cpp index 8b85e320d3b..ee5733d20f4 100644 --- a/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -50,6 +50,7 @@ #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" @@ -220,6 +221,81 @@ class LoopVectorizationLegality; 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; + + // 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::iterator_category, + NodeRef, std::ptrdiff_t, NodeRef *, NodeRef> { + using BaseT = iterator_adaptor_base< + WrappedSuccIterator, succ_iterator, + typename std::iterator_traits::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; + + static NodeRef getEntryNode(const Loop &G) { return {&G, G.getHeader()}; } + + static ChildIteratorType child_begin(NodeRef Node) { + return make_filter_range(make_range( + {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( + {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::begin(L), + scc_iterator::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 { @@ -1782,12 +1858,14 @@ private: Instruction *UnsafeAlgebraInst; }; -static void addInnerLoop(Loop &L, SmallVectorImpl &V) { - if (L.empty()) - return V.push_back(&L); - +static void addAcyclicInnerLoop(Loop &L, SmallVectorImpl &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. @@ -4395,6 +4473,9 @@ bool LoopVectorizationLegality::canVectorize() { 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"); @@ -6639,7 +6720,7 @@ bool LoopVectorizePass::runImpl( SmallVector Worklist; for (Loop *L : *LI) - addInnerLoop(*L, Worklist); + addAcyclicInnerLoop(*L, Worklist); LoopsAnalyzed += Worklist.size(); diff --git a/test/Transforms/LoopVectorize/pr28541.ll b/test/Transforms/LoopVectorize/pr28541.ll new file mode 100644 index 00000000000..7bb7f0987fd --- /dev/null +++ b/test/Transforms/LoopVectorize/pr28541.ll @@ -0,0 +1,71 @@ +; 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 +} diff --git a/unittests/ADT/SCCIteratorTest.cpp b/unittests/ADT/SCCIteratorTest.cpp index da8c04483f9..597661fd8cc 100644 --- a/unittests/ADT/SCCIteratorTest.cpp +++ b/unittests/ADT/SCCIteratorTest.cpp @@ -230,6 +230,7 @@ public: template struct GraphTraits > { typedef typename Graph::NodeType NodeType; + typedef typename Graph::NodeType *NodeRef; typedef typename Graph::ChildIterator ChildIteratorType; static inline NodeType *getEntryNode(const Graph &G) { return G.AccessNode(0); } diff --git a/unittests/Support/IteratorTest.cpp b/unittests/Support/IteratorTest.cpp index 83848328c0c..63dfa2a9d01 100644 --- a/unittests/Support/IteratorTest.cpp +++ b/unittests/Support/IteratorTest.cpp @@ -16,6 +16,24 @@ using namespace llvm; namespace { +template struct Shadow; + +struct WeirdIter : std::iterator, Shadow<1>, + Shadow<2>, Shadow<3>> {}; + +struct AdaptedIter : iterator_adaptor_base {}; + +// Test that iterator_adaptor_base forwards typedefs, if value_type is +// unchanged. +static_assert(std::is_same>::value, + ""); +static_assert( + std::is_same>::value, ""); +static_assert(std::is_same>::value, + ""); +static_assert(std::is_same>::value, + ""); + TEST(PointeeIteratorTest, Basic) { int arr[4] = { 1, 2, 3, 4 }; SmallVector V; @@ -98,4 +116,73 @@ TEST(PointeeIteratorTest, SmartPointer) { 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 Actual(Range.begin(), Range.end()); + EXPECT_EQ((SmallVector{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 Actual(Range.begin(), Range.end()); + EXPECT_GE(Counter, 7); + EXPECT_EQ((SmallVector{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 Actual(Range.begin(), Range.end()); + EXPECT_EQ((SmallVector{1, 3, 5}), Actual); +} + +TEST(FilterIteratorTest, Composition) { + auto IsOdd = [](int N) { return N % 2 == 1; }; + std::unique_ptr A[] = {make_unique(0), make_unique(1), + make_unique(2), make_unique(3), + make_unique(4), make_unique(5), + make_unique(6)}; + using PointeeIterator = pointee_iterator *>; + auto Range = make_filter_range( + make_range(PointeeIterator(std::begin(A)), PointeeIterator(std::end(A))), + IsOdd); + SmallVector Actual(Range.begin(), Range.end()); + EXPECT_EQ((SmallVector{1, 3, 5}), Actual); +} + +TEST(FilterIteratorTest, InputIterator) { + struct InputIterator + : iterator_adaptor_base { + using BaseT = + iterator_adaptor_base; + + 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 Actual(Range.begin(), Range.end()); + EXPECT_EQ((SmallVector{1, 3, 5}), Actual); +} + } // anonymous namespace -- 2.49.0