| //===- subzero/src/IceGlobalContext.h - Global context defs -----*- C++ -*-===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Declares aspects of the compilation that persist across multiple | 
 | /// functions. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H | 
 | #define SUBZERO_SRC_ICEGLOBALCONTEXT_H | 
 |  | 
 | #include "IceDefs.h" | 
 | #include "IceClFlags.h" | 
 | #include "IceInstrumentation.h" | 
 | #include "IceIntrinsics.h" | 
 | #include "IceRNG.h" | 
 | #include "IceStringPool.h" | 
 | #include "IceSwitchLowering.h" | 
 | #include "IceTargetLowering.def" | 
 | #include "IceThreading.h" | 
 | #include "IceTimerTree.h" | 
 | #include "IceTypes.h" | 
 | #include "IceUtils.h" | 
 |  | 
 | #include <array> | 
 | #include <atomic> | 
 | #include <cassert> | 
 | #include <functional> | 
 | #include <memory> | 
 | #include <mutex> | 
 | #include <thread> | 
 | #include <type_traits> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | namespace Ice { | 
 |  | 
 | class ConstantPool; | 
 | class EmitterWorkItem; | 
 | class FuncSigType; | 
 | class Instrumentation; | 
 |  | 
 | // Runtime helper function IDs | 
 |  | 
 | enum class RuntimeHelper { | 
 | #define X(Tag, Name) H_##Tag, | 
 |   RUNTIME_HELPER_FUNCTIONS_TABLE | 
 | #undef X | 
 |       H_Num | 
 | }; | 
 |  | 
 | /// OptWorkItem is a simple wrapper used to pass parse information on a function | 
 | /// block, to a translator thread. | 
 | class OptWorkItem { | 
 |   OptWorkItem(const OptWorkItem &) = delete; | 
 |   OptWorkItem &operator=(const OptWorkItem &) = delete; | 
 |  | 
 | public: | 
 |   // Get the Cfg for the funtion to translate. | 
 |   virtual std::unique_ptr<Cfg> getParsedCfg() = 0; | 
 |   virtual ~OptWorkItem() = default; | 
 |  | 
 | protected: | 
 |   OptWorkItem() = default; | 
 | }; | 
 |  | 
 | class GlobalContext { | 
 |   GlobalContext() = delete; | 
 |   GlobalContext(const GlobalContext &) = delete; | 
 |   GlobalContext &operator=(const GlobalContext &) = delete; | 
 |  | 
 |   /// CodeStats collects rudimentary statistics during translation. | 
 |   class CodeStats { | 
 |     CodeStats(const CodeStats &) = delete; | 
 |     CodeStats &operator=(const CodeStats &) = default; | 
 | #define CODESTATS_TABLE                                                        \ | 
 |   /* dump string, enum value */                                                \ | 
 |   X("Inst Count  ", InstCount)                                                 \ | 
 |   X("Regs Saved  ", RegsSaved)                                                 \ | 
 |   X("Frame Bytes ", FrameByte)                                                 \ | 
 |   X("Spills      ", NumSpills)                                                 \ | 
 |   X("Fills       ", NumFills)                                                  \ | 
 |   X("R/P Imms    ", NumRPImms) | 
 |     //#define X(str, tag) | 
 |  | 
 |   public: | 
 |     enum CSTag { | 
 | #define X(str, tag) CS_##tag, | 
 |       CODESTATS_TABLE | 
 | #undef X | 
 |           CS_NUM | 
 |     }; | 
 |     CodeStats() { reset(); } | 
 |     void reset() { Stats.fill(0); } | 
 |     void update(CSTag Tag, uint32_t Count = 1) { | 
 |       assert(static_cast<SizeT>(Tag) < Stats.size()); | 
 |       Stats[Tag] += Count; | 
 |     } | 
 |     void add(const CodeStats &Other) { | 
 |       for (uint32_t i = 0; i < Stats.size(); ++i) | 
 |         Stats[i] += Other.Stats[i]; | 
 |     } | 
 |     /// Dumps the stats for the given Cfg.  If Func==nullptr, it identifies it | 
 |     /// as the "final" cumulative stats instead as a specific function's name. | 
 |     void dump(const Cfg *Func, GlobalContext *Ctx); | 
 |  | 
 |   private: | 
 |     std::array<uint32_t, CS_NUM> Stats; | 
 |   }; | 
 |  | 
 |   /// TimerList is a vector of TimerStack objects, with extra methods | 
 |   /// to initialize and merge these vectors. | 
 |   class TimerList : public std::vector<TimerStack> { | 
 |     TimerList(const TimerList &) = delete; | 
 |     TimerList &operator=(const TimerList &) = delete; | 
 |  | 
 |   public: | 
 |     TimerList() = default; | 
 |     /// initInto() initializes a target list of timers based on the | 
 |     /// current list.  In particular, it creates the same number of | 
 |     /// timers, in the same order, with the same names, but initially | 
 |     /// empty of timing data. | 
 |     void initInto(TimerList &Dest) const { | 
 |       if (!BuildDefs::timers()) | 
 |         return; | 
 |       Dest.clear(); | 
 |       for (const TimerStack &Stack : *this) { | 
 |         Dest.push_back(TimerStack(Stack.getName())); | 
 |       } | 
 |     } | 
 |     void mergeFrom(TimerList &Src) { | 
 |       if (!BuildDefs::timers()) | 
 |         return; | 
 |       assert(size() == Src.size()); | 
 |       size_type i = 0; | 
 |       for (TimerStack &Stack : *this) { | 
 |         assert(Stack.getName() == Src[i].getName()); | 
 |         Stack.mergeFrom(Src[i]); | 
 |         ++i; | 
 |       } | 
 |     } | 
 |   }; | 
 |  | 
 |   /// ThreadContext contains thread-local data.  This data can be | 
 |   /// combined/reduced as needed after all threads complete. | 
 |   class ThreadContext { | 
 |     ThreadContext(const ThreadContext &) = delete; | 
 |     ThreadContext &operator=(const ThreadContext &) = delete; | 
 |  | 
 |   public: | 
 |     ThreadContext() = default; | 
 |     CodeStats StatsFunction; | 
 |     CodeStats StatsCumulative; | 
 |     TimerList Timers; | 
 |   }; | 
 |  | 
 | public: | 
 |   /// The dump stream is a log stream while emit is the stream code | 
 |   /// is emitted to. The error stream is strictly for logging errors. | 
 |   GlobalContext(Ostream *OsDump, Ostream *OsEmit, Ostream *OsError, | 
 |                 ELFStreamer *ELFStreamer); | 
 |   ~GlobalContext(); | 
 |  | 
 |   void dumpStrings(); | 
 |   /// | 
 |   /// The dump, error, and emit streams need to be used by only one | 
 |   /// thread at a time.  This is done by exclusively reserving the | 
 |   /// streams via lockStr() and unlockStr().  The OstreamLocker class | 
 |   /// can be used to conveniently manage this. | 
 |   /// | 
 |   /// The model is that a thread grabs the stream lock, then does an | 
 |   /// arbitrary amount of work during which far-away callees may grab | 
 |   /// the stream and do something with it, and finally the thread | 
 |   /// releases the stream lock.  This allows large chunks of output to | 
 |   /// be dumped or emitted without risking interleaving from multiple | 
 |   /// threads. | 
 |   void lockStr() { StrLock.lock(); } | 
 |   void unlockStr() { StrLock.unlock(); } | 
 |   Ostream &getStrDump() { return *StrDump; } | 
 |   Ostream &getStrError() { return *StrError; } | 
 |   Ostream &getStrEmit() { return *StrEmit; } | 
 |   void setStrEmit(Ostream &NewStrEmit) { StrEmit = &NewStrEmit; } | 
 |  | 
 |   LockedPtr<ErrorCode> getErrorStatus() { | 
 |     return LockedPtr<ErrorCode>(&ErrorStatus, &ErrorStatusLock); | 
 |   } | 
 |  | 
 |   /// \name Manage Constants. | 
 |   /// @{ | 
 |   // getConstant*() functions are not const because they might add something to | 
 |   // the constant pool. | 
 |   Constant *getConstantInt(Type Ty, int64_t Value); | 
 |   Constant *getConstantInt1(int8_t ConstantInt1) { | 
 |     ConstantInt1 &= INT8_C(1); | 
 |     switch (ConstantInt1) { | 
 |     case 0: | 
 |       return getConstantZero(IceType_i1); | 
 |     case 1: | 
 |       return ConstantTrue; | 
 |     default: | 
 |       assert(false && "getConstantInt1 not on true/false"); | 
 |       return getConstantInt1Internal(ConstantInt1); | 
 |     } | 
 |   } | 
 |   Constant *getConstantInt8(int8_t ConstantInt8) { | 
 |     switch (ConstantInt8) { | 
 |     case 0: | 
 |       return getConstantZero(IceType_i8); | 
 |     default: | 
 |       return getConstantInt8Internal(ConstantInt8); | 
 |     } | 
 |   } | 
 |   Constant *getConstantInt16(int16_t ConstantInt16) { | 
 |     switch (ConstantInt16) { | 
 |     case 0: | 
 |       return getConstantZero(IceType_i16); | 
 |     default: | 
 |       return getConstantInt16Internal(ConstantInt16); | 
 |     } | 
 |   } | 
 |   Constant *getConstantInt32(int32_t ConstantInt32) { | 
 |     switch (ConstantInt32) { | 
 |     case 0: | 
 |       return getConstantZero(IceType_i32); | 
 |     default: | 
 |       return getConstantInt32Internal(ConstantInt32); | 
 |     } | 
 |   } | 
 |   Constant *getConstantInt64(int64_t ConstantInt64) { | 
 |     switch (ConstantInt64) { | 
 |     case 0: | 
 |       return getConstantZero(IceType_i64); | 
 |     default: | 
 |       return getConstantInt64Internal(ConstantInt64); | 
 |     } | 
 |   } | 
 |   Constant *getConstantFloat(float Value); | 
 |   Constant *getConstantDouble(double Value); | 
 |   /// Returns a symbolic constant. | 
 |   Constant *getConstantSymWithEmitString(const RelocOffsetT Offset, | 
 |                                          const RelocOffsetArray &OffsetExpr, | 
 |                                          GlobalString Name, | 
 |                                          const std::string &EmitString); | 
 |   Constant *getConstantSym(RelocOffsetT Offset, GlobalString Name); | 
 |   Constant *getConstantExternSym(GlobalString Name); | 
 |   /// Returns an undef. | 
 |   Constant *getConstantUndef(Type Ty); | 
 |   /// Returns a zero value. | 
 |   Constant *getConstantZero(Type Ty); | 
 |   /// getConstantPool() returns a copy of the constant pool for constants of a | 
 |   /// given type. | 
 |   ConstantList getConstantPool(Type Ty); | 
 |   /// Returns a copy of the list of external symbols. | 
 |   ConstantList getConstantExternSyms(); | 
 |   /// @} | 
 |   Constant *getRuntimeHelperFunc(RuntimeHelper FuncID) const { | 
 |     assert(FuncID < RuntimeHelper::H_Num); | 
 |     Constant *Result = RuntimeHelperFunc[static_cast<size_t>(FuncID)]; | 
 |     assert(Result != nullptr && "No such runtime helper function"); | 
 |     return Result; | 
 |   } | 
 |   GlobalString getGlobalString(const std::string &Name); | 
 |  | 
 |   /// Return a locked pointer to the registered jump tables. | 
 |   JumpTableDataList getJumpTables(); | 
 |   /// Adds JumpTable to the list of know jump tables, for a posteriori emission. | 
 |   void addJumpTableData(JumpTableData JumpTable); | 
 |  | 
 |   /// Allocate data of type T using the global allocator. We allow entities | 
 |   /// allocated from this global allocator to be either trivially or | 
 |   /// non-trivially destructible. We optimize the case when T is trivially | 
 |   /// destructible by not registering a destructor. Destructors will be invoked | 
 |   /// during GlobalContext destruction in the reverse object creation order. | 
 |   template <typename T> | 
 |   typename std::enable_if<std::is_trivially_destructible<T>::value, T>::type * | 
 |   allocate() { | 
 |     return getAllocator()->Allocate<T>(); | 
 |   } | 
 |  | 
 |   template <typename T> | 
 |   typename std::enable_if<!std::is_trivially_destructible<T>::value, T>::type * | 
 |   allocate() { | 
 |     T *Ret = getAllocator()->Allocate<T>(); | 
 |     getDestructors()->emplace_back([Ret]() { Ret->~T(); }); | 
 |     return Ret; | 
 |   } | 
 |  | 
 |   const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; } | 
 |  | 
 |   ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); } | 
 |  | 
 |   /// Reset stats at the beginning of a function. | 
 |   void resetStats(); | 
 |   void dumpStats(const Cfg *Func = nullptr); | 
 |   void statsUpdateEmitted(uint32_t InstCount); | 
 |   void statsUpdateRegistersSaved(uint32_t Num); | 
 |   void statsUpdateFrameBytes(uint32_t Bytes); | 
 |   void statsUpdateSpills(); | 
 |   void statsUpdateFills(); | 
 |  | 
 |   /// Number of Randomized or Pooled Immediates | 
 |   void statsUpdateRPImms(); | 
 |  | 
 |   /// These are predefined TimerStackIdT values. | 
 |   enum TimerStackKind { TSK_Default = 0, TSK_Funcs, TSK_Num }; | 
 |  | 
 |   /// newTimerStackID() creates a new TimerStack in the global space. It does | 
 |   /// not affect any TimerStack objects in TLS. | 
 |   TimerStackIdT newTimerStackID(const std::string &Name); | 
 |   /// dumpTimers() dumps the global timer data.  This assumes all the | 
 |   /// thread-local copies of timer data have been merged into the global timer | 
 |   /// data. | 
 |   void dumpTimers(TimerStackIdT StackID = TSK_Default, | 
 |                   bool DumpCumulative = true); | 
 |   void dumpLocalTimers(const std::string &TimerNameOverride, | 
 |                        TimerStackIdT StackID = TSK_Default, | 
 |                        bool DumpCumulative = true); | 
 |   /// The following methods affect only the calling thread's TLS timer data. | 
 |   TimerIdT getTimerID(TimerStackIdT StackID, const std::string &Name); | 
 |   void pushTimer(TimerIdT ID, TimerStackIdT StackID); | 
 |   void popTimer(TimerIdT ID, TimerStackIdT StackID); | 
 |   void resetTimer(TimerStackIdT StackID); | 
 |   std::string getTimerName(TimerStackIdT StackID); | 
 |   void setTimerName(TimerStackIdT StackID, const std::string &NewName); | 
 |  | 
 |   /// This is the first work item sequence number that the parser produces, and | 
 |   /// correspondingly the first sequence number that the emitter thread will | 
 |   /// wait for. Start numbering at 1 to leave room for a sentinel, in case e.g. | 
 |   /// we wish to inject items with a special sequence number that may be | 
 |   /// executed out of order. | 
 |   static constexpr uint32_t getFirstSequenceNumber() { return 1; } | 
 |   /// Adds a newly parsed and constructed function to the Cfg work queue. | 
 |   /// Notifies any idle workers that a new function is available for | 
 |   /// translating. May block if the work queue is too large, in order to control | 
 |   /// memory footprint. | 
 |   void optQueueBlockingPush(std::unique_ptr<OptWorkItem> Item); | 
 |   /// Takes a Cfg from the work queue for translating. May block if the work | 
 |   /// queue is currently empty. Returns nullptr if there is no more work - the | 
 |   /// queue is empty and either end() has been called or the Sequential flag was | 
 |   /// set. | 
 |   std::unique_ptr<OptWorkItem> optQueueBlockingPop(); | 
 |   /// Notifies that no more work will be added to the work queue. | 
 |   void optQueueNotifyEnd() { OptQ.notifyEnd(); } | 
 |  | 
 |   /// Emit file header for output file. | 
 |   void emitFileHeader(); | 
 |  | 
 |   void lowerConstants(); | 
 |  | 
 |   void lowerJumpTables(); | 
 |  | 
 |   /// Emit target specific read-only data sections if any. E.g., for MIPS this | 
 |   /// generates a .MIPS.abiflags section. | 
 |   void emitTargetRODataSections(); | 
 |  | 
 |   void emitQueueBlockingPush(std::unique_ptr<EmitterWorkItem> Item); | 
 |   std::unique_ptr<EmitterWorkItem> emitQueueBlockingPop(); | 
 |   void emitQueueNotifyEnd() { EmitQ.notifyEnd(); } | 
 |  | 
 |   void initParserThread(); | 
 |   void startWorkerThreads(); | 
 |  | 
 |   void waitForWorkerThreads(); | 
 |  | 
 |   /// sets the instrumentation object to use. | 
 |   void setInstrumentation(std::unique_ptr<Instrumentation> Instr) { | 
 |     if (!BuildDefs::minimal()) | 
 |       Instrumentor = std::move(Instr); | 
 |   } | 
 |  | 
 |   void instrumentFunc(Cfg *Func) { | 
 |     if (!BuildDefs::minimal() && Instrumentor) | 
 |       Instrumentor->instrumentFunc(Func); | 
 |   } | 
 |  | 
 |   /// Translation thread startup routine. | 
 |   void translateFunctionsWrapper(ThreadContext *MyTLS); | 
 |   /// Translate functions from the Cfg queue until the queue is empty. | 
 |   void translateFunctions(); | 
 |  | 
 |   /// Emitter thread startup routine. | 
 |   void emitterWrapper(ThreadContext *MyTLS); | 
 |   /// Emit functions and global initializers from the emitter queue until the | 
 |   /// queue is empty. | 
 |   void emitItems(); | 
 |  | 
 |   /// Uses DataLowering to lower Globals. Side effects: | 
 |   ///  - discards the initializer list for the global variable in Globals. | 
 |   ///  - clears the Globals array. | 
 |   void lowerGlobals(const std::string &SectionSuffix); | 
 |  | 
 |   /// Lowers the profile information. | 
 |   void lowerProfileData(); | 
 |  | 
 |   void dumpConstantLookupCounts(); | 
 |  | 
 |   /// DisposeGlobalVariablesAfterLowering controls whether the memory used by | 
 |   /// GlobaleVariables can be reclaimed right after they have been lowered. | 
 |   /// @{ | 
 |   bool getDisposeGlobalVariablesAfterLowering() const { | 
 |     return DisposeGlobalVariablesAfterLowering; | 
 |   } | 
 |  | 
 |   void setDisposeGlobalVariablesAfterLowering(bool Value) { | 
 |     DisposeGlobalVariablesAfterLowering = Value; | 
 |   } | 
 |   /// @} | 
 |  | 
 |   LockedPtr<StringPool> getStrings() const { | 
 |     return LockedPtr<StringPool>(Strings.get(), &StringsLock); | 
 |   } | 
 |  | 
 |   LockedPtr<VariableDeclarationList> getGlobals() { | 
 |     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock); | 
 |   } | 
 |  | 
 |   /// Number of function blocks that can be queued before waiting for | 
 |   /// translation | 
 |   /// threads to consume. | 
 |   static constexpr size_t MaxOptQSize = 1 << 16; | 
 |  | 
 | private: | 
 |   // Try to ensure mutexes are allocated on separate cache lines. | 
 |  | 
 |   // Destructors collaborate with Allocator | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getAllocator() | 
 |   mutable GlobalLockType AllocLock; | 
 |   ArenaAllocator Allocator; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getInitializerAllocator() | 
 |   mutable GlobalLockType InitAllocLock; | 
 |   VariableDeclarationList Globals; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getDestructors() | 
 |   using DestructorArray = std::vector<std::function<void()>>; | 
 |   mutable GlobalLockType DestructorsLock; | 
 |   DestructorArray Destructors; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getStrings() | 
 |   mutable GlobalLockType StringsLock; | 
 |   std::unique_ptr<StringPool> Strings; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getConstPool() | 
 |   mutable GlobalLockType ConstPoolLock; | 
 |   std::unique_ptr<ConstantPool> ConstPool; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getJumpTableList() | 
 |   mutable GlobalLockType JumpTablesLock; | 
 |   JumpTableDataList JumpTableList; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getErrorStatus() | 
 |   mutable GlobalLockType ErrorStatusLock; | 
 |   ErrorCode ErrorStatus; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getStatsCumulative() | 
 |   mutable GlobalLockType StatsLock; | 
 |   CodeStats StatsCumulative; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   // Managed by getTimers() | 
 |   mutable GlobalLockType TimerLock; | 
 |   TimerList Timers; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |   /// StrLock is a global lock on the dump and emit output streams. | 
 |   using StrLockType = std::mutex; | 
 |   StrLockType StrLock; | 
 |   Ostream *StrDump;  /// Stream for dumping / diagnostics | 
 |   Ostream *StrEmit;  /// Stream for code emission | 
 |   Ostream *StrError; /// Stream for logging errors. | 
 |  | 
 |   // True if waitForWorkerThreads() has been called. | 
 |   std::atomic_bool WaitForWorkerThreadsCalled; | 
 |  | 
 |   ICE_CACHELINE_BOUNDARY; | 
 |  | 
 |   Intrinsics IntrinsicsInfo; | 
 |   // TODO(jpp): move to EmitterContext. | 
 |   std::unique_ptr<ELFObjectWriter> ObjectWriter; | 
 |   // Value defining when to wake up the main parse thread. | 
 |   const size_t OptQWakeupSize; | 
 |   BoundedProducerConsumerQueue<OptWorkItem, MaxOptQSize> OptQ; | 
 |   BoundedProducerConsumerQueue<EmitterWorkItem> EmitQ; | 
 |   // DataLowering is only ever used by a single thread at a time (either in | 
 |   // emitItems(), or in IceCompiler::run before the compilation is over.) | 
 |   // TODO(jpp): move to EmitterContext. | 
 |   std::unique_ptr<TargetDataLowering> DataLowering; | 
 |   /// If !HasEmittedCode, SubZero will accumulate all Globals (which are "true" | 
 |   /// program global variables) until the first code WorkItem is seen. | 
 |   // TODO(jpp): move to EmitterContext. | 
 |   bool HasSeenCode = false; | 
 |   // If Instrumentor is not empty then it will be used to instrument globals and | 
 |   // CFGs. | 
 |   std::unique_ptr<Instrumentation> Instrumentor = nullptr; | 
 |   // TODO(jpp): move to EmitterContext. | 
 |   VariableDeclaration *ProfileBlockInfoVarDecl = nullptr; | 
 |   std::vector<VariableDeclaration *> ProfileBlockInfos; | 
 |   /// Indicates if global variable declarations can be disposed of right after | 
 |   /// lowering. | 
 |   bool DisposeGlobalVariablesAfterLowering = true; | 
 |   Constant *ConstZeroForType[IceType_NUM]; | 
 |   Constant *ConstantTrue; | 
 |   // Holds the constants representing each runtime helper function. | 
 |   Constant *RuntimeHelperFunc[static_cast<size_t>(RuntimeHelper::H_Num)]; | 
 |  | 
 |   Constant *getConstantZeroInternal(Type Ty); | 
 |   Constant *getConstantIntInternal(Type Ty, int64_t Value); | 
 |   Constant *getConstantInt1Internal(int8_t ConstantInt1); | 
 |   Constant *getConstantInt8Internal(int8_t ConstantInt8); | 
 |   Constant *getConstantInt16Internal(int16_t ConstantInt16); | 
 |   Constant *getConstantInt32Internal(int32_t ConstantInt32); | 
 |   Constant *getConstantInt64Internal(int64_t ConstantInt64); | 
 |   LockedPtr<ArenaAllocator> getAllocator() { | 
 |     return LockedPtr<ArenaAllocator>(&Allocator, &AllocLock); | 
 |   } | 
 |   LockedPtr<VariableDeclarationList> getInitializerAllocator() { | 
 |     return LockedPtr<VariableDeclarationList>(&Globals, &InitAllocLock); | 
 |   } | 
 |   LockedPtr<ConstantPool> getConstPool() { | 
 |     return LockedPtr<ConstantPool>(ConstPool.get(), &ConstPoolLock); | 
 |   } | 
 |   LockedPtr<JumpTableDataList> getJumpTableList() { | 
 |     return LockedPtr<JumpTableDataList>(&JumpTableList, &JumpTablesLock); | 
 |   } | 
 |   LockedPtr<CodeStats> getStatsCumulative() { | 
 |     return LockedPtr<CodeStats>(&StatsCumulative, &StatsLock); | 
 |   } | 
 |   LockedPtr<TimerList> getTimers() { | 
 |     return LockedPtr<TimerList>(&Timers, &TimerLock); | 
 |   } | 
 |   LockedPtr<DestructorArray> getDestructors() { | 
 |     return LockedPtr<DestructorArray>(&Destructors, &DestructorsLock); | 
 |   } | 
 |  | 
 |   void accumulateGlobals(std::unique_ptr<VariableDeclarationList> Globls) { | 
 |     LockedPtr<VariableDeclarationList> _(&Globals, &InitAllocLock); | 
 |     if (Globls != nullptr) { | 
 |       Globals.merge(Globls.get()); | 
 |       if (!BuildDefs::minimal() && Instrumentor != nullptr) | 
 |         Instrumentor->setHasSeenGlobals(); | 
 |     } | 
 |   } | 
 |  | 
 |   void lowerGlobalsIfNoCodeHasBeenSeen() { | 
 |     if (HasSeenCode) | 
 |       return; | 
 |     constexpr char NoSuffix[] = ""; | 
 |     lowerGlobals(NoSuffix); | 
 |     HasSeenCode = true; | 
 |   } | 
 |  | 
 |   void saveBlockInfoPtrs(); | 
 |  | 
 |   llvm::SmallVector<ThreadContext *, 128> AllThreadContexts; | 
 |   llvm::SmallVector<std::thread, 128> TranslationThreads; | 
 |   llvm::SmallVector<std::thread, 128> EmitterThreads; | 
 |   // Each thread has its own TLS pointer which is also held in | 
 |   // AllThreadContexts. | 
 |   ICE_TLS_DECLARE_FIELD(ThreadContext *, TLS); | 
 |  | 
 | public: | 
 |   static void TlsInit(); | 
 | }; | 
 |  | 
 | /// Helper class to push and pop a timer marker. The constructor pushes a | 
 | /// marker, and the destructor pops it. This is for convenient timing of regions | 
 | /// of code. | 
 | class TimerMarker { | 
 |   TimerMarker() = delete; | 
 |   TimerMarker(const TimerMarker &) = delete; | 
 |   TimerMarker &operator=(const TimerMarker &) = delete; | 
 |  | 
 | public: | 
 |   TimerMarker(TimerIdT ID, GlobalContext *Ctx, | 
 |               TimerStackIdT StackID = GlobalContext::TSK_Default) | 
 |       : ID(ID), Ctx(Ctx), StackID(StackID) { | 
 |     if (BuildDefs::timers()) | 
 |       push(); | 
 |   } | 
 |   TimerMarker(TimerIdT ID, const Cfg *Func, | 
 |               TimerStackIdT StackID = GlobalContext::TSK_Default) | 
 |       : ID(ID), Ctx(nullptr), StackID(StackID) { | 
 |     // Ctx gets set at the beginning of pushCfg(). | 
 |     if (BuildDefs::timers()) | 
 |       pushCfg(Func); | 
 |   } | 
 |   TimerMarker(GlobalContext *Ctx, const std::string &FuncName) | 
 |       : ID(getTimerIdFromFuncName(Ctx, FuncName)), Ctx(Ctx), | 
 |         StackID(GlobalContext::TSK_Funcs) { | 
 |     if (BuildDefs::timers()) | 
 |       push(); | 
 |   } | 
 |  | 
 |   ~TimerMarker() { | 
 |     if (BuildDefs::timers() && Active) | 
 |       Ctx->popTimer(ID, StackID); | 
 |   } | 
 |  | 
 | private: | 
 |   void push(); | 
 |   void pushCfg(const Cfg *Func); | 
 |   static TimerIdT getTimerIdFromFuncName(GlobalContext *Ctx, | 
 |                                          const std::string &FuncName); | 
 |   const TimerIdT ID; | 
 |   GlobalContext *Ctx; | 
 |   const TimerStackIdT StackID; | 
 |   bool Active = false; | 
 | }; | 
 |  | 
 | /// Helper class for locking the streams and then automatically unlocking them. | 
 | class OstreamLocker { | 
 | private: | 
 |   OstreamLocker() = delete; | 
 |   OstreamLocker(const OstreamLocker &) = delete; | 
 |   OstreamLocker &operator=(const OstreamLocker &) = delete; | 
 |  | 
 | public: | 
 |   explicit OstreamLocker(GlobalContext *Ctx) : Ctx(Ctx) { Ctx->lockStr(); } | 
 |   ~OstreamLocker() { Ctx->unlockStr(); } | 
 |  | 
 | private: | 
 |   GlobalContext *const Ctx; | 
 | }; | 
 |  | 
 | } // end of namespace Ice | 
 |  | 
 | #endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H |