diff --git a/lld/Common/CommonLinkerContext.cpp b/lld/Common/CommonLinkerContext.cpp --- a/lld/Common/CommonLinkerContext.cpp +++ b/lld/Common/CommonLinkerContext.cpp @@ -28,12 +28,29 @@ codegen::RegisterCodeGenFlags CGF; } +AllocContext *CommonLinkerContext::alloc() { + AllocContext *context = contextIndex.get(); + if (!context) { + // Context didn't exist yet for this thread, so create a new one. + context = new AllocContext; + contextIndex.set(context); + + llvm::sys::ScopedWriter lock(allContextsMutex); + allContexts.push_back(context); + } + return context; +} + CommonLinkerContext::~CommonLinkerContext() { assert(lctx); // Explicitly call the destructors since we created the objects with placement // new in SpecificAlloc::create(). - for (auto &it : instances) - it.second->~SpecificAllocBase(); + llvm::sys::ScopedWriter lock(allContextsMutex); + for (AllocContext *context : allContexts) { + for (auto &instance : context->instances) + instance.second->~SpecificAllocBase(); + delete context; + } lctx = nullptr; } @@ -48,4 +65,5 @@ if (lctx == nullptr) return; delete lctx; + lctx = nullptr; } diff --git a/lld/Common/Memory.cpp b/lld/Common/Memory.cpp --- a/lld/Common/Memory.cpp +++ b/lld/Common/Memory.cpp @@ -15,10 +15,10 @@ SpecificAllocBase * lld::SpecificAllocBase::getOrCreate(void *tag, size_t size, size_t align, SpecificAllocBase *(&creator)(void *)) { - auto &instances = context().instances; - auto &instance = instances[tag]; - if (instance == nullptr) { - void *storage = context().bAlloc.Allocate(size, align); + AllocContext *threadContext = context().alloc(); + auto &instance = threadContext->instances[tag]; + if (!instance) { + void *storage = threadContext->bAlloc.Allocate(size, align); instance = creator(storage); } return instance; diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -554,9 +554,9 @@ } template <typename... T> Defined *makeDefined(T &&...args) { - return new (reinterpret_cast<Defined *>( - getSpecificAllocSingleton<SymbolUnion>().Allocate())) - Defined(std::forward<T>(args)...); + auto &bumpAllocator = SpecificAlloc<SymbolUnion>::get().allocator; + void *objectStorage = bumpAllocator.Allocate(); + return new (objectStorage) Defined(std::forward<T>(args)...); } void reportDuplicate(const Symbol &sym, const InputFile *newFile, 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,9 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Memory.h" +#include "llvm/Support/RWMutex.h" #include "llvm/Support/StringSaver.h" +#include "llvm/Support/ThreadLocal.h" namespace llvm { class raw_ostream; @@ -29,6 +31,13 @@ namespace lld { struct SpecificAllocBase; + +struct AllocContext { + llvm::BumpPtrAllocator bAlloc; + llvm::StringSaver saver{bAlloc}; + llvm::DenseMap<void *, SpecificAllocBase *> instances; +}; + class CommonLinkerContext { public: CommonLinkerContext(); @@ -36,11 +45,18 @@ static void destroy(); - llvm::BumpPtrAllocator bAlloc; - llvm::StringSaver saver{bAlloc}; - llvm::DenseMap<void *, SpecificAllocBase *> instances; - ErrorHandler e; + + // Returns the thread-local AllocContext. + AllocContext *alloc(); + +private: + llvm::sys::ThreadLocal<AllocContext> contextIndex; + // 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<AllocContext *> allContexts; + llvm::sys::RWMutex allContextsMutex; }; // Retrieve the global state. Currently only one state can exist per process, @@ -51,11 +67,12 @@ template <typename T = CommonLinkerContext> T &context() { return static_cast<T &>(commonContext()); } - bool hasContext(); -inline llvm::StringSaver &saver() { return context().saver; } -inline llvm::BumpPtrAllocator &bAlloc() { return context().bAlloc; } +inline llvm::StringSaver &saver() { return context().alloc()->saver; } + +inline llvm::BumpPtrAllocator &bAlloc() { return context().alloc()->bAlloc; } + } // namespace lld #endif 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 @@ -33,11 +33,31 @@ }; // An arena of specific types T, created on-demand. -template <class T> struct SpecificAlloc : public SpecificAllocBase { +template <class T> class SpecificAlloc : public SpecificAllocBase { + // Ensure that users don't accidentally create instances on the stack. + SpecificAlloc() = default; + +public: static SpecificAllocBase *create(void *storage) { return new (storage) SpecificAlloc<T>(); } - llvm::SpecificBumpPtrAllocator<T> alloc; + + // Creates the arena on-demand on the first call; or returns it, if it was + // already created. + inline static SpecificAlloc<T> &get() { + SpecificAllocBase *instance = SpecificAllocBase::getOrCreate( + &SpecificAlloc<T>::tag, sizeof(SpecificAlloc<T>), + alignof(SpecificAlloc<T>), SpecificAlloc<T>::create); + return *(SpecificAlloc<T> *)instance; + } + + // Creates new instances of T off a (almost) contiguous arena/object pool. The + // instances are destroyed whenever lldMain() goes out of scope. + template <typename... U> T *make(U &&...args) { + return new (allocator.Allocate()) T(std::forward<U>(args)...); + } + + llvm::SpecificBumpPtrAllocator<T> allocator; static int tag; }; @@ -45,21 +65,10 @@ // CommonLinkerContext::instances. Its value does not matter. template <class T> int SpecificAlloc<T>::tag = 0; -// Creates the arena on-demand on the first call; or returns it, if it was -// already created. -template <typename T> -inline llvm::SpecificBumpPtrAllocator<T> &getSpecificAllocSingleton() { - SpecificAllocBase *instance = SpecificAllocBase::getOrCreate( - &SpecificAlloc<T>::tag, sizeof(SpecificAlloc<T>), - alignof(SpecificAlloc<T>), SpecificAlloc<T>::create); - return ((SpecificAlloc<T> *)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 <typename T, typename... U> T *make(U &&... args) { - return new (getSpecificAllocSingleton<T>().Allocate()) - T(std::forward<U>(args)...); + return SpecificAlloc<T>::get().make(std::forward<U>(args)...); } } // namespace lld