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   LLVM_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
265