diff options
author | Stephen Hines <srhines@google.com> | 2014-04-23 16:57:46 -0700 |
---|---|---|
committer | Stephen Hines <srhines@google.com> | 2014-04-24 15:53:16 -0700 |
commit | 36b56886974eae4f9c5ebc96befd3e7bfe5de338 (patch) | |
tree | e6cfb69fbbd937f450eeb83bfb83b9da3b01275a /tools/lli | |
parent | 69a8640022b04415ae9fac62f8ab090601d8f889 (diff) | |
download | external_llvm-36b56886974eae4f9c5ebc96befd3e7bfe5de338.zip external_llvm-36b56886974eae4f9c5ebc96befd3e7bfe5de338.tar.gz external_llvm-36b56886974eae4f9c5ebc96befd3e7bfe5de338.tar.bz2 |
Update to LLVM 3.5a.
Change-Id: Ifadecab779f128e62e430c2b4f6ddd84953ed617
Diffstat (limited to 'tools/lli')
-rw-r--r-- | tools/lli/CMakeLists.txt | 18 | ||||
-rw-r--r-- | tools/lli/ChildTarget/CMakeLists.txt | 9 | ||||
-rw-r--r-- | tools/lli/ChildTarget/ChildTarget.cpp | 96 | ||||
-rw-r--r-- | tools/lli/ChildTarget/Makefile | 4 | ||||
-rw-r--r-- | tools/lli/ChildTarget/Unix/ChildTarget.inc | 166 | ||||
-rw-r--r-- | tools/lli/ChildTarget/Windows/ChildTarget.inc | 44 | ||||
-rw-r--r-- | tools/lli/RPCChannel.h | 49 | ||||
-rw-r--r-- | tools/lli/RemoteMemoryManager.cpp | 12 | ||||
-rw-r--r-- | tools/lli/RemoteMemoryManager.h | 42 | ||||
-rw-r--r-- | tools/lli/RemoteTarget.cpp | 39 | ||||
-rw-r--r-- | tools/lli/RemoteTarget.h | 38 | ||||
-rw-r--r-- | tools/lli/RemoteTargetExternal.cpp | 333 | ||||
-rw-r--r-- | tools/lli/RemoteTargetExternal.h | 97 | ||||
-rw-r--r-- | tools/lli/RemoteTargetMessage.h | 54 | ||||
-rw-r--r-- | tools/lli/Unix/RPCChannel.inc (renamed from tools/lli/Unix/RemoteTargetExternal.inc) | 58 | ||||
-rw-r--r-- | tools/lli/Windows/RPCChannel.inc | 29 | ||||
-rw-r--r-- | tools/lli/Windows/RemoteTargetExternal.inc | 35 | ||||
-rw-r--r-- | tools/lli/lli.cpp | 208 |
18 files changed, 793 insertions, 538 deletions
diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index 5f8c7c9..731b61a 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -1,8 +1,19 @@ - -set(LLVM_LINK_COMPONENTS mcjit jit interpreter nativecodegen bitreader asmparser irreader selectiondag native instrumentation) - add_subdirectory(ChildTarget) +set(LLVM_LINK_COMPONENTS + CodeGen + Core + ExecutionEngine + IRReader + Instrumentation + Interpreter + JIT + MCJIT + SelectionDAG + Support + native + ) + if( LLVM_USE_OPROFILE ) set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} @@ -25,3 +36,4 @@ add_llvm_tool(lli RemoteTarget.cpp RemoteTargetExternal.cpp ) +set_target_properties(lli PROPERTIES ENABLE_EXPORTS 1) diff --git a/tools/lli/ChildTarget/CMakeLists.txt b/tools/lli/ChildTarget/CMakeLists.txt index fd1ac24..9f88b2c 100644 --- a/tools/lli/ChildTarget/CMakeLists.txt +++ b/tools/lli/ChildTarget/CMakeLists.txt @@ -1,3 +1,8 @@ -add_llvm_tool(lli-child-target +set(LLVM_LINK_COMPONENTS support) + +add_llvm_executable(lli-child-target ChildTarget.cpp - ) + ../RemoteTarget.cpp +) + +set_target_properties(lli-child-target PROPERTIES FOLDER "Misc") diff --git a/tools/lli/ChildTarget/ChildTarget.cpp b/tools/lli/ChildTarget/ChildTarget.cpp index 55fcae9..0d71b17 100644 --- a/tools/lli/ChildTarget/ChildTarget.cpp +++ b/tools/lli/ChildTarget/ChildTarget.cpp @@ -1,6 +1,8 @@ #include "llvm/Config/config.h" - +#include "../RPCChannel.h" +#include "../RemoteTarget.h" #include "../RemoteTargetMessage.h" +#include "llvm/Support/Memory.h" #include <assert.h> #include <map> #include <stdint.h> @@ -11,36 +13,32 @@ using namespace llvm; class LLIChildTarget { public: - ~LLIChildTarget(); // OS-specific destructor void initialize(); LLIMessageType waitForIncomingMessage(); void handleMessage(LLIMessageType messageType); + RemoteTarget *RT; + RPCChannel RPC; private: // Incoming message handlers void handleAllocateSpace(); void handleLoadSection(bool IsCode); void handleExecute(); - void handleTerminate(); // Outgoing message handlers void sendChildActive(); void sendAllocationResult(uint64_t Addr); - void sendLoadComplete(); - void sendExecutionComplete(uint64_t Result); + void sendLoadStatus(uint32_t Status); + void sendExecutionComplete(int Result); // OS-specific functions void initializeConnection(); - int WriteBytes(const void *Data, size_t Size); - int ReadBytes(void *Data, size_t Size); - uint64_t allocate(uint32_t Alignment, uint32_t Size); - void makeSectionExecutable(uint64_t Addr, uint32_t Size); - void InvalidateInstructionCache(const void *Addr, size_t Len); - void releaseMemory(uint64_t Addr, uint32_t Size); - - // Store a map of allocated buffers to sizes. - typedef std::map<uint64_t, uint32_t> AllocMapType; - AllocMapType m_AllocatedBufferMap; + int WriteBytes(const void *Data, size_t Size) { + return RPC.WriteBytes(Data, Size) ? Size : -1; + } + int ReadBytes(void *Data, size_t Size) { + return RPC.ReadBytes(Data, Size) ? Size : -1; + } // Communication handles (OS-specific) void *ConnectionData; @@ -48,6 +46,7 @@ private: int main() { LLIChildTarget ThisChild; + ThisChild.RT = new RemoteTarget(); ThisChild.initialize(); LLIMessageType MsgType; do { @@ -55,12 +54,13 @@ int main() { ThisChild.handleMessage(MsgType); } while (MsgType != LLI_Terminate && MsgType != LLI_Error); + delete ThisChild.RT; return 0; } // Public methods void LLIChildTarget::initialize() { - initializeConnection(); + RPC.createClient(); sendChildActive(); } @@ -86,7 +86,7 @@ void LLIChildTarget::handleMessage(LLIMessageType messageType) { handleExecute(); break; case LLI_Terminate: - handleTerminate(); + RT->stop(); break; default: // FIXME: Handle error! @@ -99,6 +99,7 @@ void LLIChildTarget::handleAllocateSpace() { // Read and verify the message data size. uint32_t DataSize; int rc = ReadBytes(&DataSize, 4); + (void)rc; assert(rc == 4); assert(DataSize == 8); @@ -111,7 +112,8 @@ void LLIChildTarget::handleAllocateSpace() { assert(rc == 4); // Allocate the memory. - uint64_t Addr = allocate(Alignment, AllocSize); + uint64_t Addr; + RT->allocateSpace(AllocSize, Alignment, Addr); // Send AllocationResult message. sendAllocationResult(Addr); @@ -121,33 +123,36 @@ void LLIChildTarget::handleLoadSection(bool IsCode) { // Read the message data size. uint32_t DataSize; int rc = ReadBytes(&DataSize, 4); + (void)rc; assert(rc == 4); // Read the target load address. uint64_t Addr; rc = ReadBytes(&Addr, 8); assert(rc == 8); - size_t BufferSize = DataSize - 8; - // FIXME: Verify that this is in allocated space + if (!RT->isAllocatedMemory(Addr, BufferSize)) + return sendLoadStatus(LLI_Status_NotAllocated); // Read section data into previously allocated buffer - rc = ReadBytes((void*)Addr, DataSize - 8); - assert(rc == (int)(BufferSize)); + rc = ReadBytes((void*)Addr, BufferSize); + if (rc != (int)(BufferSize)) + return sendLoadStatus(LLI_Status_IncompleteMsg); // If IsCode, mark memory executable if (IsCode) - makeSectionExecutable(Addr, BufferSize); + sys::Memory::InvalidateInstructionCache((void *)Addr, BufferSize); // Send MarkLoadComplete message. - sendLoadComplete(); + sendLoadStatus(LLI_Status_Success); } void LLIChildTarget::handleExecute() { // Read the message data size. uint32_t DataSize; int rc = ReadBytes(&DataSize, 4); + (void)rc; assert(rc == 4); assert(DataSize == 8); @@ -157,22 +162,11 @@ void LLIChildTarget::handleExecute() { assert(rc == 8); // Call function - int Result; - int (*fn)(void) = (int(*)(void))Addr; - Result = fn(); + int32_t Result = -1; + RT->executeCode(Addr, Result); // Send ExecutionResult message. - sendExecutionComplete((int64_t)Result); -} - -void LLIChildTarget::handleTerminate() { - // Release all allocated memory - AllocMapType::iterator Begin = m_AllocatedBufferMap.begin(); - AllocMapType::iterator End = m_AllocatedBufferMap.end(); - for (AllocMapType::iterator It = Begin; It != End; ++It) { - releaseMemory(It->first, It->second); - } - m_AllocatedBufferMap.clear(); + sendExecutionComplete(Result); } // Outgoing message handlers @@ -180,6 +174,7 @@ void LLIChildTarget::sendChildActive() { // Write the message type. uint32_t MsgType = (uint32_t)LLI_ChildActive; int rc = WriteBytes(&MsgType, 4); + (void)rc; assert(rc == 4); // Write the data size. @@ -192,6 +187,7 @@ void LLIChildTarget::sendAllocationResult(uint64_t Addr) { // Write the message type. uint32_t MsgType = (uint32_t)LLI_AllocationResult; int rc = WriteBytes(&MsgType, 4); + (void)rc; assert(rc == 4); // Write the data size. @@ -204,39 +200,45 @@ void LLIChildTarget::sendAllocationResult(uint64_t Addr) { assert(rc == 8); } -void LLIChildTarget::sendLoadComplete() { +void LLIChildTarget::sendLoadStatus(uint32_t Status) { // Write the message type. - uint32_t MsgType = (uint32_t)LLI_LoadComplete; + uint32_t MsgType = (uint32_t)LLI_LoadResult; int rc = WriteBytes(&MsgType, 4); + (void)rc; assert(rc == 4); // Write the data size. - uint32_t DataSize = 0; + uint32_t DataSize = 4; rc = WriteBytes(&DataSize, 4); assert(rc == 4); + + // Write the result. + rc = WriteBytes(&Status, 4); + assert(rc == 4); } -void LLIChildTarget::sendExecutionComplete(uint64_t Result) { +void LLIChildTarget::sendExecutionComplete(int Result) { // Write the message type. uint32_t MsgType = (uint32_t)LLI_ExecutionResult; int rc = WriteBytes(&MsgType, 4); + (void)rc; assert(rc == 4); // Write the data size. - uint32_t DataSize = 8; + uint32_t DataSize = 4; rc = WriteBytes(&DataSize, 4); assert(rc == 4); // Write the result. - rc = WriteBytes(&Result, 8); - assert(rc == 8); + rc = WriteBytes(&Result, 4); + assert(rc == 4); } #ifdef LLVM_ON_UNIX -#include "Unix/ChildTarget.inc" +#include "../Unix/RPCChannel.inc" #endif #ifdef LLVM_ON_WIN32 -#include "Windows/ChildTarget.inc" +#include "../Windows/RPCChannel.inc" #endif diff --git a/tools/lli/ChildTarget/Makefile b/tools/lli/ChildTarget/Makefile index f77b144..6f4ddef 100644 --- a/tools/lli/ChildTarget/Makefile +++ b/tools/lli/ChildTarget/Makefile @@ -12,6 +12,8 @@ TOOLNAME := lli-child-target include $(LEVEL)/Makefile.config -LINK_COMPONENTS := +LINK_COMPONENTS := support + +SOURCES := ChildTarget.cpp ../RemoteTarget.cpp include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/lli/ChildTarget/Unix/ChildTarget.inc b/tools/lli/ChildTarget/Unix/ChildTarget.inc deleted file mode 100644 index cc95810..0000000 --- a/tools/lli/ChildTarget/Unix/ChildTarget.inc +++ /dev/null @@ -1,166 +0,0 @@ -//===- ChildTarget.inc - Child process for external JIT execution for Unix -==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Implementation of the Unix-specific parts of the ChildTarget class -// which executes JITed code in a separate process from where it was built. -// -//===----------------------------------------------------------------------===// - -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> - -#ifdef HAVE_SYS_MMAN_H -#include <sys/mman.h> -#endif - -#ifdef __APPLE__ -#include <mach/mach.h> -#endif - -#if defined(__mips__) -# if defined(__OpenBSD__) -# include <mips64/sysarch.h> -# else -# include <sys/cachectl.h> -# endif -#endif - -#ifdef __APPLE__ -extern "C" void sys_icache_invalidate(const void *Addr, size_t len); -#else -extern "C" void __clear_cache(void *, void*); -#endif - -namespace { - -struct ConnectionData_t { - int InputPipe; - int OutputPipe; - - ConnectionData_t(int in, int out) : InputPipe(in), OutputPipe(out) {} -}; - -} // namespace - -LLIChildTarget::~LLIChildTarget() { - delete static_cast<ConnectionData_t *>(ConnectionData); -} - -// OS-specific methods -void LLIChildTarget::initializeConnection() { - // Store the parent ends of the pipes - ConnectionData = (void*)new ConnectionData_t(STDIN_FILENO, STDOUT_FILENO); -} - -int LLIChildTarget::WriteBytes(const void *Data, size_t Size) { - return write(((ConnectionData_t*)ConnectionData)->OutputPipe, Data, Size); -} - -int LLIChildTarget::ReadBytes(void *Data, size_t Size) { - return read(((ConnectionData_t*)ConnectionData)->InputPipe, Data, Size); -} - -// The functions below duplicate functionality that is implemented in -// Support/Memory.cpp with the goal of avoiding a dependency on any -// llvm libraries. - -uint64_t LLIChildTarget::allocate(uint32_t Alignment, uint32_t Size) { - if (!Alignment) - Alignment = 16; - - static const size_t PageSize = getpagesize(); - const size_t NumPages = (Size+PageSize-1)/PageSize; - Size = NumPages*PageSize; - - int fd = -1; -#ifdef NEED_DEV_ZERO_FOR_MMAP - static int zero_fd = open("/dev/zero", O_RDWR); - if (zero_fd == -1) - return 0; - fd = zero_fd; -#endif - - int MMFlags = MAP_PRIVATE | -#ifdef HAVE_MMAP_ANONYMOUS - MAP_ANONYMOUS -#else - MAP_ANON -#endif - ; // Ends statement above - - uint64_t Addr = (uint64_t)::mmap(0, Size, PROT_READ | PROT_WRITE, MMFlags, fd, 0); - if (Addr == (uint64_t)MAP_FAILED) - return 0; - - // Align the address. - Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); - - m_AllocatedBufferMap[Addr] = Size; - - // Return aligned address - return Addr; -} - -void LLIChildTarget::makeSectionExecutable(uint64_t Addr, uint32_t Size) { - // FIXME: We have to mark the memory as RWX because multiple code chunks may - // be on the same page. The RemoteTarget interface should be changed to - // work around that. - int Result = ::mprotect((void*)Addr, Size, PROT_READ | PROT_WRITE | PROT_EXEC); - if (Result != 0) - InvalidateInstructionCache((const void *)Addr, Size); -} - -/// InvalidateInstructionCache - Before the JIT can run a block of code -/// that has been emitted it must invalidate the instruction cache on some -/// platforms. -void LLIChildTarget::InvalidateInstructionCache(const void *Addr, - size_t Len) { - -// icache invalidation for PPC and ARM. -#if defined(__APPLE__) - -# if (defined(__POWERPC__) || defined (__ppc__) || \ - defined(_POWER) || defined(_ARCH_PPC)) || defined(__arm__) - sys_icache_invalidate(const_cast<void *>(Addr), Len); -# endif - -#else - -# if (defined(__POWERPC__) || defined (__ppc__) || \ - defined(_POWER) || defined(_ARCH_PPC)) && defined(__GNUC__) - const size_t LineSize = 32; - - const intptr_t Mask = ~(LineSize - 1); - const intptr_t StartLine = ((intptr_t) Addr) & Mask; - const intptr_t EndLine = ((intptr_t) Addr + Len + LineSize - 1) & Mask; - - for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize) - asm volatile("dcbf 0, %0" : : "r"(Line)); - asm volatile("sync"); - - for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize) - asm volatile("icbi 0, %0" : : "r"(Line)); - asm volatile("isync"); -# elif defined(__arm__) && defined(__GNUC__) - // FIXME: Can we safely always call this for __GNUC__ everywhere? - const char *Start = static_cast<const char *>(Addr); - const char *End = Start + Len; - __clear_cache(const_cast<char *>(Start), const_cast<char *>(End)); -# elif defined(__mips__) - const char *Start = static_cast<const char *>(Addr); - cacheflush(const_cast<char *>(Start), Len, BCACHE); -# endif - -#endif // end apple -} - -void LLIChildTarget::releaseMemory(uint64_t Addr, uint32_t Size) { - ::munmap((void*)Addr, Size); -} diff --git a/tools/lli/ChildTarget/Windows/ChildTarget.inc b/tools/lli/ChildTarget/Windows/ChildTarget.inc deleted file mode 100644 index 45db2b0..0000000 --- a/tools/lli/ChildTarget/Windows/ChildTarget.inc +++ /dev/null @@ -1,44 +0,0 @@ -//=- ChildTarget.inc - Child process for external JIT execution for Windows -=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Non-implementation of the Windows-specific parts of the ChildTarget class -// which executes JITed code in a separate process from where it was built. -// -//===----------------------------------------------------------------------===// - -LLIChildTarget::~LLIChildTarget() { -} - -// The RemoteTargetExternal implementation should prevent us from ever getting -// here on Windows, but nothing prevents a user from running this directly. -void LLIChildTarget::initializeConnection() { - assert(0 && "lli-child-target is not implemented for Windows"); -} - -int LLIChildTarget::WriteBytes(const void *Data, size_t Size) { - return 0; -} - -int LLIChildTarget::ReadBytes(void *Data, size_t Size) { - return 0; -} - -uint64_t LLIChildTarget::allocate(uint32_t Alignment, uint32_t Size) { - return 0; -} - -void LLIChildTarget::makeSectionExecutable(uint64_t Addr, uint32_t Size) { -} - -void LLIChildTarget::InvalidateInstructionCache(const void *Addr, - size_t Len) { -} - -void LLIChildTarget::releaseMemory(uint64_t Addr, uint32_t Size) { -} diff --git a/tools/lli/RPCChannel.h b/tools/lli/RPCChannel.h new file mode 100644 index 0000000..2d8c708 --- /dev/null +++ b/tools/lli/RPCChannel.h @@ -0,0 +1,49 @@ +//===---------- RPCChannel.h - LLVM out-of-process JIT execution ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Definition of the RemoteTargetExternal class which executes JITed code in a +// separate process from where it was built. +// +//===----------------------------------------------------------------------===// + +#ifndef LLI_RPCCHANNEL_H +#define LLI_RPCCHANNEL_H + +#include <stdlib.h> +#include <string> + +namespace llvm { + +class RPCChannel { +public: + std::string ChildName; + + RPCChannel() {} + ~RPCChannel(); + + /// Start the remote process. + /// + /// @returns True on success. On failure, ErrorMsg is updated with + /// descriptive text of the encountered error. + bool createServer(); + + bool createClient(); + + // This will get filled in as a point to an OS-specific structure. + void *ConnectionData; + + bool WriteBytes(const void *Data, size_t Size); + bool ReadBytes(void *Data, size_t Size); + + void Wait(); +}; + +} // end namespace llvm + +#endif // LLI_RPCCHANNEL_H diff --git a/tools/lli/RemoteMemoryManager.cpp b/tools/lli/RemoteMemoryManager.cpp index 04fc40e..e9f4d53 100644 --- a/tools/lli/RemoteMemoryManager.cpp +++ b/tools/lli/RemoteMemoryManager.cpp @@ -109,7 +109,7 @@ void RemoteMemoryManager::notifyObjectLoaded(ExecutionEngine *EE, CurOffset += Size; } } - // Adjust to keep code and data aligned on seperate pages. + // Adjust to keep code and data aligned on separate pages. CurOffset = (CurOffset + MaxAlign - 1) / MaxAlign * MaxAlign; for (size_t i = 0, e = NumSections; i != e; ++i) { Allocation &Section = UnmappedSections[i]; @@ -129,7 +129,7 @@ void RemoteMemoryManager::notifyObjectLoaded(ExecutionEngine *EE, // Allocate space in the remote target. uint64_t RemoteAddr; - if (Target->allocateSpace(CurOffset, MaxAlign, RemoteAddr)) + if (!Target->allocateSpace(CurOffset, MaxAlign, RemoteAddr)) report_fatal_error(Target->getErrorMsg()); // Map the section addresses so relocations will get updated in the local @@ -155,13 +155,13 @@ bool RemoteMemoryManager::finalizeMemory(std::string *ErrMsg) { uint64_t RemoteAddr = I->first; const Allocation &Section = I->second; if (Section.IsCode) { - Target->loadCode(RemoteAddr, Section.MB.base(), Section.MB.size()); - + if (!Target->loadCode(RemoteAddr, Section.MB.base(), Section.MB.size())) + report_fatal_error(Target->getErrorMsg()); DEBUG(dbgs() << " loading code: " << Section.MB.base() << " to remote: 0x" << format("%llx", RemoteAddr) << "\n"); } else { - Target->loadData(RemoteAddr, Section.MB.base(), Section.MB.size()); - + if (!Target->loadData(RemoteAddr, Section.MB.base(), Section.MB.size())) + report_fatal_error(Target->getErrorMsg()); DEBUG(dbgs() << " loading data: " << Section.MB.base() << " to remote: 0x" << format("%llx", RemoteAddr) << "\n"); } diff --git a/tools/lli/RemoteMemoryManager.h b/tools/lli/RemoteMemoryManager.h index 5d0456f..11f600f 100644 --- a/tools/lli/RemoteMemoryManager.h +++ b/tools/lli/RemoteMemoryManager.h @@ -15,6 +15,7 @@ #ifndef REMOTEMEMORYMANAGER_H #define REMOTEMEMORYMANAGER_H +#include "RemoteTarget.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ExecutionEngine/JITMemoryManager.h" @@ -22,8 +23,6 @@ #include "llvm/Support/Memory.h" #include <utility> -#include "RemoteTarget.h" - namespace llvm { class RemoteMemoryManager : public JITMemoryManager { @@ -68,45 +67,48 @@ public: virtual ~RemoteMemoryManager(); uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, StringRef SectionName); + unsigned SectionID, + StringRef SectionName) override; uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, StringRef SectionName, - bool IsReadOnly); + bool IsReadOnly) override; // For now, remote symbol resolution is not support in lli. The MCJIT // interface does support this, but clients must provide their own // mechanism for finding remote symbol addresses. MCJIT will resolve // symbols from Modules it contains. - uint64_t getSymbolAddress(const std::string &Name) { return 0; } + uint64_t getSymbolAddress(const std::string &Name) override { return 0; } - void notifyObjectLoaded(ExecutionEngine *EE, const ObjectImage *Obj); + void notifyObjectLoaded(ExecutionEngine *EE, const ObjectImage *Obj) override; - bool finalizeMemory(std::string *ErrMsg); + bool finalizeMemory(std::string *ErrMsg) override; // For now, remote EH frame registration isn't supported. Remote symbol // resolution is a prerequisite to supporting remote EH frame registration. - void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) {} - void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) {} + void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, + size_t Size) override {} + void deregisterEHFrames(uint8_t *Addr, uint64_t LoadAddr, + size_t Size) override {} // 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); + void setMemoryWritable() override; + void setMemoryExecutable() override; + void setPoisonMemory(bool poison) override; + void AllocateGOT() override; + uint8_t *getGOTBase() const override; + uint8_t *startFunctionBody(const Function *F, uintptr_t &ActualSize) override; uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, - unsigned Alignment); + unsigned Alignment) override; 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); + uint8_t *FunctionEnd) override; + uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) override; + uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) override; + void deallocateFunctionBody(void *Body) override; }; } // end namespace llvm diff --git a/tools/lli/RemoteTarget.cpp b/tools/lli/RemoteTarget.cpp index 5c74e6e..55fc064 100644 --- a/tools/lli/RemoteTarget.cpp +++ b/tools/lli/RemoteTarget.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "RemoteTarget.h" -#include "RemoteTargetExternal.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Memory.h" @@ -22,28 +21,6 @@ using namespace llvm; -// Static methods -RemoteTarget *RemoteTarget::createRemoteTarget() { - return new RemoteTarget; -} - -RemoteTarget *RemoteTarget::createExternalRemoteTarget(std::string &ChildName) { -#ifdef LLVM_ON_UNIX - return new RemoteTargetExternal(ChildName); -#else - return 0; -#endif -} - -bool RemoteTarget::hostSupportsExternalRemoteTarget() { -#ifdef LLVM_ON_UNIX - return true; -#else - return false; -#endif -} - - //////////////////////////////////////////////////////////////////////////////// // Simulated remote execution // @@ -56,34 +33,36 @@ bool RemoteTarget::allocateSpace(size_t Size, unsigned Alignment, sys::MemoryBlock *Prev = Allocations.size() ? &Allocations.back() : NULL; sys::MemoryBlock Mem = sys::Memory::AllocateRWX(Size, Prev, &ErrorMsg); if (Mem.base() == NULL) - return true; + return false; if ((uintptr_t)Mem.base() % Alignment) { ErrorMsg = "unable to allocate sufficiently aligned memory"; - return true; + return false; } Address = reinterpret_cast<uint64_t>(Mem.base()); - return false; + Allocations.push_back(Mem); + return true; } bool RemoteTarget::loadData(uint64_t Address, const void *Data, size_t Size) { memcpy ((void*)Address, Data, Size); - return false; + return true; } bool RemoteTarget::loadCode(uint64_t Address, const void *Data, size_t Size) { memcpy ((void*)Address, Data, Size); sys::MemoryBlock Mem((void*)Address, Size); sys::Memory::setExecutable(Mem, &ErrorMsg); - return false; + return true; } bool RemoteTarget::executeCode(uint64_t Address, int &RetVal) { int (*fn)(void) = (int(*)(void))Address; RetVal = fn(); - return false; + return true; } -void RemoteTarget::create() { +bool RemoteTarget::create() { + return true; } void RemoteTarget::stop() { diff --git a/tools/lli/RemoteTarget.h b/tools/lli/RemoteTarget.h index c95fbd1..73e8ae2 100644 --- a/tools/lli/RemoteTarget.h +++ b/tools/lli/RemoteTarget.h @@ -25,10 +25,13 @@ namespace llvm { class RemoteTarget { - std::string ErrorMsg; bool IsRunning; - SmallVector<sys::MemoryBlock, 16> Allocations; + typedef SmallVector<sys::MemoryBlock, 16> AllocMapType; + AllocMapType Allocations; + +protected: + std::string ErrorMsg; public: StringRef getErrorMsg() const { return ErrorMsg; } @@ -39,19 +42,31 @@ public: /// @param Alignment Required minimum alignment for allocated space. /// @param[out] Address Remote address of the allocated memory. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. virtual bool allocateSpace(size_t Size, unsigned Alignment, uint64_t &Address); + bool isAllocatedMemory(uint64_t Address, uint32_t Size) { + uint64_t AddressEnd = Address + Size; + for (AllocMapType::const_iterator I = Allocations.begin(), + E = Allocations.end(); + I != E; ++I) { + if (Address >= (uint64_t)I->base() && + AddressEnd <= (uint64_t)I->base() + I->size()) + return true; + } + return false; + } + /// Load data into the target address space. /// /// @param Address Destination address in the target process. /// @param Data Source address in the host process. /// @param Size Number of bytes to copy. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. virtual bool loadData(uint64_t Address, const void *Data, @@ -63,7 +78,7 @@ public: /// @param Data Source address in the host process. /// @param Size Number of bytes to copy. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. virtual bool loadCode(uint64_t Address, const void *Data, @@ -76,12 +91,12 @@ public: /// process. /// @param[out] RetVal The integer return value of the called function. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. virtual bool executeCode(uint64_t Address, int &RetVal); - /// Minimum alignment for memory permissions. Used to seperate code and + /// Minimum alignment for memory permissions. Used to separate code and /// data regions to make sure data doesn't get marked as code or vice /// versa. /// @@ -89,18 +104,13 @@ public: virtual unsigned getPageAlignment() { return 4096; } /// Start the remote process. - virtual void create(); + virtual bool create(); /// Terminate the remote process. virtual void stop(); - RemoteTarget() : ErrorMsg(""), IsRunning(false) {} + RemoteTarget() : IsRunning(false), ErrorMsg("") {} virtual ~RemoteTarget() { if (IsRunning) stop(); } - - // Create an instance of the system-specific remote target class. - static RemoteTarget *createRemoteTarget(); - static RemoteTarget *createExternalRemoteTarget(std::string &ChildName); - static bool hostSupportsExternalRemoteTarget(); private: // Main processing function for the remote target process. Command messages // are received on file descriptor CmdFD and responses come back on OutFD. diff --git a/tools/lli/RemoteTargetExternal.cpp b/tools/lli/RemoteTargetExternal.cpp index 742a948..c1bc8df 100644 --- a/tools/lli/RemoteTargetExternal.cpp +++ b/tools/lli/RemoteTargetExternal.cpp @@ -13,12 +13,12 @@ //===----------------------------------------------------------------------===// #include "llvm/Config/config.h" - #include "RemoteTarget.h" #include "RemoteTargetExternal.h" - #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -28,135 +28,298 @@ using namespace llvm; bool RemoteTargetExternal::allocateSpace(size_t Size, unsigned Alignment, uint64_t &Address) { - SendAllocateSpace(Alignment, Size); - Receive(LLI_AllocationResult, Address); - return false; + DEBUG(dbgs() << "Message [allocate space] size: " << Size << + ", align: " << Alignment << "\n"); + if (!SendAllocateSpace(Alignment, Size)) { + ErrorMsg += ", (RemoteTargetExternal::allocateSpace)"; + return false; + } + if (!Receive(LLI_AllocationResult, Address)) { + ErrorMsg += ", (RemoteTargetExternal::allocateSpace)"; + return false; + } + if (Address == 0) { + ErrorMsg += "failed allocation, (RemoteTargetExternal::allocateSpace)"; + return false; + } + DEBUG(dbgs() << "Message [allocate space] addr: 0x" << + format("%llx", Address) << "\n"); + return true; } bool RemoteTargetExternal::loadData(uint64_t Address, const void *Data, size_t Size) { - SendLoadSection(Address, Data, (uint32_t)Size, false); - Receive(LLI_LoadComplete); - return false; + DEBUG(dbgs() << "Message [load data] addr: 0x" << format("%llx", Address) << + ", size: " << Size << "\n"); + if (!SendLoadSection(Address, Data, (uint32_t)Size, false)) { + ErrorMsg += ", (RemoteTargetExternal::loadData)"; + return false; + } + int Status = LLI_Status_Success; + if (!Receive(LLI_LoadResult, Status)) { + ErrorMsg += ", (RemoteTargetExternal::loadData)"; + return false; + } + if (Status == LLI_Status_IncompleteMsg) { + ErrorMsg += "incomplete load data, (RemoteTargetExternal::loadData)"; + return false; + } + if (Status == LLI_Status_NotAllocated) { + ErrorMsg += "data memory not allocated, (RemoteTargetExternal::loadData)"; + return false; + } + DEBUG(dbgs() << "Message [load data] complete\n"); + return true; } bool RemoteTargetExternal::loadCode(uint64_t Address, const void *Data, size_t Size) { - SendLoadSection(Address, Data, (uint32_t)Size, true); - Receive(LLI_LoadComplete); - return false; + DEBUG(dbgs() << "Message [load code] addr: 0x" << format("%llx", Address) << + ", size: " << Size << "\n"); + if (!SendLoadSection(Address, Data, (uint32_t)Size, true)) { + ErrorMsg += ", (RemoteTargetExternal::loadCode)"; + return false; + } + int Status = LLI_Status_Success; + if (!Receive(LLI_LoadResult, Status)) { + ErrorMsg += ", (RemoteTargetExternal::loadCode)"; + return false; + } + if (Status == LLI_Status_IncompleteMsg) { + ErrorMsg += "incomplete load data, (RemoteTargetExternal::loadData)"; + return false; + } + if (Status == LLI_Status_NotAllocated) { + ErrorMsg += "data memory not allocated, (RemoteTargetExternal::loadData)"; + return false; + } + DEBUG(dbgs() << "Message [load code] complete\n"); + return true; } -bool RemoteTargetExternal::executeCode(uint64_t Address, int &RetVal) { - SendExecute(Address); - - Receive(LLI_ExecutionResult, RetVal); - return false; +bool RemoteTargetExternal::executeCode(uint64_t Address, int32_t &RetVal) { + DEBUG(dbgs() << "Message [exectue code] addr: " << Address << "\n"); + if (!SendExecute(Address)) { + ErrorMsg += ", (RemoteTargetExternal::executeCode)"; + return false; + } + if (!Receive(LLI_ExecutionResult, RetVal)) { + ErrorMsg += ", (RemoteTargetExternal::executeCode)"; + return false; + } + DEBUG(dbgs() << "Message [exectue code] return: " << RetVal << "\n"); + return true; } void RemoteTargetExternal::stop() { SendTerminate(); - Wait(); + RPC.Wait(); } -void RemoteTargetExternal::SendAllocateSpace(uint32_t Alignment, uint32_t Size) { - int rc; - uint32_t MsgType = (uint32_t)LLI_AllocateSpace; - rc = WriteBytes(&MsgType, 4); - assert(rc == 4 && "Error writing message type."); +bool RemoteTargetExternal::SendAllocateSpace(uint32_t Alignment, uint32_t Size) { + if (!SendHeader(LLI_AllocateSpace)) { + ErrorMsg += ", (RemoteTargetExternal::SendAllocateSpace)"; + return false; + } - uint32_t DataSize = 8; - rc = WriteBytes(&DataSize, 4); - assert(rc == 4 && "Error writing data size."); + AppendWrite((const void *)&Alignment, 4); + AppendWrite((const void *)&Size, 4); - rc = WriteBytes(&Alignment, 4); - assert(rc == 4 && "Error writing alignment data."); - - rc = WriteBytes(&Size, 4); - assert(rc == 4 && "Error writing size data."); + if (!SendPayload()) { + ErrorMsg += ", (RemoteTargetExternal::SendAllocateSpace)"; + return false; + } + return true; } -void RemoteTargetExternal::SendLoadSection(uint64_t Addr, +bool RemoteTargetExternal::SendLoadSection(uint64_t Addr, const void *Data, uint32_t Size, bool IsCode) { - int rc; - uint32_t MsgType = IsCode ? LLI_LoadCodeSection : LLI_LoadDataSection; - rc = WriteBytes(&MsgType, 4); - assert(rc == 4 && "Error writing message type."); + LLIMessageType MsgType = IsCode ? LLI_LoadCodeSection : LLI_LoadDataSection; + if (!SendHeader(MsgType)) { + ErrorMsg += ", (RemoteTargetExternal::SendLoadSection)"; + return false; + } + + AppendWrite((const void *)&Addr, 8); + AppendWrite(Data, Size); + + if (!SendPayload()) { + ErrorMsg += ", (RemoteTargetExternal::SendLoadSection)"; + return false; + } + return true; +} - uint32_t DataSize = Size + 8; - rc = WriteBytes(&DataSize, 4); - assert(rc == 4 && "Error writing data size."); +bool RemoteTargetExternal::SendExecute(uint64_t Addr) { + if (!SendHeader(LLI_Execute)) { + ErrorMsg += ", (RemoteTargetExternal::SendExecute)"; + return false; + } - rc = WriteBytes(&Addr, 8); - assert(rc == 8 && "Error writing data."); + AppendWrite((const void *)&Addr, 8); - rc = WriteBytes(Data, Size); - assert(rc == (int)Size && "Error writing data."); + if (!SendPayload()) { + ErrorMsg += ", (RemoteTargetExternal::SendExecute)"; + return false; + } + return true; } -void RemoteTargetExternal::SendExecute(uint64_t Addr) { - int rc; - uint32_t MsgType = (uint32_t)LLI_Execute; - rc = WriteBytes(&MsgType, 4); - assert(rc == 4 && "Error writing message type."); - - uint32_t DataSize = 8; - rc = WriteBytes(&DataSize, 4); - assert(rc == 4 && "Error writing data size."); +bool RemoteTargetExternal::SendTerminate() { + return SendHeader(LLI_Terminate); + // No data or data size is sent with Terminate +} - rc = WriteBytes(&Addr, 8); - assert(rc == 8 && "Error writing data."); +bool RemoteTargetExternal::Receive(LLIMessageType Msg) { + if (!ReceiveHeader(Msg)) + return false; + int Unused; + AppendRead(&Unused, 0); + if (!ReceivePayload()) + return false; + ReceiveData.clear(); + Sizes.clear(); + return true; } -void RemoteTargetExternal::SendTerminate() { - int rc; - uint32_t MsgType = (uint32_t)LLI_Terminate; - rc = WriteBytes(&MsgType, 4); - assert(rc == 4 && "Error writing message type."); +bool RemoteTargetExternal::Receive(LLIMessageType Msg, int32_t &Data) { + if (!ReceiveHeader(Msg)) + return false; + AppendRead(&Data, 4); + if (!ReceivePayload()) + return false; + ReceiveData.clear(); + Sizes.clear(); + return true; +} - // No data or data size is sent with Terminate +bool RemoteTargetExternal::Receive(LLIMessageType Msg, uint64_t &Data) { + if (!ReceiveHeader(Msg)) + return false; + AppendRead(&Data, 8); + if (!ReceivePayload()) + return false; + ReceiveData.clear(); + Sizes.clear(); + return true; } +bool RemoteTargetExternal::ReceiveHeader(LLIMessageType ExpectedMsgType) { + assert(ReceiveData.empty() && Sizes.empty() && + "Payload vector not empty to receive header"); -void RemoteTargetExternal::Receive(LLIMessageType ExpectedMsgType) { - int rc; + // Message header, with type to follow uint32_t MsgType; - rc = ReadBytes(&MsgType, 4); - assert(rc == 4 && "Error reading message type."); - assert(MsgType == (uint32_t)ExpectedMsgType && "Error: received unexpected message type."); + if (!ReadBytes(&MsgType, 4)) { + ErrorMsg += ", (RemoteTargetExternal::ReceiveHeader)"; + return false; + } + if (MsgType != (uint32_t)ExpectedMsgType) { + ErrorMsg = "received unexpected message type"; + ErrorMsg += ". Expecting: "; + ErrorMsg += ExpectedMsgType; + ErrorMsg += ", Got: "; + ErrorMsg += MsgType; + return false; + } + return true; +} + +bool RemoteTargetExternal::ReceivePayload() { + assert(!ReceiveData.empty() && + "Payload vector empty to receive"); + assert(ReceiveData.size() == Sizes.size() && + "Unexpected mismatch between data and size"); + uint32_t TotalSize = 0; + for (int I=0, E=Sizes.size(); I < E; I++) + TotalSize += Sizes[I]; + + // Payload size header uint32_t DataSize; - rc = ReadBytes(&DataSize, 4); - assert(rc == 4 && "Error reading data size."); - assert(DataSize == 0 && "Error: unexpected data size."); + if (!ReadBytes(&DataSize, 4)) { + ErrorMsg += ", invalid data size"; + return false; + } + if (DataSize != TotalSize) { + ErrorMsg = "unexpected data size"; + ErrorMsg += ". Expecting: "; + ErrorMsg += TotalSize; + ErrorMsg += ", Got: "; + ErrorMsg += DataSize; + return false; + } + if (DataSize == 0) + return true; + + // Payload itself + for (int I=0, E=Sizes.size(); I < E; I++) { + if (!ReadBytes(ReceiveData[I], Sizes[I])) { + ErrorMsg = "unexpected data while reading message"; + return false; + } + } + + return true; } -void RemoteTargetExternal::Receive(LLIMessageType ExpectedMsgType, int &Data) { - uint64_t Temp; - Receive(ExpectedMsgType, Temp); - Data = (int)(int64_t)Temp; +bool RemoteTargetExternal::SendHeader(LLIMessageType MsgType) { + assert(SendData.empty() && Sizes.empty() && + "Payload vector not empty to send header"); + + // Message header, with type to follow + if (!WriteBytes(&MsgType, 4)) { + ErrorMsg += ", (RemoteTargetExternal::SendHeader)"; + return false; + } + return true; } -void RemoteTargetExternal::Receive(LLIMessageType ExpectedMsgType, uint64_t &Data) { - int rc; - uint32_t MsgType; - rc = ReadBytes(&MsgType, 4); - assert(rc == 4 && "Error reading message type."); - assert(MsgType == (uint32_t)ExpectedMsgType && "Error: received unexpected message type."); +bool RemoteTargetExternal::SendPayload() { + assert(!SendData.empty() && !Sizes.empty() && + "Payload vector empty to send"); + assert(SendData.size() == Sizes.size() && + "Unexpected mismatch between data and size"); + + uint32_t TotalSize = 0; + for (int I=0, E=Sizes.size(); I < E; I++) + TotalSize += Sizes[I]; + + // Payload size header + if (!WriteBytes(&TotalSize, 4)) { + ErrorMsg += ", invalid data size"; + return false; + } + if (TotalSize == 0) + return true; + + // Payload itself + for (int I=0, E=Sizes.size(); I < E; I++) { + if (!WriteBytes(SendData[I], Sizes[I])) { + ErrorMsg = "unexpected data while writing message"; + return false; + } + } + + SendData.clear(); + Sizes.clear(); + return true; +} - uint32_t DataSize; - rc = ReadBytes(&DataSize, 4); - assert(rc == 4 && "Error reading data size."); - assert(DataSize == 8 && "Error: unexpected data size."); +void RemoteTargetExternal::AppendWrite(const void *Data, uint32_t Size) { + SendData.push_back(Data); + Sizes.push_back(Size); +} - rc = ReadBytes(&Data, 8); - assert(DataSize == 8 && "Error: unexpected data."); +void RemoteTargetExternal::AppendRead(void *Data, uint32_t Size) { + ReceiveData.push_back(Data); + Sizes.push_back(Size); } #ifdef LLVM_ON_UNIX -#include "Unix/RemoteTargetExternal.inc" +#include "Unix/RPCChannel.inc" #endif #ifdef LLVM_ON_WIN32 -#include "Windows/RemoteTargetExternal.inc" +#include "Windows/RPCChannel.inc" #endif diff --git a/tools/lli/RemoteTargetExternal.h b/tools/lli/RemoteTargetExternal.h index a4bfad2..f87fc61 100644 --- a/tools/lli/RemoteTargetExternal.h +++ b/tools/lli/RemoteTargetExternal.h @@ -15,21 +15,28 @@ #ifndef LLI_REMOTETARGETEXTERNAL_H #define LLI_REMOTETARGETEXTERNAL_H -#include "llvm/Config/config.h" - +#include "RPCChannel.h" +#include "RemoteTarget.h" +#include "RemoteTargetMessage.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Memory.h" #include <stdlib.h> #include <string> -#include "RemoteTarget.h" -#include "RemoteTargetMessage.h" - namespace llvm { class RemoteTargetExternal : public RemoteTarget { + RPCChannel RPC; + + bool WriteBytes(const void *Data, size_t Size) { + return RPC.WriteBytes(Data, Size); + } + + bool ReadBytes(void *Data, size_t Size) { return RPC.ReadBytes(Data, Size); } + public: /// Allocate space in the remote target address space. /// @@ -37,11 +44,10 @@ public: /// @param Alignment Required minimum alignment for allocated space. /// @param[out] Address Remote address of the allocated memory. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - virtual bool allocateSpace(size_t Size, - unsigned Alignment, - uint64_t &Address); + bool allocateSpace(size_t Size, unsigned Alignment, + uint64_t &Address) override; /// Load data into the target address space. /// @@ -49,9 +55,9 @@ public: /// @param Data Source address in the host process. /// @param Size Number of bytes to copy. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - virtual bool loadData(uint64_t Address, const void *Data, size_t Size); + bool loadData(uint64_t Address, const void *Data, size_t Size) override; /// Load code into the target address space and prepare it for execution. /// @@ -59,9 +65,9 @@ public: /// @param Data Source address in the host process. /// @param Size Number of bytes to copy. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - virtual bool loadCode(uint64_t Address, const void *Data, size_t Size); + bool loadCode(uint64_t Address, const void *Data, size_t Size) override; /// Execute code in the target process. The called function is required /// to be of signature int "(*)(void)". @@ -70,47 +76,66 @@ public: /// process. /// @param[out] RetVal The integer return value of the called function. /// - /// @returns False on success. On failure, ErrorMsg is updated with + /// @returns True on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - virtual bool executeCode(uint64_t Address, int &RetVal); + bool executeCode(uint64_t Address, int &RetVal) override; - /// Minimum alignment for memory permissions. Used to seperate code and + /// Minimum alignment for memory permissions. Used to separate code and /// data regions to make sure data doesn't get marked as code or vice /// versa. /// /// @returns Page alignment return value. Default of 4k. - virtual unsigned getPageAlignment() { return 4096; } + unsigned getPageAlignment() override { return 4096; } + + bool create() override { + RPC.ChildName = ChildName; + if (!RPC.createServer()) + return true; - /// Start the remote process. - virtual void create(); + // We must get Ack from the client (blocking read) + if (!Receive(LLI_ChildActive)) { + ErrorMsg += ", (RPCChannel::create) - Stopping process!"; + stop(); + return false; + } + + return true; + } /// Terminate the remote process. - virtual void stop(); + void stop() override; RemoteTargetExternal(std::string &Name) : RemoteTarget(), ChildName(Name) {} - virtual ~RemoteTargetExternal(); + virtual ~RemoteTargetExternal() {} private: std::string ChildName; - // This will get filled in as a point to an OS-specific structure. - void *ConnectionData; - - void SendAllocateSpace(uint32_t Alignment, uint32_t Size); - void SendLoadSection(uint64_t Addr, + bool SendAllocateSpace(uint32_t Alignment, uint32_t Size); + bool SendLoadSection(uint64_t Addr, const void *Data, uint32_t Size, bool IsCode); - void SendExecute(uint64_t Addr); - void SendTerminate(); - - void Receive(LLIMessageType Msg); - void Receive(LLIMessageType Msg, int &Data); - void Receive(LLIMessageType Msg, uint64_t &Data); - - int WriteBytes(const void *Data, size_t Size); - int ReadBytes(void *Data, size_t Size); - void Wait(); + bool SendExecute(uint64_t Addr); + bool SendTerminate(); + + // High-level wrappers for receiving data + bool Receive(LLIMessageType Msg); + bool Receive(LLIMessageType Msg, int32_t &Data); + bool Receive(LLIMessageType Msg, uint64_t &Data); + + // Lower level target-independent read/write to deal with errors + bool ReceiveHeader(LLIMessageType Msg); + bool ReceivePayload(); + bool SendHeader(LLIMessageType Msg); + bool SendPayload(); + + // Functions to append/retrieve data from the payload + SmallVector<const void *, 2> SendData; + SmallVector<void *, 1> ReceiveData; // Future proof + SmallVector<int, 2> Sizes; + void AppendWrite(const void *Data, uint32_t Size); + void AppendRead(void *Data, uint32_t Size); }; } // end namespace llvm diff --git a/tools/lli/RemoteTargetMessage.h b/tools/lli/RemoteTargetMessage.h index 12cfa9a..cb934a1 100644 --- a/tools/lli/RemoteTargetMessage.h +++ b/tools/lli/RemoteTargetMessage.h @@ -26,20 +26,60 @@ namespace llvm { // only here for testing purposes and is therefore intended to be the simplest // implementation that will work. It is assumed that the parent and child // share characteristics like endianness. +// +// Quick description of the protocol: +// +// { Header + Payload Size + Payload } +// +// The protocol message consist of a header, the payload size (which can be +// zero), and the payload itself. The payload can contain any number of items, +// and the size has to be the sum of them all. Each end is responsible for +// reading/writing the correct number of items with the correct sizes. +// +// The current four known exchanges are: +// +// * Allocate Space: +// Parent: { LLI_AllocateSpace, 8, Alignment, Size } +// Child: { LLI_AllocationResult, 8, Address } +// +// * Load Data: +// Parent: { LLI_LoadDataSection, 8+Size, Address, Data } +// Child: { LLI_LoadComplete, 4, StatusCode } +// +// * Load Code: +// Parent: { LLI_LoadCodeSection, 8+Size, Address, Code } +// Child: { LLI_LoadComplete, 4, StatusCode } +// +// * Execute Code: +// Parent: { LLI_Execute, 8, Address } +// Child: { LLI_ExecutionResult, 4, Result } +// +// It is the responsibility of either side to check for correct headers, +// sizes and payloads, since any inconsistency would misalign the pipe, and +// result in data corruption. enum LLIMessageType { LLI_Error = -1, LLI_ChildActive = 0, // Data = not used - LLI_AllocateSpace, // Data = struct { uint_32t Align, uint_32t Size } - LLI_AllocationResult, // Data = uint64_t AllocAddress (in Child memory space) - LLI_LoadCodeSection, // Data = uint32_t Addr, followed by section contests - LLI_LoadDataSection, // Data = uint32_t Addr, followed by section contents - LLI_LoadComplete, // Data = not used - LLI_Execute, // Data = Address of function to execute - LLI_ExecutionResult, // Data = uint64_t Result + LLI_AllocateSpace, // Data = struct { uint32_t Align, uint_32t Size } + LLI_AllocationResult, // Data = uint64_t Address (child memory space) + + LLI_LoadCodeSection, // Data = uint64_t Address, void * SectionData + LLI_LoadDataSection, // Data = uint64_t Address, void * SectionData + LLI_LoadResult, // Data = uint32_t LLIMessageStatus + + LLI_Execute, // Data = uint64_t Address + LLI_ExecutionResult, // Data = uint32_t Result + LLI_Terminate // Data = not used }; +enum LLIMessageStatus { + LLI_Status_Success = 0, // Operation succeeded + LLI_Status_NotAllocated, // Address+Size not allocated in child space + LLI_Status_IncompleteMsg // Size received doesn't match request +}; + } // end namespace llvm #endif diff --git a/tools/lli/Unix/RemoteTargetExternal.inc b/tools/lli/Unix/RPCChannel.inc index 9c1a4cc..4d245d6 100644 --- a/tools/lli/Unix/RemoteTargetExternal.inc +++ b/tools/lli/Unix/RPCChannel.inc @@ -1,4 +1,4 @@ -//=- RemoteTargetExternal.inc - LLVM out-of-process JIT execution for Unix --=// +//=- RPCChannel.inc - LLVM out-of-process JIT execution for Unix --=// // // The LLVM Compiler Infrastructure // @@ -7,15 +7,17 @@ // //===----------------------------------------------------------------------===// // -// Implementation of the Unix-specific parts of the RemoteTargetExternal class +// Implementation of the Unix-specific parts of the RPCChannel class // which executes JITed code in a separate process from where it was built. // //===----------------------------------------------------------------------===// -#include <unistd.h> +#include "llvm/Support/Errno.h" +#include "llvm/Support/raw_ostream.h" #include <stdio.h> #include <stdlib.h> #include <sys/wait.h> +#include <unistd.h> namespace { @@ -30,7 +32,7 @@ struct ConnectionData_t { namespace llvm { -void RemoteTargetExternal::create() { +bool RPCChannel::createServer() { int PipeFD[2][2]; pid_t ChildPID; @@ -62,8 +64,7 @@ void RemoteTargetExternal::create() { int rc = execv(ChildName.c_str(), args); if (rc != 0) perror("Error executing child process: "); - } - else { + } else { // In the parent... // Close the child ends of the pipes @@ -71,25 +72,50 @@ void RemoteTargetExternal::create() { close(PipeFD[1][1]); // Store the parent ends of the pipes - ConnectionData = (void*)new ConnectionData_t(PipeFD[1][0], PipeFD[0][1]); - - Receive(LLI_ChildActive); + ConnectionData = (void *)new ConnectionData_t(PipeFD[1][0], PipeFD[0][1]); + return true; } + return false; } -int RemoteTargetExternal::WriteBytes(const void *Data, size_t Size) { - return write(((ConnectionData_t*)ConnectionData)->OutputPipe, Data, Size); +bool RPCChannel::createClient() { + // Store the parent ends of the pipes + ConnectionData = (void *)new ConnectionData_t(STDIN_FILENO, STDOUT_FILENO); + return true; +} + +void RPCChannel::Wait() { wait(NULL); } + +static bool CheckError(int rc, size_t Size, const char *Desc) { + if (rc < 0) { + llvm::errs() << "IO Error: " << Desc << ": " << sys::StrError() << '\n'; + return false; + } else if ((size_t)rc != Size) { + std::string ErrorMsg; + char Number[10] = { 0 }; + ErrorMsg += "Expecting "; + sprintf(Number, "%d", (uint32_t)Size); + ErrorMsg += Number; + ErrorMsg += " bytes, Got "; + sprintf(Number, "%d", rc); + ErrorMsg += Number; + llvm::errs() << "RPC Error: " << Desc << ": " << ErrorMsg << '\n'; + return false; + } + return true; } -int RemoteTargetExternal::ReadBytes(void *Data, size_t Size) { - return read(((ConnectionData_t*)ConnectionData)->InputPipe, Data, Size); +bool RPCChannel::WriteBytes(const void *Data, size_t Size) { + int rc = write(((ConnectionData_t *)ConnectionData)->OutputPipe, Data, Size); + return CheckError(rc, Size, "WriteBytes"); } -void RemoteTargetExternal::Wait() { - wait(NULL); +bool RPCChannel::ReadBytes(void *Data, size_t Size) { + int rc = read(((ConnectionData_t *)ConnectionData)->InputPipe, Data, Size); + return CheckError(rc, Size, "ReadBytes"); } -RemoteTargetExternal::~RemoteTargetExternal() { +RPCChannel::~RPCChannel() { delete static_cast<ConnectionData_t *>(ConnectionData); } diff --git a/tools/lli/Windows/RPCChannel.inc b/tools/lli/Windows/RPCChannel.inc new file mode 100644 index 0000000..82f2acb --- /dev/null +++ b/tools/lli/Windows/RPCChannel.inc @@ -0,0 +1,29 @@ +//=- RPCChannel.inc - LLVM out-of-process JIT execution for Windows --=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the Windows-specific parts of the RPCChannel class +// which executes JITed code in a separate process from where it was built. +// +//===----------------------------------------------------------------------===// + +namespace llvm { + +bool RPCChannel::createServer() { return false; } + +bool RPCChannel::createClient() { return false; } + +bool RPCChannel::WriteBytes(const void *Data, size_t Size) { return false; } + +bool RPCChannel::ReadBytes(void *Data, size_t Size) { return false; } + +void RPCChannel::Wait() {} + +RPCChannel::~RPCChannel() {} + +} // namespace llvm diff --git a/tools/lli/Windows/RemoteTargetExternal.inc b/tools/lli/Windows/RemoteTargetExternal.inc deleted file mode 100644 index aef4627..0000000 --- a/tools/lli/Windows/RemoteTargetExternal.inc +++ /dev/null @@ -1,35 +0,0 @@ -//= RemoteTargetExternal.inc - LLVM out-of-process JIT execution for Windows =// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Definition of the Windows-specific parts of the RemoteTargetExternal class -// which is meant to execute JITed code in a separate process from where it was -// built. To support this functionality on Windows, implement these functions. -// -//===----------------------------------------------------------------------===// - -namespace llvm { - -void RemoteTargetExternal::create() { -} - -int RemoteTargetExternal::WriteBytes(const void *Data, size_t Size) { - return 0; -} - -int RemoteTargetExternal::ReadBytes(void *Data, size_t Size) { - return 0; -} - -void RemoteTargetExternal::Wait() { -} - -RemoteTargetExternal::~RemoteTargetExternal() { -} - -} // namespace llvm diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index f317566..c0c0f9d 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -17,6 +17,7 @@ #include "llvm/IR/LLVMContext.h" #include "RemoteMemoryManager.h" #include "RemoteTarget.h" +#include "RemoteTargetExternal.h" #include "llvm/ADT/Triple.h" #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/LinkAllCodegenComponents.h" @@ -26,12 +27,15 @@ #include "llvm/ExecutionEngine/JITEventListener.h" #include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/TypeBuilder.h" #include "llvm/IRReader/IRReader.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/DynamicLibrary.h" @@ -91,12 +95,11 @@ namespace { // execution. The child process will be executed and will communicate with // lli via stdin/stdout pipes. cl::opt<std::string> - MCJITRemoteProcess("mcjit-remote-process", - cl::desc("Specify the filename of the process to launch " - "for remote MCJIT execution. If none is specified," - "\n\tremote execution will be simulated in-process."), - cl::value_desc("filename"), - cl::init("")); + ChildExecPath("mcjit-remote-process", + cl::desc("Specify the filename of the process to launch " + "for remote MCJIT execution. If none is specified," + "\n\tremote execution will be simulated in-process."), + cl::value_desc("filename"), cl::init("")); // Determine optimization level. cl::opt<char> @@ -138,6 +141,27 @@ namespace { cl::desc("Extra modules to be loaded"), cl::value_desc("input bitcode")); + cl::list<std::string> + ExtraObjects("extra-object", + cl::desc("Extra object files to be loaded"), + cl::value_desc("input object")); + + cl::list<std::string> + ExtraArchives("extra-archive", + cl::desc("Extra archive files to be loaded"), + cl::value_desc("input archive")); + + cl::opt<bool> + EnableCacheManager("enable-cache-manager", + cl::desc("Use cache manager to save/load mdoules"), + cl::init(false)); + + cl::opt<std::string> + ObjectCacheDir("object-cache-dir", + cl::desc("Directory to store cached object files " + "(must be user writable)"), + cl::init("")); + cl::opt<std::string> FakeArgv0("fake-argv0", cl::desc("Override the 'argv[0]' value passed into the executing" @@ -219,12 +243,91 @@ namespace { cl::init(false)); } +//===----------------------------------------------------------------------===// +// Object cache +// +// This object cache implementation writes cached objects to disk to the +// directory specified by CacheDir, using a filename provided in the module +// descriptor. The cache tries to load a saved object using that path if the +// file exists. CacheDir defaults to "", in which case objects are cached +// alongside their originating bitcodes. +// +class LLIObjectCache : public ObjectCache { +public: + LLIObjectCache(const std::string& CacheDir) : CacheDir(CacheDir) { + // Add trailing '/' to cache dir if necessary. + if (!this->CacheDir.empty() && + this->CacheDir[this->CacheDir.size() - 1] != '/') + this->CacheDir += '/'; + } + virtual ~LLIObjectCache() {} + + void notifyObjectCompiled(const Module *M, const MemoryBuffer *Obj) override { + const std::string ModuleID = M->getModuleIdentifier(); + std::string CacheName; + if (!getCacheFilename(ModuleID, CacheName)) + return; + std::string errStr; + if (!CacheDir.empty()) { // Create user-defined cache dir. + SmallString<128> dir(CacheName); + sys::path::remove_filename(dir); + sys::fs::create_directories(Twine(dir)); + } + raw_fd_ostream outfile(CacheName.c_str(), errStr, sys::fs::F_None); + outfile.write(Obj->getBufferStart(), Obj->getBufferSize()); + outfile.close(); + } + + MemoryBuffer* getObject(const Module* M) override { + const std::string ModuleID = M->getModuleIdentifier(); + std::string CacheName; + if (!getCacheFilename(ModuleID, CacheName)) + return NULL; + // Load the object from the cache filename + std::unique_ptr<MemoryBuffer> IRObjectBuffer; + MemoryBuffer::getFile(CacheName.c_str(), IRObjectBuffer, -1, false); + // If the file isn't there, that's OK. + if (!IRObjectBuffer) + return NULL; + // MCJIT will want to write into this buffer, and we don't want that + // because the file has probably just been mmapped. Instead we make + // a copy. The filed-based buffer will be released when it goes + // out of scope. + return MemoryBuffer::getMemBufferCopy(IRObjectBuffer->getBuffer()); + } + +private: + std::string CacheDir; + + bool getCacheFilename(const std::string &ModID, std::string &CacheName) { + std::string Prefix("file:"); + size_t PrefixLength = Prefix.length(); + if (ModID.substr(0, PrefixLength) != Prefix) + return false; + std::string CacheSubdir = ModID.substr(PrefixLength); +#if defined(_WIN32) + // Transform "X:\foo" => "/X\foo" for convenience. + if (isalpha(CacheSubdir[0]) && CacheSubdir[1] == ':') { + CacheSubdir[1] = CacheSubdir[0]; + CacheSubdir[0] = '/'; + } +#endif + CacheName = CacheDir + CacheSubdir; + size_t pos = CacheName.rfind('.'); + CacheName.replace(pos, CacheName.length() - pos, ".o"); + return true; + } +}; + static ExecutionEngine *EE = 0; +static LLIObjectCache *CacheManager = 0; static void do_shutdown() { // Cygwin-1.5 invokes DLL's dtors before atexit handler. #ifndef DO_NOTHING_ATEXIT delete EE; + if (CacheManager) + delete CacheManager; llvm_shutdown(); #endif } @@ -300,12 +403,20 @@ int main(int argc, char **argv, char * const *envp) { return 1; } + if (EnableCacheManager) { + if (UseMCJIT) { + std::string CacheName("file:"); + CacheName.append(InputFile); + Mod->setModuleIdentifier(CacheName); + } else + errs() << "warning: -enable-cache-manager can only be used with MCJIT."; + } + // If not jitting lazily, load the whole bitcode file eagerly too. - std::string ErrorMsg; if (NoLazyCompilation) { - if (Mod->MaterializeAllPermanently(&ErrorMsg)) { + if (error_code EC = Mod->materializeAllPermanently()) { errs() << argv[0] << ": bitcode didn't read correctly.\n"; - errs() << "Reason: " << ErrorMsg << "\n"; + errs() << "Reason: " << EC.message() << "\n"; exit(1); } } @@ -321,6 +432,7 @@ int main(int argc, char **argv, char * const *envp) { DebugIRPass->runOnModule(*Mod); } + std::string ErrorMsg; EngineBuilder builder(Mod); builder.setMArch(MArch); builder.setMCPU(MCPU); @@ -391,6 +503,11 @@ int main(int argc, char **argv, char * const *envp) { exit(1); } + if (EnableCacheManager) { + CacheManager = new LLIObjectCache(ObjectCacheDir); + EE->setObjectCache(CacheManager); + } + // 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); @@ -398,9 +515,43 @@ int main(int argc, char **argv, char * const *envp) { Err.print(argv[0], errs()); return 1; } + if (EnableCacheManager) { + if (UseMCJIT) { + std::string CacheName("file:"); + CacheName.append(ExtraModules[i]); + XMod->setModuleIdentifier(CacheName); + } + // else, we already printed a warning above. + } EE->addModule(XMod); } + for (unsigned i = 0, e = ExtraObjects.size(); i != e; ++i) { + ErrorOr<object::ObjectFile *> Obj = + object::ObjectFile::createObjectFile(ExtraObjects[i]); + if (!Obj) { + Err.print(argv[0], errs()); + return 1; + } + EE->addObjectFile(Obj.get()); + } + + for (unsigned i = 0, e = ExtraArchives.size(); i != e; ++i) { + std::unique_ptr<MemoryBuffer> ArBuf; + error_code ec; + ec = MemoryBuffer::getFileOrSTDIN(ExtraArchives[i], ArBuf); + if (ec) { + Err.print(argv[0], errs()); + return 1; + } + object::Archive *Ar = new object::Archive(ArBuf.release(), ec); + if (ec || !Ar) { + Err.print(argv[0], errs()); + return 1; + } + EE->addArchive(Ar); + } + // If the target is Cygwin/MingW and we are generating remote code, we // need an extra module to help out with linking. if (RemoteMCJIT && Triple(Mod->getTargetTriple()).isOSCygMing()) { @@ -472,7 +623,7 @@ int main(int argc, char **argv, char * const *envp) { } } - // Trigger compilation separately so code regions that need to be + // Trigger compilation separately so code regions that need to be // invalidated will be known. (void)EE->getPointerToFunction(EntryFn); // Clear instruction cache before code will be executed. @@ -511,30 +662,35 @@ int main(int argc, char **argv, char * const *envp) { // address space, assign the section addresses to resolve any relocations, // and send it to the target. - OwningPtr<RemoteTarget> Target; - if (!MCJITRemoteProcess.empty()) { // Remote execution on a child process - if (!RemoteTarget::hostSupportsExternalRemoteTarget()) { - errs() << "Warning: host does not support external remote targets.\n" - << " Defaulting to simulated remote execution\n"; - Target.reset(RemoteTarget::createRemoteTarget()); - } else { - std::string ChildEXE = sys::FindProgramByName(MCJITRemoteProcess); - if (ChildEXE == "") { - errs() << "Unable to find child target: '\''" << MCJITRemoteProcess << "\'\n"; - return -1; - } - Target.reset(RemoteTarget::createExternalRemoteTarget(ChildEXE)); + std::unique_ptr<RemoteTarget> Target; + if (!ChildExecPath.empty()) { // Remote execution on a child process +#ifndef LLVM_ON_UNIX + // FIXME: Remove this pointless fallback mode which causes tests to "pass" + // on platforms where they should XFAIL. + errs() << "Warning: host does not support external remote targets.\n" + << " Defaulting to simulated remote execution\n"; + Target.reset(new RemoteTarget); +#else + if (!sys::fs::can_execute(ChildExecPath)) { + errs() << "Unable to find usable child executable: '" << ChildExecPath + << "'\n"; + return -1; } + Target.reset(new RemoteTargetExternal(ChildExecPath)); +#endif } else { // No child process name provided, use simulated remote execution. - Target.reset(RemoteTarget::createRemoteTarget()); + Target.reset(new RemoteTarget); } // Give the memory manager a pointer to our remote target interface object. MM->setRemoteTarget(Target.get()); // Create the remote target. - Target->create(); + if (!Target->create()) { + errs() << "ERROR: " << Target->getErrorMsg() << "\n"; + return EXIT_FAILURE; + } // Since we're executing in a (at least simulated) remote address space, // we can't use the ExecutionEngine::runFunctionAsMain(). We have to @@ -551,7 +707,7 @@ int main(int argc, char **argv, char * const *envp) { DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x" << format("%llx", Entry) << "\n"); - if (Target->executeCode(Entry, Result)) + if (!Target->executeCode(Entry, Result)) errs() << "ERROR: " << Target->getErrorMsg() << "\n"; // Like static constructors, the remote target MCJIT support doesn't handle |