|  | //===- 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 |