mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-10-26 18:20:39 +00:00
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
This commit is contained in:
@@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
|
class ExecutionEngine;
|
||||||
|
class ObjectImage;
|
||||||
|
|
||||||
// RuntimeDyld clients often want to handle the memory management of
|
// RuntimeDyld clients often want to handle the memory management of
|
||||||
// what gets placed where. For JIT clients, this is the subset of
|
// what gets placed where. For JIT clients, this is the subset of
|
||||||
// JITMemoryManager required for dynamic loading of binaries.
|
// JITMemoryManager required for dynamic loading of binaries.
|
||||||
@@ -63,11 +66,24 @@ public:
|
|||||||
/// found, this function returns a null pointer. Otherwise, it prints a
|
/// found, this function returns a null pointer. Otherwise, it prints a
|
||||||
/// message to stderr and aborts.
|
/// 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.
|
/// MCJIT or RuntimeDyld. Use getSymbolAddress instead.
|
||||||
virtual void *getPointerToNamedFunction(const std::string &Name,
|
virtual void *getPointerToNamedFunction(const std::string &Name,
|
||||||
bool AbortOnFailure = true);
|
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
|
/// This method is called when object loading is complete and section page
|
||||||
/// permissions can be applied. It is up to the memory manager implementation
|
/// 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
|
/// to decide whether or not to act on this method. The memory manager will
|
||||||
|
|||||||
@@ -513,6 +513,7 @@ void MCJIT::UnregisterJITEventListener(JITEventListener *L) {
|
|||||||
}
|
}
|
||||||
void MCJIT::NotifyObjectEmitted(const ObjectImage& Obj) {
|
void MCJIT::NotifyObjectEmitted(const ObjectImage& Obj) {
|
||||||
MutexGuard locked(lock);
|
MutexGuard locked(lock);
|
||||||
|
MemMgr.notifyObjectLoaded(this, &Obj);
|
||||||
for (unsigned I = 0, S = EventListeners.size(); I < S; ++I) {
|
for (unsigned I = 0, S = EventListeners.size(); I < S; ++I) {
|
||||||
EventListeners[I]->NotifyObjectEmitted(Obj);
|
EventListeners[I]->NotifyObjectEmitted(Obj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ public:
|
|||||||
SectionID, SectionName, IsReadOnly);
|
SectionID, SectionName, IsReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void notifyObjectLoaded(ExecutionEngine *EE,
|
||||||
|
const ObjectImage *Obj) {
|
||||||
|
ClientMM->notifyObjectLoaded(EE, Obj);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void registerEHFrames(StringRef SectionData) {
|
virtual void registerEHFrames(StringRef SectionData) {
|
||||||
ClientMM->registerEHFrames(SectionData);
|
ClientMM->registerEHFrames(SectionData);
|
||||||
}
|
}
|
||||||
|
|||||||
13
test/ExecutionEngine/MCJIT/cross-module-a.ll
Normal file
13
test/ExecutionEngine/MCJIT/cross-module-a.ll
Normal file
@@ -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( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
7
test/ExecutionEngine/MCJIT/cross-module-b.ir
Normal file
7
test/ExecutionEngine/MCJIT/cross-module-b.ir
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
declare i32 @FA()
|
||||||
|
|
||||||
|
define i32 @FB() {
|
||||||
|
%r = call i32 @FA( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
9
test/ExecutionEngine/MCJIT/multi-module-a.ll
Normal file
9
test/ExecutionEngine/MCJIT/multi-module-a.ll
Normal file
@@ -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( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
7
test/ExecutionEngine/MCJIT/multi-module-b.ir
Normal file
7
test/ExecutionEngine/MCJIT/multi-module-b.ir
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
declare i32 @FC()
|
||||||
|
|
||||||
|
define i32 @FB() {
|
||||||
|
%r = call i32 @FC( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
4
test/ExecutionEngine/MCJIT/multi-module-c.ir
Normal file
4
test/ExecutionEngine/MCJIT/multi-module-c.ir
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
define i32 @FC() {
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
13
test/ExecutionEngine/MCJIT/remote/cross-module-a.ll
Normal file
13
test/ExecutionEngine/MCJIT/remote/cross-module-a.ll
Normal file
@@ -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( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
7
test/ExecutionEngine/MCJIT/remote/cross-module-b.ir
Normal file
7
test/ExecutionEngine/MCJIT/remote/cross-module-b.ir
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
declare i32 @FA()
|
||||||
|
|
||||||
|
define i32 @FB() {
|
||||||
|
%r = call i32 @FA( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
9
test/ExecutionEngine/MCJIT/remote/multi-module-a.ll
Normal file
9
test/ExecutionEngine/MCJIT/remote/multi-module-a.ll
Normal file
@@ -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( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
7
test/ExecutionEngine/MCJIT/remote/multi-module-b.ir
Normal file
7
test/ExecutionEngine/MCJIT/remote/multi-module-b.ir
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
declare i32 @FC()
|
||||||
|
|
||||||
|
define i32 @FB() {
|
||||||
|
%r = call i32 @FC( ) ; <i32> [#uses=1]
|
||||||
|
ret i32 %r
|
||||||
|
}
|
||||||
|
|
||||||
4
test/ExecutionEngine/MCJIT/remote/multi-module-c.ir
Normal file
4
test/ExecutionEngine/MCJIT/remote/multi-module-c.ir
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
define i32 @FC() {
|
||||||
|
ret i32 0
|
||||||
|
}
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ endif( LLVM_USE_INTEL_JITEVENTS )
|
|||||||
|
|
||||||
add_llvm_tool(lli
|
add_llvm_tool(lli
|
||||||
lli.cpp
|
lli.cpp
|
||||||
RecordingMemoryManager.cpp
|
RemoteMemoryManager.cpp
|
||||||
RemoteTarget.cpp
|
RemoteTarget.cpp
|
||||||
RemoteTargetExternal.cpp
|
RemoteTargetExternal.cpp
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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<Allocation>::iterator
|
|
||||||
I = AllocatedCodeMem.begin(), E = AllocatedCodeMem.end();
|
|
||||||
I != E; ++I)
|
|
||||||
sys::Memory::releaseMappedMemory(I->first);
|
|
||||||
for (SmallVectorImpl<Allocation>::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;
|
|
||||||
}
|
|
||||||
230
tools/lli/RemoteMemoryManager.cpp
Normal file
230
tools/lli/RemoteMemoryManager.cpp
Normal file
@@ -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<Allocation, 2>::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<std::pair<const Allocation*, uint64_t>, 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<const Allocation*,uint64_t>(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<const Allocation*,uint64_t>(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<void*>(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<uint64_t, const Allocation*>::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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//===- RecordingMemoryManager.h - LLI MCJIT recording memory manager ------===//
|
//===- RemoteMemoryManager.h - LLI MCJIT recording memory manager ------===//
|
||||||
//
|
//
|
||||||
// The LLVM Compiler Infrastructure
|
// The LLVM Compiler Infrastructure
|
||||||
//
|
//
|
||||||
@@ -12,24 +12,47 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#ifndef RECORDINGMEMORYMANAGER_H
|
#ifndef REMOTEMEMORYMANAGER_H
|
||||||
#define RECORDINGMEMORYMANAGER_H
|
#define REMOTEMEMORYMANAGER_H
|
||||||
|
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
#include "llvm/ExecutionEngine/JITMemoryManager.h"
|
#include "llvm/ExecutionEngine/JITMemoryManager.h"
|
||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
#include "llvm/Support/Memory.h"
|
#include "llvm/Support/Memory.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "RemoteTarget.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
class RecordingMemoryManager : public JITMemoryManager {
|
class RemoteMemoryManager : public JITMemoryManager {
|
||||||
public:
|
public:
|
||||||
typedef std::pair<sys::MemoryBlock, unsigned> Allocation;
|
// 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:
|
private:
|
||||||
SmallVector<Allocation, 16> AllocatedDataMem;
|
// This vector contains Allocation objects for all sections which we have
|
||||||
SmallVector<Allocation, 16> AllocatedCodeMem;
|
// allocated. This vector effectively owns the memory associated with the
|
||||||
|
// allocations.
|
||||||
|
SmallVector<Allocation, 2> 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<const Allocation *, 2> UnmappedSections;
|
||||||
|
|
||||||
|
// This map tracks the sections we have remapped for the remote target
|
||||||
|
// but have not yet copied to the target.
|
||||||
|
DenseMap<uint64_t, const Allocation *> MappedSections;
|
||||||
|
|
||||||
// FIXME: This is part of a work around to keep sections near one another
|
// FIXME: This is part of a work around to keep sections near one another
|
||||||
// when MCJIT performs relocations after code emission but before
|
// when MCJIT performs relocations after code emission but before
|
||||||
@@ -37,17 +60,11 @@ private:
|
|||||||
sys::MemoryBlock Near;
|
sys::MemoryBlock Near;
|
||||||
sys::MemoryBlock allocateSection(uintptr_t Size);
|
sys::MemoryBlock allocateSection(uintptr_t Size);
|
||||||
|
|
||||||
|
RemoteTarget *Target;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RecordingMemoryManager() {}
|
RemoteMemoryManager() : Target(NULL) {}
|
||||||
virtual ~RecordingMemoryManager();
|
virtual ~RemoteMemoryManager();
|
||||||
|
|
||||||
typedef SmallVectorImpl<Allocation>::const_iterator const_data_iterator;
|
|
||||||
typedef SmallVectorImpl<Allocation>::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,
|
uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
|
||||||
unsigned SectionID, StringRef SectionName);
|
unsigned SectionID, StringRef SectionName);
|
||||||
@@ -59,7 +76,12 @@ public:
|
|||||||
void *getPointerToNamedFunction(const std::string &Name,
|
void *getPointerToNamedFunction(const std::string &Name,
|
||||||
bool AbortOnFailure = true);
|
bool AbortOnFailure = true);
|
||||||
|
|
||||||
bool finalizeMemory(std::string *ErrMsg) { return false; }
|
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
|
// The following obsolete JITMemoryManager calls are stubbed out for
|
||||||
// this model.
|
// this model.
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
#define DEBUG_TYPE "lli"
|
#define DEBUG_TYPE "lli"
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "RecordingMemoryManager.h"
|
#include "RemoteMemoryManager.h"
|
||||||
#include "RemoteTarget.h"
|
#include "RemoteTarget.h"
|
||||||
#include "llvm/ADT/Triple.h"
|
#include "llvm/ADT/Triple.h"
|
||||||
#include "llvm/Bitcode/ReaderWriter.h"
|
#include "llvm/Bitcode/ReaderWriter.h"
|
||||||
@@ -131,6 +131,12 @@ namespace {
|
|||||||
cl::value_desc("function"),
|
cl::value_desc("function"),
|
||||||
cl::init("main"));
|
cl::init("main"));
|
||||||
|
|
||||||
|
cl::list<std::string>
|
||||||
|
ExtraModules("extra-modules",
|
||||||
|
cl::CommaSeparated,
|
||||||
|
cl::desc("Extra modules to be loaded"),
|
||||||
|
cl::value_desc("<input bitcode 2>,<input bitcode 3>,..."));
|
||||||
|
|
||||||
cl::opt<std::string>
|
cl::opt<std::string>
|
||||||
FakeArgv0("fake-argv0",
|
FakeArgv0("fake-argv0",
|
||||||
cl::desc("Override the 'argv[0]' value passed into the executing"
|
cl::desc("Override the 'argv[0]' value passed into the executing"
|
||||||
@@ -222,82 +228,6 @@ static void do_shutdown() {
|
|||||||
#endif
|
#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<std::pair<const void*, uint64_t>, 16> Offsets;
|
|
||||||
SmallVector<unsigned, 16> 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<const void*,uint64_t>(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<const void*,uint64_t>(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<void*>(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
|
// main Driver function
|
||||||
//
|
//
|
||||||
@@ -370,7 +300,7 @@ int main(int argc, char **argv, char * const *envp) {
|
|||||||
if (UseMCJIT && !ForceInterpreter) {
|
if (UseMCJIT && !ForceInterpreter) {
|
||||||
builder.setUseMCJIT(true);
|
builder.setUseMCJIT(true);
|
||||||
if (RemoteMCJIT)
|
if (RemoteMCJIT)
|
||||||
RTDyldMM = new RecordingMemoryManager();
|
RTDyldMM = new RemoteMemoryManager();
|
||||||
else
|
else
|
||||||
RTDyldMM = new SectionMemoryManager();
|
RTDyldMM = new SectionMemoryManager();
|
||||||
builder.setMCJITMemoryManager(RTDyldMM);
|
builder.setMCJITMemoryManager(RTDyldMM);
|
||||||
@@ -420,6 +350,16 @@ int main(int argc, char **argv, char * const *envp) {
|
|||||||
exit(1);
|
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
|
// The following functions have no effect if their respective profiling
|
||||||
// support wasn't enabled in the build configuration.
|
// support wasn't enabled in the build configuration.
|
||||||
EE->RegisterJITEventListener(
|
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
|
// it couldn't. This is a limitation of the LLI implemantation, not the
|
||||||
// MCJIT itself. FIXME.
|
// MCJIT itself. FIXME.
|
||||||
//
|
//
|
||||||
RecordingMemoryManager *MM = static_cast<RecordingMemoryManager*>(RTDyldMM);
|
RemoteMemoryManager *MM = static_cast<RemoteMemoryManager*>(RTDyldMM);
|
||||||
// Everything is prepared now, so lay out our program for the target
|
// Everything is prepared now, so lay out our program for the target
|
||||||
// address space, assign the section addresses to resolve any relocations,
|
// address space, assign the section addresses to resolve any relocations,
|
||||||
// and send it to the target.
|
// and send it to the target.
|
||||||
@@ -543,19 +483,30 @@ int main(int argc, char **argv, char * const *envp) {
|
|||||||
Target.reset(RemoteTarget::createRemoteTarget());
|
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();
|
Target->create();
|
||||||
|
|
||||||
|
// FIXME: Don't commit like this. I don't think these calls are necessary.
|
||||||
|
#if 0
|
||||||
// Trigger compilation.
|
// Trigger compilation.
|
||||||
EE->generateCodeForModule(Mod);
|
EE->generateCodeForModule(Mod);
|
||||||
|
|
||||||
// Layout the target memory.
|
// Get everything ready to execute.
|
||||||
layoutRemoteTargetMemory(Target.get(), MM);
|
EE->finalizeModule(Mod);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Since we're executing in a (at least simulated) remote address space,
|
// Since we're executing in a (at least simulated) remote address space,
|
||||||
// we can't use the ExecutionEngine::runFunctionAsMain(). We have to
|
// we can't use the ExecutionEngine::runFunctionAsMain(). We have to
|
||||||
// grab the function address directly here and tell the remote target
|
// grab the function address directly here and tell the remote target
|
||||||
// to execute the function.
|
// 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.
|
// FIXME: argv and envp handling.
|
||||||
uint64_t Entry = EE->getFunctionAddress(EntryFn->getName().str());
|
uint64_t Entry = EE->getFunctionAddress(EntryFn->getName().str());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user