Chromium Code Reviews| Index: src/IceGlobalContext.h |
| diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h |
| index cf2478fcb5c7d013401c67dace21b06432178592..a81abaed98dc439110b6b89ff32a4545b00bd62e 100644 |
| --- a/src/IceGlobalContext.h |
| +++ b/src/IceGlobalContext.h |
| @@ -16,6 +16,7 @@ |
| #define SUBZERO_SRC_ICEGLOBALCONTEXT_H |
| #include <memory> |
| +#include <mutex> |
| #include "IceDefs.h" |
| #include "IceClFlags.h" |
| @@ -29,37 +30,48 @@ namespace Ice { |
| class ClFlags; |
| class FuncSigType; |
| -// This class collects rudimentary statistics during translation. |
| -class CodeStats { |
| - CodeStats(const CodeStats &) = delete; |
| - CodeStats &operator=(const CodeStats &) = default; |
| +class GlobalContext { |
| + GlobalContext(const GlobalContext &) = delete; |
| + GlobalContext &operator=(const GlobalContext &) = delete; |
| -public: |
| - CodeStats() |
| + // CodeStats collects rudimentary statistics during translation. |
| + class CodeStats { |
| + CodeStats(const CodeStats &) = delete; |
| + CodeStats &operator=(const CodeStats &) = default; |
| + |
| + public: |
| + CodeStats() |
| : InstructionsEmitted(0), RegistersSaved(0), FrameBytes(0), Spills(0), |
| Fills(0) {} |
| - void reset() { *this = CodeStats(); } |
| - void updateEmitted(uint32_t InstCount) { InstructionsEmitted += InstCount; } |
| - void updateRegistersSaved(uint32_t Num) { RegistersSaved += Num; } |
| - void updateFrameBytes(uint32_t Bytes) { FrameBytes += Bytes; } |
| - void updateSpills() { ++Spills; } |
| - void updateFills() { ++Fills; } |
| - void dump(const IceString &Name, Ostream &Str); |
| + void reset() { *this = CodeStats(); } |
| + void updateEmitted(uint32_t InstCount) { InstructionsEmitted += InstCount; } |
| + void updateRegistersSaved(uint32_t Num) { RegistersSaved += Num; } |
| + void updateFrameBytes(uint32_t Bytes) { FrameBytes += Bytes; } |
| + void updateSpills() { ++Spills; } |
| + void updateFills() { ++Fills; } |
| + void dump(const IceString &Name, Ostream &Str); |
| + |
| + private: |
| + uint32_t InstructionsEmitted; |
| + uint32_t RegistersSaved; |
| + uint32_t FrameBytes; |
| + uint32_t Spills; |
| + uint32_t Fills; |
| + }; |
| -private: |
| - uint32_t InstructionsEmitted; |
| - uint32_t RegistersSaved; |
| - uint32_t FrameBytes; |
| - uint32_t Spills; |
| - uint32_t Fills; |
| -}; |
| + // 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() {} |
| + CodeStats StatsFunction; |
| + std::vector<TimerStack> Timers; |
| + }; |
| -// TODO: Accesses to all non-const fields of GlobalContext need to |
| -// be synchronized, especially the constant pool, the allocator, and |
| -// the output streams. |
| -class GlobalContext { |
| - GlobalContext(const GlobalContext &) = delete; |
| - GlobalContext &operator=(const GlobalContext &) = delete; |
| + typedef std::recursive_mutex GlobalLockType; |
| + typedef std::recursive_mutex StrLockType; |
|
JF
2015/01/15 01:31:01
Why recursive? I'd leave a TODO here to remove the
Jim Stichnoth
2015/01/15 07:16:29
(I moved this typedef down to where it's actually
|
| public: |
| GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer, |
| @@ -76,8 +88,38 @@ public: |
| void addVerbose(VerboseMask Mask) { VMask |= Mask; } |
| void subVerbose(VerboseMask Mask) { VMask &= ~Mask; } |
| - Ostream &getStrDump() { return *StrDump; } |
| - Ostream &getStrEmit() { return *StrEmit; } |
| + // The dump 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. |
| + void lockStr() { |
| + StrLock.lock(); |
| + assert(!IsStrLocked); |
| + IsStrLocked = true; |
| + } |
| + void unlockStr() { |
| + assert(IsStrLocked); |
| + IsStrLocked = false; |
| + StrLock.unlock(); |
| + } |
| + // Test whether we are already holding StrLock, by doing a |
| + // try_lock() and if it succeeds, checking that we didn't |
| + // recursively lock it. |
| + bool isStrLocked() { |
| + if (!StrLock.try_lock()) |
| + return false; |
| + bool WasLocked = IsStrLocked; |
| + StrLock.unlock(); |
| + return WasLocked; |
| + } |
| + Ostream &getStrDump() { |
| + assert(isStrLocked()); |
| + return *StrDump; |
| + } |
| + Ostream &getStrEmit() { |
| + assert(isStrLocked()); |
| + return *StrEmit; |
| + } |
| TargetArch getTargetArch() const { return Arch; } |
| OptLevel getOptLevel() const { return Opt; } |
| @@ -109,7 +151,7 @@ public: |
| Constant *getConstantZero(Type Ty); |
| // getConstantPool() returns a copy of the constant pool for |
| // constants of a given type. |
| - ConstantList getConstantPool(Type Ty) const; |
| + ConstantList getConstantPool(Type Ty); |
| // Returns a new function declaration, allocated in an internal |
| // memory pool. Ownership of the function is maintained by this |
| // class instance. |
| @@ -129,7 +171,10 @@ public: |
| } |
| // Allocate data of type T using the global allocator. |
| - template <typename T> T *allocate() { return Allocator.Allocate<T>(); } |
| + template <typename T> T *allocate() { |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| + return Allocator.Allocate<T>(); |
| + } |
| const Intrinsics &getIntrinsicsInfo() const { return IntrinsicsInfo; } |
| @@ -142,37 +187,42 @@ public: |
| // Reset stats at the beginning of a function. |
| void resetStats() { |
| if (ALLOW_DUMP) |
| - StatsFunction.reset(); |
| + TLS->StatsFunction.reset(); |
| } |
| void dumpStats(const IceString &Name, bool Final = false); |
| void statsUpdateEmitted(uint32_t InstCount) { |
| - if (!ALLOW_DUMP) |
| + if (!ALLOW_DUMP || !getFlags().DumpStats) |
| return; |
| - StatsFunction.updateEmitted(InstCount); |
| + TLS->StatsFunction.updateEmitted(InstCount); |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| StatsCumulative.updateEmitted(InstCount); |
| } |
| void statsUpdateRegistersSaved(uint32_t Num) { |
| - if (!ALLOW_DUMP) |
| + if (!ALLOW_DUMP || !getFlags().DumpStats) |
| return; |
| - StatsFunction.updateRegistersSaved(Num); |
| + TLS->StatsFunction.updateRegistersSaved(Num); |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| StatsCumulative.updateRegistersSaved(Num); |
| } |
| void statsUpdateFrameBytes(uint32_t Bytes) { |
| - if (!ALLOW_DUMP) |
| + if (!ALLOW_DUMP || !getFlags().DumpStats) |
| return; |
| - StatsFunction.updateFrameBytes(Bytes); |
| + TLS->StatsFunction.updateFrameBytes(Bytes); |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| StatsCumulative.updateFrameBytes(Bytes); |
| } |
| void statsUpdateSpills() { |
| - if (!ALLOW_DUMP) |
| + if (!ALLOW_DUMP || !getFlags().DumpStats) |
| return; |
| - StatsFunction.updateSpills(); |
| + TLS->StatsFunction.updateSpills(); |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| StatsCumulative.updateSpills(); |
| } |
| void statsUpdateFills() { |
| - if (!ALLOW_DUMP) |
| + if (!ALLOW_DUMP || !getFlags().DumpStats) |
| return; |
| - StatsFunction.updateFills(); |
| + TLS->StatsFunction.updateFills(); |
| + std::lock_guard<GlobalLockType> L(GlobalLock); |
| StatsCumulative.updateFills(); |
| } |
| @@ -183,8 +233,8 @@ public: |
| TSK_Num |
| }; |
| - TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name); |
| TimerStackIdT newTimerStackID(const IceString &Name); |
| + TimerIdT getTimerID(TimerStackIdT StackID, const IceString &Name); |
| void pushTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); |
| void popTimer(TimerIdT ID, TimerStackIdT StackID = TSK_Default); |
| void resetTimer(TimerStackIdT StackID); |
| @@ -193,6 +243,15 @@ public: |
| bool DumpCumulative = true); |
| private: |
| + // GlobalLock is the default coarse-grain lock for accessing members |
| + // of GlobalContext. As contention becomes an issue, more |
| + // fine-grain locks can be added. |
| + GlobalLockType GlobalLock; |
| + // StrLock is a global lock on the dump and emit output streams. |
| + // IsStrLocked is used to validate the locking protocol. |
| + StrLockType StrLock; |
| + bool IsStrLocked; |
|
JF
2015/01/15 01:31:01
If the intent is to detect races then IsStrLocked
Jim Stichnoth
2015/01/15 07:16:29
I changed it so that IsStrLocked is only read or w
JF
2015/01/15 16:28:39
The goal of IsStrLocked is to guarantee thread-saf
Jim Stichnoth
2015/01/20 16:22:53
I removed the IsStrLocked field. The stream lock
|
| + |
| Ostream *StrDump; // Stream for dumping / diagnostics |
| Ostream *StrEmit; // Stream for code emission |
| @@ -206,11 +265,15 @@ private: |
| const ClFlags &Flags; |
| RandomNumberGenerator RNG; |
| std::unique_ptr<ELFObjectWriter> ObjectWriter; |
| - CodeStats StatsFunction; |
| CodeStats StatsCumulative; |
| std::vector<TimerStack> Timers; |
| std::vector<GlobalDeclaration *> GlobalDeclarations; |
| + std::vector<ThreadContext *> AllThreadContexts; |
| + // Each thread has its own TLS pointer which is also held in |
| + // AllThreadContexts. |
| + thread_local static ThreadContext *TLS; |
| + |
| // Private helpers for mangleName() |
| typedef llvm::SmallVector<char, 32> ManglerVector; |
| void incrementSubstitutions(ManglerVector &OldName) const; |
| @@ -245,6 +308,21 @@ private: |
| bool Active; |
| }; |
| +// Helper class for locking the streams and then automatically |
| +// unlocking them. |
| +class OstreamLocker { |
| +private: |
| + OstreamLocker(const OstreamLocker &) = delete; |
| + OstreamLocker &operator=(const OstreamLocker &) = delete; |
|
JF
2015/01/15 01:31:01
OStreamLocker() = delete;
Jim Stichnoth
2015/01/15 07:16:29
Done.
|
| + |
| +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 |