diff --git a/lld/Common/CommonLinkerContext.cpp b/lld/Common/CommonLinkerContext.cpp --- a/lld/Common/CommonLinkerContext.cpp +++ b/lld/Common/CommonLinkerContext.cpp @@ -34,6 +34,12 @@ // new in SpecificAlloc::create(). for (auto &it : instances) it.second->~SpecificAllocBase(); + + for (auto &it : perThreadContexts) { + for (auto &instance : it.second->perThreadInstances) + instance.second->~SpecificAllocBase(); + delete it.second; + } lctx = nullptr; } @@ -48,4 +54,9 @@ if (lctx == nullptr) return; delete lctx; + lctx = nullptr; +} + +llvm::StringSaver &lld::perThreadSaver() { + return commonContext().perThreadContext()->perThreadSaver; } diff --git a/lld/Common/Memory.cpp b/lld/Common/Memory.cpp --- a/lld/Common/Memory.cpp +++ b/lld/Common/Memory.cpp @@ -8,10 +8,17 @@ #include "lld/Common/Memory.h" #include "lld/Common/CommonLinkerContext.h" +#include "llvm/Support/RWMutex.h" using namespace llvm; using namespace lld; +namespace { +// Value does not matter, but each thread has a copy (aka diff address) for +// this. +__thread int threadTag; +} // namespace + SpecificAllocBase * lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, SpecificAllocBase *(&creator)(void *)) { @@ -23,3 +30,38 @@ } return instance; } + +PerThreadContext *CommonLinkerContext::perThreadContext() { + llvm::sys::RWMutex &contextMutex = context().contextMutex; + void *contextKey = &threadTag; + + { + llvm::sys::ScopedReader lock(contextMutex); + auto entry = perThreadContexts.find(contextKey); + if (entry != perThreadContexts.end()) + return entry->second; + } + + PerThreadContext *perThreadContext = nullptr; + // Context didn't exist yet, so create a new one for this thread. + // TODO: look in the Map's api for a way to avoid looking up twice + { + llvm::sys::ScopedWriter lock(contextMutex); + perThreadContexts[contextKey] = perThreadContext = new PerThreadContext; + } + + return perThreadContext; +} + +SpecificAllocBase *lld::SpecificAllocBase::getOrCreatePerThread( + void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)) { + PerThreadContext *threadContext = context().perThreadContext(); + auto &instances = threadContext->perThreadInstances; + auto &instance = instances[tag]; + if (instance == nullptr) { + void *storage = threadContext->bAlloc.Allocate(size, align); + instance = creator(storage); + } + return instance; +} diff --git a/lld/include/lld/Common/CommonLinkerContext.h b/lld/include/lld/Common/CommonLinkerContext.h --- a/lld/include/lld/Common/CommonLinkerContext.h +++ b/lld/include/lld/Common/CommonLinkerContext.h @@ -19,8 +19,13 @@ #ifndef LLD_COMMON_COMMONLINKINGCONTEXT_H #define LLD_COMMON_COMMONLINKINGCONTEXT_H +#include +#include +#include + #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/Support/RWMutex.h" #include "llvm/Support/StringSaver.h" namespace llvm { @@ -29,6 +34,15 @@ namespace lld { struct SpecificAllocBase; + +// TODO Maybe just use the CommonLinkerContext class - but I dont want +// unnecessary nesting of the mutexes +struct PerThreadContext { + llvm::BumpPtrAllocator bAlloc; + llvm::DenseMap perThreadInstances; + llvm::StringSaver perThreadSaver{bAlloc}; +}; + class CommonLinkerContext { public: CommonLinkerContext(); @@ -41,11 +55,19 @@ llvm::DenseMap instances; ErrorHandler e; + + // Per thread allocs and savers. + PerThreadContext *perThreadContext(); + + llvm::DenseMap perThreadContexts; + llvm::sys::RWMutex contextMutex; }; // Retrieve the global state. Currently only one state can exist per process, // but in the future we plan on supporting an arbitrary number of LLD instances // in a single process. +// TODO: How would this affect the TLS variable's addresses? +// (hopefully not much) CommonLinkerContext &commonContext(); template T &context() { @@ -54,6 +76,9 @@ bool hasContext(); +llvm::StringSaver &perThreadSaver(); +llvm::BumpPtrAllocator &perThreadbAlloc(); + inline llvm::StringSaver &saver() { return context().saver; } inline llvm::BumpPtrAllocator &bAlloc() { return context().bAlloc; } } // namespace lld diff --git a/lld/include/lld/Common/Memory.h b/lld/include/lld/Common/Memory.h --- a/lld/include/lld/Common/Memory.h +++ b/lld/include/lld/Common/Memory.h @@ -22,14 +22,20 @@ #define LLD_COMMON_MEMORY_H #include "llvm/Support/Allocator.h" +#include "llvm/Support/RWMutex.h" namespace lld { + // A base class only used by the CommonLinkerContext to keep track of the // SpecificAlloc<> instances. struct SpecificAllocBase { virtual ~SpecificAllocBase() = default; static SpecificAllocBase *getOrCreate(void *tag, size_t size, size_t align, SpecificAllocBase *(&creator)(void *)); + + static SpecificAllocBase * + getOrCreatePerThread(void *tag, size_t size, size_t align, + SpecificAllocBase *(&creator)(void *)); }; // An arena of specific types T, created on-demand. @@ -55,6 +61,13 @@ return ((SpecificAlloc *)instance)->alloc; } +template +inline llvm::SpecificBumpPtrAllocator &getSpecificAllocSingletonPerThread() { + SpecificAllocBase *instance = SpecificAllocBase::getOrCreatePerThread( + &SpecificAlloc::tag, sizeof(SpecificAlloc), + alignof(SpecificAlloc), SpecificAlloc::create); + return ((SpecificAlloc *)instance)->alloc; +} // Creates new instances of T off a (almost) contiguous arena/object pool. The // instances are destroyed whenever lldMain() goes out of scope. template T *make(U &&... args) { @@ -62,6 +75,12 @@ T(std::forward(args)...); } +// Creates new instances of T off a (almost) contiguous arena/object pool. The +// instances are destroyed whenever lldMain() goes out of scope. +template T *makeThreadSafe(U &&...args) { + return new (getSpecificAllocSingletonPerThread().Allocate()) + T(std::forward(args)...); +} } // namespace lld #endif