From 335676ee62167c021c17b90489b49c1015116556 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Fri, 6 Sep 2019 19:21:55 +0000 Subject: [PATCH] [llvm-jitlink] Add optional slab allocator for testing locality optimizations. The llvm-jitlink utility now accepts a '-slab-allocate ' option. If given, llvm-jitlink will use a slab-based memory manager rather than the default InProcessMemoryManager. Using a slab allocator will allow reliable testing of future locality based optimizations (e.g. PLT and GOT elimination) in JITLink. The argument is a number, optionally followed by a units specifier (Kb, Mb, or Gb). If the units are not given then the number is assumed to be in Kb. llvm-svn: 371244 --- llvm/tools/llvm-jitlink/llvm-jitlink.cpp | 178 ++++++++++++++++++++++++++++++- llvm/tools/llvm-jitlink/llvm-jitlink.h | 2 +- 2 files changed, 178 insertions(+), 2 deletions(-) diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp index 64b8692..dfee972 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Timer.h" @@ -99,6 +100,13 @@ static cl::opt ShowTimes("show-times", cl::desc("Show times for llvm-jitlink phases"), cl::init(false)); +static cl::opt SlabAllocateSizeString( + "slab-allocate", + cl::desc("Allocate from a slab of the given size " + "(allowable suffixes: Kb, Mb, Gb. default = " + "Kb)"), + cl::init("")); + static cl::opt ShowRelocatedSectionContents( "show-relocated-section-contents", cl::desc("show section contents after fixups have been applied"), @@ -221,7 +229,175 @@ static void dumpSectionContents(raw_ostream &OS, AtomGraph &G) { } } -Session::Session(Triple TT) : ObjLayer(ES, MemMgr), TT(std::move(TT)) { +class JITLinkSlabAllocator final : public JITLinkMemoryManager { +public: + static Expected> + Create(uint64_t SlabSize) { + Error Err = Error::success(); + std::unique_ptr Allocator( + new JITLinkSlabAllocator(SlabSize, Err)); + if (Err) + return std::move(Err); + return std::move(Allocator); + } + + Expected> + allocate(const SegmentsRequestMap &Request) override { + + using AllocationMap = DenseMap; + + // Local class for allocation. + class IPMMAlloc : public Allocation { + public: + IPMMAlloc(AllocationMap SegBlocks) : SegBlocks(std::move(SegBlocks)) {} + MutableArrayRef getWorkingMemory(ProtectionFlags Seg) override { + assert(SegBlocks.count(Seg) && "No allocation for segment"); + return {static_cast(SegBlocks[Seg].base()), + SegBlocks[Seg].allocatedSize()}; + } + JITTargetAddress getTargetMemory(ProtectionFlags Seg) override { + assert(SegBlocks.count(Seg) && "No allocation for segment"); + return reinterpret_cast(SegBlocks[Seg].base()); + } + void finalizeAsync(FinalizeContinuation OnFinalize) override { + OnFinalize(applyProtections()); + } + Error deallocate() override { + for (auto &KV : SegBlocks) + if (auto EC = sys::Memory::releaseMappedMemory(KV.second)) + return errorCodeToError(EC); + return Error::success(); + } + + private: + Error applyProtections() { + for (auto &KV : SegBlocks) { + auto &Prot = KV.first; + auto &Block = KV.second; + if (auto EC = sys::Memory::protectMappedMemory(Block, Prot)) + return errorCodeToError(EC); + if (Prot & sys::Memory::MF_EXEC) + sys::Memory::InvalidateInstructionCache(Block.base(), + Block.allocatedSize()); + } + return Error::success(); + } + + AllocationMap SegBlocks; + }; + + AllocationMap Blocks; + + for (auto &KV : Request) { + auto &Seg = KV.second; + + if (Seg.getContentAlignment() > PageSize) + return make_error("Cannot request higher than page " + "alignment", + inconvertibleErrorCode()); + + if (PageSize % Seg.getContentAlignment() != 0) + return make_error("Page size is not a multiple of " + "alignment", + inconvertibleErrorCode()); + + uint64_t ZeroFillStart = + alignTo(Seg.getContentSize(), Seg.getZeroFillAlignment()); + uint64_t SegmentSize = ZeroFillStart + Seg.getZeroFillSize(); + + // Round segment size up to page boundary. + SegmentSize = (SegmentSize + PageSize - 1) & ~(PageSize - 1); + + // Take segment bytes from the front of the slab. + void *SlabBase = SlabRemaining.base(); + uint64_t SlabRemainingSize = SlabRemaining.allocatedSize(); + + if (SegmentSize > SlabRemainingSize) + return make_error("Slab allocator out of memory", + inconvertibleErrorCode()); + + sys::MemoryBlock SegMem(SlabBase, SegmentSize); + SlabRemaining = + sys::MemoryBlock(reinterpret_cast(SlabBase) + SegmentSize, + SlabRemainingSize - SegmentSize); + + // Zero out the zero-fill memory. + memset(static_cast(SegMem.base()) + ZeroFillStart, 0, + Seg.getZeroFillSize()); + + // Record the block for this segment. + Blocks[KV.first] = std::move(SegMem); + } + return std::unique_ptr( + new IPMMAlloc(std::move(Blocks))); + } + +private: + JITLinkSlabAllocator(uint64_t SlabSize, Error &Err) { + ErrorAsOutParameter _(&Err); + + PageSize = sys::Process::getPageSizeEstimate(); + + if (!isPowerOf2_64(PageSize)) { + Err = make_error("Page size is not a power of 2", + inconvertibleErrorCode()); + return; + } + + // Round slab request up to page size. + SlabSize = (SlabSize + PageSize - 1) & ~(PageSize - 1); + + const sys::Memory::ProtectionFlags ReadWrite = + static_cast(sys::Memory::MF_READ | + sys::Memory::MF_WRITE); + + std::error_code EC; + SlabRemaining = + sys::Memory::allocateMappedMemory(SlabSize, nullptr, ReadWrite, EC); + + if (EC) { + Err = errorCodeToError(EC); + return; + } + } + + sys::MemoryBlock SlabRemaining; + uint64_t PageSize = 0; +}; + +Expected getSlabAllocSize(StringRef SizeString) { + SizeString = SizeString.trim(); + + uint64_t Units = 1024; + + if (SizeString.endswith_lower("kb")) + SizeString = SizeString.drop_back(2).rtrim(); + else if (SizeString.endswith_lower("mb")) { + Units = 1024 * 1024; + SizeString = SizeString.drop_back(2).rtrim(); + } else if (SizeString.endswith_lower("gb")) { + Units = 1024 * 1024 * 1024; + SizeString = SizeString.drop_back(2).rtrim(); + } + + uint64_t SlabSize = 0; + if (SizeString.getAsInteger(10, SlabSize)) + return make_error("Invalid numeric format for slab size", + inconvertibleErrorCode()); + + return SlabSize * Units; +} + +static std::unique_ptr createMemoryManager() { + if (!SlabAllocateSizeString.empty()) { + auto SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString)); + return ExitOnErr(JITLinkSlabAllocator::Create(SlabSize)); + } + return std::make_unique(); +} + +Session::Session(Triple TT) + : MemMgr(createMemoryManager()), ObjLayer(ES, *MemMgr), TT(std::move(TT)) { /// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the /// Session. diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.h b/llvm/tools/llvm-jitlink/llvm-jitlink.h index 45c4902..269597a 100644 --- a/llvm/tools/llvm-jitlink/llvm-jitlink.h +++ b/llvm/tools/llvm-jitlink/llvm-jitlink.h @@ -26,7 +26,7 @@ namespace llvm { struct Session { orc::ExecutionSession ES; - jitlink::InProcessMemoryManager MemMgr; + std::unique_ptr MemMgr; orc::ObjectLinkingLayer ObjLayer; std::vector JDSearchOrder; Triple TT; -- 2.7.4