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,14 @@ // new in SpecificAlloc::create(). for (auto &it : instances) it.second->~SpecificAllocBase(); + + for (PerThreadContext **context : perThreadContexts) { + for (auto &instance : (*context)->perThreadInstances) + instance.second->~SpecificAllocBase(); + delete *context; + // Reset the tls variable 's value to sentinel in case we reuse the threads. + *context = nullptr; + } lctx = nullptr; } @@ -48,4 +56,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 @@ -6,12 +6,21 @@ // //===----------------------------------------------------------------------===// -#include "lld/Common/Memory.h" +#include + #include "lld/Common/CommonLinkerContext.h" +#include "lld/Common/Memory.h" using namespace llvm; using namespace lld; +namespace { +// TODO: may need to move these to CommonLinkerContext class when we support +// multiple processes. +// Pointer to this thread's context. +thread_local PerThreadContext *threadContext = nullptr; +} // namespace + SpecificAllocBase * lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, SpecificAllocBase *(&creator)(void *)) { @@ -23,3 +32,26 @@ } return instance; } + +PerThreadContext *CommonLinkerContext::perThreadContext() { + if (!threadContext) { + // Context didn't exist yet for this thread, so create a new one. + threadContext = new PerThreadContext; + llvm::sys::ScopedWriter lock(contextMutex); + perThreadContexts.push_back(&threadContext); + } + return threadContext; +} + +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 @@ -21,7 +21,10 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/Support/RWMutex.h" #include "llvm/Support/StringSaver.h" +#include +#include namespace llvm { class raw_ostream; @@ -29,6 +32,18 @@ namespace lld { struct SpecificAllocBase; + +// Max number of threads ever created in a process. +constexpr uint32_t MAX_THREADS = std::numeric_limits::max() - 1; + +// 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 +56,22 @@ llvm::DenseMap instances; ErrorHandler e; + + // Per thread allocs and savers. + PerThreadContext *perThreadContext(); + + // Vector of the TLS context's addresses. + // We store the TLS variable's addresses so that we can reset the variable's + // value from another thread. + std::vector 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 +80,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,11 +61,36 @@ 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) { +template T *makeUnsafe(U &&...args) { + return new (getSpecificAllocSingleton().Allocate()) + T(std::forward(args)...); +} + +// Creates new instances of T off a (almost) contiguous arena/object pool +// specific for this thread. The instances are destroyed whenever lldMain() goes +// out of scope. +template T *makePerThread(U &&...args) { + return new (getSpecificAllocSingletonPerThread().Allocate()) + T(std::forward(args)...); +} + +template T *make(U &&...args) { +#ifndef THREAD_SAFE_MEMORY return new (getSpecificAllocSingleton().Allocate()) T(std::forward(args)...); +#else + return new (getSpecificAllocSingletonPerThread().Allocate()) + T(std::forward(args)...); +#endif } } // namespace lld