Jlm
Worklist.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 #ifndef JLM_UTIL_WORKLIST_HPP
6 #define JLM_UTIL_WORKLIST_HPP
7 
8 #include "common.hpp"
9 #include "HashSet.hpp"
10 
11 #include <algorithm>
12 #include <limits>
13 #include <queue>
14 #include <stack>
15 #include <unordered_map>
16 #include <vector>
17 
18 namespace jlm::util
19 {
20 
26 template<typename T>
27 class Worklist
28 {
29 public:
30  virtual ~Worklist() = default;
31 
32  Worklist() = default;
33 
34  Worklist(const Worklist & other) = delete;
35 
36  Worklist(Worklist && other) = default;
37 
38  Worklist &
39  operator=(const Worklist & other) = delete;
40 
41  Worklist &
42  operator=(Worklist && other) = default;
43 
47  [[nodiscard]] virtual bool
48  HasMoreWorkItems() const noexcept = 0;
49 
55  [[nodiscard]] virtual T
56  PopWorkItem() = 0;
57 
63  virtual void
64  PushWorkItem(T item) = 0;
65 };
66 
73 template<typename T>
74 class LifoWorklist final : public Worklist<T>
75 {
76 public:
77  ~LifoWorklist() override = default;
78 
79  LifoWorklist() = default;
80 
81  [[nodiscard]] bool
82  HasMoreWorkItems() const noexcept override
83  {
84  return !WorkItems_.empty();
85  }
86 
87  T
88  PopWorkItem() override
89  {
91  T item = WorkItems_.top();
92  WorkItems_.pop();
93  OnList_.Remove(item);
94  return item;
95  }
96 
97  void
98  PushWorkItem(T item) override
99  {
100  if (OnList_.insert(item))
101  WorkItems_.push(item);
102  }
103 
104 private:
105  // Tracking which work items are already on the list
107 
108  // The stack used to order the work items
109  std::stack<T> WorkItems_;
110 };
111 
118 template<typename T>
119 class FifoWorklist final : public Worklist<T>
120 {
121 public:
122  ~FifoWorklist() override = default;
123 
124  FifoWorklist() = default;
125 
126  [[nodiscard]] bool
127  HasMoreWorkItems() const noexcept override
128  {
129  return !WorkItems_.empty();
130  }
131 
132  T
133  PopWorkItem() override
134  {
136  T item = WorkItems_.front();
137  WorkItems_.pop();
138  OnList_.Remove(item);
139  return item;
140  }
141 
142  void
143  PushWorkItem(T item) override
144  {
145  if (OnList_.insert(item))
146  WorkItems_.push(item);
147  }
148 
149 private:
150  // Tracking which work items are already on the list
152 
153  // The queue used to order the items
154  std::queue<T> WorkItems_;
155 };
156 
168 template<typename T>
169 class LrfWorklist final : public Worklist<T>
170 {
171 public:
172  ~LrfWorklist() override = default;
173 
174  LrfWorklist() = default;
175 
176  [[nodiscard]] bool
177  HasMoreWorkItems() const noexcept override
178  {
179  return !WorkItems_.empty();
180  }
181 
182  T
183  PopWorkItem() override
184  {
186  auto [_, item] = WorkItems_.top();
187  WorkItems_.pop();
188  // Note down the moment this item fired
189  LastFire_[item] = ++FireCounter_;
190  return item;
191  }
192 
193  void
194  PushWorkItem(T item) override
195  {
196  size_t & lastFire = LastFire_[item];
197  if (lastFire == InQueueSentinelValue)
198  return;
199 
200  // Add the work item to the priority queue based on when it was last fired
201  WorkItems_.push({ lastFire, item });
202  lastFire = InQueueSentinelValue;
203  }
204 
205 private:
206  // If a work item is currently in the queue, its "last fire" is set to this value
207  static inline const size_t InQueueSentinelValue = std::numeric_limits<size_t>::max();
208 
209  // The type of a priority queue giving the highest priority to the lowest value
210  template<typename U>
211  using MinPriorityQueue = std::priority_queue<U, std::vector<U>, std::greater<U>>;
212 
213  // The priority queue used to order the items
215 
216  // Counter used to order fire events (work items being popped)
217  size_t FireCounter_ = 0;
218 
219  // For each work item, when the item was last fired.
220  // If the work item is currently in the queue, InQueueSentinelValue is used instead
221  std::unordered_map<T, size_t> LastFire_;
222 };
223 
235 template<typename T>
236 class TwoPhaseLrfWorklist final : public Worklist<T>
237 {
238 public:
239  ~TwoPhaseLrfWorklist() override = default;
240 
241  TwoPhaseLrfWorklist() = default;
242 
243  [[nodiscard]] bool
244  HasMoreWorkItems() const noexcept override
245  {
246  return !Current_.empty() || !Next_.empty();
247  }
248 
249  T
250  PopWorkItem() override
251  {
252  if (Current_.empty())
253  {
254  JLM_ASSERT(!Next_.empty());
255  std::swap(Current_, Next_);
256  std::sort(Current_.rbegin(), Current_.rend()); // The least recently first work item gets last
257  }
258 
259  auto [_, item] = Current_.back();
260  Current_.pop_back();
261  // Note down the moment this item fired
262  LastFire_[item] = ++FireCounter_;
263  return item;
264  }
265 
266  void
267  PushWorkItem(T item) override
268  {
269  size_t & lastFire = LastFire_[item];
270  if (lastFire == InQueueSentinelValue)
271  return;
272 
273  // Add the work item to the next list, with its last fire time used for sorting
274  Next_.push_back({ lastFire, item });
275  lastFire = InQueueSentinelValue;
276  }
277 
278 private:
279  // If a work item is currently in the queue, its "last fire" is set to this value
280  static inline const size_t InQueueSentinelValue = std::numeric_limits<size_t>::max();
281 
282  // The current list being popped. The pair's first is when the work item was last fired
283  std::vector<std::pair<size_t, T>> Current_;
284 
285  // The next list. The pair's first is when the work item was last fired
286  // When current is empty, this list is sorted by least recently fired, and becomes the new current
287  std::vector<std::pair<size_t, T>> Next_;
288 
289  // Counter used to order fire events (work items being popped)
290  size_t FireCounter_ = 0;
291 
292  // For each work item, when the item was last fired.
293  // If the work item is currently in the queue, InQueueSentinelValue is used instead
294  std::unordered_map<T, size_t> LastFire_;
295 };
296 
305 template<typename T>
306 class Workset final : public Worklist<T>
307 {
308 public:
309  ~Workset() override = default;
310 
311  Workset() = default;
312 
313  [[nodiscard]] bool
314  HasMoreWorkItems() const noexcept override
315  {
316  return !PushedItems_.IsEmpty();
317  }
318 
319  T
320  PopWorkItem() override
321  {
322  JLM_UNREACHABLE("The Workset does not provide an iteration order");
323  }
324 
325  void
326  PushWorkItem(T item) override
327  {
328  PushedItems_.insert(item);
329  }
330 
331  [[nodiscard]] bool
332  HasWorkItem(T item) const noexcept
333  {
334  return PushedItems_.Contains(item);
335  }
336 
337  void
339  {
340  PushedItems_.Remove(item);
341  }
342 
343 private:
345 };
346 
347 }
348 
349 #endif // JLM_UTIL_WORKLIST_HPP
void PushWorkItem(T item) override
Definition: Worklist.hpp:143
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:127
T PopWorkItem() override
Definition: Worklist.hpp:133
util::HashSet< T > OnList_
Definition: Worklist.hpp:151
std::queue< T > WorkItems_
Definition: Worklist.hpp:154
~FifoWorklist() override=default
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:82
util::HashSet< T > OnList_
Definition: Worklist.hpp:106
~LifoWorklist() override=default
T PopWorkItem() override
Definition: Worklist.hpp:88
void PushWorkItem(T item) override
Definition: Worklist.hpp:98
std::stack< T > WorkItems_
Definition: Worklist.hpp:109
MinPriorityQueue< std::pair< size_t, T > > WorkItems_
Definition: Worklist.hpp:214
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:177
std::unordered_map< T, size_t > LastFire_
Definition: Worklist.hpp:221
T PopWorkItem() override
Definition: Worklist.hpp:183
std::priority_queue< U, std::vector< U >, std::greater< U > > MinPriorityQueue
Definition: Worklist.hpp:211
~LrfWorklist() override=default
void PushWorkItem(T item) override
Definition: Worklist.hpp:194
std::vector< std::pair< size_t, T > > Current_
Definition: Worklist.hpp:283
void PushWorkItem(T item) override
Definition: Worklist.hpp:267
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:244
~TwoPhaseLrfWorklist() override=default
std::unordered_map< T, size_t > LastFire_
Definition: Worklist.hpp:294
std::vector< std::pair< size_t, T > > Next_
Definition: Worklist.hpp:287
virtual ~Worklist()=default
virtual T PopWorkItem()=0
virtual bool HasMoreWorkItems() const noexcept=0
Worklist(const Worklist &other)=delete
Worklist(Worklist &&other)=default
Worklist & operator=(Worklist &&other)=default
Worklist & operator=(const Worklist &other)=delete
virtual void PushWorkItem(T item)=0
util::HashSet< T > PushedItems_
Definition: Worklist.hpp:344
~Workset() override=default
bool HasWorkItem(T item) const noexcept
Definition: Worklist.hpp:332
bool HasMoreWorkItems() const noexcept override
Definition: Worklist.hpp:314
T PopWorkItem() override
Definition: Worklist.hpp:320
void RemoveWorkItem(T item)
Definition: Worklist.hpp:338
void PushWorkItem(T item) override
Definition: Worklist.hpp:326
#define JLM_ASSERT(x)
Definition: common.hpp:16
#define JLM_UNREACHABLE(msg)
Definition: common.hpp:43