Home | History | Annotate | Download | only in src
      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     };
    100     CodeStats() { 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     }
    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.
    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     }
    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.
    184   void lockStr() { StrLock.lock(); }
    185   void unlockStr() { StrLock.unlock(); }
    186   Ostream &getStrDump() { return *StrDump; }
    187   Ostream &getStrError() { return *StrError; }
    188   Ostream &getStrEmit() { return *StrEmit; }
    189   void setStrEmit(Ostream &NewStrEmit) { StrEmit = &NewStrEmit; }
    190 
    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);
    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   }
    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   }
    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   }
    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   }
    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   /// @}
    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 *
    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 *
    289   allocate() {
    290     T *Ret = getAllocator()->Allocate<T>();
    291     getDestructors()->emplace_back([Ret]() { Ret->~T(); });
    292     return Ret;
    293   }
    294 
    295   const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; }
    296 
    297   ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
    298 
    299   /// Reset stats at the beginning of a function.
    300   void resetStats() {
    301     if (BuildDefs::dump())
    302       ICE_TLS_GET_FIELD(TLS)->StatsFunction.reset();
    303   }
    304   void dumpStats(const Cfg *Func = nullptr);
    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   }
    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   }
    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   }
    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   }
    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
    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.
    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.
    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();
    404   void emitQueueNotifyEnd() { EmitQ.notifyEnd(); }
    405 
    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 
    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.
    436   void setInstrumentation(std::unique_ptr<Instrumentation> Instr) {
    437     if (!BuildDefs::minimal())
    438       Instrumentor = std::move(Instr);
    439   }
    440 
    441   void instrumentFunc(Cfg *Func) {
    442     if (!BuildDefs::minimal() && Instrumentor)
    443       Instrumentor->instrumentFunc(Func);
    444   }
    445 
    446   /// Translation thread startup routine.
    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.
    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   /// @{
    476   bool getDisposeGlobalVariablesAfterLowering() const {
    477     return DisposeGlobalVariablesAfterLowering;
    478   }
    479 
    480   void setDisposeGlobalVariablesAfterLowering(bool Value) {
    481     DisposeGlobalVariablesAfterLowering = Value;
    482   }
    483   /// @}
    484 
    485   LockedPtr<StringPool> getStrings() const {
    486     return LockedPtr<StringPool>(Strings.get(), &StringsLock);
    487   }
    488 
    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);
    597   LockedPtr<ArenaAllocator> getAllocator() {
    598     return LockedPtr<ArenaAllocator>(&Allocator, &AllocLock);
    599   }
    600   LockedPtr<VariableDeclarationList> getInitializerAllocator() {
    601     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock);
    602   }
    603   LockedPtr<ConstantPool> getConstPool() {
    604     return LockedPtr<ConstantPool>(ConstPool.get(), &ConstPoolLock);
    605   }
    606   LockedPtr<JumpTableDataList> getJumpTableList() {
    607     return LockedPtr<JumpTableDataList>(&JumpTableList, &JumpTablesLock);
    608   }
    609   LockedPtr<CodeStats> getStatsCumulative() {
    610     return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock);
    611   }
    612   LockedPtr<TimerList> getTimers() {
    613     return LockedPtr<TimerList>(&Timers, &TimerLock);
    614   }
    615   LockedPtr<DestructorArray> getDestructors() {
    616     return LockedPtr<DestructorArray>(&Destructors, &DestructorsLock);
    617   }
    618 
    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 
    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:
    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)
    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)
    666       : ID(ID), Ctx(nullptr), StackID(StackID) {
    667     // Ctx gets set at the beginning of pushCfg().
    668     if (BuildDefs::timers())
    669       pushCfg(Func);
    670   }
    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 
    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:
    702   explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); }
    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