Calling reserve() used to require an RPC call. This commit allows large ranges of executor address space to be reserved. Subsequent calls to reserve() will return subranges of already reserved address space while there is still space available. Differential Revision: https://reviews.llvm.org/D130392
165 lines
5.0 KiB
C++
165 lines
5.0 KiB
C++
//=== MapperJITLinkMemoryManager.cpp - Memory management with MemoryMapper ===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/ExecutionEngine/Orc/MapperJITLinkMemoryManager.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ExecutionEngine/JITLink/JITLink.h"
|
|
#include "llvm/Support/Process.h"
|
|
|
|
#include <limits>
|
|
|
|
using namespace llvm::jitlink;
|
|
|
|
namespace llvm {
|
|
namespace orc {
|
|
|
|
class MapperJITLinkMemoryManager::InFlightAlloc
|
|
: public JITLinkMemoryManager::InFlightAlloc {
|
|
public:
|
|
InFlightAlloc(MapperJITLinkMemoryManager &Parent, LinkGraph &G,
|
|
ExecutorAddr AllocAddr,
|
|
std::vector<MemoryMapper::AllocInfo::SegInfo> Segs)
|
|
: Parent(Parent), G(G), AllocAddr(AllocAddr), Segs(std::move(Segs)) {}
|
|
|
|
void finalize(OnFinalizedFunction OnFinalize) override {
|
|
MemoryMapper::AllocInfo AI;
|
|
AI.MappingBase = AllocAddr;
|
|
|
|
std::swap(AI.Segments, Segs);
|
|
std::swap(AI.Actions, G.allocActions());
|
|
|
|
Parent.Mapper->initialize(AI, [&](Expected<ExecutorAddr> Result) {
|
|
if (!Result) {
|
|
OnFinalize(Result.takeError());
|
|
return;
|
|
}
|
|
|
|
OnFinalize(FinalizedAlloc(*Result));
|
|
});
|
|
}
|
|
|
|
void abandon(OnAbandonedFunction OnFinalize) override {
|
|
Parent.Mapper->release({AllocAddr}, std::move(OnFinalize));
|
|
}
|
|
|
|
private:
|
|
MapperJITLinkMemoryManager &Parent;
|
|
LinkGraph &G;
|
|
ExecutorAddr AllocAddr;
|
|
std::vector<MemoryMapper::AllocInfo::SegInfo> Segs;
|
|
};
|
|
|
|
MapperJITLinkMemoryManager::MapperJITLinkMemoryManager(
|
|
size_t ReservationGranularity, std::unique_ptr<MemoryMapper> Mapper)
|
|
: ReservationUnits(ReservationGranularity), Mapper(std::move(Mapper)) {}
|
|
|
|
void MapperJITLinkMemoryManager::allocate(const JITLinkDylib *JD, LinkGraph &G,
|
|
OnAllocatedFunction OnAllocated) {
|
|
BasicLayout BL(G);
|
|
|
|
// find required address space
|
|
auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(Mapper->getPageSize());
|
|
if (!SegsSizes) {
|
|
OnAllocated(SegsSizes.takeError());
|
|
return;
|
|
}
|
|
|
|
auto TotalSize = SegsSizes->total();
|
|
|
|
Mutex.lock();
|
|
|
|
// find an already reserved range that is large enough
|
|
ExecutorAddrRange SelectedRange{};
|
|
std::vector<ExecutorAddrRange>::iterator SelectedRangeIt;
|
|
SelectedRangeIt =
|
|
llvm::find_if(AvailableMemory, [TotalSize](ExecutorAddrRange Range) {
|
|
return TotalSize < Range.size();
|
|
});
|
|
if (SelectedRangeIt != AvailableMemory.end()) {
|
|
SelectedRange = *SelectedRangeIt;
|
|
AvailableMemory.erase(SelectedRangeIt);
|
|
}
|
|
|
|
auto CompleteAllocation = [this, &G, BL = std::move(BL),
|
|
OnAllocated = std::move(OnAllocated)](
|
|
Expected<ExecutorAddrRange> Result) mutable {
|
|
if (!Result) {
|
|
Mutex.unlock();
|
|
return OnAllocated(Result.takeError());
|
|
}
|
|
|
|
auto NextSegAddr = Result->Start;
|
|
|
|
std::vector<MemoryMapper::AllocInfo::SegInfo> SegInfos;
|
|
|
|
for (auto &KV : BL.segments()) {
|
|
auto &AG = KV.first;
|
|
auto &Seg = KV.second;
|
|
|
|
auto TotalSize = Seg.ContentSize + Seg.ZeroFillSize;
|
|
|
|
Seg.Addr = NextSegAddr;
|
|
Seg.WorkingMem = Mapper->prepare(NextSegAddr, TotalSize);
|
|
|
|
NextSegAddr += alignTo(TotalSize, Mapper->getPageSize());
|
|
|
|
MemoryMapper::AllocInfo::SegInfo SI;
|
|
SI.Offset = Seg.Addr - Result->Start;
|
|
SI.ContentSize = Seg.ContentSize;
|
|
SI.ZeroFillSize = Seg.ZeroFillSize;
|
|
SI.Prot = toSysMemoryProtectionFlags(AG.getMemProt());
|
|
SI.WorkingMem = Seg.WorkingMem;
|
|
|
|
SegInfos.push_back(SI);
|
|
}
|
|
|
|
UsedMemory.insert({Result->Start, NextSegAddr - Result->Start});
|
|
|
|
if (NextSegAddr < Result->End) {
|
|
// Save the remaining memory for reuse in next allocation(s)
|
|
auto RemainingRange = ExecutorAddrRange(NextSegAddr, Result->End);
|
|
AvailableMemory.push_back(RemainingRange);
|
|
}
|
|
Mutex.unlock();
|
|
|
|
if (auto Err = BL.apply()) {
|
|
OnAllocated(std::move(Err));
|
|
return;
|
|
}
|
|
|
|
OnAllocated(std::make_unique<InFlightAlloc>(*this, G, Result->Start,
|
|
std::move(SegInfos)));
|
|
};
|
|
|
|
if (SelectedRange.empty()) { // no already reserved range was found
|
|
auto TotalAllocation = alignTo(TotalSize, ReservationUnits);
|
|
Mapper->reserve(TotalAllocation, std::move(CompleteAllocation));
|
|
} else {
|
|
CompleteAllocation(SelectedRange);
|
|
}
|
|
}
|
|
|
|
void MapperJITLinkMemoryManager::deallocate(
|
|
std::vector<FinalizedAlloc> Allocs, OnDeallocatedFunction OnDeallocated) {
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
for (auto &FA : Allocs) {
|
|
ExecutorAddr Addr = FA.getAddress();
|
|
ExecutorAddrDiff Size = UsedMemory[Addr];
|
|
UsedMemory.erase(Addr);
|
|
|
|
AvailableMemory.push_back({Addr, Addr + Size});
|
|
FA.release();
|
|
}
|
|
OnDeallocated(Error::success());
|
|
}
|
|
|
|
} // end namespace orc
|
|
} // end namespace llvm
|