[llvm-commits] [llvm] r168721 - in /llvm/trunk: include/llvm/ExecutionEngine/SectionMemoryManager.h lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp unittests/ExecutionEngine/MCJIT/CMakeLists.txt unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp unittests/ExecutionEngine/MCJIT/MCJITTestBase.h unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h
Benjamin Kramer
benny.kra at gmail.com
Tue Nov 27 12:17:32 PST 2012
On 27.11.2012, at 20:42, Andrew Kaylor <andrew.kaylor at intel.com> wrote:
> Author: akaylor
> Date: Tue Nov 27 13:42:02 2012
> New Revision: 168721
>
> URL: http://llvm.org/viewvc/llvm-project?rev=168721&view=rev
> Log:
> Moving SectionMemoryManager into RuntimeDyld and adding unit tests for it.
>
> The SectionMemoryManager now supports (and requires) applying section-specific page permissions. Clients using this memory manager must call either MCJIT::finalizeObject() or SectionMemoryManager::applyPermissions() before executing JITed code.
>
> See r168718 for changes from the previous implementation.
>
> Added:
> llvm/trunk/include/llvm/ExecutionEngine/SectionMemoryManager.h
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp
> llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp
> Removed:
> llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp
> llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h
> Modified:
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt
> llvm/trunk/unittests/ExecutionEngine/MCJIT/CMakeLists.txt
> llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
>
> Added: llvm/trunk/include/llvm/ExecutionEngine/SectionMemoryManager.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/SectionMemoryManager.h?rev=168721&view=auto
> ==============================================================================
> --- llvm/trunk/include/llvm/ExecutionEngine/SectionMemoryManager.h (added)
> +++ llvm/trunk/include/llvm/ExecutionEngine/SectionMemoryManager.h Tue Nov 27 13:42:02 2012
> @@ -0,0 +1,176 @@
> +//===- SectionMemoryManager.h - Memory manager for MCJIT/RtDyld -*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file contains the declaration of a section-based memory manager used by
> +// the MCJIT execution engine and RuntimeDyld.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> +#define LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> +
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ExecutionEngine/JITMemoryManager.h"
> +#include "llvm/Support/ErrorHandling.h"
> +#include "llvm/Support/Memory.h"
> +
> +namespace llvm {
> +
> +/// This is a simple memory manager which implements the methods called by
> +/// the RuntimeDyld class to allocate memory for section-based loading of
> +/// objects, usually those generated by the MCJIT execution engine.
> +///
> +/// This memory manager allocates all section memory as read-write. The
> +/// RuntimeDyld will copy JITed section memory into these allocated blocks
> +/// and perform any necessary linking and relocations.
> +///
> +/// Any client using this memory manager MUST ensure that section-specific
> +/// page permissions have been applied before attempting to execute functions
> +/// in the JITed object. Permissions can be applied either by calling
> +/// MCJIT::finalizeObject or by calling SectionMemoryManager::applyPermissions
> +/// directly. Clients of MCJIT should call MCJIT::finalizeObject.
> +class SectionMemoryManager : public JITMemoryManager {
This adds a cyclic dependency from RuntimeDyld to JIT. Can JITMemoryManager also be moved from JIT to RuntimeDyld?
- Ben
> + SectionMemoryManager(const SectionMemoryManager&) LLVM_DELETED_FUNCTION;
> + void operator=(const SectionMemoryManager&) LLVM_DELETED_FUNCTION;
> +
> +public:
> + SectionMemoryManager() { }
> + virtual ~SectionMemoryManager();
> +
> + /// \brief Allocates a memory block of (at least) the given size suitable for
> + /// executable code.
> + ///
> + /// The value of \p Alignment must be a power of two. If \p Alignment is zero
> + /// a default alignment of 16 will be used.
> + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
> + unsigned SectionID);
> +
> + /// \brief Allocates a memory block of (at least) the given size suitable for
> + /// executable code.
> + ///
> + /// The value of \p Alignment must be a power of two. If \p Alignment is zero
> + /// a default alignment of 16 will be used.
> + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
> + unsigned SectionID,
> + bool isReadOnly);
> +
> + /// \brief Applies section-specific memory permissions.
> + ///
> + /// This method is called when object loading is complete and section page
> + /// permissions can be applied. It is up to the memory manager implementation
> + /// to decide whether or not to act on this method. The memory manager will
> + /// typically allocate all sections as read-write and then apply specific
> + /// permissions when this method is called. Code sections cannot be executed
> + /// until this function has been called.
> + ///
> + /// \returns true if an error occurred, false otherwise.
> + virtual bool applyPermissions(std::string *ErrMsg = 0);
> +
> + /// This method returns the address of the specified function. As such it is
> + /// only useful for resolving library symbols, not code generated symbols.
> + ///
> + /// If \p AbortOnFailure is false and no function with the given name is
> + /// found, this function returns a null pointer. Otherwise, it prints a
> + /// message to stderr and aborts.
> + virtual void *getPointerToNamedFunction(const std::string &Name,
> + bool AbortOnFailure = true);
> +
> + /// \brief 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.
> + ///
> + /// This method is not called by RuntimeDyld or MCJIT during the load
> + /// process. Clients may call this function when needed. See the lli
> + /// tool for example use.
> + virtual void invalidateInstructionCache();
> +
> +private:
> + struct MemoryGroup {
> + SmallVector<sys::MemoryBlock, 16> AllocatedMem;
> + SmallVector<sys::MemoryBlock, 16> FreeMem;
> + sys::MemoryBlock Near;
> + };
> +
> + uint8_t *allocateSection(MemoryGroup &MemGroup, uintptr_t Size,
> + unsigned Alignment);
> +
> + error_code applyMemoryGroupPermissions(MemoryGroup &MemGroup,
> + unsigned Permissions);
> +
> + MemoryGroup CodeMem;
> + MemoryGroup RWDataMem;
> + MemoryGroup RODataMem;
> +
> +public:
> + ///
> + /// Functions below are not used by MCJIT or RuntimeDyld, but must be
> + /// implemented because they are declared as pure virtuals in the base class.
> + ///
> +
> + 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!");
> + }
> +};
> +
> +}
> +
> +#endif // LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> +
>
> Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt?rev=168721&r1=168720&r2=168721&view=diff
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt (original)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt Tue Nov 27 13:42:02 2012
> @@ -3,4 +3,5 @@
> RuntimeDyld.cpp
> RuntimeDyldELF.cpp
> RuntimeDyldMachO.cpp
> + SectionMemoryManager.cpp
> )
>
> Added: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp?rev=168721&view=auto
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp (added)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/SectionMemoryManager.cpp Tue Nov 27 13:42:02 2012
> @@ -0,0 +1,226 @@
> +//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This file implements the section-based memory manager used by the MCJIT
> +// execution engine and RuntimeDyld
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Config/config.h"
> +#include "llvm/ExecutionEngine/SectionMemoryManager.h"
> +#include "llvm/Support/DynamicLibrary.h"
> +#include "llvm/Support/MathExtras.h"
> +
> +#ifdef __linux__
> + // These includes used by SectionMemoryManager::getPointerToNamedFunction()
> + // for Glibc trickery. See comments in this function for more information.
> + #ifdef HAVE_SYS_STAT_H
> + #include <sys/stat.h>
> + #endif
> + #include <fcntl.h>
> + #include <unistd.h>
> +#endif
> +
> +namespace llvm {
> +
> +uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
> + unsigned Alignment,
> + unsigned SectionID,
> + bool IsReadOnly) {
> + if (IsReadOnly)
> + return allocateSection(RODataMem, Size, Alignment);
> + return allocateSection(RWDataMem, Size, Alignment);
> +}
> +
> +uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
> + unsigned Alignment,
> + unsigned SectionID) {
> + return allocateSection(CodeMem, Size, Alignment);
> +}
> +
> +uint8_t *SectionMemoryManager::allocateSection(MemoryGroup &MemGroup,
> + uintptr_t Size,
> + unsigned Alignment) {
> + if (!Alignment)
> + Alignment = 16;
> +
> + assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
> +
> + uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1)/Alignment + 1);
> + uintptr_t Addr = 0;
> +
> + // Look in the list of free memory regions and use a block there if one
> + // is available.
> + for (int i = 0, e = MemGroup.FreeMem.size(); i != e; ++i) {
> + sys::MemoryBlock &MB = MemGroup.FreeMem[i];
> + if (MB.size() >= RequiredSize) {
> + 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.
> + MemGroup.FreeMem[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.
> + // Note that all sections get allocated as read-write. The permissions will
> + // be updated later based on memory group.
> + //
> + // FIXME: It would be useful to define a default allocation size (or add
> + // it as a constructor parameter) to minimize the number of allocations.
> + //
> + // FIXME: Initialize the Near member for each memory group to avoid
> + // interleaving.
> + error_code ec;
> + sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(RequiredSize,
> + &MemGroup.Near,
> + sys::Memory::MF_READ |
> + sys::Memory::MF_WRITE,
> + ec);
> + if (ec) {
> + // FIXME: Add error propogation to the interface.
> + return NULL;
> + }
> +
> + // Save this address as the basis for our next request
> + MemGroup.Near = MB;
> +
> + MemGroup.AllocatedMem.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 allocateMappedMemory 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)
> + MemGroup.FreeMem.push_back(sys::MemoryBlock((void*)(Addr + Size), FreeSize));
> +
> + // Return aligned address
> + return (uint8_t*)Addr;
> +}
> +
> +bool SectionMemoryManager::applyPermissions(std::string *ErrMsg)
> +{
> + // FIXME: Should in-progress permissions be reverted if an error occurs?
> + error_code ec;
> +
> + // Make code memory executable.
> + ec = applyMemoryGroupPermissions(CodeMem,
> + sys::Memory::MF_READ | sys::Memory::MF_EXEC);
> + if (ec) {
> + if (ErrMsg) {
> + *ErrMsg = ec.message();
> + }
> + return true;
> + }
> +
> + // Make read-only data memory read-only.
> + ec = applyMemoryGroupPermissions(RODataMem,
> + sys::Memory::MF_READ | sys::Memory::MF_EXEC);
> + if (ec) {
> + if (ErrMsg) {
> + *ErrMsg = ec.message();
> + }
> + return true;
> + }
> +
> + // Read-write data memory already has the correct permissions
> +
> + return false;
> +}
> +
> +error_code SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
> + unsigned Permissions) {
> +
> + for (int i = 0, e = MemGroup.AllocatedMem.size(); i != e; ++i) {
> + error_code ec;
> + ec = sys::Memory::protectMappedMemory(MemGroup.AllocatedMem[i],
> + Permissions);
> + if (ec) {
> + return ec;
> + }
> + }
> +
> + return error_code::success();
> +}
> +
> +void SectionMemoryManager::invalidateInstructionCache() {
> + for (int i = 0, e = CodeMem.AllocatedMem.size(); i != e; ++i)
> + sys::Memory::InvalidateInstructionCache(CodeMem.AllocatedMem[i].base(),
> + CodeMem.AllocatedMem[i].size());
> +}
> +
> +static int jit_noop() {
> + return 0;
> +}
> +
> +void *SectionMemoryManager::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__
> +
> + // We should not invoke parent's ctors/dtors from generated main()!
> + // On Mingw and Cygwin, the symbol __main is resolved to
> + // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors
> + // (and register wrong callee's dtors with atexit(3)).
> + // We expect ExecutionEngine::runStaticConstructorsDestructors()
> + // is called before ExecutionEngine::runFunctionAsMain() is called.
> + if (Name == "__main") return (void*)(intptr_t)&jit_noop;
> +
> + 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;
> +}
> +
> +SectionMemoryManager::~SectionMemoryManager() {
> + for (unsigned i = 0, e = CodeMem.AllocatedMem.size(); i != e; ++i)
> + sys::Memory::releaseMappedMemory(CodeMem.AllocatedMem[i]);
> + for (unsigned i = 0, e = RWDataMem.AllocatedMem.size(); i != e; ++i)
> + sys::Memory::releaseMappedMemory(RWDataMem.AllocatedMem[i]);
> + for (unsigned i = 0, e = RODataMem.AllocatedMem.size(); i != e; ++i)
> + sys::Memory::releaseMappedMemory(RODataMem.AllocatedMem[i]);
> +}
> +
> +} // namespace llvm
> +
>
> Modified: llvm/trunk/unittests/ExecutionEngine/MCJIT/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/CMakeLists.txt?rev=168721&r1=168720&r2=168721&view=diff
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/CMakeLists.txt (original)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/CMakeLists.txt Tue Nov 27 13:42:02 2012
> @@ -2,14 +2,14 @@
> asmparser
> bitreader
> bitwriter
> - mcjit
> jit
> + mcjit
> nativecodegen
> )
>
> set(MCJITTestsSources
> MCJITTest.cpp
> - SectionMemoryManager.cpp
> + MCJITMemoryManagerTest.cpp
> )
>
> if(MSVC)
>
> Added: llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp?rev=168721&view=auto
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp (added)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp Tue Nov 27 13:42:02 2012
> @@ -0,0 +1,172 @@
> +//===- MCJITMemoryManagerTest.cpp - Unit tests for the JIT memory manager -===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "gtest/gtest.h"
> +#include "llvm/ADT/OwningPtr.h"
> +#include "llvm/ExecutionEngine/SectionMemoryManager.h"
> +#include "llvm/ExecutionEngine/JIT.h"
> +
> +using namespace llvm;
> +
> +namespace {
> +
> +TEST(MCJITMemoryManagerTest, BasicAllocations) {
> + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
> +
> + uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1);
> + uint8_t *data1 = MemMgr->allocateDataSection(256, 0, 2, true);
> + uint8_t *code2 = MemMgr->allocateCodeSection(256, 0, 3);
> + uint8_t *data2 = MemMgr->allocateDataSection(256, 0, 4, false);
> +
> + EXPECT_NE((uint8_t*)0, code1);
> + EXPECT_NE((uint8_t*)0, code2);
> + EXPECT_NE((uint8_t*)0, data1);
> + EXPECT_NE((uint8_t*)0, data2);
> +
> + // Initialize the data
> + for (unsigned i = 0; i < 256; ++i) {
> + code1[i] = 1;
> + code2[i] = 2;
> + data1[i] = 3;
> + data2[i] = 4;
> + }
> +
> + // Verify the data (this is checking for overlaps in the addresses)
> + for (unsigned i = 0; i < 256; ++i) {
> + EXPECT_EQ(1, code1[i]);
> + EXPECT_EQ(2, code2[i]);
> + EXPECT_EQ(3, data1[i]);
> + EXPECT_EQ(4, data2[i]);
> + }
> +
> + std::string Error;
> + EXPECT_FALSE(MemMgr->applyPermissions(&Error));
> +}
> +
> +TEST(MCJITMemoryManagerTest, LargeAllocations) {
> + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
> +
> + uint8_t *code1 = MemMgr->allocateCodeSection(0x100000, 0, 1);
> + uint8_t *data1 = MemMgr->allocateDataSection(0x100000, 0, 2, true);
> + uint8_t *code2 = MemMgr->allocateCodeSection(0x100000, 0, 3);
> + uint8_t *data2 = MemMgr->allocateDataSection(0x100000, 0, 4, false);
> +
> + EXPECT_NE((uint8_t*)0, code1);
> + EXPECT_NE((uint8_t*)0, code2);
> + EXPECT_NE((uint8_t*)0, data1);
> + EXPECT_NE((uint8_t*)0, data2);
> +
> + // Initialize the data
> + for (unsigned i = 0; i < 0x100000; ++i) {
> + code1[i] = 1;
> + code2[i] = 2;
> + data1[i] = 3;
> + data2[i] = 4;
> + }
> +
> + // Verify the data (this is checking for overlaps in the addresses)
> + for (unsigned i = 0; i < 0x100000; ++i) {
> + EXPECT_EQ(1, code1[i]);
> + EXPECT_EQ(2, code2[i]);
> + EXPECT_EQ(3, data1[i]);
> + EXPECT_EQ(4, data2[i]);
> + }
> +
> + std::string Error;
> + EXPECT_FALSE(MemMgr->applyPermissions(&Error));
> +}
> +
> +TEST(MCJITMemoryManagerTest, ManyAllocations) {
> + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
> +
> + uint8_t* code[10000];
> + uint8_t* data[10000];
> +
> + for (unsigned i = 0; i < 10000; ++i) {
> + const bool isReadOnly = i % 2 == 0;
> +
> + code[i] = MemMgr->allocateCodeSection(32, 0, 1);
> + data[i] = MemMgr->allocateDataSection(32, 0, 2, isReadOnly);
> +
> + for (unsigned j = 0; j < 32; j++) {
> + code[i][j] = 1 + (i % 254);
> + data[i][j] = 2 + (i % 254);
> + }
> +
> + EXPECT_NE((uint8_t *)0, code[i]);
> + EXPECT_NE((uint8_t *)0, data[i]);
> + }
> +
> + // Verify the data (this is checking for overlaps in the addresses)
> + for (unsigned i = 0; i < 10000; ++i) {
> + for (unsigned j = 0; j < 32;j++ ) {
> + uint8_t ExpectedCode = 1 + (i % 254);
> + uint8_t ExpectedData = 2 + (i % 254);
> + EXPECT_EQ(ExpectedCode, code[i][j]);
> + EXPECT_EQ(ExpectedData, data[i][j]);
> + }
> + }
> +
> + std::string Error;
> + EXPECT_FALSE(MemMgr->applyPermissions(&Error));
> +}
> +
> +TEST(MCJITMemoryManagerTest, ManyVariedAllocations) {
> + OwningPtr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
> +
> + uint8_t* code[10000];
> + uint8_t* data[10000];
> +
> + for (unsigned i = 0; i < 10000; ++i) {
> + uintptr_t CodeSize = i % 16 + 1;
> + uintptr_t DataSize = i % 8 + 1;
> +
> + bool isReadOnly = i % 3 == 0;
> + unsigned Align = 8 << (i % 4);
> +
> + code[i] = MemMgr->allocateCodeSection(CodeSize, Align, i);
> + data[i] = MemMgr->allocateDataSection(DataSize, Align, i + 10000,
> + isReadOnly);
> +
> + for (unsigned j = 0; j < CodeSize; j++) {
> + code[i][j] = 1 + (i % 254);
> + }
> +
> + for (unsigned j = 0; j < DataSize; j++) {
> + data[i][j] = 2 + (i % 254);
> + }
> +
> + EXPECT_NE((uint8_t *)0, code[i]);
> + EXPECT_NE((uint8_t *)0, data[i]);
> +
> + uintptr_t CodeAlign = Align ? (uintptr_t)code[i] % Align : 0;
> + uintptr_t DataAlign = Align ? (uintptr_t)data[i] % Align : 0;
> +
> + EXPECT_EQ((uintptr_t)0, CodeAlign);
> + EXPECT_EQ((uintptr_t)0, DataAlign);
> + }
> +
> + for (unsigned i = 0; i < 10000; ++i) {
> + uintptr_t CodeSize = i % 16 + 1;
> + uintptr_t DataSize = i % 8 + 1;
> +
> + for (unsigned j = 0; j < CodeSize; j++) {
> + uint8_t ExpectedCode = 1 + (i % 254);
> + EXPECT_EQ(ExpectedCode, code[i][j]);
> + }
> +
> + for (unsigned j = 0; j < DataSize; j++) {
> + uint8_t ExpectedData = 2 + (i % 254);
> + EXPECT_EQ(ExpectedData, data[i][j]);
> + }
> + }
> +}
> +
> +} // Namespace
> +
>
> Modified: llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h?rev=168721&r1=168720&r2=168721&view=diff
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h (original)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h Tue Nov 27 13:42:02 2012
> @@ -21,6 +21,7 @@
> #include "llvm/ADT/SmallVector.h"
> #include "llvm/Config/config.h"
> #include "llvm/ExecutionEngine/ExecutionEngine.h"
> +#include "llvm/ExecutionEngine/SectionMemoryManager.h"
> #include "llvm/Function.h"
> #include "llvm/IRBuilder.h"
> #include "llvm/LLVMContext.h"
> @@ -30,8 +31,6 @@
> #include "llvm/Support/TargetSelect.h"
> #include "llvm/TypeBuilder.h"
>
> -#include "SectionMemoryManager.h"
> -
> // Used to skip tests on unsupported architectures and operating systems.
> // To skip a test, add this macro at the top of a test-case in a suite that
> // inherits from MCJITTestBase. See MCJITTest.cpp for examples.
>
> Removed: llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp?rev=168720&view=auto
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp (original)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.cpp (removed)
> @@ -1,226 +0,0 @@
> -//===- SectionMemoryManager.cpp - Memory manager for MCJIT/RtDyld *- C++ -*-==//
> -//
> -// The LLVM Compiler Infrastructure
> -//
> -// This file is distributed under the University of Illinois Open Source
> -// License. See LICENSE.TXT for details.
> -//
> -//===----------------------------------------------------------------------===//
> -//
> -// This file implements the section-based memory manager used by the MCJIT
> -// execution engine and RuntimeDyld
> -//
> -//===----------------------------------------------------------------------===//
> -
> -#include "llvm/Config/config.h"
> -#include "llvm/Support/DynamicLibrary.h"
> -#include "llvm/Support/MathExtras.h"
> -#include "SectionMemoryManager.h"
> -
> -#ifdef __linux__
> - // These includes used by SectionMemoryManager::getPointerToNamedFunction()
> - // for Glibc trickery. See comments in this function for more information.
> - #ifdef HAVE_SYS_STAT_H
> - #include <sys/stat.h>
> - #endif
> - #include <fcntl.h>
> - #include <unistd.h>
> -#endif
> -
> -namespace llvm {
> -
> -uint8_t *SectionMemoryManager::allocateDataSection(uintptr_t Size,
> - unsigned Alignment,
> - unsigned SectionID,
> - bool IsReadOnly) {
> - if (IsReadOnly)
> - return allocateSection(RODataMem, Size, Alignment);
> - return allocateSection(RWDataMem, Size, Alignment);
> -}
> -
> -uint8_t *SectionMemoryManager::allocateCodeSection(uintptr_t Size,
> - unsigned Alignment,
> - unsigned SectionID) {
> - return allocateSection(CodeMem, Size, Alignment);
> -}
> -
> -uint8_t *SectionMemoryManager::allocateSection(MemoryGroup &MemGroup,
> - uintptr_t Size,
> - unsigned Alignment) {
> - if (!Alignment)
> - Alignment = 16;
> -
> - assert(!(Alignment & (Alignment - 1)) && "Alignment must be a power of two.");
> -
> - uintptr_t RequiredSize = Alignment * ((Size + Alignment - 1)/Alignment + 1);
> - uintptr_t Addr = 0;
> -
> - // Look in the list of free memory regions and use a block there if one
> - // is available.
> - for (int i = 0, e = MemGroup.FreeMem.size(); i != e; ++i) {
> - sys::MemoryBlock &MB = MemGroup.FreeMem[i];
> - if (MB.size() >= RequiredSize) {
> - 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.
> - MemGroup.FreeMem[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.
> - // Note that all sections get allocated as read-write. The permissions will
> - // be updated later based on memory group.
> - //
> - // FIXME: It would be useful to define a default allocation size (or add
> - // it as a constructor parameter) to minimize the number of allocations.
> - //
> - // FIXME: Initialize the Near member for each memory group to avoid
> - // interleaving.
> - error_code ec;
> - sys::MemoryBlock MB = sys::Memory::allocateMappedMemory(RequiredSize,
> - &MemGroup.Near,
> - sys::Memory::MF_READ |
> - sys::Memory::MF_WRITE,
> - ec);
> - if (ec) {
> - // FIXME: Add error propogation to the interface.
> - return NULL;
> - }
> -
> - // Save this address as the basis for our next request
> - MemGroup.Near = MB;
> -
> - MemGroup.AllocatedMem.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 allocateMappedMemory 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)
> - MemGroup.FreeMem.push_back(sys::MemoryBlock((void*)(Addr + Size), FreeSize));
> -
> - // Return aligned address
> - return (uint8_t*)Addr;
> -}
> -
> -bool SectionMemoryManager::applyPermissions(std::string *ErrMsg)
> -{
> - // FIXME: Should in-progress permissions be reverted if an error occurs?
> - error_code ec;
> -
> - // Make code memory executable.
> - ec = applyMemoryGroupPermissions(CodeMem,
> - sys::Memory::MF_READ | sys::Memory::MF_EXEC);
> - if (ec) {
> - if (ErrMsg) {
> - *ErrMsg = ec.message();
> - }
> - return true;
> - }
> -
> - // Make read-only data memory read-only.
> - ec = applyMemoryGroupPermissions(RODataMem,
> - sys::Memory::MF_READ | sys::Memory::MF_EXEC);
> - if (ec) {
> - if (ErrMsg) {
> - *ErrMsg = ec.message();
> - }
> - return true;
> - }
> -
> - // Read-write data memory already has the correct permissions
> -
> - return false;
> -}
> -
> -error_code SectionMemoryManager::applyMemoryGroupPermissions(MemoryGroup &MemGroup,
> - unsigned Permissions) {
> -
> - for (int i = 0, e = MemGroup.AllocatedMem.size(); i != e; ++i) {
> - error_code ec;
> - ec = sys::Memory::protectMappedMemory(MemGroup.AllocatedMem[i],
> - Permissions);
> - if (ec) {
> - return ec;
> - }
> - }
> -
> - return error_code::success();
> -}
> -
> -void SectionMemoryManager::invalidateInstructionCache() {
> - for (int i = 0, e = CodeMem.AllocatedMem.size(); i != e; ++i)
> - sys::Memory::InvalidateInstructionCache(CodeMem.AllocatedMem[i].base(),
> - CodeMem.AllocatedMem[i].size());
> -}
> -
> -static int jit_noop() {
> - return 0;
> -}
> -
> -void *SectionMemoryManager::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__
> -
> - // We should not invoke parent's ctors/dtors from generated main()!
> - // On Mingw and Cygwin, the symbol __main is resolved to
> - // callee's(eg. tools/lli) one, to invoke wrong duplicated ctors
> - // (and register wrong callee's dtors with atexit(3)).
> - // We expect ExecutionEngine::runStaticConstructorsDestructors()
> - // is called before ExecutionEngine::runFunctionAsMain() is called.
> - if (Name == "__main") return (void*)(intptr_t)&jit_noop;
> -
> - 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;
> -}
> -
> -SectionMemoryManager::~SectionMemoryManager() {
> - for (unsigned i = 0, e = CodeMem.AllocatedMem.size(); i != e; ++i)
> - sys::Memory::releaseMappedMemory(CodeMem.AllocatedMem[i]);
> - for (unsigned i = 0, e = RWDataMem.AllocatedMem.size(); i != e; ++i)
> - sys::Memory::releaseMappedMemory(RWDataMem.AllocatedMem[i]);
> - for (unsigned i = 0, e = RODataMem.AllocatedMem.size(); i != e; ++i)
> - sys::Memory::releaseMappedMemory(RODataMem.AllocatedMem[i]);
> -}
> -
> -} // namespace llvm
> -
>
> Removed: llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h?rev=168720&view=auto
> ==============================================================================
> --- llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h (original)
> +++ llvm/trunk/unittests/ExecutionEngine/MCJIT/SectionMemoryManager.h (removed)
> @@ -1,176 +0,0 @@
> -//===- SectionMemoryManager.h - Memory manager for MCJIT/RtDyld -*- C++ -*-===//
> -//
> -// The LLVM Compiler Infrastructure
> -//
> -// This file is distributed under the University of Illinois Open Source
> -// License. See LICENSE.TXT for details.
> -//
> -//===----------------------------------------------------------------------===//
> -//
> -// This file contains the declaration of a section-based memory manager used by
> -// the MCJIT execution engine and RuntimeDyld.
> -//
> -//===----------------------------------------------------------------------===//
> -
> -#ifndef LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> -#define LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> -
> -#include "llvm/ADT/SmallVector.h"
> -#include "llvm/ExecutionEngine/JITMemoryManager.h"
> -#include "llvm/Support/ErrorHandling.h"
> -#include "llvm/Support/Memory.h"
> -
> -namespace llvm {
> -
> -/// This is a simple memory manager which implements the methods called by
> -/// the RuntimeDyld class to allocate memory for section-based loading of
> -/// objects, usually those generated by the MCJIT execution engine.
> -///
> -/// This memory manager allocates all section memory as read-write. The
> -/// RuntimeDyld will copy JITed section memory into these allocated blocks
> -/// and perform any necessary linking and relocations.
> -///
> -/// Any client using this memory manager MUST ensure that section-specific
> -/// page permissions have been applied before attempting to execute functions
> -/// in the JITed object. Permissions can be applied either by calling
> -/// MCJIT::finalizeObject or by calling SectionMemoryManager::applyPermissions
> -/// directly. Clients of MCJIT should call MCJIT::finalizeObject.
> -class SectionMemoryManager : public JITMemoryManager {
> - SectionMemoryManager(const SectionMemoryManager&) LLVM_DELETED_FUNCTION;
> - void operator=(const SectionMemoryManager&) LLVM_DELETED_FUNCTION;
> -
> -public:
> - SectionMemoryManager() { }
> - virtual ~SectionMemoryManager();
> -
> - /// \brief Allocates a memory block of (at least) the given size suitable for
> - /// executable code.
> - ///
> - /// The value of \p Alignment must be a power of two. If \p Alignment is zero
> - /// a default alignment of 16 will be used.
> - virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment,
> - unsigned SectionID);
> -
> - /// \brief Allocates a memory block of (at least) the given size suitable for
> - /// executable code.
> - ///
> - /// The value of \p Alignment must be a power of two. If \p Alignment is zero
> - /// a default alignment of 16 will be used.
> - virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
> - unsigned SectionID,
> - bool isReadOnly);
> -
> - /// \brief Applies section-specific memory permissions.
> - ///
> - /// This method is called when object loading is complete and section page
> - /// permissions can be applied. It is up to the memory manager implementation
> - /// to decide whether or not to act on this method. The memory manager will
> - /// typically allocate all sections as read-write and then apply specific
> - /// permissions when this method is called. Code sections cannot be executed
> - /// until this function has been called.
> - ///
> - /// \returns true if an error occurred, false otherwise.
> - virtual bool applyPermissions(std::string *ErrMsg = 0);
> -
> - /// This method returns the address of the specified function. As such it is
> - /// only useful for resolving library symbols, not code generated symbols.
> - ///
> - /// If \p AbortOnFailure is false and no function with the given name is
> - /// found, this function returns a null pointer. Otherwise, it prints a
> - /// message to stderr and aborts.
> - virtual void *getPointerToNamedFunction(const std::string &Name,
> - bool AbortOnFailure = true);
> -
> - /// \brief 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.
> - ///
> - /// This method is not called by RuntimeDyld or MCJIT during the load
> - /// process. Clients may call this function when needed. See the lli
> - /// tool for example use.
> - virtual void invalidateInstructionCache();
> -
> -private:
> - struct MemoryGroup {
> - SmallVector<sys::MemoryBlock, 16> AllocatedMem;
> - SmallVector<sys::MemoryBlock, 16> FreeMem;
> - sys::MemoryBlock Near;
> - };
> -
> - uint8_t *allocateSection(MemoryGroup &MemGroup, uintptr_t Size,
> - unsigned Alignment);
> -
> - error_code applyMemoryGroupPermissions(MemoryGroup &MemGroup,
> - unsigned Permissions);
> -
> - MemoryGroup CodeMem;
> - MemoryGroup RWDataMem;
> - MemoryGroup RODataMem;
> -
> -public:
> - ///
> - /// Functions below are not used by MCJIT or RuntimeDyld, but must be
> - /// implemented because they are declared as pure virtuals in the base class.
> - ///
> -
> - 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!");
> - }
> -};
> -
> -}
> -
> -#endif // LLVM_EXECUTION_ENGINE_SECTION_MEMORY_MANAGER_H
> -
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
More information about the llvm-commits
mailing list