Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 14 | pmbaty | 1 | //===- GenericCycleInfo.h - Info for Cycles in any IR ------*- C++ -*------===// | 
| 2 | // | ||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| 4 | // See https://llvm.org/LICENSE.txt for license information. | ||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| 6 | // | ||
| 7 | //===----------------------------------------------------------------------===// | ||
| 8 | /// | ||
| 9 | /// \file | ||
| 10 | /// \brief Find all cycles in a control-flow graph, including irreducible loops. | ||
| 11 | /// | ||
| 12 | /// See docs/CycleTerminology.rst for a formal definition of cycles. | ||
| 13 | /// | ||
| 14 | /// Briefly: | ||
| 15 | /// - A cycle is a generalization of a loop which can represent | ||
| 16 | ///   irreducible control flow. | ||
| 17 | /// - Cycles identified in a program are implementation defined, | ||
| 18 | ///   depending on the DFS traversal chosen. | ||
| 19 | /// - Cycles are well-nested, and form a forest with a parent-child | ||
| 20 | ///   relationship. | ||
| 21 | /// - In any choice of DFS, every natural loop L is represented by a | ||
| 22 | ///   unique cycle C which is a superset of L. | ||
| 23 | /// - In the absence of irreducible control flow, the cycles are | ||
| 24 | ///   exactly the natural loops in the program. | ||
| 25 | /// | ||
| 26 | //===----------------------------------------------------------------------===// | ||
| 27 | |||
| 28 | #ifndef LLVM_ADT_GENERICCYCLEINFO_H | ||
| 29 | #define LLVM_ADT_GENERICCYCLEINFO_H | ||
| 30 | |||
| 31 | #include "llvm/ADT/ArrayRef.h" | ||
| 32 | #include "llvm/ADT/DenseMap.h" | ||
| 33 | #include "llvm/ADT/GenericSSAContext.h" | ||
| 34 | #include "llvm/ADT/GraphTraits.h" | ||
| 35 | #include "llvm/ADT/SmallVector.h" | ||
| 36 | #include "llvm/ADT/iterator.h" | ||
| 37 | #include "llvm/Support/Debug.h" | ||
| 38 | #include "llvm/Support/Printable.h" | ||
| 39 | #include "llvm/Support/raw_ostream.h" | ||
| 40 | #include <vector> | ||
| 41 | |||
| 42 | namespace llvm { | ||
| 43 | |||
| 44 | template <typename ContextT> class GenericCycleInfo; | ||
| 45 | template <typename ContextT> class GenericCycleInfoCompute; | ||
| 46 | |||
| 47 | /// A possibly irreducible generalization of a \ref Loop. | ||
| 48 | template <typename ContextT> class GenericCycle { | ||
| 49 | public: | ||
| 50 | using BlockT = typename ContextT::BlockT; | ||
| 51 | using FunctionT = typename ContextT::FunctionT; | ||
| 52 | template <typename> friend class GenericCycleInfo; | ||
| 53 | template <typename> friend class GenericCycleInfoCompute; | ||
| 54 | |||
| 55 | private: | ||
| 56 |   /// The parent cycle. Is null for the root "cycle". Top-level cycles point | ||
| 57 |   /// at the root. | ||
| 58 | GenericCycle *ParentCycle = nullptr; | ||
| 59 | |||
| 60 |   /// The entry block(s) of the cycle. The header is the only entry if | ||
| 61 |   /// this is a loop. Is empty for the root "cycle", to avoid | ||
| 62 |   /// unnecessary memory use. | ||
| 63 | SmallVector<BlockT *, 1> Entries; | ||
| 64 | |||
| 65 |   /// Child cycles, if any. | ||
| 66 | std::vector<std::unique_ptr<GenericCycle>> Children; | ||
| 67 | |||
| 68 |   /// Basic blocks that are contained in the cycle, including entry blocks, | ||
| 69 |   /// and including blocks that are part of a child cycle. | ||
| 70 | std::vector<BlockT *> Blocks; | ||
| 71 | |||
| 72 |   /// Depth of the cycle in the tree. The root "cycle" is at depth 0. | ||
| 73 |   /// | ||
| 74 |   /// \note Depths are not necessarily contiguous. However, child loops always | ||
| 75 |   ///       have strictly greater depth than their parents, and sibling loops | ||
| 76 |   ///       always have the same depth. | ||
| 77 | unsigned Depth = 0; | ||
| 78 | |||
| 79 | void clear() { | ||
| 80 | Entries.clear(); | ||
| 81 | Children.clear(); | ||
| 82 | Blocks.clear(); | ||
| 83 | Depth = 0; | ||
| 84 | ParentCycle = nullptr; | ||
| 85 |   } | ||
| 86 | |||
| 87 | void appendEntry(BlockT *Block) { Entries.push_back(Block); } | ||
| 88 | void appendBlock(BlockT *Block) { Blocks.push_back(Block); } | ||
| 89 | |||
| 90 | GenericCycle(const GenericCycle &) = delete; | ||
| 91 | GenericCycle &operator=(const GenericCycle &) = delete; | ||
| 92 | GenericCycle(GenericCycle &&Rhs) = delete; | ||
| 93 | GenericCycle &operator=(GenericCycle &&Rhs) = delete; | ||
| 94 | |||
| 95 | public: | ||
| 96 | GenericCycle() = default; | ||
| 97 | |||
| 98 |   /// \brief Whether the cycle is a natural loop. | ||
| 99 | bool isReducible() const { return Entries.size() == 1; } | ||
| 100 | |||
| 101 | BlockT *getHeader() const { return Entries[0]; } | ||
| 102 | |||
| 103 | const SmallVectorImpl<BlockT *> & getEntries() const { | ||
| 104 | return Entries; | ||
| 105 |   } | ||
| 106 | |||
| 107 |   /// \brief Return whether \p Block is an entry block of the cycle. | ||
| 108 | bool isEntry(const BlockT *Block) const { | ||
| 109 | return is_contained(Entries, Block); | ||
| 110 |   } | ||
| 111 | |||
| 112 |   /// \brief Return whether \p Block is contained in the cycle. | ||
| 113 | bool contains(const BlockT *Block) const { | ||
| 114 | return is_contained(Blocks, Block); | ||
| 115 |   } | ||
| 116 | |||
| 117 |   /// \brief Returns true iff this cycle contains \p C. | ||
| 118 |   /// | ||
| 119 |   /// Note: Non-strict containment check, i.e. returns true if C is the | ||
| 120 |   /// same cycle. | ||
| 121 | bool contains(const GenericCycle *C) const; | ||
| 122 | |||
| 123 | const GenericCycle *getParentCycle() const { return ParentCycle; } | ||
| 124 | GenericCycle *getParentCycle() { return ParentCycle; } | ||
| 125 | unsigned getDepth() const { return Depth; } | ||
| 126 | |||
| 127 |   /// Return all of the successor blocks of this cycle. | ||
| 128 |   /// | ||
| 129 |   /// These are the blocks _outside of the current cycle_ which are | ||
| 130 |   /// branched to. | ||
| 131 | void getExitBlocks(SmallVectorImpl<BlockT *> &TmpStorage) const; | ||
| 132 | |||
| 133 |   /// Return the preheader block for this cycle. Pre-header is well-defined for | ||
| 134 |   /// reducible cycle in docs/LoopTerminology.rst as: the only one entering | ||
| 135 |   /// block and its only edge is to the entry block. Return null for irreducible | ||
| 136 |   /// cycles. | ||
| 137 | BlockT *getCyclePreheader() const; | ||
| 138 | |||
| 139 |   /// If the cycle has exactly one entry with exactly one predecessor, return | ||
| 140 |   /// it, otherwise return nullptr. | ||
| 141 | BlockT *getCyclePredecessor() const; | ||
| 142 | |||
| 143 |   /// Iteration over child cycles. | ||
| 144 |   //@{ | ||
| 145 | using const_child_iterator_base = | ||
| 146 | typename std::vector<std::unique_ptr<GenericCycle>>::const_iterator; | ||
| 147 |   struct const_child_iterator | ||
| 148 | : iterator_adaptor_base<const_child_iterator, const_child_iterator_base> { | ||
| 149 | using Base = | ||
| 150 | iterator_adaptor_base<const_child_iterator, const_child_iterator_base>; | ||
| 151 | |||
| 152 | const_child_iterator() = default; | ||
| 153 | explicit const_child_iterator(const_child_iterator_base I) : Base(I) {} | ||
| 154 | |||
| 155 | const const_child_iterator_base &wrapped() { return Base::wrapped(); } | ||
| 156 | GenericCycle *operator*() const { return Base::I->get(); } | ||
| 157 | }; | ||
| 158 | |||
| 159 | const_child_iterator child_begin() const { | ||
| 160 | return const_child_iterator{Children.begin()}; | ||
| 161 |   } | ||
| 162 | const_child_iterator child_end() const { | ||
| 163 | return const_child_iterator{Children.end()}; | ||
| 164 |   } | ||
| 165 | size_t getNumChildren() const { return Children.size(); } | ||
| 166 | iterator_range<const_child_iterator> children() const { | ||
| 167 | return llvm::make_range(const_child_iterator{Children.begin()}, | ||
| 168 | const_child_iterator{Children.end()}); | ||
| 169 |   } | ||
| 170 |   //@} | ||
| 171 | |||
| 172 |   /// Iteration over blocks in the cycle (including entry blocks). | ||
| 173 |   //@{ | ||
| 174 | using const_block_iterator = typename std::vector<BlockT *>::const_iterator; | ||
| 175 | |||
| 176 | const_block_iterator block_begin() const { | ||
| 177 | return const_block_iterator{Blocks.begin()}; | ||
| 178 |   } | ||
| 179 | const_block_iterator block_end() const { | ||
| 180 | return const_block_iterator{Blocks.end()}; | ||
| 181 |   } | ||
| 182 | size_t getNumBlocks() const { return Blocks.size(); } | ||
| 183 | iterator_range<const_block_iterator> blocks() const { | ||
| 184 | return llvm::make_range(block_begin(), block_end()); | ||
| 185 |   } | ||
| 186 |   //@} | ||
| 187 | |||
| 188 |   /// Iteration over entry blocks. | ||
| 189 |   //@{ | ||
| 190 | using const_entry_iterator = | ||
| 191 | typename SmallVectorImpl<BlockT *>::const_iterator; | ||
| 192 | |||
| 193 | size_t getNumEntries() const { return Entries.size(); } | ||
| 194 | iterator_range<const_entry_iterator> entries() const { | ||
| 195 | return llvm::make_range(Entries.begin(), Entries.end()); | ||
| 196 |   } | ||
| 197 |   //@} | ||
| 198 | |||
| 199 | Printable printEntries(const ContextT &Ctx) const { | ||
| 200 | return Printable([this, &Ctx](raw_ostream &Out) { | ||
| 201 | bool First = true; | ||
| 202 | for (auto *Entry : Entries) { | ||
| 203 | if (!First) | ||
| 204 | Out << ' '; | ||
| 205 | First = false; | ||
| 206 | Out << Ctx.print(Entry); | ||
| 207 |       } | ||
| 208 | }); | ||
| 209 |   } | ||
| 210 | |||
| 211 | Printable print(const ContextT &Ctx) const { | ||
| 212 | return Printable([this, &Ctx](raw_ostream &Out) { | ||
| 213 | Out << "depth=" << Depth << ": entries(" << printEntries(Ctx) << ')'; | ||
| 214 | |||
| 215 | for (auto *Block : Blocks) { | ||
| 216 | if (isEntry(Block)) | ||
| 217 | continue; | ||
| 218 | |||
| 219 | Out << ' ' << Ctx.print(Block); | ||
| 220 |       } | ||
| 221 | }); | ||
| 222 |   } | ||
| 223 | }; | ||
| 224 | |||
| 225 | /// \brief Cycle information for a function. | ||
| 226 | template <typename ContextT> class GenericCycleInfo { | ||
| 227 | public: | ||
| 228 | using BlockT = typename ContextT::BlockT; | ||
| 229 | using CycleT = GenericCycle<ContextT>; | ||
| 230 | using FunctionT = typename ContextT::FunctionT; | ||
| 231 | template <typename> friend class GenericCycle; | ||
| 232 | template <typename> friend class GenericCycleInfoCompute; | ||
| 233 | |||
| 234 | private: | ||
| 235 |   ContextT Context; | ||
| 236 | |||
| 237 |   /// Map basic blocks to their inner-most containing cycle. | ||
| 238 | DenseMap<BlockT *, CycleT *> BlockMap; | ||
| 239 | |||
| 240 |   /// Map basic blocks to their top level containing cycle. | ||
| 241 | DenseMap<BlockT *, CycleT *> BlockMapTopLevel; | ||
| 242 | |||
| 243 |   /// Top-level cycles discovered by any DFS. | ||
| 244 |   /// | ||
| 245 |   /// Note: The implementation treats the nullptr as the parent of | ||
| 246 |   /// every top-level cycle. See \ref contains for an example. | ||
| 247 | std::vector<std::unique_ptr<CycleT>> TopLevelCycles; | ||
| 248 | |||
| 249 |   /// Move \p Child to \p NewParent by manipulating Children vectors. | ||
| 250 |   /// | ||
| 251 |   /// Note: This is an incomplete operation that does not update the depth of | ||
| 252 |   /// the subtree. | ||
| 253 | void moveTopLevelCycleToNewParent(CycleT *NewParent, CycleT *Child); | ||
| 254 | |||
| 255 | public: | ||
| 256 | GenericCycleInfo() = default; | ||
| 257 | GenericCycleInfo(GenericCycleInfo &&) = default; | ||
| 258 | GenericCycleInfo &operator=(GenericCycleInfo &&) = default; | ||
| 259 | |||
| 260 | void clear(); | ||
| 261 | void compute(FunctionT &F); | ||
| 262 | |||
| 263 | FunctionT *getFunction() const { return Context.getFunction(); } | ||
| 264 | const ContextT &getSSAContext() const { return Context; } | ||
| 265 | |||
| 266 | CycleT *getCycle(const BlockT *Block) const; | ||
| 267 | unsigned getCycleDepth(const BlockT *Block) const; | ||
| 268 | CycleT *getTopLevelParentCycle(BlockT *Block); | ||
| 269 | |||
| 270 |   /// Methods for debug and self-test. | ||
| 271 |   //@{ | ||
| 272 | #ifndef NDEBUG | ||
| 273 | bool validateTree() const; | ||
| 274 | #endif | ||
| 275 | void print(raw_ostream &Out) const; | ||
| 276 | void dump() const { print(dbgs()); } | ||
| 277 |   //@} | ||
| 278 | |||
| 279 |   /// Iteration over top-level cycles. | ||
| 280 |   //@{ | ||
| 281 | using const_toplevel_iterator_base = | ||
| 282 | typename std::vector<std::unique_ptr<CycleT>>::const_iterator; | ||
| 283 |   struct const_toplevel_iterator | ||
| 284 | : iterator_adaptor_base<const_toplevel_iterator, | ||
| 285 | const_toplevel_iterator_base> { | ||
| 286 | using Base = iterator_adaptor_base<const_toplevel_iterator, | ||
| 287 | const_toplevel_iterator_base>; | ||
| 288 | |||
| 289 | const_toplevel_iterator() = default; | ||
| 290 | explicit const_toplevel_iterator(const_toplevel_iterator_base I) | ||
| 291 | : Base(I) {} | ||
| 292 | |||
| 293 | const const_toplevel_iterator_base &wrapped() { return Base::wrapped(); } | ||
| 294 | CycleT *operator*() const { return Base::I->get(); } | ||
| 295 | }; | ||
| 296 | |||
| 297 | const_toplevel_iterator toplevel_begin() const { | ||
| 298 | return const_toplevel_iterator{TopLevelCycles.begin()}; | ||
| 299 |   } | ||
| 300 | const_toplevel_iterator toplevel_end() const { | ||
| 301 | return const_toplevel_iterator{TopLevelCycles.end()}; | ||
| 302 |   } | ||
| 303 | |||
| 304 | iterator_range<const_toplevel_iterator> toplevel_cycles() const { | ||
| 305 | return llvm::make_range(const_toplevel_iterator{TopLevelCycles.begin()}, | ||
| 306 | const_toplevel_iterator{TopLevelCycles.end()}); | ||
| 307 |   } | ||
| 308 |   //@} | ||
| 309 | }; | ||
| 310 | |||
| 311 | /// \brief GraphTraits for iterating over a sub-tree of the CycleT tree. | ||
| 312 | template <typename CycleRefT, typename ChildIteratorT> struct CycleGraphTraits { | ||
| 313 | using NodeRef = CycleRefT; | ||
| 314 | |||
| 315 | using nodes_iterator = ChildIteratorT; | ||
| 316 | using ChildIteratorType = nodes_iterator; | ||
| 317 | |||
| 318 | static NodeRef getEntryNode(NodeRef Graph) { return Graph; } | ||
| 319 | |||
| 320 | static ChildIteratorType child_begin(NodeRef Ref) { | ||
| 321 | return Ref->child_begin(); | ||
| 322 |   } | ||
| 323 | static ChildIteratorType child_end(NodeRef Ref) { return Ref->child_end(); } | ||
| 324 | |||
| 325 |   // Not implemented: | ||
| 326 |   // static nodes_iterator nodes_begin(GraphType *G) | ||
| 327 |   // static nodes_iterator nodes_end  (GraphType *G) | ||
| 328 |   //    nodes_iterator/begin/end - Allow iteration over all nodes in the graph | ||
| 329 | |||
| 330 |   // typedef EdgeRef           - Type of Edge token in the graph, which should | ||
| 331 |   //                             be cheap to copy. | ||
| 332 |   // typedef ChildEdgeIteratorType - Type used to iterate over children edges in | ||
| 333 |   //                             graph, dereference to a EdgeRef. | ||
| 334 | |||
| 335 |   // static ChildEdgeIteratorType child_edge_begin(NodeRef) | ||
| 336 |   // static ChildEdgeIteratorType child_edge_end(NodeRef) | ||
| 337 |   //     Return iterators that point to the beginning and ending of the | ||
| 338 |   //     edge list for the given callgraph node. | ||
| 339 |   // | ||
| 340 |   // static NodeRef edge_dest(EdgeRef) | ||
| 341 |   //     Return the destination node of an edge. | ||
| 342 |   // static unsigned       size       (GraphType *G) | ||
| 343 |   //    Return total number of nodes in the graph | ||
| 344 | }; | ||
| 345 | |||
| 346 | template <typename BlockT> | ||
| 347 | struct GraphTraits<const GenericCycle<BlockT> *> | ||
| 348 | : CycleGraphTraits<const GenericCycle<BlockT> *, | ||
| 349 | typename GenericCycle<BlockT>::const_child_iterator> {}; | ||
| 350 | template <typename BlockT> | ||
| 351 | struct GraphTraits<GenericCycle<BlockT> *> | ||
| 352 | : CycleGraphTraits<GenericCycle<BlockT> *, | ||
| 353 | typename GenericCycle<BlockT>::const_child_iterator> {}; | ||
| 354 | |||
| 355 | } // namespace llvm | ||
| 356 | |||
| 357 | #endif // LLVM_ADT_GENERICCYCLEINFO_H |