xref: /aosp_15_r20/external/swiftshader/third_party/subzero/src/IceThreading.h (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker //===- subzero/src/IceThreading.h - Threading functions ---------*- C++ -*-===//
2*03ce13f7SAndroid Build Coastguard Worker //
3*03ce13f7SAndroid Build Coastguard Worker //                        The Subzero Code Generator
4*03ce13f7SAndroid Build Coastguard Worker //
5*03ce13f7SAndroid Build Coastguard Worker // This file is distributed under the University of Illinois Open Source
6*03ce13f7SAndroid Build Coastguard Worker // License. See LICENSE.TXT for details.
7*03ce13f7SAndroid Build Coastguard Worker //
8*03ce13f7SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
9*03ce13f7SAndroid Build Coastguard Worker ///
10*03ce13f7SAndroid Build Coastguard Worker /// \file
11*03ce13f7SAndroid Build Coastguard Worker /// \brief Declares threading-related functions.
12*03ce13f7SAndroid Build Coastguard Worker ///
13*03ce13f7SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
14*03ce13f7SAndroid Build Coastguard Worker 
15*03ce13f7SAndroid Build Coastguard Worker #ifndef SUBZERO_SRC_ICETHREADING_H
16*03ce13f7SAndroid Build Coastguard Worker #define SUBZERO_SRC_ICETHREADING_H
17*03ce13f7SAndroid Build Coastguard Worker 
18*03ce13f7SAndroid Build Coastguard Worker #include "IceDefs.h"
19*03ce13f7SAndroid Build Coastguard Worker 
20*03ce13f7SAndroid Build Coastguard Worker #include <condition_variable>
21*03ce13f7SAndroid Build Coastguard Worker #include <memory>
22*03ce13f7SAndroid Build Coastguard Worker #include <mutex>
23*03ce13f7SAndroid Build Coastguard Worker #include <utility>
24*03ce13f7SAndroid Build Coastguard Worker 
25*03ce13f7SAndroid Build Coastguard Worker namespace Ice {
26*03ce13f7SAndroid Build Coastguard Worker 
27*03ce13f7SAndroid Build Coastguard Worker /// BoundedProducerConsumerQueue is a work queue that allows multiple producers
28*03ce13f7SAndroid Build Coastguard Worker /// and multiple consumers. A producer adds entries using blockingPush(), and
29*03ce13f7SAndroid Build Coastguard Worker /// may block if the queue is "full". A producer uses notifyEnd() to indicate
30*03ce13f7SAndroid Build Coastguard Worker /// that no more entries will be added. A consumer removes an item using
31*03ce13f7SAndroid Build Coastguard Worker /// blockingPop(), which will return nullptr if notifyEnd() has been called and
32*03ce13f7SAndroid Build Coastguard Worker /// the queue is empty (it never returns nullptr if the queue contained any
33*03ce13f7SAndroid Build Coastguard Worker /// items).
34*03ce13f7SAndroid Build Coastguard Worker ///
35*03ce13f7SAndroid Build Coastguard Worker /// The MaxSize ctor arg controls the maximum size the queue can grow to
36*03ce13f7SAndroid Build Coastguard Worker /// (subject to a hard limit of MaxStaticSize-1). The Sequential arg indicates
37*03ce13f7SAndroid Build Coastguard Worker /// purely sequential execution in which the single thread should never wait().
38*03ce13f7SAndroid Build Coastguard Worker ///
39*03ce13f7SAndroid Build Coastguard Worker /// Two condition variables are used in the implementation. GrewOrEnded signals
40*03ce13f7SAndroid Build Coastguard Worker /// a waiting worker that a producer has changed the state of the queue. Shrunk
41*03ce13f7SAndroid Build Coastguard Worker /// signals a blocked producer that a consumer has changed the state of the
42*03ce13f7SAndroid Build Coastguard Worker /// queue.
43*03ce13f7SAndroid Build Coastguard Worker ///
44*03ce13f7SAndroid Build Coastguard Worker /// The methods begin with Sequential-specific code to be most clear. The lock
45*03ce13f7SAndroid Build Coastguard Worker /// and condition variables are not used in the Sequential case.
46*03ce13f7SAndroid Build Coastguard Worker ///
47*03ce13f7SAndroid Build Coastguard Worker /// Internally, the queue is implemented as a circular array of size
48*03ce13f7SAndroid Build Coastguard Worker /// MaxStaticSize, where the queue boundaries are denoted by the Front and Back
49*03ce13f7SAndroid Build Coastguard Worker /// fields. Front==Back indicates an empty queue.
50*03ce13f7SAndroid Build Coastguard Worker template <typename T, size_t MaxStaticSize = 128>
51*03ce13f7SAndroid Build Coastguard Worker class BoundedProducerConsumerQueue {
52*03ce13f7SAndroid Build Coastguard Worker   BoundedProducerConsumerQueue() = delete;
53*03ce13f7SAndroid Build Coastguard Worker   BoundedProducerConsumerQueue(const BoundedProducerConsumerQueue &) = delete;
54*03ce13f7SAndroid Build Coastguard Worker   BoundedProducerConsumerQueue &
55*03ce13f7SAndroid Build Coastguard Worker   operator=(const BoundedProducerConsumerQueue &) = delete;
56*03ce13f7SAndroid Build Coastguard Worker 
57*03ce13f7SAndroid Build Coastguard Worker public:
58*03ce13f7SAndroid Build Coastguard Worker   BoundedProducerConsumerQueue(bool Sequential, size_t MaxSize = MaxStaticSize)
MaxSize(std::min (MaxSize,MaxStaticSize))59*03ce13f7SAndroid Build Coastguard Worker       : MaxSize(std::min(MaxSize, MaxStaticSize)), Sequential(Sequential) {}
blockingPush(std::unique_ptr<T> Item)60*03ce13f7SAndroid Build Coastguard Worker   void blockingPush(std::unique_ptr<T> Item) {
61*03ce13f7SAndroid Build Coastguard Worker     {
62*03ce13f7SAndroid Build Coastguard Worker       std::unique_lock<GlobalLockType> L(Lock);
63*03ce13f7SAndroid Build Coastguard Worker       // If the work queue is already "full", wait for a consumer to grab an
64*03ce13f7SAndroid Build Coastguard Worker       // element and shrink the queue.
65*03ce13f7SAndroid Build Coastguard Worker       Shrunk.wait(L, [this] { return size() < MaxSize || Sequential; });
66*03ce13f7SAndroid Build Coastguard Worker       push(std::move(Item));
67*03ce13f7SAndroid Build Coastguard Worker     }
68*03ce13f7SAndroid Build Coastguard Worker     GrewOrEnded.notify_one();
69*03ce13f7SAndroid Build Coastguard Worker   }
70*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<T> blockingPop(size_t NotifyWhenDownToSize = MaxStaticSize) {
71*03ce13f7SAndroid Build Coastguard Worker     std::unique_ptr<T> Item;
72*03ce13f7SAndroid Build Coastguard Worker     bool ShouldNotifyProducer = false;
73*03ce13f7SAndroid Build Coastguard Worker     {
74*03ce13f7SAndroid Build Coastguard Worker       std::unique_lock<GlobalLockType> L(Lock);
75*03ce13f7SAndroid Build Coastguard Worker       GrewOrEnded.wait(L, [this] { return IsEnded || !empty() || Sequential; });
76*03ce13f7SAndroid Build Coastguard Worker       if (!empty()) {
77*03ce13f7SAndroid Build Coastguard Worker         Item = pop();
78*03ce13f7SAndroid Build Coastguard Worker         ShouldNotifyProducer = (size() < NotifyWhenDownToSize) && !IsEnded;
79*03ce13f7SAndroid Build Coastguard Worker       }
80*03ce13f7SAndroid Build Coastguard Worker     }
81*03ce13f7SAndroid Build Coastguard Worker     if (ShouldNotifyProducer)
82*03ce13f7SAndroid Build Coastguard Worker       Shrunk.notify_one();
83*03ce13f7SAndroid Build Coastguard Worker     return Item;
84*03ce13f7SAndroid Build Coastguard Worker   }
notifyEnd()85*03ce13f7SAndroid Build Coastguard Worker   void notifyEnd() {
86*03ce13f7SAndroid Build Coastguard Worker     {
87*03ce13f7SAndroid Build Coastguard Worker       std::lock_guard<GlobalLockType> L(Lock);
88*03ce13f7SAndroid Build Coastguard Worker       IsEnded = true;
89*03ce13f7SAndroid Build Coastguard Worker     }
90*03ce13f7SAndroid Build Coastguard Worker     GrewOrEnded.notify_all();
91*03ce13f7SAndroid Build Coastguard Worker   }
92*03ce13f7SAndroid Build Coastguard Worker 
93*03ce13f7SAndroid Build Coastguard Worker private:
94*03ce13f7SAndroid Build Coastguard Worker   const static size_t MaxStaticSizeMask = MaxStaticSize - 1;
95*03ce13f7SAndroid Build Coastguard Worker   static_assert(!(MaxStaticSize & (MaxStaticSize - 1)),
96*03ce13f7SAndroid Build Coastguard Worker                 "MaxStaticSize must be a power of 2");
97*03ce13f7SAndroid Build Coastguard Worker 
98*03ce13f7SAndroid Build Coastguard Worker   ICE_CACHELINE_BOUNDARY;
99*03ce13f7SAndroid Build Coastguard Worker   /// WorkItems and Lock are read/written by all.
100*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<T> WorkItems[MaxStaticSize];
101*03ce13f7SAndroid Build Coastguard Worker   ICE_CACHELINE_BOUNDARY;
102*03ce13f7SAndroid Build Coastguard Worker   /// Lock guards access to WorkItems, Front, Back, and IsEnded.
103*03ce13f7SAndroid Build Coastguard Worker   GlobalLockType Lock;
104*03ce13f7SAndroid Build Coastguard Worker 
105*03ce13f7SAndroid Build Coastguard Worker   ICE_CACHELINE_BOUNDARY;
106*03ce13f7SAndroid Build Coastguard Worker   /// GrewOrEnded is written by the producers and read by the consumers. It is
107*03ce13f7SAndroid Build Coastguard Worker   /// notified (by the producer) when something is added to the queue, in case
108*03ce13f7SAndroid Build Coastguard Worker   /// consumers are waiting for a non-empty queue.
109*03ce13f7SAndroid Build Coastguard Worker   std::condition_variable GrewOrEnded;
110*03ce13f7SAndroid Build Coastguard Worker   /// Back is the index into WorkItems[] of where the next element will be
111*03ce13f7SAndroid Build Coastguard Worker   /// pushed. (More precisely, Back&MaxStaticSize is the index.) It is written
112*03ce13f7SAndroid Build Coastguard Worker   /// by the producers, and read by all via size() and empty().
113*03ce13f7SAndroid Build Coastguard Worker   size_t Back = 0;
114*03ce13f7SAndroid Build Coastguard Worker 
115*03ce13f7SAndroid Build Coastguard Worker   ICE_CACHELINE_BOUNDARY;
116*03ce13f7SAndroid Build Coastguard Worker   /// Shrunk is notified (by the consumer) when something is removed from the
117*03ce13f7SAndroid Build Coastguard Worker   /// queue, in case a producer is waiting for the queue to drop below maximum
118*03ce13f7SAndroid Build Coastguard Worker   /// capacity. It is written by the consumers and read by the producers.
119*03ce13f7SAndroid Build Coastguard Worker   std::condition_variable Shrunk;
120*03ce13f7SAndroid Build Coastguard Worker   /// Front is the index into WorkItems[] of the oldest element, i.e. the next
121*03ce13f7SAndroid Build Coastguard Worker   /// to be popped. (More precisely Front&MaxStaticSize is the index.) It is
122*03ce13f7SAndroid Build Coastguard Worker   /// written by the consumers, and read by all via size() and empty().
123*03ce13f7SAndroid Build Coastguard Worker   size_t Front = 0;
124*03ce13f7SAndroid Build Coastguard Worker 
125*03ce13f7SAndroid Build Coastguard Worker   ICE_CACHELINE_BOUNDARY;
126*03ce13f7SAndroid Build Coastguard Worker 
127*03ce13f7SAndroid Build Coastguard Worker   /// MaxSize and Sequential are read by all and written by none.
128*03ce13f7SAndroid Build Coastguard Worker   const size_t MaxSize;
129*03ce13f7SAndroid Build Coastguard Worker   const bool Sequential;
130*03ce13f7SAndroid Build Coastguard Worker   /// IsEnded is read by the consumers, and only written once by the producer.
131*03ce13f7SAndroid Build Coastguard Worker   bool IsEnded = false;
132*03ce13f7SAndroid Build Coastguard Worker 
133*03ce13f7SAndroid Build Coastguard Worker   /// The lock must be held when the following methods are called.
empty()134*03ce13f7SAndroid Build Coastguard Worker   bool empty() const { return Front == Back; }
size()135*03ce13f7SAndroid Build Coastguard Worker   size_t size() const { return Back - Front; }
push(std::unique_ptr<T> Item)136*03ce13f7SAndroid Build Coastguard Worker   void push(std::unique_ptr<T> Item) {
137*03ce13f7SAndroid Build Coastguard Worker     WorkItems[Back++ & MaxStaticSizeMask] = std::move(Item);
138*03ce13f7SAndroid Build Coastguard Worker     assert(size() <= MaxStaticSize);
139*03ce13f7SAndroid Build Coastguard Worker   }
pop()140*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<T> pop() {
141*03ce13f7SAndroid Build Coastguard Worker     assert(!empty());
142*03ce13f7SAndroid Build Coastguard Worker     return std::move(WorkItems[Front++ & MaxStaticSizeMask]);
143*03ce13f7SAndroid Build Coastguard Worker   }
144*03ce13f7SAndroid Build Coastguard Worker };
145*03ce13f7SAndroid Build Coastguard Worker 
146*03ce13f7SAndroid Build Coastguard Worker /// EmitterWorkItem is a simple wrapper around a pointer that represents a work
147*03ce13f7SAndroid Build Coastguard Worker /// item to be emitted, i.e. a function or a set of global declarations and
148*03ce13f7SAndroid Build Coastguard Worker /// initializers, and it includes a sequence number so that work items can be
149*03ce13f7SAndroid Build Coastguard Worker /// emitted in a particular order for deterministic output. It acts like an
150*03ce13f7SAndroid Build Coastguard Worker /// interface class, but instead of making the classes of interest inherit from
151*03ce13f7SAndroid Build Coastguard Worker /// EmitterWorkItem, it wraps pointers to these classes. Some space is wasted
152*03ce13f7SAndroid Build Coastguard Worker /// compared to storing the pointers in a union, but not too much due to the
153*03ce13f7SAndroid Build Coastguard Worker /// work granularity.
154*03ce13f7SAndroid Build Coastguard Worker class EmitterWorkItem {
155*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem() = delete;
156*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem(const EmitterWorkItem &) = delete;
157*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem &operator=(const EmitterWorkItem &) = delete;
158*03ce13f7SAndroid Build Coastguard Worker 
159*03ce13f7SAndroid Build Coastguard Worker public:
160*03ce13f7SAndroid Build Coastguard Worker   /// ItemKind can be one of the following:
161*03ce13f7SAndroid Build Coastguard Worker   ///
162*03ce13f7SAndroid Build Coastguard Worker   /// WI_Nop: No actual work. This is a placeholder to maintain sequence numbers
163*03ce13f7SAndroid Build Coastguard Worker   /// in case there is a translation error.
164*03ce13f7SAndroid Build Coastguard Worker   ///
165*03ce13f7SAndroid Build Coastguard Worker   /// WI_GlobalInits: A list of global declarations and initializers.
166*03ce13f7SAndroid Build Coastguard Worker   ///
167*03ce13f7SAndroid Build Coastguard Worker   /// WI_Asm: A function that has already had emitIAS() called on it. The work
168*03ce13f7SAndroid Build Coastguard Worker   /// is transferred via the Assembler buffer, and the originating Cfg has been
169*03ce13f7SAndroid Build Coastguard Worker   /// deleted (to recover lots of memory).
170*03ce13f7SAndroid Build Coastguard Worker   ///
171*03ce13f7SAndroid Build Coastguard Worker   /// WI_Cfg: A Cfg that has not yet had emit() or emitIAS() called on it. This
172*03ce13f7SAndroid Build Coastguard Worker   /// is only used as a debugging configuration when we want to emit "readable"
173*03ce13f7SAndroid Build Coastguard Worker   /// assembly code, possibly annotated with liveness and other information only
174*03ce13f7SAndroid Build Coastguard Worker   /// available in the Cfg and not in the Assembler buffer.
175*03ce13f7SAndroid Build Coastguard Worker   enum ItemKind { WI_Nop, WI_GlobalInits, WI_Asm, WI_Cfg };
176*03ce13f7SAndroid Build Coastguard Worker   /// Constructor for a WI_Nop work item.
177*03ce13f7SAndroid Build Coastguard Worker   explicit EmitterWorkItem(uint32_t Seq);
178*03ce13f7SAndroid Build Coastguard Worker   /// Constructor for a WI_GlobalInits work item.
179*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem(uint32_t Seq, std::unique_ptr<VariableDeclarationList> D);
180*03ce13f7SAndroid Build Coastguard Worker   /// Constructor for a WI_Asm work item.
181*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem(uint32_t Seq, std::unique_ptr<Assembler> A);
182*03ce13f7SAndroid Build Coastguard Worker   /// Constructor for a WI_Cfg work item.
183*03ce13f7SAndroid Build Coastguard Worker   EmitterWorkItem(uint32_t Seq, std::unique_ptr<Cfg> F);
getSequenceNumber()184*03ce13f7SAndroid Build Coastguard Worker   uint32_t getSequenceNumber() const { return Sequence; }
getKind()185*03ce13f7SAndroid Build Coastguard Worker   ItemKind getKind() const { return Kind; }
186*03ce13f7SAndroid Build Coastguard Worker   void setGlobalInits(std::unique_ptr<VariableDeclarationList> GloblInits);
187*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<VariableDeclarationList> getGlobalInits();
188*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<Assembler> getAsm();
189*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<Cfg> getCfg();
190*03ce13f7SAndroid Build Coastguard Worker 
191*03ce13f7SAndroid Build Coastguard Worker private:
192*03ce13f7SAndroid Build Coastguard Worker   const uint32_t Sequence;
193*03ce13f7SAndroid Build Coastguard Worker   const ItemKind Kind;
194*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<VariableDeclarationList> GlobalInits;
195*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<Assembler> Function;
196*03ce13f7SAndroid Build Coastguard Worker   std::unique_ptr<Cfg> RawFunc;
197*03ce13f7SAndroid Build Coastguard Worker };
198*03ce13f7SAndroid Build Coastguard Worker 
199*03ce13f7SAndroid Build Coastguard Worker } // end of namespace Ice
200*03ce13f7SAndroid Build Coastguard Worker 
201*03ce13f7SAndroid Build Coastguard Worker #endif // SUBZERO_SRC_ICETHREADING_H
202