From: Andrew Kaylor Date: Wed, 2 Oct 2013 17:12:36 +0000 (+0000) Subject: Adding out-of-process execution support to lli. X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=0ab5c6c16b1b09d76c3ba2d70443b10bcc26169c;p=oota-llvm.git Adding out-of-process execution support to lli. At this time only Unix-based systems are supported. Windows has stubs and should re-route to the simulated mode. Thanks to Sriram Murali for contributions to this patch. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@191843 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/test/ExecutionEngine/MCJIT/remote/simpletest-remote.ll b/test/ExecutionEngine/MCJIT/remote/simpletest-remote.ll index f1a69d8d5d5..797121831f5 100644 --- a/test/ExecutionEngine/MCJIT/remote/simpletest-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/simpletest-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit %s > /dev/null +; RUN: %lli_mcjit -remote-mcjit -mcjit-remote-process=lli-child-target %s > /dev/null ; XFAIL: mips define i32 @bar() { diff --git a/test/ExecutionEngine/MCJIT/remote/stubs-remote.ll b/test/ExecutionEngine/MCJIT/remote/stubs-remote.ll index 47a710db0e5..99c2fe57f69 100644 --- a/test/ExecutionEngine/MCJIT/remote/stubs-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/stubs-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit -disable-lazy-compilation=false %s +; RUN: %lli_mcjit -remote-mcjit -disable-lazy-compilation=false -mcjit-remote-process=lli-child-target %s ; XFAIL: mips define i32 @main() nounwind { diff --git a/test/ExecutionEngine/MCJIT/remote/test-common-symbols-remote.ll b/test/ExecutionEngine/MCJIT/remote/test-common-symbols-remote.ll index eb2e68606da..023263ec011 100644 --- a/test/ExecutionEngine/MCJIT/remote/test-common-symbols-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/test-common-symbols-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit -O0 -disable-lazy-compilation=false %s +; RUN: %lli_mcjit -remote-mcjit -O0 -disable-lazy-compilation=false -mcjit-remote-process=lli-child-target %s ; XFAIL: mips ; The intention of this test is to verify that symbols mapped to COMMON in ELF diff --git a/test/ExecutionEngine/MCJIT/remote/test-data-align-remote.ll b/test/ExecutionEngine/MCJIT/remote/test-data-align-remote.ll index 874eeae2069..ca520c8000b 100644 --- a/test/ExecutionEngine/MCJIT/remote/test-data-align-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/test-data-align-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit -O0 %s +; RUN: %lli_mcjit -remote-mcjit -O0 -mcjit-remote-process=lli-child-target %s ; XFAIL: mips ; Check that a variable is always aligned as specified. diff --git a/test/ExecutionEngine/MCJIT/remote/test-fp-no-external-funcs-remote.ll b/test/ExecutionEngine/MCJIT/remote/test-fp-no-external-funcs-remote.ll index d7e8c35716c..dd3e3edae30 100644 --- a/test/ExecutionEngine/MCJIT/remote/test-fp-no-external-funcs-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/test-fp-no-external-funcs-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit %s > /dev/null +; RUN: %lli_mcjit -remote-mcjit -mcjit-remote-process=lli-child-target %s > /dev/null ; XFAIL: mips define double @test(double* %DP, double %Arg) { diff --git a/test/ExecutionEngine/MCJIT/remote/test-global-init-nonzero-remote.ll b/test/ExecutionEngine/MCJIT/remote/test-global-init-nonzero-remote.ll index 5337c5d2582..90a8822a79c 100644 --- a/test/ExecutionEngine/MCJIT/remote/test-global-init-nonzero-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/test-global-init-nonzero-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit %s > /dev/null +; RUN: %lli_mcjit -remote-mcjit -mcjit-remote-process=lli-child-target %s > /dev/null ; XFAIL: mips @count = global i32 1, align 4 diff --git a/test/ExecutionEngine/MCJIT/remote/test-ptr-reloc-remote.ll b/test/ExecutionEngine/MCJIT/remote/test-ptr-reloc-remote.ll index bd044b600a2..8b562972b5d 100644 --- a/test/ExecutionEngine/MCJIT/remote/test-ptr-reloc-remote.ll +++ b/test/ExecutionEngine/MCJIT/remote/test-ptr-reloc-remote.ll @@ -1,4 +1,4 @@ -; RUN: %lli_mcjit -remote-mcjit -O0 %s +; RUN: %lli_mcjit -remote-mcjit -O0 -mcjit-remote-process=lli-child-target %s @.str = private unnamed_addr constant [6 x i8] c"data1\00", align 1 @ptr = global i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), align 4 diff --git a/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index 98f411d07a4..578c414cce9 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -1,6 +1,8 @@ set(LLVM_LINK_COMPONENTS mcjit jit interpreter nativecodegen bitreader asmparser irreader selectiondag native instrumentation) +add_subdirectory(ChildTarget) + if( LLVM_USE_OPROFILE ) set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} @@ -21,4 +23,5 @@ add_llvm_tool(lli lli.cpp RecordingMemoryManager.cpp RemoteTarget.cpp + RemoteTargetExternal.cpp ) diff --git a/tools/lli/ChildTarget/CMakeLists.txt b/tools/lli/ChildTarget/CMakeLists.txt new file mode 100644 index 00000000000..fd1ac24f044 --- /dev/null +++ b/tools/lli/ChildTarget/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_tool(lli-child-target + ChildTarget.cpp + ) diff --git a/tools/lli/ChildTarget/ChildTarget.cpp b/tools/lli/ChildTarget/ChildTarget.cpp new file mode 100644 index 00000000000..a59209a160c --- /dev/null +++ b/tools/lli/ChildTarget/ChildTarget.cpp @@ -0,0 +1,241 @@ +#include "llvm/Config/config.h" + +#include "../RemoteTargetMessage.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +class LLIChildTarget { +public: + void initialize(); + LLIMessageType waitForIncomingMessage(); + void handleMessage(LLIMessageType messageType); + +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); + + // 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 AllocMapType; + AllocMapType m_AllocatedBufferMap; + + // Communication handles (OS-specific) + void *ConnectionData; +}; + +int main() { + LLIChildTarget ThisChild; + ThisChild.initialize(); + LLIMessageType MsgType; + do { + MsgType = ThisChild.waitForIncomingMessage(); + ThisChild.handleMessage(MsgType); + } while (MsgType != LLI_Terminate && + MsgType != LLI_Error); + return 0; +} + +// Public methods +void LLIChildTarget::initialize() { + initializeConnection(); + sendChildActive(); +} + +LLIMessageType LLIChildTarget::waitForIncomingMessage() { + int32_t MsgType = -1; + if (ReadBytes(&MsgType, 4) > 0) + return (LLIMessageType)MsgType; + return LLI_Error; +} + +void LLIChildTarget::handleMessage(LLIMessageType messageType) { + switch (messageType) { + case LLI_AllocateSpace: + handleAllocateSpace(); + break; + case LLI_LoadCodeSection: + handleLoadSection(true); + break; + case LLI_LoadDataSection: + handleLoadSection(false); + break; + case LLI_Execute: + handleExecute(); + break; + case LLI_Terminate: + handleTerminate(); + break; + default: + // FIXME: Handle error! + break; + } +} + +// Incoming message handlers +void LLIChildTarget::handleAllocateSpace() { + // Read and verify the message data size. + uint32_t DataSize; + int rc = ReadBytes(&DataSize, 4); + assert(rc == 4); + assert(DataSize == 8); + + // Read the message arguments. + uint32_t Alignment; + uint32_t AllocSize; + rc = ReadBytes(&Alignment, 4); + assert(rc == 4); + rc = ReadBytes(&AllocSize, 4); + assert(rc == 4); + + // Allocate the memory. + uint64_t Addr = allocate(Alignment, AllocSize); + + // Send AllocationResult message. + sendAllocationResult(Addr); +} + +void LLIChildTarget::handleLoadSection(bool IsCode) { + // Read the message data size. + uint32_t DataSize; + int rc = ReadBytes(&DataSize, 4); + 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 + + // Read section data into previously allocated buffer + rc = ReadBytes((void*)Addr, DataSize - 8); + assert(rc == (int)(BufferSize)); + + // If IsCode, mark memory executable + if (IsCode) + makeSectionExecutable(Addr, BufferSize); + + // Send MarkLoadComplete message. + sendLoadComplete(); +} + +void LLIChildTarget::handleExecute() { + // Read the message data size. + uint32_t DataSize; + int rc = ReadBytes(&DataSize, 4); + assert(rc == 4); + assert(DataSize == 8); + + // Read the target address. + uint64_t Addr; + rc = ReadBytes(&Addr, 8); + assert(rc == 8); + + // Call function + int Result; + int (*fn)(void) = (int(*)(void))Addr; + Result = fn(); + + // 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(); +} + +// Outgoing message handlers +void LLIChildTarget::sendChildActive() { + // Write the message type. + uint32_t MsgType = (uint32_t)LLI_ChildActive; + int rc = WriteBytes(&MsgType, 4); + assert(rc == 4); + + // Write the data size. + uint32_t DataSize = 0; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4); +} + +void LLIChildTarget::sendAllocationResult(uint64_t Addr) { + // Write the message type. + uint32_t MsgType = (uint32_t)LLI_AllocationResult; + int rc = WriteBytes(&MsgType, 4); + assert(rc == 4); + + // Write the data size. + uint32_t DataSize = 8; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4); + + // Write the allocated address. + rc = WriteBytes(&Addr, 8); + assert(rc == 8); +} + +void LLIChildTarget::sendLoadComplete() { + // Write the message type. + uint32_t MsgType = (uint32_t)LLI_LoadComplete; + int rc = WriteBytes(&MsgType, 4); + assert(rc == 4); + + // Write the data size. + uint32_t DataSize = 0; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4); +} + +void LLIChildTarget::sendExecutionComplete(uint64_t Result) { + // Write the message type. + uint32_t MsgType = (uint32_t)LLI_ExecutionResult; + int rc = WriteBytes(&MsgType, 4); + assert(rc == 4); + + + // Write the data size. + uint32_t DataSize = 8; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4); + + // Write the result. + rc = WriteBytes(&Result, 8); + assert(rc == 8); +} + +#ifdef LLVM_ON_UNIX +#include "Unix/ChildTarget.inc" +#endif + +#ifdef LLVM_ON_WIN32 +#include "Windows/ChildTarget.inc" +#endif diff --git a/tools/lli/ChildTarget/Makefile b/tools/lli/ChildTarget/Makefile new file mode 100644 index 00000000000..f77b144aabf --- /dev/null +++ b/tools/lli/ChildTarget/Makefile @@ -0,0 +1,17 @@ +##===- tools/lli/Makefile ------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL := ../../.. +TOOLNAME := lli-child-target + +include $(LEVEL)/Makefile.config + +LINK_COMPONENTS := + +include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/lli/ChildTarget/Unix/ChildTarget.inc b/tools/lli/ChildTarget/Unix/ChildTarget.inc new file mode 100644 index 00000000000..9550e508d62 --- /dev/null +++ b/tools/lli/ChildTarget/Unix/ChildTarget.inc @@ -0,0 +1,141 @@ +//===- 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 +#include +#include +#include + +namespace { + +struct ConnectionData_t { + int InputPipe; + int OutputPipe; + + ConnectionData_t(int in, int out) : InputPipe(in), OutputPipe(out) {} +}; + +} // namespace + +// 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(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(Addr); + const char *End = Start + Len; + __clear_cache(const_cast(Start), const_cast(End)); +# elif defined(__mips__) + const char *Start = static_cast(Addr); + cacheflush(const_cast(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 new file mode 100644 index 00000000000..bb95aff000c --- /dev/null +++ b/tools/lli/ChildTarget/Windows/ChildTarget.inc @@ -0,0 +1,41 @@ +//=- 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. +// +//===----------------------------------------------------------------------===// + +// 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/Makefile b/tools/lli/Makefile index 7a40427f5c3..eca5d833149 100644 --- a/tools/lli/Makefile +++ b/tools/lli/Makefile @@ -10,6 +10,8 @@ LEVEL := ../.. TOOLNAME := lli +PARALLEL_DIRS := ChildTarget + include $(LEVEL)/Makefile.config LINK_COMPONENTS := mcjit jit instrumentation interpreter nativecodegen bitreader asmparser irreader selectiondag native diff --git a/tools/lli/RemoteTarget.cpp b/tools/lli/RemoteTarget.cpp index 212bdfda1cd..5c74e6e7a04 100644 --- a/tools/lli/RemoteTarget.cpp +++ b/tools/lli/RemoteTarget.cpp @@ -13,13 +13,44 @@ //===----------------------------------------------------------------------===// #include "RemoteTarget.h" +#include "RemoteTargetExternal.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Memory.h" #include #include + 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 +// +// This implementation will simply move generated code and data to a new memory +// location in the current executable and let it run from there. +//////////////////////////////////////////////////////////////////////////////// + bool RemoteTarget::allocateSpace(size_t Size, unsigned Alignment, uint64_t &Address) { sys::MemoryBlock *Prev = Allocations.size() ? &Allocations.back() : NULL; diff --git a/tools/lli/RemoteTarget.h b/tools/lli/RemoteTarget.h index b2a6d0ef1d4..c95fbd1ae9e 100644 --- a/tools/lli/RemoteTarget.h +++ b/tools/lli/RemoteTarget.h @@ -41,7 +41,9 @@ public: /// /// @returns False on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - bool allocateSpace(size_t Size, unsigned Alignment, uint64_t &Address); + virtual bool allocateSpace(size_t Size, + unsigned Alignment, + uint64_t &Address); /// Load data into the target address space. /// @@ -51,7 +53,9 @@ public: /// /// @returns False on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - bool loadData(uint64_t Address, const void *Data, size_t Size); + virtual bool loadData(uint64_t Address, + const void *Data, + size_t Size); /// Load code into the target address space and prepare it for execution. /// @@ -61,7 +65,9 @@ public: /// /// @returns False on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - bool loadCode(uint64_t Address, const void *Data, size_t Size); + virtual bool loadCode(uint64_t Address, + const void *Data, + size_t Size); /// Execute code in the target process. The called function is required /// to be of signature int "(*)(void)". @@ -72,24 +78,29 @@ public: /// /// @returns False on success. On failure, ErrorMsg is updated with /// descriptive text of the encountered error. - bool executeCode(uint64_t Address, int &RetVal); + virtual bool executeCode(uint64_t Address, + int &RetVal); /// Minimum alignment for memory permissions. Used to seperate 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. - unsigned getPageAlignment() { return 4096; } + virtual unsigned getPageAlignment() { return 4096; } /// Start the remote process. - void create(); + virtual void create(); /// Terminate the remote process. - void stop(); + virtual void stop(); RemoteTarget() : ErrorMsg(""), IsRunning(false) {} - ~RemoteTarget() { if (IsRunning) stop(); } + 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 new file mode 100644 index 00000000000..59ad2d34329 --- /dev/null +++ b/tools/lli/RemoteTargetExternal.cpp @@ -0,0 +1,162 @@ +//===---- RemoteTargetExternal.cpp - 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. +// +//===----------------------------------------------------------------------===// +// +// Implementation of the RemoteTargetExternal class which executes JITed code +// in a separate process from where it was built. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Config/config.h" + +#include "RemoteTarget.h" +#include "RemoteTargetExternal.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +bool RemoteTargetExternal::allocateSpace(size_t Size, unsigned Alignment, + uint64_t &Address) { + SendAllocateSpace(Alignment, Size); + Receive(LLI_AllocationResult, Address); + return false; +} + +bool RemoteTargetExternal::loadData(uint64_t Address, const void *Data, size_t Size) { + SendLoadSection(Address, Data, (uint32_t)Size, false); + Receive(LLI_LoadComplete); + return false; +} + +bool RemoteTargetExternal::loadCode(uint64_t Address, const void *Data, size_t Size) { + SendLoadSection(Address, Data, (uint32_t)Size, true); + Receive(LLI_LoadComplete); + return false; +} + +bool RemoteTargetExternal::executeCode(uint64_t Address, int &RetVal) { + SendExecute(Address); + + Receive(LLI_ExecutionResult, RetVal); + return false; +} + +void RemoteTargetExternal::stop() { + SendTerminate(); + 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."); + + uint32_t DataSize = 8; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4 && "Error writing data size."); + + rc = WriteBytes(&Alignment, 4); + assert(rc == 4 && "Error writing alignment data."); + + rc = WriteBytes(&Size, 4); + assert(rc == 4 && "Error writing size data."); +} + +void 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."); + + uint32_t DataSize = Size + 8; + rc = WriteBytes(&DataSize, 4); + assert(rc == 4 && "Error writing data size."); + + rc = WriteBytes(&Addr, 8); + assert(rc == 8 && "Error writing data."); + + rc = WriteBytes(Data, Size); + assert(rc == (int)Size && "Error writing data."); +} + +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."); + + rc = WriteBytes(&Addr, 8); + assert(rc == 8 && "Error writing data."); +} + +void RemoteTargetExternal::SendTerminate() { + int rc; + uint32_t MsgType = (uint32_t)LLI_Terminate; + rc = WriteBytes(&MsgType, 4); + assert(rc == 4 && "Error writing message type."); + + // No data or data size is sent with Terminate +} + + +void RemoteTargetExternal::Receive(LLIMessageType ExpectedMsgType) { + int rc; + uint32_t MsgType; + rc = ReadBytes(&MsgType, 4); + assert(rc == 4 && "Error reading message type."); + assert(MsgType == ExpectedMsgType && "Error: received unexpected message type."); + + uint32_t DataSize; + rc = ReadBytes(&DataSize, 4); + assert(rc == 4 && "Error reading data size."); + assert(DataSize == 0 && "Error: unexpected data size."); +} + +void RemoteTargetExternal::Receive(LLIMessageType ExpectedMsgType, int &Data) { + uint64_t Temp; + Receive(ExpectedMsgType, Temp); + Data = (int)(int64_t)Temp; +} + +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 == ExpectedMsgType && "Error: received unexpected message type."); + + uint32_t DataSize; + rc = ReadBytes(&DataSize, 4); + assert(rc == 4 && "Error reading data size."); + assert(DataSize == 8 && "Error: unexpected data size."); + + rc = ReadBytes(&Data, 8); + assert(DataSize == 8 && "Error: unexpected data."); +} + +#ifdef LLVM_ON_UNIX +#include "Unix/RemoteTargetExternal.inc" +#endif + +#ifdef LLVM_ON_WIN32 +#include "Windows/RemoteTargetExternal.inc" +#endif diff --git a/tools/lli/RemoteTargetExternal.h b/tools/lli/RemoteTargetExternal.h new file mode 100644 index 00000000000..9a3644ac550 --- /dev/null +++ b/tools/lli/RemoteTargetExternal.h @@ -0,0 +1,118 @@ +//===----- RemoteTargetExternal.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_REMOTETARGETEXTERNAL_H +#define LLI_REMOTETARGETEXTERNAL_H + +#include "llvm/Config/config.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/Memory.h" +#include +#include + +#include "RemoteTarget.h" +#include "RemoteTargetMessage.h" + +namespace llvm { + +class RemoteTargetExternal : public RemoteTarget { +public: + /// Allocate space in the remote target address space. + /// + /// @param Size Amount of space, in bytes, to allocate. + /// @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 + /// descriptive text of the encountered error. + virtual bool allocateSpace(size_t Size, + unsigned Alignment, + uint64_t &Address); + + /// 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 + /// descriptive text of the encountered error. + virtual bool loadData(uint64_t Address, const void *Data, size_t Size); + + /// Load code into the target address space and prepare it for execution. + /// + /// @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 + /// descriptive text of the encountered error. + virtual bool loadCode(uint64_t Address, const void *Data, size_t Size); + + /// Execute code in the target process. The called function is required + /// to be of signature int "(*)(void)". + /// + /// @param Address Address of the loaded function in the target + /// process. + /// @param[out] RetVal The integer return value of the called function. + /// + /// @returns False 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 + /// 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; } + + /// Start the remote process. + virtual void create(); + + /// Terminate the remote process. + virtual void stop(); + + RemoteTargetExternal(std::string &Name) : RemoteTarget(), ChildName(Name) {} + 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, + 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(); +}; + +} // end namespace llvm + +#endif // LLI_REMOTETARGETEXTERNAL_H diff --git a/tools/lli/RemoteTargetMessage.h b/tools/lli/RemoteTargetMessage.h new file mode 100644 index 00000000000..12cfa9a4049 --- /dev/null +++ b/tools/lli/RemoteTargetMessage.h @@ -0,0 +1,45 @@ +//===---- RemoteTargetMessage.h - LLI out-of-process message protocol -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Definition of the LLIMessageType enum which is used for communication with a +// child process for remote execution. +// +//===----------------------------------------------------------------------===// + +#ifndef LLI_REMOTETARGETMESSAGE_H +#define LLI_REMOTETARGETMESSAGE_H + +namespace llvm { + +// LLI messages from parent-to-child or vice versa follow an exceedingly simple +// protocol where the first four bytes represent the message type, the next +// four bytes represent the size of data for the command and following bytes +// represent the actual data. +// +// The protocol is not intended to be robust, secure or fault-tolerant. It is +// 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. + +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_Terminate // Data = not used +}; + +} // end namespace llvm + +#endif diff --git a/tools/lli/Unix/RemoteTargetExternal.inc b/tools/lli/Unix/RemoteTargetExternal.inc new file mode 100644 index 00000000000..fd6c16b563f --- /dev/null +++ b/tools/lli/Unix/RemoteTargetExternal.inc @@ -0,0 +1,91 @@ +//=- RemoteTargetExternal.inc - LLVM out-of-process 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 RemoteTargetExternal class +// which executes JITed code in a separate process from where it was built. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include + +namespace { + +struct ConnectionData_t { + int InputPipe; + int OutputPipe; + + ConnectionData_t(int in, int out) : InputPipe(in), OutputPipe(out) {} +}; + +} // namespace + +namespace llvm { + +void RemoteTargetExternal::create() { + int PipeFD[2][2]; + pid_t ChildPID; + + pipe(PipeFD[0]); + pipe(PipeFD[1]); + + ChildPID = fork(); + + if (ChildPID == 0) { + // In the child... + + // Close the parent ends of the pipes + close(PipeFD[0][1]); + close(PipeFD[1][0]); + + // Use our pipes as stdin and stdout + if (PipeFD[0][0] != STDIN_FILENO) { + dup2(PipeFD[0][0], STDIN_FILENO); + close(PipeFD[0][0]); + } + if (PipeFD[1][1] != STDOUT_FILENO) { + dup2(PipeFD[1][1], STDOUT_FILENO); + close(PipeFD[1][1]); + } + + // Execute the child process. + char *args[1] = { NULL }; + int rc = execv(ChildName.c_str(), args); + if (rc != 0) + perror("Error executing child process: "); + } + else { + // In the parent... + + // Close the child ends of the pipes + close(PipeFD[0][0]); + 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); + } +} + +int RemoteTargetExternal::WriteBytes(const void *Data, size_t Size) { + return write(((ConnectionData_t*)ConnectionData)->OutputPipe, Data, Size); +} + +int RemoteTargetExternal::ReadBytes(void *Data, size_t Size) { + return read(((ConnectionData_t*)ConnectionData)->InputPipe, Data, Size); +} + +void RemoteTargetExternal::Wait() { + wait(NULL); +} + +} // namespace llvm \ No newline at end of file diff --git a/tools/lli/Windows/RemoteTargetExternal.inc b/tools/lli/Windows/RemoteTargetExternal.inc new file mode 100644 index 00000000000..6536d9974a0 --- /dev/null +++ b/tools/lli/Windows/RemoteTargetExternal.inc @@ -0,0 +1,32 @@ +//= 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() { +} + +} // namespace llvm diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index ba9cb99d5a6..17b8ea79930 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -41,6 +41,7 @@ #include "llvm/Support/PluginLoader.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" @@ -83,6 +84,18 @@ namespace { cl::desc("Execute MCJIT'ed code in a separate process."), cl::init(false)); + // Manually specify the child process for remote execution. This overrides + // the simulated remote execution that allocates address space for child + // execution. The child process resides in the disk and communicates with lli + // via stdin/stdout pipes. + cl::opt + 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("")); + // Determine optimization level. cl::opt OptLevel("O", @@ -481,30 +494,50 @@ int main(int argc, char **argv, char * const *envp) { // Everything is prepared now, so lay out our program for the target // address space, assign the section addresses to resolve any relocations, // and send it to the target. - RemoteTarget Target; - Target.create(); + + OwningPtr 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(MCJITRemoteProcess)); + } + } else { + // No child process name provided, use simulated remote execution. + Target.reset(RemoteTarget::createRemoteTarget()); + } + + // Create the remote target + Target->create(); // Trigger compilation. EE->generateCodeForModule(Mod); // Layout the target memory. - layoutRemoteTargetMemory(&Target, MM); + layoutRemoteTargetMemory(Target.get(), MM); // Since we're executing in a (at least simulated) remote address space, // we can't use the ExecutionEngine::runFunctionAsMain(). We have to // grab the function address directly here and tell the remote target // to execute the function. // FIXME: argv and envp handling. - uint64_t Entry = (uint64_t)EE->getPointerToFunction(EntryFn); + uint64_t Entry = EE->getFunctionAddress(EntryFn->getName().str()); DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x" << format("%llx", Entry) << "\n"); - if (Target.executeCode(Entry, Result)) - errs() << "ERROR: " << Target.getErrorMsg() << "\n"; + if (Target->executeCode(Entry, Result)) + errs() << "ERROR: " << Target->getErrorMsg() << "\n"; - Target.stop(); - } else { + Target->stop(); + } else { // !RemoteMCJIT // Trigger compilation separately so code regions that need to be // invalidated will be known. (void)EE->getPointerToFunction(EntryFn);