diff options
Diffstat (limited to 'llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp')
| -rw-r--r-- | llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp | 579 |
1 files changed, 486 insertions, 93 deletions
diff --git a/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp index 36067ccf2753..831b9b26d2fd 100644 --- a/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp @@ -1,135 +1,528 @@ //===--- JITLinkMemoryManager.cpp - JITLinkMemoryManager implementation ---===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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/JITLink/JITLinkMemoryManager.h" +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Process.h" +#define DEBUG_TYPE "jitlink" + +using namespace llvm; + +namespace { + +// FIXME: Remove this copy of CWrapperFunctionResult as soon as JITLink can +// depend on shared utils from Orc. + +// Must be kept in-sync with compiler-rt/lib/orc/c-api.h. +union CWrapperFunctionResultDataUnion { + char *ValuePtr; + char Value[sizeof(ValuePtr)]; +}; + +// Must be kept in-sync with compiler-rt/lib/orc/c-api.h. +typedef struct { + CWrapperFunctionResultDataUnion Data; + size_t Size; +} CWrapperFunctionResult; + +Error toError(CWrapperFunctionResult R) { + bool HasError = false; + std::string ErrMsg; + if (R.Size) { + bool Large = R.Size > sizeof(CWrapperFunctionResultDataUnion); + char *Content = Large ? R.Data.ValuePtr : R.Data.Value; + if (Content[0]) { + HasError = true; + constexpr unsigned StrStart = 1 + sizeof(uint64_t); + ErrMsg.resize(R.Size - StrStart); + memcpy(&ErrMsg[0], Content + StrStart, R.Size - StrStart); + } + if (Large) + free(R.Data.ValuePtr); + } else if (R.Data.ValuePtr) { + HasError = true; + ErrMsg = R.Data.ValuePtr; + free(R.Data.ValuePtr); + } + + if (HasError) + return make_error<StringError>(std::move(ErrMsg), inconvertibleErrorCode()); + return Error::success(); +} +} // namespace + namespace llvm { namespace jitlink { JITLinkMemoryManager::~JITLinkMemoryManager() = default; -JITLinkMemoryManager::Allocation::~Allocation() = default; +JITLinkMemoryManager::InFlightAlloc::~InFlightAlloc() = default; + +static Error runAllocAction(JITLinkMemoryManager::AllocActionCall &C) { + using WrapperFnTy = CWrapperFunctionResult (*)(const void *, size_t); + auto *Fn = jitTargetAddressToPointer<WrapperFnTy>(C.FnAddr); + + return toError(Fn(jitTargetAddressToPointer<const void *>(C.CtxAddr), + static_cast<size_t>(C.CtxSize))); +} + +BasicLayout::BasicLayout(LinkGraph &G) : G(G) { + + for (auto &Sec : G.sections()) { + // Skip empty sections. + if (empty(Sec.blocks())) + continue; + + auto &Seg = Segments[{Sec.getMemProt(), Sec.getMemDeallocPolicy()}]; + for (auto *B : Sec.blocks()) + if (LLVM_LIKELY(!B->isZeroFill())) + Seg.ContentBlocks.push_back(B); + else + Seg.ZeroFillBlocks.push_back(B); + } + + // Build Segments map. + auto CompareBlocks = [](const Block *LHS, const Block *RHS) { + // Sort by section, address and size + if (LHS->getSection().getOrdinal() != RHS->getSection().getOrdinal()) + return LHS->getSection().getOrdinal() < RHS->getSection().getOrdinal(); + if (LHS->getAddress() != RHS->getAddress()) + return LHS->getAddress() < RHS->getAddress(); + return LHS->getSize() < RHS->getSize(); + }; + + LLVM_DEBUG(dbgs() << "Generated BasicLayout for " << G.getName() << ":\n"); + for (auto &KV : Segments) { + auto &Seg = KV.second; + + llvm::sort(Seg.ContentBlocks, CompareBlocks); + llvm::sort(Seg.ZeroFillBlocks, CompareBlocks); + + for (auto *B : Seg.ContentBlocks) { + Seg.ContentSize = alignToBlock(Seg.ContentSize, *B); + Seg.ContentSize += B->getSize(); + Seg.Alignment = std::max(Seg.Alignment, Align(B->getAlignment())); + } + + uint64_t SegEndOffset = Seg.ContentSize; + for (auto *B : Seg.ZeroFillBlocks) { + SegEndOffset = alignToBlock(SegEndOffset, *B); + SegEndOffset += B->getSize(); + Seg.Alignment = std::max(Seg.Alignment, Align(B->getAlignment())); + } + Seg.ZeroFillSize = SegEndOffset - Seg.ContentSize; + + LLVM_DEBUG({ + dbgs() << " Seg " << KV.first + << ": content-size=" << formatv("{0:x}", Seg.ContentSize) + << ", zero-fill-size=" << formatv("{0:x}", Seg.ZeroFillSize) + << ", align=" << formatv("{0:x}", Seg.Alignment.value()) << "\n"; + }); + } +} + +Expected<BasicLayout::ContiguousPageBasedLayoutSizes> +BasicLayout::getContiguousPageBasedLayoutSizes(uint64_t PageSize) { + ContiguousPageBasedLayoutSizes SegsSizes; + + for (auto &KV : segments()) { + auto &AG = KV.first; + auto &Seg = KV.second; + + if (Seg.Alignment > PageSize) + return make_error<StringError>("Segment alignment greater than page size", + inconvertibleErrorCode()); + + uint64_t SegSize = alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize); + if (AG.getMemDeallocPolicy() == MemDeallocPolicy::Standard) + SegsSizes.StandardSegs += SegSize; + else + SegsSizes.FinalizeSegs += SegSize; + } + + return SegsSizes; +} + +Error BasicLayout::apply() { + for (auto &KV : Segments) { + auto &Seg = KV.second; + + assert(!(Seg.ContentBlocks.empty() && Seg.ZeroFillBlocks.empty()) && + "Empty section recorded?"); + + for (auto *B : Seg.ContentBlocks) { + // Align addr and working-mem-offset. + Seg.Addr = alignToBlock(Seg.Addr, *B); + Seg.NextWorkingMemOffset = alignToBlock(Seg.NextWorkingMemOffset, *B); + + // Update block addr. + B->setAddress(Seg.Addr); + Seg.Addr += B->getSize(); + + // Copy content to working memory, then update content to point at working + // memory. + memcpy(Seg.WorkingMem + Seg.NextWorkingMemOffset, B->getContent().data(), + B->getSize()); + B->setMutableContent( + {Seg.WorkingMem + Seg.NextWorkingMemOffset, B->getSize()}); + Seg.NextWorkingMemOffset += B->getSize(); + } + + for (auto *B : Seg.ZeroFillBlocks) { + // Align addr. + Seg.Addr = alignToBlock(Seg.Addr, *B); + // Update block addr. + B->setAddress(Seg.Addr); + Seg.Addr += B->getSize(); + } + + Seg.ContentBlocks.clear(); + Seg.ZeroFillBlocks.clear(); + } + + return Error::success(); +} + +JITLinkMemoryManager::AllocActions &BasicLayout::graphAllocActions() { + return G.allocActions(); +} + +void SimpleSegmentAlloc::Create(JITLinkMemoryManager &MemMgr, + const JITLinkDylib *JD, SegmentMap Segments, + OnCreatedFunction OnCreated) { -Expected<std::unique_ptr<JITLinkMemoryManager::Allocation>> -InProcessMemoryManager::allocate(const JITLinkDylib *JD, - const SegmentsRequestMap &Request) { + static_assert(AllocGroup::NumGroups == 16, + "AllocGroup has changed. Section names below must be updated"); + StringRef AGSectionNames[] = { + "__---.standard", "__R--.standard", "__-W-.standard", "__RW-.standard", + "__--X.standard", "__R-X.standard", "__-WX.standard", "__RWX.standard", + "__---.finalize", "__R--.finalize", "__-W-.finalize", "__RW-.finalize", + "__--X.finalize", "__R-X.finalize", "__-WX.finalize", "__RWX.finalize"}; - using AllocationMap = DenseMap<unsigned, sys::MemoryBlock>; + auto G = + std::make_unique<LinkGraph>("", Triple(), 0, support::native, nullptr); + AllocGroupSmallMap<Block *> ContentBlocks; - // 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 NextAddr = 0x100000; + for (auto &KV : Segments) { + auto &AG = KV.first; + auto &Seg = KV.second; + + auto AGSectionName = + AGSectionNames[static_cast<unsigned>(AG.getMemProt()) | + static_cast<bool>(AG.getMemDeallocPolicy()) << 3]; + + auto &Sec = G->createSection(AGSectionName, AG.getMemProt()); + Sec.setMemDeallocPolicy(AG.getMemDeallocPolicy()); + + if (Seg.ContentSize != 0) { + NextAddr = alignTo(NextAddr, Seg.ContentAlign); + auto &B = + G->createMutableContentBlock(Sec, G->allocateBuffer(Seg.ContentSize), + NextAddr, Seg.ContentAlign.value(), 0); + ContentBlocks[AG] = &B; + NextAddr += Seg.ContentSize; } - JITTargetAddress getTargetMemory(ProtectionFlags Seg) override { - assert(SegBlocks.count(Seg) && "No allocation for segment"); - return pointerToJITTargetAddress(SegBlocks[Seg].base()); + } + + // GRef declared separately since order-of-argument-eval isn't specified. + auto &GRef = *G; + MemMgr.allocate(JD, GRef, + [G = std::move(G), ContentBlocks = std::move(ContentBlocks), + OnCreated = std::move(OnCreated)]( + JITLinkMemoryManager::AllocResult Alloc) mutable { + if (!Alloc) + OnCreated(Alloc.takeError()); + else + OnCreated(SimpleSegmentAlloc(std::move(G), + std::move(ContentBlocks), + std::move(*Alloc))); + }); +} + +Expected<SimpleSegmentAlloc> +SimpleSegmentAlloc::Create(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD, + SegmentMap Segments) { + std::promise<MSVCPExpected<SimpleSegmentAlloc>> AllocP; + auto AllocF = AllocP.get_future(); + Create(MemMgr, JD, std::move(Segments), + [&](Expected<SimpleSegmentAlloc> Result) { + AllocP.set_value(std::move(Result)); + }); + return AllocF.get(); +} + +SimpleSegmentAlloc::SimpleSegmentAlloc(SimpleSegmentAlloc &&) = default; +SimpleSegmentAlloc & +SimpleSegmentAlloc::operator=(SimpleSegmentAlloc &&) = default; +SimpleSegmentAlloc::~SimpleSegmentAlloc() {} + +SimpleSegmentAlloc::SegmentInfo SimpleSegmentAlloc::getSegInfo(AllocGroup AG) { + auto I = ContentBlocks.find(AG); + if (I != ContentBlocks.end()) { + auto &B = *I->second; + return {B.getAddress(), B.getAlreadyMutableContent()}; + } + return {}; +} + +SimpleSegmentAlloc::SimpleSegmentAlloc( + std::unique_ptr<LinkGraph> G, AllocGroupSmallMap<Block *> ContentBlocks, + std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc) + : G(std::move(G)), ContentBlocks(std::move(ContentBlocks)), + Alloc(std::move(Alloc)) {} + +class InProcessMemoryManager::IPInFlightAlloc + : public JITLinkMemoryManager::InFlightAlloc { +public: + IPInFlightAlloc(InProcessMemoryManager &MemMgr, LinkGraph &G, BasicLayout BL, + sys::MemoryBlock StandardSegments, + sys::MemoryBlock FinalizationSegments) + : MemMgr(MemMgr), G(G), BL(std::move(BL)), + StandardSegments(std::move(StandardSegments)), + FinalizationSegments(std::move(FinalizationSegments)) {} + + void finalize(OnFinalizedFunction OnFinalized) override { + + // Apply memory protections to all segments. + if (auto Err = applyProtections()) { + OnFinalized(std::move(Err)); + return; } - void finalizeAsync(FinalizeContinuation OnFinalize) override { - OnFinalize(applyProtections()); + + // Run finalization actions. + // FIXME: Roll back previous successful actions on failure. + std::vector<AllocActionCall> DeallocActions; + DeallocActions.reserve(G.allocActions().size()); + for (auto &ActPair : G.allocActions()) { + if (ActPair.Finalize.FnAddr) + if (auto Err = runAllocAction(ActPair.Finalize)) { + OnFinalized(std::move(Err)); + return; + } + if (ActPair.Dealloc.FnAddr) + DeallocActions.push_back(ActPair.Dealloc); } - Error deallocate() override { - if (SegBlocks.empty()) - return Error::success(); - void *SlabStart = SegBlocks.begin()->second.base(); - char *SlabEnd = (char *)SlabStart; - for (auto &KV : SegBlocks) { - SlabStart = std::min(SlabStart, KV.second.base()); - SlabEnd = std::max(SlabEnd, (char *)(KV.second.base()) + - KV.second.allocatedSize()); - } - size_t SlabSize = SlabEnd - (char *)SlabStart; - assert((SlabSize % sys::Process::getPageSizeEstimate()) == 0 && - "Slab size is not a multiple of page size"); - sys::MemoryBlock Slab(SlabStart, SlabSize); - if (auto EC = sys::Memory::releaseMappedMemory(Slab)) + G.allocActions().clear(); + + // Release the finalize segments slab. + if (auto EC = sys::Memory::releaseMappedMemory(FinalizationSegments)) { + OnFinalized(errorCodeToError(EC)); + return; + } + + // Continue with finalized allocation. + OnFinalized(MemMgr.createFinalizedAlloc(std::move(StandardSegments), + std::move(DeallocActions))); + } + + void abandon(OnAbandonedFunction OnAbandoned) override { + Error Err = Error::success(); + if (auto EC = sys::Memory::releaseMappedMemory(FinalizationSegments)) + Err = joinErrors(std::move(Err), errorCodeToError(EC)); + if (auto EC = sys::Memory::releaseMappedMemory(StandardSegments)) + Err = joinErrors(std::move(Err), errorCodeToError(EC)); + OnAbandoned(std::move(Err)); + } + +private: + Error applyProtections() { + for (auto &KV : BL.segments()) { + const auto &AG = KV.first; + auto &Seg = KV.second; + + auto Prot = toSysMemoryProtectionFlags(AG.getMemProt()); + + uint64_t SegSize = + alignTo(Seg.ContentSize + Seg.ZeroFillSize, MemMgr.PageSize); + sys::MemoryBlock MB(Seg.WorkingMem, SegSize); + if (auto EC = sys::Memory::protectMappedMemory(MB, Prot)) return errorCodeToError(EC); - return Error::success(); + if (Prot & sys::Memory::MF_EXEC) + sys::Memory::InvalidateInstructionCache(MB.base(), MB.allocatedSize()); } + 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(); + InProcessMemoryManager &MemMgr; + LinkGraph &G; + BasicLayout BL; + sys::MemoryBlock StandardSegments; + sys::MemoryBlock FinalizationSegments; +}; + +Expected<std::unique_ptr<InProcessMemoryManager>> +InProcessMemoryManager::Create() { + if (auto PageSize = sys::Process::getPageSize()) + return std::make_unique<InProcessMemoryManager>(*PageSize); + else + return PageSize.takeError(); +} + +void InProcessMemoryManager::allocate(const JITLinkDylib *JD, LinkGraph &G, + OnAllocatedFunction OnAllocated) { + + // FIXME: Just check this once on startup. + if (!isPowerOf2_64((uint64_t)PageSize)) { + OnAllocated(make_error<StringError>("Page size is not a power of 2", + inconvertibleErrorCode())); + return; + } + + BasicLayout BL(G); + + /// Scan the request and calculate the group and total sizes. + /// Check that segment size is no larger than a page. + auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(PageSize); + if (!SegsSizes) { + OnAllocated(SegsSizes.takeError()); + return; + } + + /// Check that the total size requested (including zero fill) is not larger + /// than a size_t. + if (SegsSizes->total() > std::numeric_limits<size_t>::max()) { + OnAllocated(make_error<JITLinkError>( + "Total requested size " + formatv("{0:x}", SegsSizes->total()) + + " for graph " + G.getName() + " exceeds address space")); + return; + } + + // Allocate one slab for the whole thing (to make sure everything is + // in-range), then partition into standard and finalization blocks. + // + // FIXME: Make two separate allocations in the future to reduce + // fragmentation: finalization segments will usually be a single page, and + // standard segments are likely to be more than one page. Where multiple + // allocations are in-flight at once (likely) the current approach will leave + // a lot of single-page holes. + sys::MemoryBlock Slab; + sys::MemoryBlock StandardSegsMem; + sys::MemoryBlock FinalizeSegsMem; + { + const sys::Memory::ProtectionFlags ReadWrite = + static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ | + sys::Memory::MF_WRITE); + + std::error_code EC; + Slab = sys::Memory::allocateMappedMemory(SegsSizes->total(), nullptr, + ReadWrite, EC); + + if (EC) { + OnAllocated(errorCodeToError(EC)); + return; } - AllocationMap SegBlocks; - }; + // Zero-fill the whole slab up-front. + memset(Slab.base(), 0, Slab.allocatedSize()); + + StandardSegsMem = {Slab.base(), + static_cast<size_t>(SegsSizes->StandardSegs)}; + FinalizeSegsMem = {(void *)((char *)Slab.base() + SegsSizes->StandardSegs), + static_cast<size_t>(SegsSizes->FinalizeSegs)}; + } - if (!isPowerOf2_64((uint64_t)sys::Process::getPageSizeEstimate())) - return make_error<StringError>("Page size is not a power of 2", - inconvertibleErrorCode()); + auto NextStandardSegAddr = pointerToJITTargetAddress(StandardSegsMem.base()); + auto NextFinalizeSegAddr = pointerToJITTargetAddress(FinalizeSegsMem.base()); - AllocationMap Blocks; - const sys::Memory::ProtectionFlags ReadWrite = - static_cast<sys::Memory::ProtectionFlags>(sys::Memory::MF_READ | - sys::Memory::MF_WRITE); + LLVM_DEBUG({ + dbgs() << "InProcessMemoryManager allocated:\n"; + if (SegsSizes->StandardSegs) + dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextStandardSegAddr, + NextStandardSegAddr + StandardSegsMem.allocatedSize()) + << " to stardard segs\n"; + else + dbgs() << " no standard segs\n"; + if (SegsSizes->FinalizeSegs) + dbgs() << formatv(" [ {0:x16} -- {1:x16} ]", NextFinalizeSegAddr, + NextFinalizeSegAddr + FinalizeSegsMem.allocatedSize()) + << " to finalize segs\n"; + else + dbgs() << " no finalize segs\n"; + }); - // Compute the total number of pages to allocate. - size_t TotalSize = 0; - for (auto &KV : Request) { - const auto &Seg = KV.second; + // Build ProtMap, assign addresses. + for (auto &KV : BL.segments()) { + auto &AG = KV.first; + auto &Seg = KV.second; - if (Seg.getAlignment() > sys::Process::getPageSizeEstimate()) - return make_error<StringError>("Cannot request higher than page " - "alignment", - inconvertibleErrorCode()); + auto &SegAddr = (AG.getMemDeallocPolicy() == MemDeallocPolicy::Standard) + ? NextStandardSegAddr + : NextFinalizeSegAddr; - TotalSize = alignTo(TotalSize, sys::Process::getPageSizeEstimate()); - TotalSize += Seg.getContentSize(); - TotalSize += Seg.getZeroFillSize(); + Seg.WorkingMem = jitTargetAddressToPointer<char *>(SegAddr); + Seg.Addr = SegAddr; + + SegAddr += alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize); } - // Allocate one slab to cover all the segments. - std::error_code EC; - auto SlabRemaining = - sys::Memory::allocateMappedMemory(TotalSize, nullptr, ReadWrite, EC); + if (auto Err = BL.apply()) { + OnAllocated(std::move(Err)); + return; + } - if (EC) - return errorCodeToError(EC); + OnAllocated(std::make_unique<IPInFlightAlloc>(*this, G, std::move(BL), + std::move(StandardSegsMem), + std::move(FinalizeSegsMem))); +} - // Allocate segment memory from the slab. - for (auto &KV : Request) { +void InProcessMemoryManager::deallocate(std::vector<FinalizedAlloc> Allocs, + OnDeallocatedFunction OnDeallocated) { + std::vector<sys::MemoryBlock> StandardSegmentsList; + std::vector<std::vector<AllocActionCall>> DeallocActionsList; - const auto &Seg = KV.second; + { + std::lock_guard<std::mutex> Lock(FinalizedAllocsMutex); + for (auto &Alloc : Allocs) { + auto *FA = + jitTargetAddressToPointer<FinalizedAllocInfo *>(Alloc.release()); + StandardSegmentsList.push_back(std::move(FA->StandardSegments)); + if (!FA->DeallocActions.empty()) + DeallocActionsList.push_back(std::move(FA->DeallocActions)); + FA->~FinalizedAllocInfo(); + FinalizedAllocInfos.Deallocate(FA); + } + } + + Error DeallocErr = Error::success(); - uint64_t SegmentSize = alignTo(Seg.getContentSize() + Seg.getZeroFillSize(), - sys::Process::getPageSizeEstimate()); - assert(SlabRemaining.allocatedSize() >= SegmentSize && - "Mapping exceeds allocation"); + while (!DeallocActionsList.empty()) { + auto &DeallocActions = DeallocActionsList.back(); + auto &StandardSegments = StandardSegmentsList.back(); - sys::MemoryBlock SegMem(SlabRemaining.base(), SegmentSize); - SlabRemaining = sys::MemoryBlock((char *)SlabRemaining.base() + SegmentSize, - SlabRemaining.allocatedSize() - SegmentSize); + /// Run any deallocate calls. + while (!DeallocActions.empty()) { + if (auto Err = runAllocAction(DeallocActions.back())) + DeallocErr = joinErrors(std::move(DeallocErr), std::move(Err)); + DeallocActions.pop_back(); + } - // Zero out the zero-fill memory. - memset(static_cast<char *>(SegMem.base()) + Seg.getContentSize(), 0, - Seg.getZeroFillSize()); + /// Release the standard segments slab. + if (auto EC = sys::Memory::releaseMappedMemory(StandardSegments)) + DeallocErr = joinErrors(std::move(DeallocErr), errorCodeToError(EC)); - // Record the block for this segment. - Blocks[KV.first] = std::move(SegMem); + DeallocActionsList.pop_back(); + StandardSegmentsList.pop_back(); } - return std::unique_ptr<InProcessMemoryManager::Allocation>( - new IPMMAlloc(std::move(Blocks))); + OnDeallocated(std::move(DeallocErr)); +} + +JITLinkMemoryManager::FinalizedAlloc +InProcessMemoryManager::createFinalizedAlloc( + sys::MemoryBlock StandardSegments, + std::vector<AllocActionCall> DeallocActions) { + std::lock_guard<std::mutex> Lock(FinalizedAllocsMutex); + auto *FA = FinalizedAllocInfos.Allocate<FinalizedAllocInfo>(); + new (FA) FinalizedAllocInfo( + {std::move(StandardSegments), std::move(DeallocActions)}); + return FinalizedAlloc(pointerToJITTargetAddress(FA)); } } // end namespace jitlink |
