
The JITLinkMemoryManager::InFlightAlloc::abandon method should only abandon memory for the current allocation, not any other allocations. In MapperJITLinkMemoryManager this corresponds to the deinitialize operation, not the deallocate operation (which releases whole slabs of memory that may be shared by many allocations). No testcase: This was spotted by inspection. The failing program was linking concurrently when one linker instance raised an error. Through the call to abandon an entire underlying slab was deallocated, resulting in segfaults in other concurrent links that were sharing that slab.
189 lines
5.6 KiB
C++
189 lines
5.6 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/ExecutionEngine/JITLink/JITLink.h"
|
|
#include "llvm/Support/Process.h"
|
|
|
|
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, [OnFinalize = std::move(OnFinalize)](
|
|
Expected<ExecutorAddr> Result) mutable {
|
|
if (!Result) {
|
|
OnFinalize(Result.takeError());
|
|
return;
|
|
}
|
|
|
|
OnFinalize(FinalizedAlloc(*Result));
|
|
});
|
|
}
|
|
|
|
void abandon(OnAbandonedFunction OnFinalize) override {
|
|
Parent.Mapper->deinitialize({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), AvailableMemory(AMAllocator),
|
|
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();
|
|
|
|
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.AG = AG;
|
|
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)
|
|
AvailableMemory.insert(NextSegAddr, Result->End - 1, true);
|
|
}
|
|
Mutex.unlock();
|
|
|
|
if (auto Err = BL.apply()) {
|
|
OnAllocated(std::move(Err));
|
|
return;
|
|
}
|
|
|
|
OnAllocated(std::make_unique<InFlightAlloc>(*this, G, Result->Start,
|
|
std::move(SegInfos)));
|
|
};
|
|
|
|
Mutex.lock();
|
|
|
|
// find an already reserved range that is large enough
|
|
ExecutorAddrRange SelectedRange{};
|
|
|
|
for (AvailableMemoryMap::iterator It = AvailableMemory.begin();
|
|
It != AvailableMemory.end(); It++) {
|
|
if (It.stop() - It.start() + 1 >= TotalSize) {
|
|
SelectedRange = ExecutorAddrRange(It.start(), It.stop() + 1);
|
|
It.erase();
|
|
break;
|
|
}
|
|
}
|
|
|
|
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::vector<ExecutorAddr> Bases;
|
|
Bases.reserve(Allocs.size());
|
|
for (auto &FA : Allocs) {
|
|
ExecutorAddr Addr = FA.getAddress();
|
|
Bases.push_back(Addr);
|
|
}
|
|
|
|
Mapper->deinitialize(Bases, [this, Allocs = std::move(Allocs),
|
|
OnDeallocated = std::move(OnDeallocated)](
|
|
llvm::Error Err) mutable {
|
|
// TODO: How should we treat memory that we fail to deinitialize?
|
|
// We're currently bailing out and treating it as "burned" -- should we
|
|
// require that a failure to deinitialize still reset the memory so that
|
|
// we can reclaim it?
|
|
if (Err) {
|
|
for (auto &FA : Allocs)
|
|
FA.release();
|
|
OnDeallocated(std::move(Err));
|
|
return;
|
|
}
|
|
|
|
{
|
|
std::lock_guard<std::mutex> Lock(Mutex);
|
|
|
|
for (auto &FA : Allocs) {
|
|
ExecutorAddr Addr = FA.getAddress();
|
|
ExecutorAddrDiff Size = UsedMemory[Addr];
|
|
|
|
UsedMemory.erase(Addr);
|
|
AvailableMemory.insert(Addr, Addr + Size - 1, true);
|
|
|
|
FA.release();
|
|
}
|
|
}
|
|
|
|
OnDeallocated(Error::success());
|
|
});
|
|
}
|
|
|
|
} // end namespace orc
|
|
} // end namespace llvm
|