Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(501)

Unified Diff: src/IceGlobalContext.h

Issue 848193003: Subzero: Add locking to prepare for multithreaded translation. (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: Document the OstreamLocker class Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « src/IceConverter.cpp ('k') | src/IceGlobalContext.cpp » ('j') | src/IceGlobalContext.cpp » ('J')

Powered by Google App Engine
This is Rietveld 408576698