blob: f59f46e3bad590a55b23ba71732a95f6e2a864d9 [file] [log] [blame]
Jim Stichnothbbca7542015-02-11 16:08:31 -08001//===- subzero/src/IceThreading.h - Threading functions ---------*- C++ -*-===//
2//
3// The Subzero Code Generator
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
Andrew Scull9612d322015-07-06 14:53:25 -07009///
10/// \file
11/// This file declares threading-related functions.
12///
Jim Stichnothbbca7542015-02-11 16:08:31 -080013//===----------------------------------------------------------------------===//
14
15#ifndef SUBZERO_SRC_ICETHREADING_H
16#define SUBZERO_SRC_ICETHREADING_H
17
John Porto67f8de92015-06-25 10:14:17 -070018#include "IceDefs.h"
19
Jim Stichnothbbca7542015-02-11 16:08:31 -080020#include <condition_variable>
21#include <mutex>
22
Jim Stichnothbbca7542015-02-11 16:08:31 -080023namespace Ice {
24
Andrew Scull9612d322015-07-06 14:53:25 -070025/// BoundedProducerConsumerQueue is a work queue that allows multiple
26/// producers and multiple consumers. A producer adds entries using
27/// blockingPush(), and may block if the queue is "full". A producer
28/// uses notifyEnd() to indicate that no more entries will be added. A
29/// consumer removes an item using blockingPop(), which will return
30/// nullptr if notifyEnd() has been called and the queue is empty (it
31/// never returns nullptr if the queue contained any items).
32///
33/// The MaxSize ctor arg controls the maximum size the queue can grow
34/// to (subject to a hard limit of MaxStaticSize-1). The Sequential
35/// arg indicates purely sequential execution in which the single
36/// thread should never wait().
37///
38/// Two condition variables are used in the implementation.
39/// GrewOrEnded signals a waiting worker that a producer has changed
40/// the state of the queue. Shrunk signals a blocked producer that a
41/// consumer has changed the state of the queue.
42///
43/// The methods begin with Sequential-specific code to be most clear.
44/// The lock and condition variables are not used in the Sequential
45/// case.
46///
47/// Internally, the queue is implemented as a circular array of size
48/// MaxStaticSize, where the queue boundaries are denoted by the Front
49/// and Back fields. Front==Back indicates an empty queue.
Jim Stichnothbbca7542015-02-11 16:08:31 -080050template <typename T, size_t MaxStaticSize = 128>
51class BoundedProducerConsumerQueue {
52 BoundedProducerConsumerQueue() = delete;
53 BoundedProducerConsumerQueue(const BoundedProducerConsumerQueue &) = delete;
54 BoundedProducerConsumerQueue &
55 operator=(const BoundedProducerConsumerQueue &) = delete;
56
57public:
58 BoundedProducerConsumerQueue(bool Sequential, size_t MaxSize = MaxStaticSize)
Jim Stichnotheafb56c2015-06-22 10:35:22 -070059 : MaxSize(std::min(MaxSize, MaxStaticSize)), Sequential(Sequential) {}
Jim Stichnothbbca7542015-02-11 16:08:31 -080060 void blockingPush(T *Item) {
61 {
62 std::unique_lock<GlobalLockType> L(Lock);
63 // If the work queue is already "full", wait for a consumer to
64 // grab an element and shrink the queue.
65 Shrunk.wait(L, [this] { return size() < MaxSize || Sequential; });
66 push(Item);
67 }
68 GrewOrEnded.notify_one();
69 }
70 T *blockingPop() {
71 T *Item = nullptr;
72 bool ShouldNotifyProducer = false;
73 {
74 std::unique_lock<GlobalLockType> L(Lock);
75 GrewOrEnded.wait(L, [this] { return IsEnded || !empty() || Sequential; });
76 if (!empty()) {
77 Item = pop();
78 ShouldNotifyProducer = !IsEnded;
79 }
80 }
81 if (ShouldNotifyProducer)
82 Shrunk.notify_one();
83 return Item;
84 }
85 void notifyEnd() {
86 {
87 std::lock_guard<GlobalLockType> L(Lock);
88 IsEnded = true;
89 }
90 GrewOrEnded.notify_all();
91 }
92
93private:
94 const static size_t MaxStaticSizeMask = MaxStaticSize - 1;
95 static_assert(!(MaxStaticSize & (MaxStaticSize - 1)),
96 "MaxStaticSize must be a power of 2");
97
Jim Stichnothbbca7542015-02-11 16:08:31 -080098 ICE_CACHELINE_BOUNDARY;
Andrew Scull9612d322015-07-06 14:53:25 -070099 /// WorkItems and Lock are read/written by all.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800100 T *WorkItems[MaxStaticSize];
101 ICE_CACHELINE_BOUNDARY;
Andrew Scull9612d322015-07-06 14:53:25 -0700102 /// Lock guards access to WorkItems, Front, Back, and IsEnded.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800103 GlobalLockType Lock;
104
105 ICE_CACHELINE_BOUNDARY;
Andrew Scull9612d322015-07-06 14:53:25 -0700106 /// GrewOrEnded is written by the producers and read by the
107 /// consumers. It is notified (by the producer) when something is
108 /// added to the queue, in case consumers are waiting for a non-empty
109 /// queue.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800110 std::condition_variable GrewOrEnded;
Andrew Scull9612d322015-07-06 14:53:25 -0700111 /// Back is the index into WorkItems[] of where the next element will
112 /// be pushed. (More precisely, Back&MaxStaticSize is the index.)
113 /// It is written by the producers, and read by all via size() and
114 /// empty().
Jim Stichnotheafb56c2015-06-22 10:35:22 -0700115 size_t Back = 0;
Jim Stichnothbbca7542015-02-11 16:08:31 -0800116
117 ICE_CACHELINE_BOUNDARY;
Andrew Scull9612d322015-07-06 14:53:25 -0700118 /// Shrunk is notified (by the consumer) when something is removed
119 /// from the queue, in case a producer is waiting for the queue to
120 /// drop below maximum capacity. It is written by the consumers and
121 /// read by the producers.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800122 std::condition_variable Shrunk;
Andrew Scull9612d322015-07-06 14:53:25 -0700123 /// Front is the index into WorkItems[] of the oldest element,
124 /// i.e. the next to be popped. (More precisely Front&MaxStaticSize
125 /// is the index.) It is written by the consumers, and read by all
126 /// via size() and empty().
Jim Stichnotheafb56c2015-06-22 10:35:22 -0700127 size_t Front = 0;
Jim Stichnothbbca7542015-02-11 16:08:31 -0800128
129 ICE_CACHELINE_BOUNDARY;
130
Andrew Scull9612d322015-07-06 14:53:25 -0700131 /// MaxSize and Sequential are read by all and written by none.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800132 const size_t MaxSize;
133 const bool Sequential;
Andrew Scull9612d322015-07-06 14:53:25 -0700134 /// IsEnded is read by the consumers, and only written once by the
135 /// producer.
Jim Stichnotheafb56c2015-06-22 10:35:22 -0700136 bool IsEnded = false;
Jim Stichnothbbca7542015-02-11 16:08:31 -0800137
Andrew Scull9612d322015-07-06 14:53:25 -0700138 /// The lock must be held when the following methods are called.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800139 bool empty() const { return Front == Back; }
140 size_t size() const { return Back - Front; }
141 void push(T *Item) {
142 WorkItems[Back++ & MaxStaticSizeMask] = Item;
143 assert(size() <= MaxStaticSize);
144 }
145 T *pop() {
146 assert(!empty());
147 return WorkItems[Front++ & MaxStaticSizeMask];
148 }
149};
150
Andrew Scull9612d322015-07-06 14:53:25 -0700151/// EmitterWorkItem is a simple wrapper around a pointer that
152/// represents a work item to be emitted, i.e. a function or a set of
153/// global declarations and initializers, and it includes a sequence
154/// number so that work items can be emitted in a particular order for
155/// deterministic output. It acts like an interface class, but instead
156/// of making the classes of interest inherit from EmitterWorkItem, it
157/// wraps pointers to these classes. Some space is wasted compared to
158/// storing the pointers in a union, but not too much due to the work
159/// granularity.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800160class EmitterWorkItem {
161 EmitterWorkItem() = delete;
162 EmitterWorkItem(const EmitterWorkItem &) = delete;
163 EmitterWorkItem &operator=(const EmitterWorkItem &) = delete;
164
165public:
Andrew Scull9612d322015-07-06 14:53:25 -0700166 /// ItemKind can be one of the following:
167 ///
168 /// WI_Nop: No actual work. This is a placeholder to maintain
169 /// sequence numbers in case there is a translation error.
170 ///
171 /// WI_GlobalInits: A list of global declarations and initializers.
172 ///
173 /// WI_Asm: A function that has already had emitIAS() called on it.
174 /// The work is transferred via the Assembler buffer, and the
175 /// originating Cfg has been deleted (to recover lots of memory).
176 ///
177 /// WI_Cfg: A Cfg that has not yet had emit() or emitIAS() called on
178 /// it. This is only used as a debugging configuration when we want
179 /// to emit "readable" assembly code, possibly annotated with
180 /// liveness and other information only available in the Cfg and not
181 /// in the Assembler buffer.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800182 enum ItemKind { WI_Nop, WI_GlobalInits, WI_Asm, WI_Cfg };
Andrew Scull9612d322015-07-06 14:53:25 -0700183 /// Constructor for a WI_Nop work item.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800184 explicit EmitterWorkItem(uint32_t Seq);
Andrew Scull9612d322015-07-06 14:53:25 -0700185 /// Constructor for a WI_GlobalInits work item.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800186 EmitterWorkItem(uint32_t Seq, VariableDeclarationList *D);
Andrew Scull9612d322015-07-06 14:53:25 -0700187 /// Constructor for a WI_Asm work item.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800188 EmitterWorkItem(uint32_t Seq, Assembler *A);
Andrew Scull9612d322015-07-06 14:53:25 -0700189 /// Constructor for a WI_Cfg work item.
Jim Stichnothbbca7542015-02-11 16:08:31 -0800190 EmitterWorkItem(uint32_t Seq, Cfg *F);
191 uint32_t getSequenceNumber() const { return Sequence; }
192 ItemKind getKind() const { return Kind; }
John Portof8b4cc82015-06-09 18:06:19 -0700193 void setGlobalInits(std::unique_ptr<VariableDeclarationList> GloblInits);
Jim Stichnothbbca7542015-02-11 16:08:31 -0800194 std::unique_ptr<VariableDeclarationList> getGlobalInits();
195 std::unique_ptr<Assembler> getAsm();
196 std::unique_ptr<Cfg> getCfg();
197
198private:
199 const uint32_t Sequence;
200 const ItemKind Kind;
201 std::unique_ptr<VariableDeclarationList> GlobalInits;
202 std::unique_ptr<Assembler> Function;
203 std::unique_ptr<Cfg> RawFunc;
204};
205
206} // end of namespace Ice
207
208#endif // SUBZERO_SRC_ICETHREADING_H