Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | //===- PriorityWorklist.h - Worklist with insertion priority ----*- 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 | /// |
||
| 11 | /// This file provides a priority worklist. See the class comments for details. |
||
| 12 | /// |
||
| 13 | //===----------------------------------------------------------------------===// |
||
| 14 | |||
| 15 | #ifndef LLVM_ADT_PRIORITYWORKLIST_H |
||
| 16 | #define LLVM_ADT_PRIORITYWORKLIST_H |
||
| 17 | |||
| 18 | #include "llvm/ADT/DenseMap.h" |
||
| 19 | #include "llvm/ADT/STLExtras.h" |
||
| 20 | #include "llvm/ADT/SmallVector.h" |
||
| 21 | #include "llvm/Support/Compiler.h" |
||
| 22 | #include <cassert> |
||
| 23 | #include <cstddef> |
||
| 24 | #include <iterator> |
||
| 25 | #include <type_traits> |
||
| 26 | #include <vector> |
||
| 27 | |||
| 28 | namespace llvm { |
||
| 29 | |||
| 30 | /// A FILO worklist that prioritizes on re-insertion without duplication. |
||
| 31 | /// |
||
| 32 | /// This is very similar to a \c SetVector with the primary difference that |
||
| 33 | /// while re-insertion does not create a duplicate, it does adjust the |
||
| 34 | /// visitation order to respect the last insertion point. This can be useful |
||
| 35 | /// when the visit order needs to be prioritized based on insertion point |
||
| 36 | /// without actually having duplicate visits. |
||
| 37 | /// |
||
| 38 | /// Note that this doesn't prevent re-insertion of elements which have been |
||
| 39 | /// visited -- if you need to break cycles, a set will still be necessary. |
||
| 40 | /// |
||
| 41 | /// The type \c T must be default constructable to a null value that will be |
||
| 42 | /// ignored. It is an error to insert such a value, and popping elements will |
||
| 43 | /// never produce such a value. It is expected to be used with common nullable |
||
| 44 | /// types like pointers or optionals. |
||
| 45 | /// |
||
| 46 | /// Internally this uses a vector to store the worklist and a map to identify |
||
| 47 | /// existing elements in the worklist. Both of these may be customized, but the |
||
| 48 | /// map must support the basic DenseMap API for mapping from a T to an integer |
||
| 49 | /// index into the vector. |
||
| 50 | /// |
||
| 51 | /// A partial specialization is provided to automatically select a SmallVector |
||
| 52 | /// and a SmallDenseMap if custom data structures are not provided. |
||
| 53 | template <typename T, typename VectorT = std::vector<T>, |
||
| 54 | typename MapT = DenseMap<T, ptrdiff_t>> |
||
| 55 | class PriorityWorklist { |
||
| 56 | public: |
||
| 57 | using value_type = T; |
||
| 58 | using key_type = T; |
||
| 59 | using reference = T&; |
||
| 60 | using const_reference = const T&; |
||
| 61 | using size_type = typename MapT::size_type; |
||
| 62 | |||
| 63 | /// Construct an empty PriorityWorklist |
||
| 64 | PriorityWorklist() = default; |
||
| 65 | |||
| 66 | /// Determine if the PriorityWorklist is empty or not. |
||
| 67 | bool empty() const { |
||
| 68 | return V.empty(); |
||
| 69 | } |
||
| 70 | |||
| 71 | /// Returns the number of elements in the worklist. |
||
| 72 | size_type size() const { |
||
| 73 | return M.size(); |
||
| 74 | } |
||
| 75 | |||
| 76 | /// Count the number of elements of a given key in the PriorityWorklist. |
||
| 77 | /// \returns 0 if the element is not in the PriorityWorklist, 1 if it is. |
||
| 78 | size_type count(const key_type &key) const { |
||
| 79 | return M.count(key); |
||
| 80 | } |
||
| 81 | |||
| 82 | /// Return the last element of the PriorityWorklist. |
||
| 83 | const T &back() const { |
||
| 84 | assert(!empty() && "Cannot call back() on empty PriorityWorklist!"); |
||
| 85 | return V.back(); |
||
| 86 | } |
||
| 87 | |||
| 88 | /// Insert a new element into the PriorityWorklist. |
||
| 89 | /// \returns true if the element was inserted into the PriorityWorklist. |
||
| 90 | bool insert(const T &X) { |
||
| 91 | assert(X != T() && "Cannot insert a null (default constructed) value!"); |
||
| 92 | auto InsertResult = M.insert({X, V.size()}); |
||
| 93 | if (InsertResult.second) { |
||
| 94 | // Fresh value, just append it to the vector. |
||
| 95 | V.push_back(X); |
||
| 96 | return true; |
||
| 97 | } |
||
| 98 | |||
| 99 | auto &Index = InsertResult.first->second; |
||
| 100 | assert(V[Index] == X && "Value not actually at index in map!"); |
||
| 101 | if (Index != (ptrdiff_t)(V.size() - 1)) { |
||
| 102 | // If the element isn't at the back, null it out and append a fresh one. |
||
| 103 | V[Index] = T(); |
||
| 104 | Index = (ptrdiff_t)V.size(); |
||
| 105 | V.push_back(X); |
||
| 106 | } |
||
| 107 | return false; |
||
| 108 | } |
||
| 109 | |||
| 110 | /// Insert a sequence of new elements into the PriorityWorklist. |
||
| 111 | template <typename SequenceT> |
||
| 112 | std::enable_if_t<!std::is_convertible<SequenceT, T>::value> |
||
| 113 | insert(SequenceT &&Input) { |
||
| 114 | if (std::begin(Input) == std::end(Input)) |
||
| 115 | // Nothing to do for an empty input sequence. |
||
| 116 | return; |
||
| 117 | |||
| 118 | // First pull the input sequence into the vector as a bulk append |
||
| 119 | // operation. |
||
| 120 | ptrdiff_t StartIndex = V.size(); |
||
| 121 | V.insert(V.end(), std::begin(Input), std::end(Input)); |
||
| 122 | // Now walk backwards fixing up the index map and deleting any duplicates. |
||
| 123 | for (ptrdiff_t i = V.size() - 1; i >= StartIndex; --i) { |
||
| 124 | auto InsertResult = M.insert({V[i], i}); |
||
| 125 | if (InsertResult.second) |
||
| 126 | continue; |
||
| 127 | |||
| 128 | // If the existing index is before this insert's start, nuke that one and |
||
| 129 | // move it up. |
||
| 130 | ptrdiff_t &Index = InsertResult.first->second; |
||
| 131 | if (Index < StartIndex) { |
||
| 132 | V[Index] = T(); |
||
| 133 | Index = i; |
||
| 134 | continue; |
||
| 135 | } |
||
| 136 | |||
| 137 | // Otherwise the existing one comes first so just clear out the value in |
||
| 138 | // this slot. |
||
| 139 | V[i] = T(); |
||
| 140 | } |
||
| 141 | } |
||
| 142 | |||
| 143 | /// Remove the last element of the PriorityWorklist. |
||
| 144 | void pop_back() { |
||
| 145 | assert(!empty() && "Cannot remove an element when empty!"); |
||
| 146 | assert(back() != T() && "Cannot have a null element at the back!"); |
||
| 147 | M.erase(back()); |
||
| 148 | do { |
||
| 149 | V.pop_back(); |
||
| 150 | } while (!V.empty() && V.back() == T()); |
||
| 151 | } |
||
| 152 | |||
| 153 | [[nodiscard]] T pop_back_val() { |
||
| 154 | T Ret = back(); |
||
| 155 | pop_back(); |
||
| 156 | return Ret; |
||
| 157 | } |
||
| 158 | |||
| 159 | /// Erase an item from the worklist. |
||
| 160 | /// |
||
| 161 | /// Note that this is constant time due to the nature of the worklist implementation. |
||
| 162 | bool erase(const T& X) { |
||
| 163 | auto I = M.find(X); |
||
| 164 | if (I == M.end()) |
||
| 165 | return false; |
||
| 166 | |||
| 167 | assert(V[I->second] == X && "Value not actually at index in map!"); |
||
| 168 | if (I->second == (ptrdiff_t)(V.size() - 1)) { |
||
| 169 | do { |
||
| 170 | V.pop_back(); |
||
| 171 | } while (!V.empty() && V.back() == T()); |
||
| 172 | } else { |
||
| 173 | V[I->second] = T(); |
||
| 174 | } |
||
| 175 | M.erase(I); |
||
| 176 | return true; |
||
| 177 | } |
||
| 178 | |||
| 179 | /// Erase items from the set vector based on a predicate function. |
||
| 180 | /// |
||
| 181 | /// This is intended to be equivalent to the following code, if we could |
||
| 182 | /// write it: |
||
| 183 | /// |
||
| 184 | /// \code |
||
| 185 | /// V.erase(remove_if(V, P), V.end()); |
||
| 186 | /// \endcode |
||
| 187 | /// |
||
| 188 | /// However, PriorityWorklist doesn't expose non-const iterators, making any |
||
| 189 | /// algorithm like remove_if impossible to use. |
||
| 190 | /// |
||
| 191 | /// \returns true if any element is removed. |
||
| 192 | template <typename UnaryPredicate> |
||
| 193 | bool erase_if(UnaryPredicate P) { |
||
| 194 | typename VectorT::iterator E = |
||
| 195 | remove_if(V, TestAndEraseFromMap<UnaryPredicate>(P, M)); |
||
| 196 | if (E == V.end()) |
||
| 197 | return false; |
||
| 198 | for (auto I = V.begin(); I != E; ++I) |
||
| 199 | if (*I != T()) |
||
| 200 | M[*I] = I - V.begin(); |
||
| 201 | V.erase(E, V.end()); |
||
| 202 | return true; |
||
| 203 | } |
||
| 204 | |||
| 205 | /// Reverse the items in the PriorityWorklist. |
||
| 206 | /// |
||
| 207 | /// This does an in-place reversal. Other kinds of reverse aren't easy to |
||
| 208 | /// support in the face of the worklist semantics. |
||
| 209 | |||
| 210 | /// Completely clear the PriorityWorklist |
||
| 211 | void clear() { |
||
| 212 | M.clear(); |
||
| 213 | V.clear(); |
||
| 214 | } |
||
| 215 | |||
| 216 | private: |
||
| 217 | /// A wrapper predicate designed for use with std::remove_if. |
||
| 218 | /// |
||
| 219 | /// This predicate wraps a predicate suitable for use with std::remove_if to |
||
| 220 | /// call M.erase(x) on each element which is slated for removal. This just |
||
| 221 | /// allows the predicate to be move only which we can't do with lambdas |
||
| 222 | /// today. |
||
| 223 | template <typename UnaryPredicateT> |
||
| 224 | class TestAndEraseFromMap { |
||
| 225 | UnaryPredicateT P; |
||
| 226 | MapT &M; |
||
| 227 | |||
| 228 | public: |
||
| 229 | TestAndEraseFromMap(UnaryPredicateT P, MapT &M) |
||
| 230 | : P(std::move(P)), M(M) {} |
||
| 231 | |||
| 232 | bool operator()(const T &Arg) { |
||
| 233 | if (Arg == T()) |
||
| 234 | // Skip null values in the PriorityWorklist. |
||
| 235 | return false; |
||
| 236 | |||
| 237 | if (P(Arg)) { |
||
| 238 | M.erase(Arg); |
||
| 239 | return true; |
||
| 240 | } |
||
| 241 | return false; |
||
| 242 | } |
||
| 243 | }; |
||
| 244 | |||
| 245 | /// The map from value to index in the vector. |
||
| 246 | MapT M; |
||
| 247 | |||
| 248 | /// The vector of elements in insertion order. |
||
| 249 | VectorT V; |
||
| 250 | }; |
||
| 251 | |||
| 252 | /// A version of \c PriorityWorklist that selects small size optimized data |
||
| 253 | /// structures for the vector and map. |
||
| 254 | template <typename T, unsigned N> |
||
| 255 | class SmallPriorityWorklist |
||
| 256 | : public PriorityWorklist<T, SmallVector<T, N>, |
||
| 257 | SmallDenseMap<T, ptrdiff_t>> { |
||
| 258 | public: |
||
| 259 | SmallPriorityWorklist() = default; |
||
| 260 | }; |
||
| 261 | |||
| 262 | } // end namespace llvm |
||
| 263 | |||
| 264 | #endif // LLVM_ADT_PRIORITYWORKLIST_H |