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   void dumpStats(const Cfg *Func = nullptr);
    302   void statsUpdateEmitted(uint32_t InstCount);
    303   void statsUpdateRegistersSaved(uint32_t Num);
    304   void statsUpdateFrameBytes(uint32_t Bytes);
    305   void statsUpdateSpills();
    306   void statsUpdateFills();
    307 
    308   /// Number of Randomized or Pooled Immediates
    309   void statsUpdateRPImms();
    310 
    311   /// These are predefined TimerStackIdT values.
    312   enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num };
    313 
    314   /// newTimerStackID() creates a new TimerStack in the global space. It does
    315   /// not affect any TimerStack objects in TLS.
    316   TimerStackIdT newTimerStackID(const std::string &Name);
    317   /// dumpTimers() dumps the global timer data.  This assumes all the
    318   /// thread-local copies of timer data have been merged into the global timer
    319   /// data.
    320   void dumpTimers(TimerStackIdT StackID = TSK_Default,
    321                   bool DumpCumulative = true);
    322   void dumpLocalTimers(const std::string &TimerNameOverride,
    323                        TimerStackIdT StackID = TSK_Default,
    324                        bool DumpCumulative = true);
    325   /// The following methods affect only the calling thread's TLS timer data.
    326   TimerIdT getTimerID(TimerStackIdT StackID, const std::string &Name);
    327   void pushTimer(TimerIdT ID, TimerStackIdT StackID);
    328   void popTimer(TimerIdT ID, TimerStackIdT StackID);
    329   void resetTimer(TimerStackIdT StackID);
    330   std::string getTimerName(TimerStackIdT StackID);
    331   void setTimerName(TimerStackIdT StackID, const std::string &NewName);
    332 
    333   /// This is the first work item sequence number that the parser produces, and
    334   /// correspondingly the first sequence number that the emitter thread will
    335   /// wait for. Start numbering at 1 to leave room for a sentinel, in case e.g.
    336   /// we wish to inject items with a special sequence number that may be
    337   /// executed out of order.
    338   static constexpr uint32_t getFirstSequenceNumber() { return 1; }
    339   /// Adds a newly parsed and constructed function to the Cfg work queue.
    340   /// Notifies any idle workers that a new function is available for
    341   /// translating. May block if the work queue is too large, in order to control
    342   /// memory footprint.
    343   void optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item);
    344   /// Takes a Cfg from the work queue for translating. May block if the work
    345   /// queue is currently empty. Returns nullptr if there is no more work - the
    346   /// queue is empty and either end() has been called or the Sequential flag was
    347   /// set.
    348   std::unique_ptr<OptWorkItem> optQueueBlockingPop();
    349   /// Notifies that no more work will be added to the work queue.
    350   void optQueueNotifyEnd() { OptQ.notifyEnd(); }
    351 
    352   /// Emit file header for output file.
    353   void emitFileHeader();
    354 
    355   void lowerConstants();
    356 
    357   void lowerJumpTables();
    358 
    359   /// Emit target specific read-only data sections if any. E.g., for MIPS this
    360   /// generates a .MIPS.abiflags section.
    361   void emitTargetRODataSections();
    362 
    363   void emitQueueBlockingPush(std::unique_ptr<EmitterWorkItem> Item);
    364   std::unique_ptr<EmitterWorkItem> emitQueueBlockingPop();
    365   void emitQueueNotifyEnd() { EmitQ.notifyEnd(); }
    366 
    367   void initParserThread();
    368   void startWorkerThreads();
    369 
    370   void waitForWorkerThreads();
    371 
    372   /// sets the instrumentation object to use.
    373   void setInstrumentation(std::unique_ptr<Instrumentation> Instr) {
    374     if (!BuildDefs::minimal())
    375       Instrumentor = std::move(Instr);
    376   }
    377 
    378   void instrumentFunc(Cfg *Func) {
    379     if (!BuildDefs::minimal() && Instrumentor)
    380       Instrumentor->instrumentFunc(Func);
    381   }
    382 
    383   /// Translation thread startup routine.
    384   void translateFunctionsWrapper(ThreadContext *MyTLS);
    385   /// Translate functions from the Cfg queue until the queue is empty.
    386   void translateFunctions();
    387 
    388   /// Emitter thread startup routine.
    389   void emitterWrapper(ThreadContext *MyTLS);
    390   /// Emit functions and global initializers from the emitter queue until the
    391   /// queue is empty.
    392   void emitItems();
    393 
    394   /// Uses DataLowering to lower Globals. Side effects:
    395   ///  - discards the initializer list for the global variable in Globals.
    396   ///  - clears the Globals array.
    397   void lowerGlobals(const std::string &SectionSuffix);
    398 
    399   /// Lowers the profile information.
    400   void lowerProfileData();
    401 
    402   void dumpConstantLookupCounts();
    403 
    404   /// DisposeGlobalVariablesAfterLowering controls whether the memory used by
    405   /// GlobaleVariables can be reclaimed right after they have been lowered.
    406   /// @{
    407   bool getDisposeGlobalVariablesAfterLowering() const {
    408     return DisposeGlobalVariablesAfterLowering;
    409   }
    410 
    411   void setDisposeGlobalVariablesAfterLowering(bool Value) {
    412     DisposeGlobalVariablesAfterLowering = Value;
    413   }
    414   /// @}
    415 
    416   LockedPtr<StringPool> getStrings() const {
    417     return LockedPtr<StringPool>(Strings.get(), &StringsLock);
    418   }
    419 
    420   LockedPtr<VariableDeclarationList> getGlobals() {
    421     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock);
    422   }
    423 
    424   /// Number of function blocks that can be queued before waiting for
    425   /// translation
    426   /// threads to consume.
    427   static constexpr size_t MaxOptQSize = 1 << 16;
    428 
    429 private:
    430   // Try to ensure mutexes are allocated on separate cache lines.
    431 
    432   // Destructors collaborate with Allocator
    433   ICE_CACHELINE_BOUNDARY;
    434   // Managed by getAllocator()
    435   mutable GlobalLockType AllocLock;
    436   ArenaAllocator Allocator;
    437 
    438   ICE_CACHELINE_BOUNDARY;
    439   // Managed by getInitializerAllocator()
    440   mutable GlobalLockType InitAllocLock;
    441   VariableDeclarationList Globals;
    442 
    443   ICE_CACHELINE_BOUNDARY;
    444   // Managed by getDestructors()
    445   using DestructorArray = std::vector<std::function<void()>>;
    446   mutable GlobalLockType DestructorsLock;
    447   DestructorArray Destructors;
    448 
    449   ICE_CACHELINE_BOUNDARY;
    450   // Managed by getStrings()
    451   mutable GlobalLockType StringsLock;
    452   std::unique_ptr<StringPool> Strings;
    453 
    454   ICE_CACHELINE_BOUNDARY;
    455   // Managed by getConstPool()
    456   mutable GlobalLockType ConstPoolLock;
    457   std::unique_ptr<ConstantPool> ConstPool;
    458 
    459   ICE_CACHELINE_BOUNDARY;
    460   // Managed by getJumpTableList()
    461   mutable GlobalLockType JumpTablesLock;
    462   JumpTableDataList JumpTableList;
    463 
    464   ICE_CACHELINE_BOUNDARY;
    465   // Managed by getErrorStatus()
    466   mutable GlobalLockType ErrorStatusLock;
    467   ErrorCode ErrorStatus;
    468 
    469   ICE_CACHELINE_BOUNDARY;
    470   // Managed by getStatsCumulative()
    471   mutable GlobalLockType StatsLock;
    472   CodeStats StatsCumulative;
    473 
    474   ICE_CACHELINE_BOUNDARY;
    475   // Managed by getTimers()
    476   mutable GlobalLockType TimerLock;
    477   TimerList Timers;
    478 
    479   ICE_CACHELINE_BOUNDARY;
    480   /// StrLock is a global lock on the dump and emit output streams.
    481   using StrLockType = std::mutex;
    482   StrLockType StrLock;
    483   Ostream *StrDump;  /// Stream for dumping / diagnostics
    484   Ostream *StrEmit;  /// Stream for code emission
    485   Ostream *StrError; /// Stream for logging errors.
    486 
    487   // True if waitForWorkerThreads() has been called.
    488   std::atomic_bool WaitForWorkerThreadsCalled;
    489 
    490   ICE_CACHELINE_BOUNDARY;
    491 
    492   Intrinsics IntrinsicsInfo;
    493   // TODO(jpp): move to EmitterContext.
    494   std::unique_ptr<ELFObjectWriter> ObjectWriter;
    495   // Value defining when to wake up the main parse thread.
    496   const size_t OptQWakeupSize;
    497   BoundedProducerConsumerQueue<OptWorkItem, MaxOptQSize> OptQ;
    498   BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ;
    499   // DataLowering is only ever used by a single thread at a time (either in
    500   // emitItems(), or in IceCompiler::run before the compilation is over.)
    501   // TODO(jpp): move to EmitterContext.
    502   std::unique_ptr<TargetDataLowering> DataLowering;
    503   /// If !HasEmittedCode, SubZero will accumulate all Globals (which are "true"
    504   /// program global variables) until the first code WorkItem is seen.
    505   // TODO(jpp): move to EmitterContext.
    506   bool HasSeenCode = false;
    507   // If Instrumentor is not empty then it will be used to instrument globals and
    508   // CFGs.
    509   std::unique_ptr<Instrumentation> Instrumentor = nullptr;
    510   // TODO(jpp): move to EmitterContext.
    511   VariableDeclaration *ProfileBlockInfoVarDecl = nullptr;
    512   std::vector<VariableDeclaration *> ProfileBlockInfos;
    513   /// Indicates if global variable declarations can be disposed of right after
    514   /// lowering.
    515   bool DisposeGlobalVariablesAfterLowering = true;
    516   Constant *ConstZeroForType[IceType_NUM];
    517   Constant *ConstantTrue;
    518   // Holds the constants representing each runtime helper function.
    519   Constant *RuntimeHelperFunc[static_cast<size_t>(RuntimeHelper::H_Num)];
    520 
    521   Constant *getConstantZeroInternal(Type Ty);
    522   Constant *getConstantIntInternal(Type Ty, int64_t Value);
    523   Constant *getConstantInt1Internal(int8_t ConstantInt1);
    524   Constant *getConstantInt8Internal(int8_t ConstantInt8);
    525   Constant *getConstantInt16Internal(int16_t ConstantInt16);
    526   Constant *getConstantInt32Internal(int32_t ConstantInt32);
    527   Constant *getConstantInt64Internal(int64_t ConstantInt64);
    528   LockedPtr<ArenaAllocator> getAllocator() {
    529     return LockedPtr<ArenaAllocator>(&Allocator, &AllocLock);
    530   }
    531   LockedPtr<VariableDeclarationList> getInitializerAllocator() {
    532     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock);
    533   }
    534   LockedPtr<ConstantPool> getConstPool() {
    535     return LockedPtr<ConstantPool>(ConstPool.get(), &ConstPoolLock);
    536   }
    537   LockedPtr<JumpTableDataList> getJumpTableList() {
    538     return LockedPtr<JumpTableDataList>(&JumpTableList, &JumpTablesLock);
    539   }
    540   LockedPtr<CodeStats> getStatsCumulative() {
    541     return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock);
    542   }
    543   LockedPtr<TimerList> getTimers() {
    544     return LockedPtr<TimerList>(&Timers, &TimerLock);
    545   }
    546   LockedPtr<DestructorArray> getDestructors() {
    547     return LockedPtr<DestructorArray>(&Destructors, &DestructorsLock);
    548   }
    549 
    550   void accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls) {
    551     LockedPtr<VariableDeclarationList> _(&Globals, &InitAllocLock);
    552     if (Globls != nullptr) {
    553       Globals.merge(Globls.get());
    554       if (!BuildDefs::minimal() && Instrumentor != nullptr)
    555         Instrumentor->setHasSeenGlobals();
    556     }
    557   }
    558 
    559   void lowerGlobalsIfNoCodeHasBeenSeen() {
    560     if (HasSeenCode)
    561       return;
    562     constexpr char NoSuffix[] = "";
    563     lowerGlobals(NoSuffix);
    564     HasSeenCode = true;
    565   }
    566 
    567   void saveBlockInfoPtrs();
    568 
    569   llvm::SmallVector<ThreadContext *, 128> AllThreadContexts;
    570   llvm::SmallVector<std::thread, 128> TranslationThreads;
    571   llvm::SmallVector<std::thread, 128> EmitterThreads;
    572   // Each thread has its own TLS pointer which is also held in
    573   // AllThreadContexts.
    574   ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS);
    575 
    576 public:
    577   static void TlsInit();
    578 };
    579 
    580 /// Helper class to push and pop a timer marker. The constructor pushes a
    581 /// marker, and the destructor pops it. This is for convenient timing of regions
    582 /// of code.
    583 class TimerMarker {
    584   TimerMarker() = delete;
    585   TimerMarker(const TimerMarker &) = delete;
    586   TimerMarker &operator=(const TimerMarker &) = delete;
    587 
    588 public:
    589   TimerMarker(TimerIdT ID, GlobalContext *Ctx,
    590               TimerStackIdT StackID = GlobalContext::TSK_Default)
    591       : ID(ID), Ctx(Ctx), StackID(StackID) {
    592     if (BuildDefs::timers())
    593       push();
    594   }
    595   TimerMarker(TimerIdT ID, const Cfg *Func,
    596               TimerStackIdT StackID = GlobalContext::TSK_Default)
    597       : ID(ID), Ctx(nullptr), StackID(StackID) {
    598     // Ctx gets set at the beginning of pushCfg().
    599     if (BuildDefs::timers())
    600       pushCfg(Func);
    601   }
    602   TimerMarker(GlobalContext *Ctx, const std::string &FuncName)
    603       : ID(getTimerIdFromFuncName(Ctx, FuncName)), Ctx(Ctx),
    604         StackID(GlobalContext::TSK_Funcs) {
    605     if (BuildDefs::timers())
    606       push();
    607   }
    608 
    609   ~TimerMarker() {
    610     if (BuildDefs::timers() && Active)
    611       Ctx->popTimer(ID, StackID);
    612   }
    613 
    614 private:
    615   void push();
    616   void pushCfg(const Cfg *Func);
    617   static TimerIdT getTimerIdFromFuncName(GlobalContext *Ctx,
    618                                          const std::string &FuncName);
    619   const TimerIdT ID;
    620   GlobalContext *Ctx;
    621   const TimerStackIdT StackID;
    622   bool Active = false;
    623 };
    624 
    625 /// Helper class for locking the streams and then automatically unlocking them.
    626 class OstreamLocker {
    627 private:
    628   OstreamLocker() = delete;
    629   OstreamLocker(const OstreamLocker &) = delete;
    630   OstreamLocker &operator=(const OstreamLocker &) = delete;
    631 
    632 public:
    633   explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); }
    634   ~OstreamLocker() { Ctx->unlockStr(); }
    635 
    636 private:
    637   GlobalContext *const Ctx;
    638 };
    639 
    640 } // end of namespace Ice
    641 
    642 #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H
    643