Skip to content

Commit

Permalink
[llvm-jitlink] Add optional slab allocator for testing locality optim…
Browse files Browse the repository at this point in the history
…izations.

The llvm-jitlink utility now accepts a '-slab-allocate <size>' 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 <size> 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.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@371244 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
lhames committed Sep 6, 2019
1 parent 50eeea1 commit afaf30a
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 2 deletions.
178 changes: 177 additions & 1 deletion tools/llvm-jitlink/llvm-jitlink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -99,6 +100,13 @@ static cl::opt<bool> ShowTimes("show-times",
cl::desc("Show times for llvm-jitlink phases"),
cl::init(false));

static cl::opt<std::string> 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<bool> ShowRelocatedSectionContents(
"show-relocated-section-contents",
cl::desc("show section contents after fixups have been applied"),
Expand Down Expand Up @@ -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<std::unique_ptr<JITLinkSlabAllocator>>
Create(uint64_t SlabSize) {
Error Err = Error::success();
std::unique_ptr<JITLinkSlabAllocator> Allocator(
new JITLinkSlabAllocator(SlabSize, Err));
if (Err)
return std::move(Err);
return std::move(Allocator);
}

Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>>
allocate(const SegmentsRequestMap &Request) override {

using AllocationMap = DenseMap<unsigned, sys::MemoryBlock>;

// Local class for allocation.
class IPMMAlloc : public Allocation {
public:
IPMMAlloc(AllocationMap SegBlocks) : SegBlocks(std::move(SegBlocks)) {}
MutableArrayRef<char> getWorkingMemory(ProtectionFlags Seg) override {
assert(SegBlocks.count(Seg) && "No allocation for segment");
return {static_cast<char *>(SegBlocks[Seg].base()),
SegBlocks[Seg].allocatedSize()};
}
JITTargetAddress getTargetMemory(ProtectionFlags Seg) override {
assert(SegBlocks.count(Seg) && "No allocation for segment");
return reinterpret_cast<JITTargetAddress>(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<StringError>("Cannot request higher than page "
"alignment",
inconvertibleErrorCode());

if (PageSize % Seg.getContentAlignment() != 0)
return make_error<StringError>("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<StringError>("Slab allocator out of memory",
inconvertibleErrorCode());

sys::MemoryBlock SegMem(SlabBase, SegmentSize);
SlabRemaining =
sys::MemoryBlock(reinterpret_cast<char *>(SlabBase) + SegmentSize,
SlabRemainingSize - SegmentSize);

// Zero out the zero-fill memory.
memset(static_cast<char *>(SegMem.base()) + ZeroFillStart, 0,
Seg.getZeroFillSize());

// Record the block for this segment.
Blocks[KV.first] = std::move(SegMem);
}
return std::unique_ptr<InProcessMemoryManager::Allocation>(
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<StringError>("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::ProtectionFlags>(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<uint64_t> 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<StringError>("Invalid numeric format for slab size",
inconvertibleErrorCode());

return SlabSize * Units;
}

static std::unique_ptr<jitlink::JITLinkMemoryManager> createMemoryManager() {
if (!SlabAllocateSizeString.empty()) {
auto SlabSize = ExitOnErr(getSlabAllocSize(SlabAllocateSizeString));
return ExitOnErr(JITLinkSlabAllocator::Create(SlabSize));
}
return std::make_unique<jitlink::InProcessMemoryManager>();
}

Session::Session(Triple TT)
: MemMgr(createMemoryManager()), ObjLayer(ES, *MemMgr), TT(std::move(TT)) {

/// Local ObjectLinkingLayer::Plugin class to forward modifyPassConfig to the
/// Session.
Expand Down
2 changes: 1 addition & 1 deletion tools/llvm-jitlink/llvm-jitlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace llvm {

struct Session {
orc::ExecutionSession ES;
jitlink::InProcessMemoryManager MemMgr;
std::unique_ptr<jitlink::JITLinkMemoryManager> MemMgr;
orc::ObjectLinkingLayer ObjLayer;
std::vector<orc::JITDylib *> JDSearchOrder;
Triple TT;
Expand Down

0 comments on commit afaf30a

Please sign in to comment.