Index: include/llvm/ExecutionEngine/ExecutionEngine.h =================================================================== --- include/llvm/ExecutionEngine/ExecutionEngine.h +++ include/llvm/ExecutionEngine/ExecutionEngine.h @@ -255,6 +255,15 @@ /// object. This method has no effect for the interpeter. virtual void finalizeObject() {} + /// freeGeneratedCode - clear out all generated code and related state. + /// + /// Calling freeGeneratedCode resets the ExecutionEngine to a state where it + /// owns the modules it did before the call, but does not have any generated + /// code. + virtual void freeGeneratedCode() { + llvm_unreachable("freeGeneratedCode unimplemented!"); + } + /// runStaticConstructorsDestructors - This method is used to execute all of /// the static constructors or destructors for a program. /// Index: include/llvm/ExecutionEngine/RuntimeDyld.h =================================================================== --- include/llvm/ExecutionEngine/RuntimeDyld.h +++ include/llvm/ExecutionEngine/RuntimeDyld.h @@ -105,6 +105,12 @@ StringRef SectionName, bool IsReadOnly) = 0; + /// Free all of the memory allocated by allocateDataSection and + /// allocateCodeSection that this memory manager currently owns. + virtual void freeAllocatedSections() { + llvm_unreachable("freeAllocatedSections unimplemented!"); + } + /// Inform the memory manager about the total amount of memory required to /// allocate all sections to be loaded: /// \p CodeSize - the total size of all code sections @@ -197,6 +203,12 @@ /// Resolve the relocations for all symbols we currently know about. void resolveRelocations(); + /// Clear the set of relocations yet to be applied to generated code. + void clearPendingRelocations(); + + /// Clear out information held about section addresses and global symbols. + void clearSectionReferences(); + /// Map a section to its target address space value. /// Map the address of a JIT section as returned from the memory manager /// to the address in the target process as the running code will see it. Index: include/llvm/ExecutionEngine/SectionMemoryManager.h =================================================================== --- include/llvm/ExecutionEngine/SectionMemoryManager.h +++ include/llvm/ExecutionEngine/SectionMemoryManager.h @@ -60,6 +60,10 @@ unsigned SectionID, StringRef SectionName, bool isReadOnly) override; + /// \brief Free all of the memory allocated by \p allocateDataSection and \p + /// allocateCodeSection that this memory manager currently owns. + void freeAllocatedSections() override; + /// \brief Update section-specific memory permissions and other attributes. /// /// This method is called when object loading is complete and section page Index: lib/ExecutionEngine/MCJIT/MCJIT.h =================================================================== --- lib/ExecutionEngine/MCJIT/MCJIT.h +++ lib/ExecutionEngine/MCJIT/MCJIT.h @@ -204,8 +204,10 @@ ModulePtrSet::iterator I, ModulePtrSet::iterator E); + void freeGeneratedCodeInternal(bool FreeAllocatedSections); + public: - ~MCJIT() override; + ~MCJIT() override { freeGeneratedCodeInternal(false); } /// @name ExecutionEngine interface implementation /// @{ @@ -215,6 +217,8 @@ void addArchive(object::OwningBinary O) override; bool removeModule(Module *M) override; + void freeGeneratedCode() override { freeGeneratedCodeInternal(true); } + /// FindFunctionNamed - Search all of the active modules to find the one that /// defines FnName. This is very slow operation and shouldn't be used for /// general code. Index: lib/ExecutionEngine/MCJIT/MCJIT.cpp =================================================================== --- lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -89,18 +89,6 @@ RegisterJITEventListener(JITEventListener::createGDBRegistrationListener()); } -MCJIT::~MCJIT() { - MutexGuard locked(lock); - - Dyld.deregisterEHFrames(); - - for (auto &Obj : LoadedObjects) - if (Obj) - NotifyFreeingObject(*Obj); - - Archives.clear(); -} - void MCJIT::addModule(std::unique_ptr M) { MutexGuard locked(lock); OwnedModules.addModule(std::move(M)); @@ -608,3 +596,25 @@ return nullptr; return ClientResolver->findSymbol(Name); } + +void MCJIT::freeGeneratedCodeInternal(bool FreeAllocatedSections) { + MutexGuard locked(lock); + + Dyld.deregisterEHFrames(); + Dyld.clearPendingRelocations(); + Dyld.clearSectionReferences(); + + for (auto &Obj : LoadedObjects) + if (Obj) + NotifyFreeingObject(*Obj); + + // Not all memory managers support freeAllocatedSections, it is the client's + // responsibility to ensure that MemMgr->freeAllocatedSections() is + // implemented if it wants to call freeGeneratedCode(). + if (FreeAllocatedSections) + MemMgr->freeAllocatedSections(); + + LoadedObjects.clear(); + Buffers.clear(); + Archives.clear(); +} Index: lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp =================================================================== --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -35,6 +35,8 @@ namespace llvm { +void RuntimeDyldImpl::clearPendingEHFrameRegistrations() {} + void RuntimeDyldImpl::registerEHFrames() {} void RuntimeDyldImpl::deregisterEHFrames() {} @@ -101,6 +103,18 @@ } } +void RuntimeDyldImpl::clearSectionReferences() { + MutexGuard locked(lock); + Sections.clear(); + GlobalSymbolTable.clear(); +} + +void RuntimeDyldImpl::clearPendingRelocations() { + MutexGuard locked(lock); + Relocations.clear(); + ExternalSymbolRelocations.clear(); +} + void RuntimeDyldImpl::mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress) { MutexGuard locked(lock); @@ -925,6 +939,18 @@ void RuntimeDyld::resolveRelocations() { Dyld->resolveRelocations(); } +void RuntimeDyld::clearSectionReferences() { + if (Dyld) { + Dyld->clearPendingEHFrameRegistrations(); + Dyld->clearSectionReferences(); + } +} + +void RuntimeDyld::clearPendingRelocations() { + if (Dyld) + Dyld->clearPendingRelocations(); +} + void RuntimeDyld::reassignSectionAddress(unsigned SectionID, uint64_t Addr) { Dyld->reassignSectionAddress(SectionID, Addr); } Index: lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h =================================================================== --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -146,6 +146,10 @@ RuntimeDyld::SymbolResolver &Resolver); ~RuntimeDyldELF() override; + void clearPendingEHFrameRegistrations() override { + UnregisteredEHFrameSections.clear(); + } + std::unique_ptr loadObject(const object::ObjectFile &O) override; Index: lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h =================================================================== --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -424,6 +424,10 @@ void resolveRelocations(); + void clearSectionReferences(); + void clearPendingRelocations(); + virtual void clearPendingEHFrameRegistrations(); + void reassignSectionAddress(unsigned SectionID, uint64_t Addr); void mapSectionAddress(const void *LocalAddress, uint64_t TargetAddress); Index: lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h =================================================================== --- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h +++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h @@ -80,6 +80,10 @@ return RelocationEntry(SectionID, Offset, RelType, 0, IsPCRel, Size); } + void clearPendingEHFrameRegistrations() override { + UnregisteredEHFrameSections.clear(); + } + /// Construct a RelocationValueRef representing the relocation target. /// For Symbols in known sections, this will return a RelocationValueRef /// representing a (SectionID, Offset) pair. Index: lib/ExecutionEngine/SectionMemoryManager.cpp =================================================================== --- lib/ExecutionEngine/SectionMemoryManager.cpp +++ lib/ExecutionEngine/SectionMemoryManager.cpp @@ -165,14 +165,22 @@ CodeMem.AllocatedMem[i].size()); } -SectionMemoryManager::~SectionMemoryManager() { +void SectionMemoryManager::freeAllocatedSections() { for (unsigned i = 0, e = CodeMem.AllocatedMem.size(); i != e; ++i) sys::Memory::releaseMappedMemory(CodeMem.AllocatedMem[i]); for (unsigned i = 0, e = RWDataMem.AllocatedMem.size(); i != e; ++i) sys::Memory::releaseMappedMemory(RWDataMem.AllocatedMem[i]); for (unsigned i = 0, e = RODataMem.AllocatedMem.size(); i != e; ++i) sys::Memory::releaseMappedMemory(RODataMem.AllocatedMem[i]); + + for (auto *MGroup : {&CodeMem, &RWDataMem, &RODataMem}) { + MGroup->AllocatedMem.clear(); + MGroup->FreeMem.clear(); + MGroup->Near = sys::MemoryBlock(); + } } +SectionMemoryManager::~SectionMemoryManager() { freeAllocatedSections(); } + } // namespace llvm Index: unittests/ExecutionEngine/MCJIT/CMakeLists.txt =================================================================== --- unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -18,6 +18,7 @@ MCJITMemoryManagerTest.cpp MCJITMultipleModuleTest.cpp MCJITObjectCacheTest.cpp + MCJITObjectCleanupTest.cpp ) if(MSVC) Index: unittests/ExecutionEngine/MCJIT/MCJITObjectCleanupTest.cpp =================================================================== --- /dev/null +++ unittests/ExecutionEngine/MCJIT/MCJITObjectCleanupTest.cpp @@ -0,0 +1,102 @@ +//===- MCJITObjectCleanupTest.cpp - Unit tests for MCJIT object cleanup ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCJITTestBase.h" +#include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct TestMemoryManager : public SectionMemoryManager { + bool ResolutionEnabled; + int SectionsAllocated; + + TestMemoryManager() : ResolutionEnabled(false), SectionsAllocated(0) {} + + virtual uint64_t getSymbolAddress(const std::string &Name) override { + if (ResolutionEnabled && Name == "external_callee") + return 5000; + return SectionMemoryManager::getSymbolAddress(Name); + } + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName, + bool IsReadOnly) override { + SectionsAllocated++; + return SectionMemoryManager::allocateDataSection(Size, Alignment, SectionID, + SectionName, IsReadOnly); + } + + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, + StringRef SectionName) override { + SectionsAllocated++; + return SectionMemoryManager::allocateCodeSection(Size, Alignment, SectionID, + SectionName); + } + + virtual void freeAllocatedSections() override { + SectionsAllocated = 0; + SectionMemoryManager::freeAllocatedSections(); + } +}; + +class MCJITObjectCleanupTest : public testing::Test, public MCJITTestBase { +protected: + Module *SourceWithExternalSyms; + Module *SourceWithoutExternalSyms; + TestMemoryManager *TMM; + +public: + MCJITObjectCleanupTest() + : MCJITTestBase(new TestMemoryManager), SourceWithExternalSyms(nullptr), + SourceWithoutExternalSyms(nullptr), TMM(nullptr) {} + + void SetUp() override { + TMM = (TestMemoryManager *)MM.get(); + + M.reset(createEmptyModule("epoch")); + createJIT(std::move(M)); + + SourceWithExternalSyms = createEmptyModule("SourceWithExternalSyms"); + Function *Callee = insertExternalReferenceToFunction( + SourceWithExternalSyms, "external_callee"); + insertSimpleCallFunction(SourceWithExternalSyms, Callee); + + SourceWithoutExternalSyms = createEmptyModule("SourceWithExternalSyms"); + insertAddFunction(SourceWithoutExternalSyms); + } +}; + +TEST_F(MCJITObjectCleanupTest, Basic) { + SKIP_UNSUPPORTED_PLATFORM; + + TMM->ResolutionEnabled = true; + TheJIT->addModule(std::unique_ptr(SourceWithExternalSyms)); + TheJIT->generateCodeForModule(SourceWithExternalSyms); + + EXPECT_NE(TMM->SectionsAllocated, 0) + << "Generating code should have allocated some sections!"; + + TheJIT->removeModule(SourceWithExternalSyms); + TheJIT->freeGeneratedCode(); + + EXPECT_EQ(TMM->SectionsAllocated, 0) << "All sections should have bene freed " + "after the call to " + "freeGeneratedCode!"; + + TMM->ResolutionEnabled = false; + TheJIT->addModule(std::unique_ptr(SourceWithoutExternalSyms)); + TheJIT->generateCodeForModule(SourceWithoutExternalSyms); + TheJIT->finalizeObject(); +} +} Index: unittests/ExecutionEngine/MCJIT/MCJITTestBase.h =================================================================== --- unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -282,15 +282,10 @@ class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder { protected: - - MCJITTestBase() - : TrivialModuleBuilder(HostTriple) - , OptLevel(CodeGenOpt::None) - , RelocModel(Reloc::Default) - , CodeModel(CodeModel::Default) - , MArch("") - , MM(new SectionMemoryManager) - { + explicit MCJITTestBase(RTDyldMemoryManager *MemManager = nullptr) + : TrivialModuleBuilder(HostTriple), OptLevel(CodeGenOpt::None), + RelocModel(Reloc::Default), CodeModel(CodeModel::Default), MArch(""), + MM(MemManager ? MemManager : new SectionMemoryManager) { // The architectures below are known to be compatible with MCJIT as they // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be // kept in sync.