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 (PerThreadContext *context : perThreadContexts) { + for (auto &instance : context->perThreadInstances) + instance.second->~SpecificAllocBase(); + delete context; + } 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 @@ -6,12 +6,35 @@ // //===----------------------------------------------------------------------===// -#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. +// Value of this is the index of the thread's Context in the +// CommonLinkerContext. +// XXX: 128-bit ints are not always lock-free, so limiting to 32-bit atomic for +// now. +thread_local uint32_t threadTag = 0; +std::atomic threadCounts(0); + +// Returns the index for this thread's context. +inline uint32_t getThreadIndex() { + if (threadTag == 0) { + threadTag = ++threadCounts; + } + assert(threadTag < lld::MAX_THREADS); + return threadTag; +} + +} // namespace + SpecificAllocBase * lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, SpecificAllocBase *(&creator)(void *)) { @@ -23,3 +46,26 @@ } return instance; } + +PerThreadContext *CommonLinkerContext::perThreadContext() { + uint32_t threadIdx = getThreadIndex(); + PerThreadContext *perThreadContext = perThreadContexts[threadIdx]; + // Context didn't exist yet, so create a new one for this thread. + if (!perThreadContext) + perThreadContexts[threadIdx] = 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 @@ -22,6 +22,8 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" #include "llvm/Support/StringSaver.h" +#include +#include namespace llvm { class raw_ostream; @@ -29,6 +31,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 +55,19 @@ llvm::DenseMap instances; ErrorHandler e; + + // Per thread allocs and savers. + PerThreadContext *perThreadContext(); + // [0] is unused to make thread's index checking simpler, because 0 is + // sentinel for the index. + std::array perThreadContexts; }; // 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,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