diff options
author | Danil Malyshev <dmalyshev@accesssoftek.com> | 2012-05-16 18:50:11 +0000 |
---|---|---|
committer | Danil Malyshev <dmalyshev@accesssoftek.com> | 2012-05-16 18:50:11 +0000 |
commit | 068c65b22d50c265b51886062b2b9c1cb696d67d (patch) | |
tree | 291d4c251d82265b2a45db53727414c6dee2abe0 /tools | |
parent | ab53f8ea6a8aa616b2ae153a5ff941e4a855a07d (diff) |
Added LLIMCJITMemoryManager to the lli. This manager will be used for MCJIT instead of DefaultJIMMemoryManager.
It's more flexible for MCJIT tasks, in addition it's provides a invalidation instruction cache for code sections which will be used before JIT code will be executed.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@156933 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r-- | tools/lli/lli.cpp | 204 | ||||
-rw-r--r-- | tools/llvm-rtdyld/llvm-rtdyld.cpp | 25 |
2 files changed, 227 insertions, 2 deletions
diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index e1759ccede..a1cf92e2c3 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -35,8 +35,20 @@ #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Memory.h" #include <cerrno> +#ifdef __linux__ +// These includes used by LLIMCJITMemoryManager::getPointerToNamedFunction() +// for Glibc trickery. Look comments in this function for more information. +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif +#include <fcntl.h> +#include <unistd.h> +#endif + #ifdef __CYGWIN__ #include <cygwin/version.h> #if defined(CYGWIN_VERSION_DLL_MAJOR) && CYGWIN_VERSION_DLL_MAJOR<1007 @@ -175,6 +187,191 @@ static void do_shutdown() { #endif } +// Memory manager for MCJIT +class LLIMCJITMemoryManager : public JITMemoryManager { +public: + SmallVector<sys::MemoryBlock, 16> AllocatedDataMem; + SmallVector<sys::MemoryBlock, 16> AllocatedCodeMem; + SmallVector<sys::MemoryBlock, 16> FreeCodeMem; + + LLIMCJITMemoryManager() { }; + ~LLIMCJITMemoryManager(); + + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true); + + // Invalidate instruction cache for code sections. Some platforms with + // separate data cache and instruction cache require explicit cache flush, + // otherwise JIT code manipulations (like resolved relocations) will get to + // the data cache but not to the instruction cache. + virtual void invalidateInstructionCache(); + + // The MCJITMemoryManager doesn't use the following functions, so we don't + // need implement them. + virtual void setMemoryWritable() { + llvm_unreachable("Unexpected call!"); + }; + virtual void setMemoryExecutable() { + llvm_unreachable("Unexpected call!"); + }; + virtual void setPoisonMemory(bool poison) { + llvm_unreachable("Unexpected call!"); + }; + virtual void AllocateGOT() { + llvm_unreachable("Unexpected call!"); + }; + virtual uint8_t *getGOTBase() const { + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual uint8_t *startFunctionBody(const Function *F, + uintptr_t &ActualSize){ + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual uint8_t *allocateStub(const GlobalValue* F, unsigned StubSize, + unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) { + llvm_unreachable("Unexpected call!"); + }; + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual void deallocateFunctionBody(void *Body) { + llvm_unreachable("Unexpected call!"); + }; + virtual uint8_t* startExceptionTable(const Function* F, + uintptr_t &ActualSize) { + llvm_unreachable("Unexpected call!"); + return 0; + }; + virtual void endExceptionTable(const Function *F, uint8_t *TableStart, + uint8_t *TableEnd, uint8_t* FrameRegister) { + llvm_unreachable("Unexpected call!"); + }; + virtual void deallocateExceptionTable(void *ET) { + llvm_unreachable("Unexpected call!"); + }; +}; + +uint8_t *LLIMCJITMemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + uint8_t *Addr = (uint8_t*)calloc((Size + Alignment - 1)/Alignment, Alignment); + AllocatedDataMem.push_back(sys::MemoryBlock(Addr, Size)); + return Addr; +} + +uint8_t *LLIMCJITMemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) { + if (!Alignment) + Alignment = 16; + unsigned NeedAllocate = Alignment * ((Size + Alignment - 1)/Alignment + 1); + uintptr_t Addr = 0; + // Look in the list of free code memory regions and use a block there if one + // is available. + for (int i = 0, e = FreeCodeMem.size(); i != e; ++i) { + sys::MemoryBlock &MB = FreeCodeMem[i]; + if (MB.size() >= NeedAllocate) { + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // Store cutted free memory block. + FreeCodeMem[i] = sys::MemoryBlock((void*)(Addr + Size), + EndOfBlock - Addr - Size); + return (uint8_t*)Addr; + } + } + + // No pre-allocated free block was large enough. Allocate a new memory region. + sys::MemoryBlock MB = sys::Memory::AllocateRWX(NeedAllocate, 0, 0); + + AllocatedCodeMem.push_back(MB); + Addr = (uintptr_t)MB.base(); + uintptr_t EndOfBlock = Addr + MB.size(); + // Align the address. + Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1); + // The AllocateRWX may allocate much more memory than we need. In this case, + // we store the unused memory as a free memory block. + unsigned FreeSize = EndOfBlock-Addr-Size; + if (FreeSize > 16) + FreeCodeMem.push_back(sys::MemoryBlock((void*)(Addr + Size), FreeSize)); + + // Return aligned address + return (uint8_t*)Addr; +} + +void LLIMCJITMemoryManager::invalidateInstructionCache() { + for (int i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::InvalidateInstructionCache(AllocatedCodeMem[i].base(), + AllocatedCodeMem[i].size()); +} + +void *LLIMCJITMemoryManager::getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure) { +#if defined(__linux__) + //===--------------------------------------------------------------------===// + // Function stubs that are invoked instead of certain library calls + // + // Force the following functions to be linked in to anything that uses the + // JIT. This is a hack designed to work around the all-too-clever Glibc + // strategy of making these functions work differently when inlined vs. when + // not inlined, and hiding their real definitions in a separate archive file + // that the dynamic linker can't see. For more info, search for + // 'libc_nonshared.a' on Google, or read http://llvm.org/PR274. + if (Name == "stat") return (void*)(intptr_t)&stat; + if (Name == "fstat") return (void*)(intptr_t)&fstat; + if (Name == "lstat") return (void*)(intptr_t)&lstat; + if (Name == "stat64") return (void*)(intptr_t)&stat64; + if (Name == "fstat64") return (void*)(intptr_t)&fstat64; + if (Name == "lstat64") return (void*)(intptr_t)&lstat64; + if (Name == "atexit") return (void*)(intptr_t)&atexit; + if (Name == "mknod") return (void*)(intptr_t)&mknod; +#endif // __linux__ + + const char *NameStr = Name.c_str(); + void *Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr); + if (Ptr) return Ptr; + + // If it wasn't found and if it starts with an underscore ('_') character, + // try again without the underscore. + if (NameStr[0] == '_') { + Ptr = sys::DynamicLibrary::SearchForAddressOfSymbol(NameStr+1); + if (Ptr) return Ptr; + } + + if (AbortOnFailure) + report_fatal_error("Program used external function '" + Name + + "' which could not be resolved!"); + return 0; +} + +LLIMCJITMemoryManager::~LLIMCJITMemoryManager() { + for (unsigned i = 0, e = AllocatedCodeMem.size(); i != e; ++i) + sys::Memory::ReleaseRWX(AllocatedCodeMem[i]); + for (unsigned i = 0, e = AllocatedDataMem.size(); i != e; ++i) + free(AllocatedDataMem[i].base()); +} + //===----------------------------------------------------------------------===// // main Driver function // @@ -233,8 +430,11 @@ int main(int argc, char **argv, char * const *envp) { Mod->setTargetTriple(Triple::normalize(TargetTriple)); // Enable MCJIT if desired. + LLIMCJITMemoryManager *JMM = 0; if (UseMCJIT && !ForceInterpreter) { builder.setUseMCJIT(true); + JMM = new LLIMCJITMemoryManager(); + builder.setJITMemoryManager(JMM); } CodeGenOpt::Level OLvl = CodeGenOpt::Default; @@ -265,6 +465,10 @@ int main(int argc, char **argv, char * const *envp) { exit(1); } + // Clear instruction cache before code will be executed. + if (JMM) + JMM->invalidateInstructionCache(); + // The following functions have no effect if their respective profiling // support wasn't enabled in the build configuration. EE->RegisterJITEventListener( diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 01a7d15807..95de8d8a4d 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -63,18 +63,37 @@ public: return 0; } + // Invalidate instruction cache for sections with execute permissions. + // Some platforms with separate data cache and instruction cache require + // explicit cache flush, otherwise JIT code manipulations (like resolved + // relocations) will get to the data cache but not to the instruction cache. + virtual void invalidateInstructionCache(); }; uint8_t *TrivialMemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { - return (uint8_t*)sys::Memory::AllocateRWX(Size, 0, 0).base(); + sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, 0, 0); + FunctionMemory.push_back(MB); + return (uint8_t*)MB.base(); } uint8_t *TrivialMemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID) { - return (uint8_t*)sys::Memory::AllocateRWX(Size, 0, 0).base(); + sys::MemoryBlock MB = sys::Memory::AllocateRWX(Size, 0, 0); + DataMemory.push_back(MB); + return (uint8_t*)MB.base(); +} + +void TrivialMemoryManager::invalidateInstructionCache() { + for (int i = 0, e = FunctionMemory.size(); i != e; ++i) + sys::Memory::InvalidateInstructionCache(FunctionMemory[i].base(), + FunctionMemory[i].size()); + + for (int i = 0, e = DataMemory.size(); i != e; ++i) + sys::Memory::InvalidateInstructionCache(DataMemory[i].base(), + DataMemory[i].size()); } static const char *ProgramName; @@ -113,6 +132,8 @@ static int executeInput() { // Resolve all the relocations we can. Dyld.resolveRelocations(); + // Clear instruction cache before code will be executed. + MemMgr->invalidateInstructionCache(); // FIXME: Error out if there are unresolved relocations. |