From b868e9101c138016aad5bd910b67f40a3213d6fc Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Fri, 4 Oct 2013 00:49:38 +0000 Subject: Adding support and tests for multiple module handling in lli git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191938 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/ExecutionEngine/RTDyldMemoryManager.h | 20 +- lib/ExecutionEngine/MCJIT/MCJIT.cpp | 1 + lib/ExecutionEngine/MCJIT/MCJIT.h | 5 + test/ExecutionEngine/MCJIT/cross-module-a.ll | 13 ++ test/ExecutionEngine/MCJIT/cross-module-b.ir | 7 + test/ExecutionEngine/MCJIT/multi-module-a.ll | 9 + test/ExecutionEngine/MCJIT/multi-module-b.ir | 7 + test/ExecutionEngine/MCJIT/multi-module-c.ir | 4 + .../ExecutionEngine/MCJIT/remote/cross-module-a.ll | 13 ++ .../ExecutionEngine/MCJIT/remote/cross-module-b.ir | 7 + .../ExecutionEngine/MCJIT/remote/multi-module-a.ll | 9 + .../ExecutionEngine/MCJIT/remote/multi-module-b.ir | 7 + .../ExecutionEngine/MCJIT/remote/multi-module-c.ir | 4 + tools/lli/CMakeLists.txt | 2 +- tools/lli/RecordingMemoryManager.cpp | 126 ----------- tools/lli/RecordingMemoryManager.h | 83 -------- tools/lli/RemoteMemoryManager.cpp | 230 +++++++++++++++++++++ tools/lli/RemoteMemoryManager.h | 105 ++++++++++ tools/lli/lli.cpp | 115 +++-------- 19 files changed, 473 insertions(+), 294 deletions(-) create mode 100644 test/ExecutionEngine/MCJIT/cross-module-a.ll create mode 100644 test/ExecutionEngine/MCJIT/cross-module-b.ir create mode 100644 test/ExecutionEngine/MCJIT/multi-module-a.ll create mode 100644 test/ExecutionEngine/MCJIT/multi-module-b.ir create mode 100644 test/ExecutionEngine/MCJIT/multi-module-c.ir create mode 100644 test/ExecutionEngine/MCJIT/remote/cross-module-a.ll create mode 100644 test/ExecutionEngine/MCJIT/remote/cross-module-b.ir create mode 100644 test/ExecutionEngine/MCJIT/remote/multi-module-a.ll create mode 100644 test/ExecutionEngine/MCJIT/remote/multi-module-b.ir create mode 100644 test/ExecutionEngine/MCJIT/remote/multi-module-c.ir delete mode 100644 tools/lli/RecordingMemoryManager.cpp delete mode 100644 tools/lli/RecordingMemoryManager.h create mode 100644 tools/lli/RemoteMemoryManager.cpp create mode 100644 tools/lli/RemoteMemoryManager.h diff --git a/include/llvm/ExecutionEngine/RTDyldMemoryManager.h b/include/llvm/ExecutionEngine/RTDyldMemoryManager.h index 5932687585..09a5cb4884 100644 --- a/include/llvm/ExecutionEngine/RTDyldMemoryManager.h +++ b/include/llvm/ExecutionEngine/RTDyldMemoryManager.h @@ -21,6 +21,9 @@ namespace llvm { +class ExecutionEngine; +class ObjectImage; + // RuntimeDyld clients often want to handle the memory management of // what gets placed where. For JIT clients, this is the subset of // JITMemoryManager required for dynamic loading of binaries. @@ -41,7 +44,7 @@ public: virtual uint8_t *allocateCodeSection( uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName) = 0; - + /// Allocate a memory block of (at least) the given size suitable for data. /// The SectionID is a unique identifier assigned by the JIT engine, and /// optionally recorded by the memory manager to access a loaded section. @@ -63,11 +66,24 @@ public: /// found, this function returns a null pointer. Otherwise, it prints a /// message to stderr and aborts. /// - /// This function is deprecated for memory managers used to be used with + /// This function is deprecated for memory managers to be used with /// MCJIT or RuntimeDyld. Use getSymbolAddress instead. virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true); + /// This method is called after an object has been loaded into memory but + /// before relocations are applied to the loaded sections. The object load + /// may have been initiated by MCJIT to resolve an external symbol for another + /// object that is being finalized. In that case, the object about which + /// the memory manager is being notified will be finalized immediately after + /// the memory manager returns from this call. + /// + /// Memory managers which are preparing code for execution in an external + /// address space can use this call to remap the section addresses for the + /// newly loaded object. + virtual void notifyObjectLoaded(ExecutionEngine *EE, + const ObjectImage *) {} + /// This method is called when object loading is complete and section page /// permissions can be applied. It is up to the memory manager implementation /// to decide whether or not to act on this method. The memory manager will diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.cpp b/lib/ExecutionEngine/MCJIT/MCJIT.cpp index 09fc75c6ae..541ba9eb62 100644 --- a/lib/ExecutionEngine/MCJIT/MCJIT.cpp +++ b/lib/ExecutionEngine/MCJIT/MCJIT.cpp @@ -513,6 +513,7 @@ void MCJIT::UnregisterJITEventListener(JITEventListener *L) { } void MCJIT::NotifyObjectEmitted(const ObjectImage& Obj) { MutexGuard locked(lock); + MemMgr.notifyObjectLoaded(this, &Obj); for (unsigned I = 0, S = EventListeners.size(); I < S; ++I) { EventListeners[I]->NotifyObjectEmitted(Obj); } diff --git a/lib/ExecutionEngine/MCJIT/MCJIT.h b/lib/ExecutionEngine/MCJIT/MCJIT.h index a40a9e429b..6969ff13c0 100644 --- a/lib/ExecutionEngine/MCJIT/MCJIT.h +++ b/lib/ExecutionEngine/MCJIT/MCJIT.h @@ -46,6 +46,11 @@ public: SectionID, SectionName, IsReadOnly); } + virtual void notifyObjectLoaded(ExecutionEngine *EE, + const ObjectImage *Obj) { + ClientMM->notifyObjectLoaded(EE, Obj); + } + virtual void registerEHFrames(StringRef SectionData) { ClientMM->registerEHFrames(SectionData); } diff --git a/test/ExecutionEngine/MCJIT/cross-module-a.ll b/test/ExecutionEngine/MCJIT/cross-module-a.ll new file mode 100644 index 0000000000..201a6701fb --- /dev/null +++ b/test/ExecutionEngine/MCJIT/cross-module-a.ll @@ -0,0 +1,13 @@ +; RUN: %lli_mcjit -extra-modules=%p/cross-module-b.ir %s > /dev/null + +declare i32 @FB() + +define i32 @FA() { + ret i32 0 +} + +define i32 @main() { + %r = call i32 @FB( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/cross-module-b.ir b/test/ExecutionEngine/MCJIT/cross-module-b.ir new file mode 100644 index 0000000000..6870117411 --- /dev/null +++ b/test/ExecutionEngine/MCJIT/cross-module-b.ir @@ -0,0 +1,7 @@ +declare i32 @FA() + +define i32 @FB() { + %r = call i32 @FA( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/multi-module-a.ll b/test/ExecutionEngine/MCJIT/multi-module-a.ll new file mode 100644 index 0000000000..9c6de8dd8b --- /dev/null +++ b/test/ExecutionEngine/MCJIT/multi-module-a.ll @@ -0,0 +1,9 @@ +; RUN: %lli_mcjit -extra-modules=%p/multi-module-b.ir,%p/multi-module-c.ir %s > /dev/null + +declare i32 @FB() + +define i32 @main() { + %r = call i32 @FB( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/multi-module-b.ir b/test/ExecutionEngine/MCJIT/multi-module-b.ir new file mode 100644 index 0000000000..103b601e7f --- /dev/null +++ b/test/ExecutionEngine/MCJIT/multi-module-b.ir @@ -0,0 +1,7 @@ +declare i32 @FC() + +define i32 @FB() { + %r = call i32 @FC( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/multi-module-c.ir b/test/ExecutionEngine/MCJIT/multi-module-c.ir new file mode 100644 index 0000000000..b39306be9e --- /dev/null +++ b/test/ExecutionEngine/MCJIT/multi-module-c.ir @@ -0,0 +1,4 @@ +define i32 @FC() { + ret i32 0 +} + diff --git a/test/ExecutionEngine/MCJIT/remote/cross-module-a.ll b/test/ExecutionEngine/MCJIT/remote/cross-module-a.ll new file mode 100644 index 0000000000..1e55d2d6a2 --- /dev/null +++ b/test/ExecutionEngine/MCJIT/remote/cross-module-a.ll @@ -0,0 +1,13 @@ +; RUN: %lli_mcjit -extra-modules=%p/cross-module-b.ir -disable-lazy-compilation=true -remote-mcjit -mcjit-remote-process=lli-child-target %s > /dev/null + +declare i32 @FB() + +define i32 @FA() { + ret i32 0 +} + +define i32 @main() { + %r = call i32 @FB( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/remote/cross-module-b.ir b/test/ExecutionEngine/MCJIT/remote/cross-module-b.ir new file mode 100644 index 0000000000..6870117411 --- /dev/null +++ b/test/ExecutionEngine/MCJIT/remote/cross-module-b.ir @@ -0,0 +1,7 @@ +declare i32 @FA() + +define i32 @FB() { + %r = call i32 @FA( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/remote/multi-module-a.ll b/test/ExecutionEngine/MCJIT/remote/multi-module-a.ll new file mode 100644 index 0000000000..f49acf7c18 --- /dev/null +++ b/test/ExecutionEngine/MCJIT/remote/multi-module-a.ll @@ -0,0 +1,9 @@ +; RUN: %lli_mcjit -extra-modules=%p/multi-module-b.ir,%p/multi-module-c.ir -disable-lazy-compilation=true -remote-mcjit -mcjit-remote-process=lli-child-target %s > /dev/null + +declare i32 @FB() + +define i32 @main() { + %r = call i32 @FB( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/remote/multi-module-b.ir b/test/ExecutionEngine/MCJIT/remote/multi-module-b.ir new file mode 100644 index 0000000000..103b601e7f --- /dev/null +++ b/test/ExecutionEngine/MCJIT/remote/multi-module-b.ir @@ -0,0 +1,7 @@ +declare i32 @FC() + +define i32 @FB() { + %r = call i32 @FC( ) ; [#uses=1] + ret i32 %r +} + diff --git a/test/ExecutionEngine/MCJIT/remote/multi-module-c.ir b/test/ExecutionEngine/MCJIT/remote/multi-module-c.ir new file mode 100644 index 0000000000..b39306be9e --- /dev/null +++ b/test/ExecutionEngine/MCJIT/remote/multi-module-c.ir @@ -0,0 +1,4 @@ +define i32 @FC() { + ret i32 0 +} + diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index 578c414cce..5f8c7c9261 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -21,7 +21,7 @@ endif( LLVM_USE_INTEL_JITEVENTS ) add_llvm_tool(lli lli.cpp - RecordingMemoryManager.cpp + RemoteMemoryManager.cpp RemoteTarget.cpp RemoteTargetExternal.cpp ) diff --git a/tools/lli/RecordingMemoryManager.cpp b/tools/lli/RecordingMemoryManager.cpp deleted file mode 100644 index d54b8e434d..0000000000 --- a/tools/lli/RecordingMemoryManager.cpp +++ /dev/null @@ -1,126 +0,0 @@ -//===- RecordingMemoryManager.cpp - Recording memory manager --------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This memory manager allocates local storage and keeps a record of each -// allocation. Iterators are provided for all data and code allocations. -// -//===----------------------------------------------------------------------===// - -#include "RecordingMemoryManager.h" -using namespace llvm; - -RecordingMemoryManager::~RecordingMemoryManager() { - for (SmallVectorImpl::iterator - I = AllocatedCodeMem.begin(), E = AllocatedCodeMem.end(); - I != E; ++I) - sys::Memory::releaseMappedMemory(I->first); - for (SmallVectorImpl::iterator - I = AllocatedDataMem.begin(), E = AllocatedDataMem.end(); - I != E; ++I) - sys::Memory::releaseMappedMemory(I->first); -} - -uint8_t *RecordingMemoryManager:: -allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, - StringRef SectionName) { - // The recording memory manager is just a local copy of the remote target. - // The alignment requirement is just stored here for later use. Regular - // heap storage is sufficient here, but we're using mapped memory to work - // around a bug in MCJIT. - sys::MemoryBlock Block = allocateSection(Size); - AllocatedCodeMem.push_back(Allocation(Block, Alignment)); - return (uint8_t*)Block.base(); -} - -uint8_t *RecordingMemoryManager:: -allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, StringRef SectionName, - bool IsReadOnly) { - // The recording memory manager is just a local copy of the remote target. - // The alignment requirement is just stored here for later use. Regular - // heap storage is sufficient here, but we're using mapped memory to work - // around a bug in MCJIT. - sys::MemoryBlock Block = allocateSection(Size); - AllocatedDataMem.push_back(Allocation(Block, Alignment)); - return (uint8_t*)Block.base(); -} - -sys::MemoryBlock RecordingMemoryManager::allocateSection(uintptr_t Size) { - error_code ec; - sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(Size, - &Near, - sys::Memory::MF_READ | - sys::Memory::MF_WRITE, - ec); - assert(!ec && MB.base()); - - // FIXME: This is part of a work around to keep sections near one another - // when MCJIT performs relocations after code emission but before - // the generated code is moved to the remote target. - // Save this address as the basis for our next request - Near = MB; - return MB; -} - -void RecordingMemoryManager::setMemoryWritable() { llvm_unreachable("Unexpected!"); } -void RecordingMemoryManager::setMemoryExecutable() { llvm_unreachable("Unexpected!"); } -void RecordingMemoryManager::setPoisonMemory(bool poison) { llvm_unreachable("Unexpected!"); } -void RecordingMemoryManager::AllocateGOT() { llvm_unreachable("Unexpected!"); } -uint8_t *RecordingMemoryManager::getGOTBase() const { - llvm_unreachable("Unexpected!"); - return 0; -} -uint8_t *RecordingMemoryManager::startFunctionBody(const Function *F, uintptr_t &ActualSize){ - llvm_unreachable("Unexpected!"); - return 0; -} -uint8_t *RecordingMemoryManager::allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return 0; -} -void RecordingMemoryManager::endFunctionBody(const Function *F, uint8_t *FunctionStart, - uint8_t *FunctionEnd) { - llvm_unreachable("Unexpected!"); -} -uint8_t *RecordingMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return 0; -} -uint8_t *RecordingMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) { - llvm_unreachable("Unexpected!"); - return 0; -} -void RecordingMemoryManager::deallocateFunctionBody(void *Body) { - llvm_unreachable("Unexpected!"); -} - -static int jit_noop() { - return 0; -} - -void *RecordingMemoryManager::getPointerToNamedFunction(const std::string &Name, - bool AbortOnFailure) { - // We should not invoke parent's ctors/dtors from generated main()! - // On Mingw and Cygwin, the symbol __main is resolved to - // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors - // (and register wrong callee's dtors with atexit(3)). - // We expect ExecutionEngine::runStaticConstructorsDestructors() - // is called before ExecutionEngine::runFunctionAsMain() is called. - if (Name == "__main") return (void*)(intptr_t)&jit_noop; - - // FIXME: Would it be responsible to provide GOT? - if (AbortOnFailure) { - if (Name == "_GLOBAL_OFFSET_TABLE_") - report_fatal_error("Program used external function '" + Name + - "' which could not be resolved!"); - } - - return NULL; -} diff --git a/tools/lli/RecordingMemoryManager.h b/tools/lli/RecordingMemoryManager.h deleted file mode 100644 index 05f4807d04..0000000000 --- a/tools/lli/RecordingMemoryManager.h +++ /dev/null @@ -1,83 +0,0 @@ -//===- RecordingMemoryManager.h - LLI MCJIT recording memory manager ------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This memory manager allocates local storage and keeps a record of each -// allocation. Iterators are provided for all data and code allocations. -// -//===----------------------------------------------------------------------===// - -#ifndef RECORDINGMEMORYMANAGER_H -#define RECORDINGMEMORYMANAGER_H - -#include "llvm/ADT/SmallVector.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/Memory.h" -#include - -namespace llvm { - -class RecordingMemoryManager : public JITMemoryManager { -public: - typedef std::pair Allocation; - -private: - SmallVector AllocatedDataMem; - SmallVector AllocatedCodeMem; - - // FIXME: This is part of a work around to keep sections near one another - // when MCJIT performs relocations after code emission but before - // the generated code is moved to the remote target. - sys::MemoryBlock Near; - sys::MemoryBlock allocateSection(uintptr_t Size); - -public: - RecordingMemoryManager() {} - virtual ~RecordingMemoryManager(); - - typedef SmallVectorImpl::const_iterator const_data_iterator; - typedef SmallVectorImpl::const_iterator const_code_iterator; - - const_data_iterator data_begin() const { return AllocatedDataMem.begin(); } - const_data_iterator data_end() const { return AllocatedDataMem.end(); } - const_code_iterator code_begin() const { return AllocatedCodeMem.begin(); } - const_code_iterator code_end() const { return AllocatedCodeMem.end(); } - - uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, StringRef SectionName); - - uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, StringRef SectionName, - bool IsReadOnly); - - void *getPointerToNamedFunction(const std::string &Name, - bool AbortOnFailure = true); - - bool finalizeMemory(std::string *ErrMsg) { return false; } - - // The following obsolete JITMemoryManager calls are stubbed out for - // this model. - void setMemoryWritable(); - void setMemoryExecutable(); - void setPoisonMemory(bool poison); - void AllocateGOT(); - uint8_t *getGOTBase() const; - uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize); - uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment); - void endFunctionBody(const Function *F, uint8_t *FunctionStart, - uint8_t *FunctionEnd); - uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); - uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); - void deallocateFunctionBody(void *Body); -}; - -} // end namespace llvm - -#endif diff --git a/tools/lli/RemoteMemoryManager.cpp b/tools/lli/RemoteMemoryManager.cpp new file mode 100644 index 0000000000..1f86066051 --- /dev/null +++ b/tools/lli/RemoteMemoryManager.cpp @@ -0,0 +1,230 @@ +//===---- RemoteMemoryManager.cpp - Recording memory manager --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This memory manager allocates local storage and keeps a record of each +// allocation. Iterators are provided for all data and code allocations. +// +//===----------------------------------------------------------------------===// + +#define DEBUG_TYPE "lli" +#include "RemoteMemoryManager.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/ObjectImage.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" + +using namespace llvm; + +RemoteMemoryManager::~RemoteMemoryManager() { + for (SmallVector::iterator + I = AllocatedSections.begin(), E = AllocatedSections.end(); + I != E; ++I) + sys::Memory::releaseMappedMemory(I->MB); +} + +uint8_t *RemoteMemoryManager:: +allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, + StringRef SectionName) { + // The recording memory manager is just a local copy of the remote target. + // The alignment requirement is just stored here for later use. Regular + // heap storage is sufficient here, but we're using mapped memory to work + // around a bug in MCJIT. + sys::MemoryBlock Block = allocateSection(Size); + AllocatedSections.push_back( Allocation(Block, Alignment, true) ); + UnmappedSections.push_back( &AllocatedSections.back() ); + return (uint8_t*)Block.base(); +} + +uint8_t *RemoteMemoryManager:: +allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName, + bool IsReadOnly) { + // The recording memory manager is just a local copy of the remote target. + // The alignment requirement is just stored here for later use. Regular + // heap storage is sufficient here, but we're using mapped memory to work + // around a bug in MCJIT. + sys::MemoryBlock Block = allocateSection(Size); + AllocatedSections.push_back( Allocation(Block, Alignment, false) ); + UnmappedSections.push_back( &AllocatedSections.back() ); + return (uint8_t*)Block.base(); +} + +sys::MemoryBlock RemoteMemoryManager::allocateSection(uintptr_t Size) { + error_code ec; + sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(Size, + &Near, + sys::Memory::MF_READ | + sys::Memory::MF_WRITE, + ec); + assert(!ec && MB.base()); + + // FIXME: This is part of a work around to keep sections near one another + // when MCJIT performs relocations after code emission but before + // the generated code is moved to the remote target. + // Save this address as the basis for our next request + Near = MB; + return MB; +} + +void RemoteMemoryManager::notifyObjectLoaded(ExecutionEngine *EE, + const ObjectImage *Obj) { + // The client should have called setRemoteTarget() before triggering any + // code generation. + assert(Target); + if (!Target) + return; + + // FIXME: Make this function thread safe. + + // Lay out our sections in order, with all the code sections first, then + // all the data sections. + uint64_t CurOffset = 0; + unsigned MaxAlign = Target->getPageAlignment(); + SmallVector, 16> Offsets; + unsigned NumSections = UnmappedSections.size(); + // We're going to go through the list twice to separate code and data, but + // it's a very small list, so that's OK. + for (size_t i = 0, e = NumSections; i != e; ++i) { + const Allocation *Section = UnmappedSections[i]; + assert(Section); + if (Section->IsCode) { + unsigned Size = Section->MB.size(); + unsigned Align = Section->Alignment; + DEBUG(dbgs() << "code region: size " << Size + << ", alignment " << Align << "\n"); + // Align the current offset up to whatever is needed for the next + // section. + CurOffset = (CurOffset + Align - 1) / Align * Align; + // Save off the address of the new section and allocate its space. + Offsets.push_back(std::pair(Section, + CurOffset)); + CurOffset += Size; + } + } + // Adjust to keep code and data aligned on seperate pages. + CurOffset = (CurOffset + MaxAlign - 1) / MaxAlign * MaxAlign; + for (size_t i = 0, e = NumSections; i != e; ++i) { + const Allocation *Section = UnmappedSections[i]; + assert(Section); + if (!Section->IsCode) { + unsigned Size = Section->MB.size(); + unsigned Align = Section->Alignment; + DEBUG(dbgs() << "data region: size " << Size + << ", alignment " << Align << "\n"); + // Align the current offset up to whatever is needed for the next + // section. + CurOffset = (CurOffset + Align - 1) / Align * Align; + // Save off the address of the new section and allocate its space. + Offsets.push_back(std::pair(Section, + CurOffset)); + CurOffset += Size; + } + } + + // Allocate space in the remote target. + uint64_t RemoteAddr; + if (Target->allocateSpace(CurOffset, MaxAlign, RemoteAddr)) + report_fatal_error(Target->getErrorMsg()); + + // Map the section addresses so relocations will get updated in the local + // copies of the sections. + for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { + uint64_t Addr = RemoteAddr + Offsets[i].second; + EE->mapSectionAddress(const_cast(Offsets[i].first->MB.base()), Addr); + + DEBUG(dbgs() << " Mapping local: " << Offsets[i].first->MB.base() + << " to remote: 0x" << format("%llx", Addr) << "\n"); + + MappedSections[Addr] = Offsets[i].first; + } + + UnmappedSections.clear(); +} + +bool RemoteMemoryManager::finalizeMemory(std::string *ErrMsg) { + // FIXME: Make this function thread safe. + for (DenseMap::iterator + I = MappedSections.begin(), E = MappedSections.end(); + I != E; ++I) { + uint64_t RemoteAddr = I->first; + const Allocation *Section = I->second; + if (Section->IsCode) { + Target->loadCode(RemoteAddr, Section->MB.base(), Section->MB.size()); + + DEBUG(dbgs() << " loading code: " << Section->MB.base() + << " to remote: 0x" << format("%llx", RemoteAddr) << "\n"); + } else { + Target->loadData(RemoteAddr, Section->MB.base(), Section->MB.size()); + + DEBUG(dbgs() << " loading data: " << Section->MB.base() + << " to remote: 0x" << format("%llx", RemoteAddr) << "\n"); + } + } + + MappedSections.clear(); + + return false; +} + +void RemoteMemoryManager::setMemoryWritable() { llvm_unreachable("Unexpected!"); } +void RemoteMemoryManager::setMemoryExecutable() { llvm_unreachable("Unexpected!"); } +void RemoteMemoryManager::setPoisonMemory(bool poison) { llvm_unreachable("Unexpected!"); } +void RemoteMemoryManager::AllocateGOT() { llvm_unreachable("Unexpected!"); } +uint8_t *RemoteMemoryManager::getGOTBase() const { + llvm_unreachable("Unexpected!"); + return 0; +} +uint8_t *RemoteMemoryManager::startFunctionBody(const Function *F, uintptr_t &ActualSize){ + llvm_unreachable("Unexpected!"); + return 0; +} +uint8_t *RemoteMemoryManager::allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment) { + llvm_unreachable("Unexpected!"); + return 0; +} +void RemoteMemoryManager::endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) { + llvm_unreachable("Unexpected!"); +} +uint8_t *RemoteMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected!"); + return 0; +} +uint8_t *RemoteMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected!"); + return 0; +} +void RemoteMemoryManager::deallocateFunctionBody(void *Body) { + llvm_unreachable("Unexpected!"); +} + +static int jit_noop() { + return 0; +} + +void *RemoteMemoryManager::getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure) { + // We should not invoke parent's ctors/dtors from generated main()! + // On Mingw and Cygwin, the symbol __main is resolved to + // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors + // (and register wrong callee's dtors with atexit(3)). + // We expect ExecutionEngine::runStaticConstructorsDestructors() + // is called before ExecutionEngine::runFunctionAsMain() is called. + if (Name == "__main") return (void*)(intptr_t)&jit_noop; + + // FIXME: Would it be responsible to provide GOT? + if (AbortOnFailure) { + if (Name == "_GLOBAL_OFFSET_TABLE_") + report_fatal_error("Program used external function '" + Name + + "' which could not be resolved!"); + } + + return NULL; +} diff --git a/tools/lli/RemoteMemoryManager.h b/tools/lli/RemoteMemoryManager.h new file mode 100644 index 0000000000..dc74666672 --- /dev/null +++ b/tools/lli/RemoteMemoryManager.h @@ -0,0 +1,105 @@ +//===- RemoteMemoryManager.h - LLI MCJIT recording memory manager ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This memory manager allocates local storage and keeps a record of each +// allocation. Iterators are provided for all data and code allocations. +// +//===----------------------------------------------------------------------===// + +#ifndef REMOTEMEMORYMANAGER_H +#define REMOTEMEMORYMANAGER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Memory.h" +#include + +#include "RemoteTarget.h" + +namespace llvm { + +class RemoteMemoryManager : public JITMemoryManager { +public: + // Notice that this structure takes ownership of the memory allocated. + struct Allocation { + Allocation(sys::MemoryBlock mb, unsigned a, bool code) + : MB(mb), Alignment(a), IsCode(code) {} + + sys::MemoryBlock MB; + unsigned Alignment; + bool IsCode; + }; + +private: + // This vector contains Allocation objects for all sections which we have + // allocated. This vector effectively owns the memory associated with the + // allocations. + SmallVector AllocatedSections; + + // This vector contains pointers to Allocation objects for any sections we + // have allocated locally but have not yet remapped for the remote target. + // When we receive notification of a completed module load, we will map + // these sections into the remote target. + SmallVector UnmappedSections; + + // This map tracks the sections we have remapped for the remote target + // but have not yet copied to the target. + DenseMap MappedSections; + + // FIXME: This is part of a work around to keep sections near one another + // when MCJIT performs relocations after code emission but before + // the generated code is moved to the remote target. + sys::MemoryBlock Near; + sys::MemoryBlock allocateSection(uintptr_t Size); + + RemoteTarget *Target; + +public: + RemoteMemoryManager() : Target(NULL) {} + virtual ~RemoteMemoryManager(); + + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName); + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, StringRef SectionName, + bool IsReadOnly); + + void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true); + + void notifyObjectLoaded(ExecutionEngine *EE, const ObjectImage *Obj); + + bool finalizeMemory(std::string *ErrMsg); + + // This is a non-interface function used by lli + void setRemoteTarget(RemoteTarget *T) { Target = T; } + + // The following obsolete JITMemoryManager calls are stubbed out for + // this model. + void setMemoryWritable(); + void setMemoryExecutable(); + void setPoisonMemory(bool poison); + void AllocateGOT(); + uint8_t *getGOTBase() const; + uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize); + uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment); + void endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd); + uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); + uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); + void deallocateFunctionBody(void *Body); +}; + +} // end namespace llvm + +#endif diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 1cdd91b693..ac0d219272 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -15,7 +15,7 @@ #define DEBUG_TYPE "lli" #include "llvm/IR/LLVMContext.h" -#include "RecordingMemoryManager.h" +#include "RemoteMemoryManager.h" #include "RemoteTarget.h" #include "llvm/ADT/Triple.h" #include "llvm/Bitcode/ReaderWriter.h" @@ -131,6 +131,12 @@ namespace { cl::value_desc("function"), cl::init("main")); + cl::list + ExtraModules("extra-modules", + cl::CommaSeparated, + cl::desc("Extra modules to be loaded"), + cl::value_desc(",,...")); + cl::opt FakeArgv0("fake-argv0", cl::desc("Override the 'argv[0]' value passed into the executing" @@ -222,82 +228,6 @@ static void do_shutdown() { #endif } -void layoutRemoteTargetMemory(RemoteTarget *T, RecordingMemoryManager *JMM) { - // Lay out our sections in order, with all the code sections first, then - // all the data sections. - uint64_t CurOffset = 0; - unsigned MaxAlign = T->getPageAlignment(); - SmallVector, 16> Offsets; - SmallVector Sizes; - for (RecordingMemoryManager::const_code_iterator I = JMM->code_begin(), - E = JMM->code_end(); - I != E; ++I) { - DEBUG(dbgs() << "code region: size " << I->first.size() - << ", alignment " << I->second << "\n"); - // Align the current offset up to whatever is needed for the next - // section. - unsigned Align = I->second; - CurOffset = (CurOffset + Align - 1) / Align * Align; - // Save off the address of the new section and allocate its space. - Offsets.push_back(std::pair(I->first.base(), CurOffset)); - Sizes.push_back(I->first.size()); - CurOffset += I->first.size(); - } - // Adjust to keep code and data aligned on seperate pages. - CurOffset = (CurOffset + MaxAlign - 1) / MaxAlign * MaxAlign; - unsigned FirstDataIndex = Offsets.size(); - for (RecordingMemoryManager::const_data_iterator I = JMM->data_begin(), - E = JMM->data_end(); - I != E; ++I) { - DEBUG(dbgs() << "data region: size " << I->first.size() - << ", alignment " << I->second << "\n"); - // Align the current offset up to whatever is needed for the next - // section. - unsigned Align = I->second; - CurOffset = (CurOffset + Align - 1) / Align * Align; - // Save off the address of the new section and allocate its space. - Offsets.push_back(std::pair(I->first.base(), CurOffset)); - Sizes.push_back(I->first.size()); - CurOffset += I->first.size(); - } - - // Allocate space in the remote target. - uint64_t RemoteAddr; - if (T->allocateSpace(CurOffset, MaxAlign, RemoteAddr)) - report_fatal_error(T->getErrorMsg()); - // Map the section addresses so relocations will get updated in the local - // copies of the sections. - for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { - uint64_t Addr = RemoteAddr + Offsets[i].second; - EE->mapSectionAddress(const_cast(Offsets[i].first), Addr); - - DEBUG(dbgs() << " Mapping local: " << Offsets[i].first - << " to remote: 0x" << format("%llx", Addr) << "\n"); - - } - - // Trigger application of relocations - EE->finalizeObject(); - - // Now load it all to the target. - for (unsigned i = 0, e = Offsets.size(); i != e; ++i) { - uint64_t Addr = RemoteAddr + Offsets[i].second; - - if (i < FirstDataIndex) { - T->loadCode(Addr, Offsets[i].first, Sizes[i]); - - DEBUG(dbgs() << " loading code: " << Offsets[i].first - << " to remote: 0x" << format("%llx", Addr) << "\n"); - } else { - T->loadData(Addr, Offsets[i].first, Sizes[i]); - - DEBUG(dbgs() << " loading data: " << Offsets[i].first - << " to remote: 0x" << format("%llx", Addr) << "\n"); - } - - } -} - //===----------------------------------------------------------------------===// // main Driver function // @@ -370,7 +300,7 @@ int main(int argc, char **argv, char * const *envp) { if (UseMCJIT && !ForceInterpreter) { builder.setUseMCJIT(true); if (RemoteMCJIT) - RTDyldMM = new RecordingMemoryManager(); + RTDyldMM = new RemoteMemoryManager(); else RTDyldMM = new SectionMemoryManager(); builder.setMCJITMemoryManager(RTDyldMM); @@ -420,6 +350,16 @@ int main(int argc, char **argv, char * const *envp) { exit(1); } + // Load any additional modules specified on the command line. + for (unsigned i = 0, e = ExtraModules.size(); i != e; ++i) { + Module *XMod = ParseIRFile(ExtraModules[i], Err, Context); + if (!XMod) { + Err.print(argv[0], errs()); + return 1; + } + EE->addModule(XMod); + } + // The following functions have no effect if their respective profiling // support wasn't enabled in the build configuration. EE->RegisterJITEventListener( @@ -519,7 +459,7 @@ int main(int argc, char **argv, char * const *envp) { // it couldn't. This is a limitation of the LLI implemantation, not the // MCJIT itself. FIXME. // - RecordingMemoryManager *MM = static_cast(RTDyldMM); + RemoteMemoryManager *MM = static_cast(RTDyldMM); // Everything is prepared now, so lay out our program for the target // address space, assign the section addresses to resolve any relocations, // and send it to the target. @@ -543,19 +483,30 @@ int main(int argc, char **argv, char * const *envp) { Target.reset(RemoteTarget::createRemoteTarget()); } - // Create the remote target + // Give the memory manager a pointer to our remote target interface object. + MM->setRemoteTarget(Target.get()); + + // Create the remote target. Target->create(); +// FIXME: Don't commit like this. I don't think these calls are necessary. +#if 0 // Trigger compilation. EE->generateCodeForModule(Mod); - // Layout the target memory. - layoutRemoteTargetMemory(Target.get(), MM); + // Get everything ready to execute. + EE->finalizeModule(Mod); +#endif // Since we're executing in a (at least simulated) remote address space, // we can't use the ExecutionEngine::runFunctionAsMain(). We have to // grab the function address directly here and tell the remote target // to execute the function. + // + // Our memory manager will map generated code into the remote address + // space as it is loaded and copy the bits over during the finalizeMemory + // operation. + // // FIXME: argv and envp handling. uint64_t Entry = EE->getFunctionAddress(EntryFn->getName().str()); -- cgit v1.2.3