| //===---- EPCGenericJITLinkMemoryManager.cpp -- Mem management via EPC ----===// | |
| // | |
| // 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/EPCGenericJITLinkMemoryManager.h" | |
| #include "llvm/ExecutionEngine/JITLink/JITLink.h" | |
| #include "llvm/ExecutionEngine/Orc/LookupAndRecordAddrs.h" | |
| #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" | |
| #include <limits> | |
| using namespace llvm::jitlink; | |
| namespace llvm { | |
| namespace orc { | |
| class EPCGenericJITLinkMemoryManager::InFlightAlloc | |
| : public jitlink::JITLinkMemoryManager::InFlightAlloc { | |
| public: | |
| // FIXME: The C++98 initializer is an attempt to work around compile failures | |
| // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397. | |
| // We should be able to switch this back to member initialization once that | |
| // issue is fixed. | |
| struct SegInfo { | |
| SegInfo() : WorkingMem(nullptr), ContentSize(0), ZeroFillSize(0) {} | |
| char *WorkingMem; | |
| ExecutorAddr Addr; | |
| uint64_t ContentSize; | |
| uint64_t ZeroFillSize; | |
| }; | |
| using SegInfoMap = AllocGroupSmallMap<SegInfo>; | |
| InFlightAlloc(EPCGenericJITLinkMemoryManager &Parent, LinkGraph &G, | |
| ExecutorAddr AllocAddr, SegInfoMap Segs) | |
| : Parent(Parent), G(G), AllocAddr(AllocAddr), Segs(std::move(Segs)) {} | |
| void finalize(OnFinalizedFunction OnFinalize) override { | |
| tpctypes::FinalizeRequest FR; | |
| for (auto &KV : Segs) { | |
| assert(KV.second.ContentSize <= std::numeric_limits<size_t>::max()); | |
| FR.Segments.push_back(tpctypes::SegFinalizeRequest{ | |
| KV.first, | |
| KV.second.Addr, | |
| alignTo(KV.second.ContentSize + KV.second.ZeroFillSize, | |
| Parent.EPC.getPageSize()), | |
| {KV.second.WorkingMem, static_cast<size_t>(KV.second.ContentSize)}}); | |
| } | |
| // Transfer allocation actions. | |
| std::swap(FR.Actions, G.allocActions()); | |
| Parent.EPC.callSPSWrapperAsync< | |
| rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>( | |
| Parent.SAs.Finalize, | |
| [OnFinalize = std::move(OnFinalize), AllocAddr = this->AllocAddr]( | |
| Error SerializationErr, Error FinalizeErr) mutable { | |
| // FIXME: Release abandoned alloc. | |
| if (SerializationErr) { | |
| cantFail(std::move(FinalizeErr)); | |
| OnFinalize(std::move(SerializationErr)); | |
| } else if (FinalizeErr) | |
| OnFinalize(std::move(FinalizeErr)); | |
| else | |
| OnFinalize(FinalizedAlloc(AllocAddr)); | |
| }, | |
| Parent.SAs.Allocator, std::move(FR)); | |
| } | |
| void abandon(OnAbandonedFunction OnAbandoned) override { | |
| // FIXME: Return memory to pool instead. | |
| Parent.EPC.callSPSWrapperAsync< | |
| rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>( | |
| Parent.SAs.Deallocate, | |
| [OnAbandoned = std::move(OnAbandoned)](Error SerializationErr, | |
| Error DeallocateErr) mutable { | |
| if (SerializationErr) { | |
| cantFail(std::move(DeallocateErr)); | |
| OnAbandoned(std::move(SerializationErr)); | |
| } else | |
| OnAbandoned(std::move(DeallocateErr)); | |
| }, | |
| Parent.SAs.Allocator, ArrayRef<ExecutorAddr>(AllocAddr)); | |
| } | |
| private: | |
| EPCGenericJITLinkMemoryManager &Parent; | |
| LinkGraph &G; | |
| ExecutorAddr AllocAddr; | |
| SegInfoMap Segs; | |
| }; | |
| void EPCGenericJITLinkMemoryManager::allocate(const JITLinkDylib *JD, | |
| LinkGraph &G, | |
| OnAllocatedFunction OnAllocated) { | |
| BasicLayout BL(G); | |
| auto Pages = BL.getContiguousPageBasedLayoutSizes(EPC.getPageSize()); | |
| if (!Pages) | |
| return OnAllocated(Pages.takeError()); | |
| EPC.callSPSWrapperAsync<rt::SPSSimpleExecutorMemoryManagerReserveSignature>( | |
| SAs.Reserve, | |
| [this, BL = std::move(BL), OnAllocated = std::move(OnAllocated)]( | |
| Error SerializationErr, Expected<ExecutorAddr> AllocAddr) mutable { | |
| if (SerializationErr) { | |
| cantFail(AllocAddr.takeError()); | |
| return OnAllocated(std::move(SerializationErr)); | |
| } | |
| if (!AllocAddr) | |
| return OnAllocated(AllocAddr.takeError()); | |
| completeAllocation(*AllocAddr, std::move(BL), std::move(OnAllocated)); | |
| }, | |
| SAs.Allocator, Pages->total()); | |
| } | |
| void EPCGenericJITLinkMemoryManager::deallocate( | |
| std::vector<FinalizedAlloc> Allocs, OnDeallocatedFunction OnDeallocated) { | |
| EPC.callSPSWrapperAsync< | |
| rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>( | |
| SAs.Deallocate, | |
| [OnDeallocated = std::move(OnDeallocated)](Error SerErr, | |
| Error DeallocErr) mutable { | |
| if (SerErr) { | |
| cantFail(std::move(DeallocErr)); | |
| OnDeallocated(std::move(SerErr)); | |
| } else | |
| OnDeallocated(std::move(DeallocErr)); | |
| }, | |
| SAs.Allocator, Allocs); | |
| for (auto &A : Allocs) | |
| A.release(); | |
| } | |
| void EPCGenericJITLinkMemoryManager::completeAllocation( | |
| ExecutorAddr AllocAddr, BasicLayout BL, OnAllocatedFunction OnAllocated) { | |
| InFlightAlloc::SegInfoMap SegInfos; | |
| ExecutorAddr NextSegAddr = AllocAddr; | |
| for (auto &KV : BL.segments()) { | |
| const auto &AG = KV.first; | |
| auto &Seg = KV.second; | |
| Seg.Addr = NextSegAddr; | |
| KV.second.WorkingMem = BL.getGraph().allocateBuffer(Seg.ContentSize).data(); | |
| NextSegAddr += ExecutorAddrDiff( | |
| alignTo(Seg.ContentSize + Seg.ZeroFillSize, EPC.getPageSize())); | |
| auto &SegInfo = SegInfos[AG]; | |
| SegInfo.ContentSize = Seg.ContentSize; | |
| SegInfo.ZeroFillSize = Seg.ZeroFillSize; | |
| SegInfo.Addr = ExecutorAddr(Seg.Addr); | |
| SegInfo.WorkingMem = Seg.WorkingMem; | |
| } | |
| if (auto Err = BL.apply()) | |
| return OnAllocated(std::move(Err)); | |
| OnAllocated(std::make_unique<InFlightAlloc>(*this, BL.getGraph(), AllocAddr, | |
| std::move(SegInfos))); | |
| } | |
| } // end namespace orc | |
| } // end namespace llvm |