• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- subzero/src/IceGlobalContext.h - Global context defs -----*- 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 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Declares aspects of the compilation that persist across multiple
12 /// functions.
13 ///
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H
17 #define SUBZERO_SRC_ICEGLOBALCONTEXT_H
18 
19 #include "IceDefs.h"
20 #include "IceClFlags.h"
21 #include "IceInstrumentation.h"
22 #include "IceIntrinsics.h"
23 #include "IceRNG.h"
24 #include "IceStringPool.h"
25 #include "IceSwitchLowering.h"
26 #include "IceTargetLowering.def"
27 #include "IceThreading.h"
28 #include "IceTimerTree.h"
29 #include "IceTypes.h"
30 #include "IceUtils.h"
31 
32 #include <array>
33 #include <atomic>
34 #include <cassert>
35 #include <functional>
36 #include <memory>
37 #include <mutex>
38 #include <thread>
39 #include <type_traits>
40 #include <utility>
41 #include <vector>
42 
43 namespace Ice {
44 
45 class ConstantPool;
46 class EmitterWorkItem;
47 class FuncSigType;
48 class Instrumentation;
49 
50 // Runtime helper function IDs
51 
52 enum class RuntimeHelper {
53 #define X(Tag, Name) H_##Tag,
54   RUNTIME_HELPER_FUNCTIONS_TABLE
55 #undef X
56       H_Num
57 };
58 
59 /// OptWorkItem is a simple wrapper used to pass parse information on a function
60 /// block, to a translator thread.
61 class OptWorkItem {
62   OptWorkItem(const OptWorkItem &) = delete;
63   OptWorkItem &operator=(const OptWorkItem &) = delete;
64 
65 public:
66   // Get the Cfg for the funtion to translate.
67   virtual std::unique_ptr<Cfg> getParsedCfg() = 0;
68   virtual ~OptWorkItem() = default;
69 
70 protected:
71   OptWorkItem() = default;
72 };
73 
74 class GlobalContext {
75   GlobalContext() = delete;
76   GlobalContext(const GlobalContext &) = delete;
77   GlobalContext &operator=(const GlobalContext &) = delete;
78 
79   /// CodeStats collects rudimentary statistics during translation.
80   class CodeStats {
81     CodeStats(const CodeStats &) = delete;
82     CodeStats &operator=(const CodeStats &) = default;
83 #define CODESTATS_TABLE                                                        \
84   /* dump string, enum value */                                                \
85   X("Inst Count  ", InstCount)                                                 \
86   X("Regs Saved  ", RegsSaved)                                                 \
87   X("Frame Bytes ", FrameByte)                                                 \
88   X("Spills      ", NumSpills)                                                 \
89   X("Fills       ", NumFills)                                                  \
90   X("R/P Imms    ", NumRPImms)
91     //#define X(str, tag)
92 
93   public:
94     enum CSTag {
95 #define X(str, tag) CS_##tag,
96       CODESTATS_TABLE
97 #undef X
98           CS_NUM
99     };
CodeStats()100     CodeStats() { reset(); }
reset()101     void reset() { Stats.fill(0); }
102     void update(CSTag Tag, uint32_t Count = 1) {
103       assert(Tag < Stats.size());
104       Stats[Tag] += Count;
105     }
add(const CodeStats & Other)106     void add(const CodeStats &Other) {
107       for (uint32_t i = 0; i < Stats.size(); ++i)
108         Stats[i] += Other.Stats[i];
109     }
110     /// Dumps the stats for the given Cfg.  If Func==nullptr, it identifies it
111     /// as the "final" cumulative stats instead as a specific function's name.
112     void dump(const Cfg *Func, GlobalContext *Ctx);
113 
114   private:
115     std::array<uint32_t, CS_NUM> Stats;
116   };
117 
118   /// TimerList is a vector of TimerStack objects, with extra methods
119   /// to initialize and merge these vectors.
120   class TimerList : public std::vector<TimerStack> {
121     TimerList(const TimerList &) = delete;
122     TimerList &operator=(const TimerList &) = delete;
123 
124   public:
125     TimerList() = default;
126     /// initInto() initializes a target list of timers based on the
127     /// current list.  In particular, it creates the same number of
128     /// timers, in the same order, with the same names, but initially
129     /// empty of timing data.
initInto(TimerList & Dest)130     void initInto(TimerList &Dest) const {
131       if (!BuildDefs::timers())
132         return;
133       Dest.clear();
134       for (const TimerStack &Stack : *this) {
135         Dest.push_back(TimerStack(Stack.getName()));
136       }
137     }
mergeFrom(TimerList & Src)138     void mergeFrom(TimerList &Src) {
139       if (!BuildDefs::timers())
140         return;
141       assert(size() == Src.size());
142       size_type i = 0;
143       for (TimerStack &Stack : *this) {
144         assert(Stack.getName() == Src[i].getName());
145         Stack.mergeFrom(Src[i]);
146         ++i;
147       }
148     }
149   };
150 
151   /// ThreadContext contains thread-local data.  This data can be
152   /// combined/reduced as needed after all threads complete.
153   class ThreadContext {
154     ThreadContext(const ThreadContext &) = delete;
155     ThreadContext &operator=(const ThreadContext &) = delete;
156 
157   public:
158     ThreadContext() = default;
159     CodeStats StatsFunction;
160     CodeStats StatsCumulative;
161     TimerList Timers;
162   };
163 
164 public:
165   /// The dump stream is a log stream while emit is the stream code
166   /// is emitted to. The error stream is strictly for logging errors.
167   GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError,
168                 ELFStreamer *ELFStreamer);
169   ~GlobalContext();
170 
171   void dumpStrings();
172   ///
173   /// The dump, error, and emit streams need to be used by only one
174   /// thread at a time.  This is done by exclusively reserving the
175   /// streams via lockStr() and unlockStr().  The OstreamLocker class
176   /// can be used to conveniently manage this.
177   ///
178   /// The model is that a thread grabs the stream lock, then does an
179   /// arbitrary amount of work during which far-away callees may grab
180   /// the stream and do something with it, and finally the thread
181   /// releases the stream lock.  This allows large chunks of output to
182   /// be dumped or emitted without risking interleaving from multiple
183   /// threads.
lockStr()184   void lockStr() { StrLock.lock(); }
unlockStr()185   void unlockStr() { StrLock.unlock(); }
getStrDump()186   Ostream &getStrDump() { return *StrDump; }
getStrError()187   Ostream &getStrError() { return *StrError; }
getStrEmit()188   Ostream &getStrEmit() { return *StrEmit; }
setStrEmit(Ostream & NewStrEmit)189   void setStrEmit(Ostream &NewStrEmit) { StrEmit = &NewStrEmit; }
190 
getErrorStatus()191   LockedPtr<ErrorCode> getErrorStatus() {
192     return LockedPtr<ErrorCode>(&ErrorStatus, &ErrorStatusLock);
193   }
194 
195   /// \name Manage Constants.
196   /// @{
197   // getConstant*() functions are not const because they might add something to
198   // the constant pool.
199   Constant *getConstantInt(Type Ty, int64_t Value);
getConstantInt1(int8_t ConstantInt1)200   Constant *getConstantInt1(int8_t ConstantInt1) {
201     ConstantInt1 &= INT8_C(1);
202     switch (ConstantInt1) {
203     case 0:
204       return getConstantZero(IceType_i1);
205     case 1:
206       return ConstantTrue;
207     default:
208       assert(false && "getConstantInt1 not on true/false");
209       return getConstantInt1Internal(ConstantInt1);
210     }
211   }
getConstantInt8(int8_t ConstantInt8)212   Constant *getConstantInt8(int8_t ConstantInt8) {
213     switch (ConstantInt8) {
214     case 0:
215       return getConstantZero(IceType_i8);
216     default:
217       return getConstantInt8Internal(ConstantInt8);
218     }
219   }
getConstantInt16(int16_t ConstantInt16)220   Constant *getConstantInt16(int16_t ConstantInt16) {
221     switch (ConstantInt16) {
222     case 0:
223       return getConstantZero(IceType_i16);
224     default:
225       return getConstantInt16Internal(ConstantInt16);
226     }
227   }
getConstantInt32(int32_t ConstantInt32)228   Constant *getConstantInt32(int32_t ConstantInt32) {
229     switch (ConstantInt32) {
230     case 0:
231       return getConstantZero(IceType_i32);
232     default:
233       return getConstantInt32Internal(ConstantInt32);
234     }
235   }
getConstantInt64(int64_t ConstantInt64)236   Constant *getConstantInt64(int64_t ConstantInt64) {
237     switch (ConstantInt64) {
238     case 0:
239       return getConstantZero(IceType_i64);
240     default:
241       return getConstantInt64Internal(ConstantInt64);
242     }
243   }
244   Constant *getConstantFloat(float Value);
245   Constant *getConstantDouble(double Value);
246   /// Returns a symbolic constant.
247   Constant *getConstantSymWithEmitString(const RelocOffsetT Offset,
248                                          const RelocOffsetArray &OffsetExpr,
249                                          GlobalString Name,
250                                          const std::string &EmitString);
251   Constant *getConstantSym(RelocOffsetT Offset, GlobalString Name);
252   Constant *getConstantExternSym(GlobalString Name);
253   /// Returns an undef.
254   Constant *getConstantUndef(Type Ty);
255   /// Returns a zero value.
256   Constant *getConstantZero(Type Ty);
257   /// getConstantPool() returns a copy of the constant pool for constants of a
258   /// given type.
259   ConstantList getConstantPool(Type Ty);
260   /// Returns a copy of the list of external symbols.
261   ConstantList getConstantExternSyms();
262   /// @}
getRuntimeHelperFunc(RuntimeHelper FuncID)263   Constant *getRuntimeHelperFunc(RuntimeHelper FuncID) const {
264     assert(FuncID < RuntimeHelper::H_Num);
265     Constant *Result = RuntimeHelperFunc[static_cast<size_t>(FuncID)];
266     assert(Result != nullptr && "No such runtime helper function");
267     return Result;
268   }
269   GlobalString getGlobalString(const std::string &Name);
270 
271   /// Return a locked pointer to the registered jump tables.
272   JumpTableDataList getJumpTables();
273   /// Adds JumpTable to the list of know jump tables, for a posteriori emission.
274   void addJumpTableData(JumpTableData JumpTable);
275 
276   /// Allocate data of type T using the global allocator. We allow entities
277   /// allocated from this global allocator to be either trivially or
278   /// non-trivially destructible. We optimize the case when T is trivially
279   /// destructible by not registering a destructor. Destructors will be invoked
280   /// during GlobalContext destruction in the reverse object creation order.
281   template <typename T>
282   typename std::enable_if<std::is_trivially_destructible<T>::value, T>::type *
allocate()283   allocate() {
284     return getAllocator()->Allocate<T>();
285   }
286 
287   template <typename T>
288   typename std::enable_if<!std::is_trivially_destructible<T>::value, T>::type *
allocate()289   allocate() {
290     T *Ret = getAllocator()->Allocate<T>();
291     getDestructors()->emplace_back([Ret]() { Ret->~T(); });
292     return Ret;
293   }
294 
getIntrinsicsInfo()295   const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; }
296 
getObjectWriter()297   ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
298 
299   /// Reset stats at the beginning of a function.
resetStats()300   void resetStats() {
301     if (BuildDefs::dump())
302       ICE_TLS_GET_FIELD(TLS)->StatsFunction.reset();
303   }
304   void dumpStats(const Cfg *Func = nullptr);
statsUpdateEmitted(uint32_t InstCount)305   void statsUpdateEmitted(uint32_t InstCount) {
306     if (!getFlags().getDumpStats())
307       return;
308     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
309     Tls->StatsFunction.update(CodeStats::CS_InstCount, InstCount);
310     Tls->StatsCumulative.update(CodeStats::CS_InstCount, InstCount);
311   }
statsUpdateRegistersSaved(uint32_t Num)312   void statsUpdateRegistersSaved(uint32_t Num) {
313     if (!getFlags().getDumpStats())
314       return;
315     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
316     Tls->StatsFunction.update(CodeStats::CS_RegsSaved, Num);
317     Tls->StatsCumulative.update(CodeStats::CS_RegsSaved, Num);
318   }
statsUpdateFrameBytes(uint32_t Bytes)319   void statsUpdateFrameBytes(uint32_t Bytes) {
320     if (!getFlags().getDumpStats())
321       return;
322     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
323     Tls->StatsFunction.update(CodeStats::CS_FrameByte, Bytes);
324     Tls->StatsCumulative.update(CodeStats::CS_FrameByte, Bytes);
325   }
statsUpdateSpills()326   void statsUpdateSpills() {
327     if (!getFlags().getDumpStats())
328       return;
329     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
330     Tls->StatsFunction.update(CodeStats::CS_NumSpills);
331     Tls->StatsCumulative.update(CodeStats::CS_NumSpills);
332   }
statsUpdateFills()333   void statsUpdateFills() {
334     if (!getFlags().getDumpStats())
335       return;
336     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
337     Tls->StatsFunction.update(CodeStats::CS_NumFills);
338     Tls->StatsCumulative.update(CodeStats::CS_NumFills);
339   }
340 
341   /// Number of Randomized or Pooled Immediates
statsUpdateRPImms()342   void statsUpdateRPImms() {
343     if (!getFlags().getDumpStats())
344       return;
345     ThreadContext *Tls = ICE_TLS_GET_FIELD(TLS);
346     Tls->StatsFunction.update(CodeStats::CS_NumRPImms);
347     Tls->StatsCumulative.update(CodeStats::CS_NumRPImms);
348   }
349 
350   /// These are predefined TimerStackIdT values.
351   enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num };
352 
353   /// newTimerStackID() creates a new TimerStack in the global space. It does
354   /// not affect any TimerStack objects in TLS.
355   TimerStackIdT newTimerStackID(const std::string &Name);
356   /// dumpTimers() dumps the global timer data.  This assumes all the
357   /// thread-local copies of timer data have been merged into the global timer
358   /// data.
359   void dumpTimers(TimerStackIdT StackID = TSK_Default,
360                   bool DumpCumulative = true);
361   void dumpLocalTimers(const std::string &TimerNameOverride,
362                        TimerStackIdT StackID = TSK_Default,
363                        bool DumpCumulative = true);
364   /// The following methods affect only the calling thread's TLS timer data.
365   TimerIdT getTimerID(TimerStackIdT StackID, const std::string &Name);
366   void pushTimer(TimerIdT ID, TimerStackIdT StackID);
367   void popTimer(TimerIdT ID, TimerStackIdT StackID);
368   void resetTimer(TimerStackIdT StackID);
369   std::string getTimerName(TimerStackIdT StackID);
370   void setTimerName(TimerStackIdT StackID, const std::string &NewName);
371 
372   /// This is the first work item sequence number that the parser produces, and
373   /// correspondingly the first sequence number that the emitter thread will
374   /// wait for. Start numbering at 1 to leave room for a sentinel, in case e.g.
375   /// we wish to inject items with a special sequence number that may be
376   /// executed out of order.
getFirstSequenceNumber()377   static constexpr uint32_t getFirstSequenceNumber() { return 1; }
378   /// Adds a newly parsed and constructed function to the Cfg work queue.
379   /// Notifies any idle workers that a new function is available for
380   /// translating. May block if the work queue is too large, in order to control
381   /// memory footprint.
382   void optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item);
383   /// Takes a Cfg from the work queue for translating. May block if the work
384   /// queue is currently empty. Returns nullptr if there is no more work - the
385   /// queue is empty and either end() has been called or the Sequential flag was
386   /// set.
387   std::unique_ptr<OptWorkItem> optQueueBlockingPop();
388   /// Notifies that no more work will be added to the work queue.
optQueueNotifyEnd()389   void optQueueNotifyEnd() { OptQ.notifyEnd(); }
390 
391   /// Emit file header for output file.
392   void emitFileHeader();
393 
394   void lowerConstants();
395 
396   void lowerJumpTables();
397 
398   /// Emit target specific read-only data sections if any. E.g., for MIPS this
399   /// generates a .MIPS.abiflags section.
400   void emitTargetRODataSections();
401 
402   void emitQueueBlockingPush(std::unique_ptr<EmitterWorkItem> Item);
403   std::unique_ptr<EmitterWorkItem> emitQueueBlockingPop();
emitQueueNotifyEnd()404   void emitQueueNotifyEnd() { EmitQ.notifyEnd(); }
405 
initParserThread()406   void initParserThread() {
407     ThreadContext *Tls = new ThreadContext();
408     auto Timers = getTimers();
409     Timers->initInto(Tls->Timers);
410     AllThreadContexts.push_back(Tls);
411     ICE_TLS_SET_FIELD(TLS, Tls);
412   }
413 
startWorkerThreads()414   void startWorkerThreads() {
415     size_t NumWorkers = getFlags().getNumTranslationThreads();
416     auto Timers = getTimers();
417     for (size_t i = 0; i < NumWorkers; ++i) {
418       ThreadContext *WorkerTLS = new ThreadContext();
419       Timers->initInto(WorkerTLS->Timers);
420       AllThreadContexts.push_back(WorkerTLS);
421       TranslationThreads.push_back(std::thread(
422           &GlobalContext::translateFunctionsWrapper, this, WorkerTLS));
423     }
424     if (NumWorkers) {
425       ThreadContext *WorkerTLS = new ThreadContext();
426       Timers->initInto(WorkerTLS->Timers);
427       AllThreadContexts.push_back(WorkerTLS);
428       EmitterThreads.push_back(
429           std::thread(&GlobalContext::emitterWrapper, this, WorkerTLS));
430     }
431   }
432 
433   void waitForWorkerThreads();
434 
435   /// sets the instrumentation object to use.
setInstrumentation(std::unique_ptr<Instrumentation> Instr)436   void setInstrumentation(std::unique_ptr<Instrumentation> Instr) {
437     if (!BuildDefs::minimal())
438       Instrumentor = std::move(Instr);
439   }
440 
instrumentFunc(Cfg * Func)441   void instrumentFunc(Cfg *Func) {
442     if (!BuildDefs::minimal() && Instrumentor)
443       Instrumentor->instrumentFunc(Func);
444   }
445 
446   /// Translation thread startup routine.
translateFunctionsWrapper(ThreadContext * MyTLS)447   void translateFunctionsWrapper(ThreadContext *MyTLS) {
448     ICE_TLS_SET_FIELD(TLS, MyTLS);
449     translateFunctions();
450   }
451   /// Translate functions from the Cfg queue until the queue is empty.
452   void translateFunctions();
453 
454   /// Emitter thread startup routine.
emitterWrapper(ThreadContext * MyTLS)455   void emitterWrapper(ThreadContext *MyTLS) {
456     ICE_TLS_SET_FIELD(TLS, MyTLS);
457     emitItems();
458   }
459   /// Emit functions and global initializers from the emitter queue until the
460   /// queue is empty.
461   void emitItems();
462 
463   /// Uses DataLowering to lower Globals. Side effects:
464   ///  - discards the initializer list for the global variable in Globals.
465   ///  - clears the Globals array.
466   void lowerGlobals(const std::string &SectionSuffix);
467 
468   /// Lowers the profile information.
469   void lowerProfileData();
470 
471   void dumpConstantLookupCounts();
472 
473   /// DisposeGlobalVariablesAfterLowering controls whether the memory used by
474   /// GlobaleVariables can be reclaimed right after they have been lowered.
475   /// @{
getDisposeGlobalVariablesAfterLowering()476   bool getDisposeGlobalVariablesAfterLowering() const {
477     return DisposeGlobalVariablesAfterLowering;
478   }
479 
setDisposeGlobalVariablesAfterLowering(bool Value)480   void setDisposeGlobalVariablesAfterLowering(bool Value) {
481     DisposeGlobalVariablesAfterLowering = Value;
482   }
483   /// @}
484 
getStrings()485   LockedPtr<StringPool> getStrings() const {
486     return LockedPtr<StringPool>(Strings.get(), &StringsLock);
487   }
488 
getGlobals()489   LockedPtr<VariableDeclarationList> getGlobals() {
490     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock);
491   }
492 
493   /// Number of function blocks that can be queued before waiting for
494   /// translation
495   /// threads to consume.
496   static constexpr size_t MaxOptQSize = 1 << 16;
497 
498 private:
499   // Try to ensure mutexes are allocated on separate cache lines.
500 
501   // Destructors collaborate with Allocator
502   ICE_CACHELINE_BOUNDARY;
503   // Managed by getAllocator()
504   mutable GlobalLockType AllocLock;
505   ArenaAllocator Allocator;
506 
507   ICE_CACHELINE_BOUNDARY;
508   // Managed by getInitializerAllocator()
509   mutable GlobalLockType InitAllocLock;
510   VariableDeclarationList Globals;
511 
512   ICE_CACHELINE_BOUNDARY;
513   // Managed by getDestructors()
514   using DestructorArray = std::vector<std::function<void()>>;
515   mutable GlobalLockType DestructorsLock;
516   DestructorArray Destructors;
517 
518   ICE_CACHELINE_BOUNDARY;
519   // Managed by getStrings()
520   mutable GlobalLockType StringsLock;
521   std::unique_ptr<StringPool> Strings;
522 
523   ICE_CACHELINE_BOUNDARY;
524   // Managed by getConstPool()
525   mutable GlobalLockType ConstPoolLock;
526   std::unique_ptr<ConstantPool> ConstPool;
527 
528   ICE_CACHELINE_BOUNDARY;
529   // Managed by getJumpTableList()
530   mutable GlobalLockType JumpTablesLock;
531   JumpTableDataList JumpTableList;
532 
533   ICE_CACHELINE_BOUNDARY;
534   // Managed by getErrorStatus()
535   mutable GlobalLockType ErrorStatusLock;
536   ErrorCode ErrorStatus;
537 
538   ICE_CACHELINE_BOUNDARY;
539   // Managed by getStatsCumulative()
540   mutable GlobalLockType StatsLock;
541   CodeStats StatsCumulative;
542 
543   ICE_CACHELINE_BOUNDARY;
544   // Managed by getTimers()
545   mutable GlobalLockType TimerLock;
546   TimerList Timers;
547 
548   ICE_CACHELINE_BOUNDARY;
549   /// StrLock is a global lock on the dump and emit output streams.
550   using StrLockType = std::mutex;
551   StrLockType StrLock;
552   Ostream *StrDump;  /// Stream for dumping / diagnostics
553   Ostream *StrEmit;  /// Stream for code emission
554   Ostream *StrError; /// Stream for logging errors.
555 
556   // True if waitForWorkerThreads() has been called.
557   std::atomic_bool WaitForWorkerThreadsCalled;
558 
559   ICE_CACHELINE_BOUNDARY;
560 
561   Intrinsics IntrinsicsInfo;
562   // TODO(jpp): move to EmitterContext.
563   std::unique_ptr<ELFObjectWriter> ObjectWriter;
564   // Value defining when to wake up the main parse thread.
565   const size_t OptQWakeupSize;
566   BoundedProducerConsumerQueue<OptWorkItem, MaxOptQSize> OptQ;
567   BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ;
568   // DataLowering is only ever used by a single thread at a time (either in
569   // emitItems(), or in IceCompiler::run before the compilation is over.)
570   // TODO(jpp): move to EmitterContext.
571   std::unique_ptr<TargetDataLowering> DataLowering;
572   /// If !HasEmittedCode, SubZero will accumulate all Globals (which are "true"
573   /// program global variables) until the first code WorkItem is seen.
574   // TODO(jpp): move to EmitterContext.
575   bool HasSeenCode = false;
576   // If Instrumentor is not empty then it will be used to instrument globals and
577   // CFGs.
578   std::unique_ptr<Instrumentation> Instrumentor = nullptr;
579   // TODO(jpp): move to EmitterContext.
580   VariableDeclaration *ProfileBlockInfoVarDecl = nullptr;
581   std::vector<VariableDeclaration *> ProfileBlockInfos;
582   /// Indicates if global variable declarations can be disposed of right after
583   /// lowering.
584   bool DisposeGlobalVariablesAfterLowering = true;
585   Constant *ConstZeroForType[IceType_NUM];
586   Constant *ConstantTrue;
587   // Holds the constants representing each runtime helper function.
588   Constant *RuntimeHelperFunc[static_cast<size_t>(RuntimeHelper::H_Num)];
589 
590   Constant *getConstantZeroInternal(Type Ty);
591   Constant *getConstantIntInternal(Type Ty, int64_t Value);
592   Constant *getConstantInt1Internal(int8_t ConstantInt1);
593   Constant *getConstantInt8Internal(int8_t ConstantInt8);
594   Constant *getConstantInt16Internal(int16_t ConstantInt16);
595   Constant *getConstantInt32Internal(int32_t ConstantInt32);
596   Constant *getConstantInt64Internal(int64_t ConstantInt64);
getAllocator()597   LockedPtr<ArenaAllocator> getAllocator() {
598     return LockedPtr<ArenaAllocator>(&Allocator, &AllocLock);
599   }
getInitializerAllocator()600   LockedPtr<VariableDeclarationList> getInitializerAllocator() {
601     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock);
602   }
getConstPool()603   LockedPtr<ConstantPool> getConstPool() {
604     return LockedPtr<ConstantPool>(ConstPool.get(), &ConstPoolLock);
605   }
getJumpTableList()606   LockedPtr<JumpTableDataList> getJumpTableList() {
607     return LockedPtr<JumpTableDataList>(&JumpTableList, &JumpTablesLock);
608   }
getStatsCumulative()609   LockedPtr<CodeStats> getStatsCumulative() {
610     return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock);
611   }
getTimers()612   LockedPtr<TimerList> getTimers() {
613     return LockedPtr<TimerList>(&Timers, &TimerLock);
614   }
getDestructors()615   LockedPtr<DestructorArray> getDestructors() {
616     return LockedPtr<DestructorArray>(&Destructors, &DestructorsLock);
617   }
618 
accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls)619   void accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls) {
620     LockedPtr<VariableDeclarationList> _(&Globals, &InitAllocLock);
621     if (Globls != nullptr) {
622       Globals.merge(Globls.get());
623       if (!BuildDefs::minimal() && Instrumentor != nullptr)
624         Instrumentor->setHasSeenGlobals();
625     }
626   }
627 
lowerGlobalsIfNoCodeHasBeenSeen()628   void lowerGlobalsIfNoCodeHasBeenSeen() {
629     if (HasSeenCode)
630       return;
631     constexpr char NoSuffix[] = "";
632     lowerGlobals(NoSuffix);
633     HasSeenCode = true;
634   }
635 
636   void saveBlockInfoPtrs();
637 
638   llvm::SmallVector<ThreadContext *, 128> AllThreadContexts;
639   llvm::SmallVector<std::thread, 128> TranslationThreads;
640   llvm::SmallVector<std::thread, 128> EmitterThreads;
641   // Each thread has its own TLS pointer which is also held in
642   // AllThreadContexts.
643   ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
644 
645 public:
TlsInit()646   static void TlsInit() { ICE_TLS_INIT_FIELD(TLS); }
647 };
648 
649 /// Helper class to push and pop a timer marker. The constructor pushes a
650 /// marker, and the destructor pops it. This is for convenient timing of regions
651 /// of code.
652 class TimerMarker {
653   TimerMarker() = delete;
654   TimerMarker(const TimerMarker &) = delete;
655   TimerMarker &operator=(const TimerMarker &) = delete;
656 
657 public:
658   TimerMarker(TimerIdT ID, GlobalContext *Ctx,
659               TimerStackIdT StackID = GlobalContext::TSK_Default)
ID(ID)660       : ID(ID), Ctx(Ctx), StackID(StackID) {
661     if (BuildDefs::timers())
662       push();
663   }
664   TimerMarker(TimerIdT ID, const Cfg *Func,
665               TimerStackIdT StackID = GlobalContext::TSK_Default)
ID(ID)666       : ID(ID), Ctx(nullptr), StackID(StackID) {
667     // Ctx gets set at the beginning of pushCfg().
668     if (BuildDefs::timers())
669       pushCfg(Func);
670   }
TimerMarker(GlobalContext * Ctx,const std::string & FuncName)671   TimerMarker(GlobalContext *Ctx, const std::string &FuncName)
672       : ID(getTimerIdFromFuncName(Ctx, FuncName)), Ctx(Ctx),
673         StackID(GlobalContext::TSK_Funcs) {
674     if (BuildDefs::timers())
675       push();
676   }
677 
~TimerMarker()678   ~TimerMarker() {
679     if (BuildDefs::timers() && Active)
680       Ctx->popTimer(ID, StackID);
681   }
682 
683 private:
684   void push();
685   void pushCfg(const Cfg *Func);
686   static TimerIdT getTimerIdFromFuncName(GlobalContext *Ctx,
687                                          const std::string &FuncName);
688   const TimerIdT ID;
689   GlobalContext *Ctx;
690   const TimerStackIdT StackID;
691   bool Active = false;
692 };
693 
694 /// Helper class for locking the streams and then automatically unlocking them.
695 class OstreamLocker {
696 private:
697   OstreamLocker() = delete;
698   OstreamLocker(const OstreamLocker &) = delete;
699   OstreamLocker &operator=(const OstreamLocker &) = delete;
700 
701 public:
OstreamLocker(GlobalContext * Ctx)702   explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); }
~OstreamLocker()703   ~OstreamLocker() { Ctx->unlockStr(); }
704 
705 private:
706   GlobalContext *const Ctx;
707 };
708 
709 } // end of namespace Ice
710 
711 #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H
712