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