//===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===// // // 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/TargetProcess/SimpleExecutorMemoryManager.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h" #include "llvm/Support/FormatVariadic.h" #define DEBUG_TYPE "orc" namespace llvm { namespace orc { namespace rt_bootstrap { SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() { assert(Slabs.empty() && "shutdown not called?"); } Expected SimpleExecutorMemoryManager::reserve(uint64_t Size) { std::error_code EC; auto MB = sys::Memory::allocateMappedMemory( Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); if (EC) return errorCodeToError(EC); std::lock_guard Lock(M); assert(!Slabs.count(MB.base()) && "Duplicate allocation addr"); Slabs[MB.base()].Size = Size; return ExecutorAddr::fromPtr(MB.base()); } Expected SimpleExecutorMemoryManager::initialize(tpctypes::FinalizeRequest &FR) { std::vector DeallocationActions; if (FR.Segments.empty()) { if (FR.Actions.empty()) return make_error("Finalization request is empty", inconvertibleErrorCode()); else return make_error("Finalization actions attached to empty " "finalization request", inconvertibleErrorCode()); } ExecutorAddrRange RR(FR.Segments.front().Addr, FR.Segments.front().Addr); std::vector MBsToReset; auto ResetMBs = make_scope_exit([&]() { for (auto &MB : MBsToReset) sys::Memory::protectMappedMemory(MB, sys::Memory::MF_READ | sys::Memory::MF_WRITE); sys::Memory::InvalidateInstructionCache(RR.Start.toPtr(), RR.size()); }); // Copy content and apply permissions. for (auto &Seg : FR.Segments) { RR.Start = std::min(RR.Start, Seg.Addr); RR.End = std::max(RR.End, Seg.Addr + Seg.Size); // Check segment ranges. if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size())) return make_error( formatv("Segment {0:x} content size ({1:x} bytes) " "exceeds segment size ({2:x} bytes)", Seg.Addr.getValue(), Seg.Content.size(), Seg.Size), inconvertibleErrorCode()); ExecutorAddr SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size); if (LLVM_UNLIKELY(Seg.Addr < RR.Start || SegEnd > RR.End)) return make_error( formatv("Segment {0:x} -- {1:x} crosses boundary of " "allocation {2:x} -- {3:x}", Seg.Addr, SegEnd, RR.Start, RR.End), inconvertibleErrorCode()); char *Mem = Seg.Addr.toPtr(); if (!Seg.Content.empty()) memcpy(Mem, Seg.Content.data(), Seg.Content.size()); memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size()); assert(Seg.Size <= std::numeric_limits::max()); sys::MemoryBlock MB(Mem, Seg.Size); if (auto EC = sys::Memory::protectMappedMemory( MB, toSysMemoryProtectionFlags(Seg.RAG.Prot))) return errorCodeToError(EC); MBsToReset.push_back(MB); if ((Seg.RAG.Prot & MemProt::Exec) == MemProt::Exec) sys::Memory::InvalidateInstructionCache(Mem, Seg.Size); } auto DeallocActions = runFinalizeActions(FR.Actions); if (!DeallocActions) return DeallocActions.takeError(); { std::lock_guard Lock(M); auto Region = createRegionInfo(RR, "In initialize"); if (!Region) return Region.takeError(); Region->DeallocActions = std::move(*DeallocActions); } // Successful initialization. ResetMBs.release(); return RR.Start; } Error SimpleExecutorMemoryManager::deinitialize( const std::vector &InitKeys) { Error Err = Error::success(); for (auto &KeyAddr : llvm::reverse(InitKeys)) { std::vector DeallocActions; { std::scoped_lock Lock(M); auto Slab = getSlabInfo(KeyAddr, "In deinitialize"); if (!Slab) { Err = joinErrors(std::move(Err), Slab.takeError()); continue; } auto RI = getRegionInfo(*Slab, KeyAddr, "In deinitialize"); if (!RI) { Err = joinErrors(std::move(Err), RI.takeError()); continue; } DeallocActions = std::move(RI->DeallocActions); } Err = joinErrors(std::move(Err), runDeallocActions(std::move(DeallocActions))); } return Err; } Error SimpleExecutorMemoryManager::release( const std::vector &Bases) { Error Err = Error::success(); // TODO: Prohibit new initializations within the slabs being removed? for (auto &Base : llvm::reverse(Bases)) { std::vector DeallocActions; sys::MemoryBlock MB; { std::scoped_lock Lock(M); auto SlabI = Slabs.find(Base.toPtr()); if (SlabI == Slabs.end()) { Err = joinErrors( std::move(Err), make_error("In release, " + formatv("{0:x}", Base) + " is not part of any reserved " "address range", inconvertibleErrorCode())); continue; } auto &Slab = SlabI->second; for (auto &[Addr, Region] : Slab.Regions) llvm::copy(Region.DeallocActions, back_inserter(DeallocActions)); MB = {Base.toPtr(), Slab.Size}; Slabs.erase(SlabI); } Err = joinErrors(std::move(Err), runDeallocActions(DeallocActions)); if (auto EC = sys::Memory::releaseMappedMemory(MB)) Err = joinErrors(std::move(Err), errorCodeToError(EC)); } return Err; } Error SimpleExecutorMemoryManager::shutdown() { // TODO: Prevent new allocations during shutdown. std::vector Bases; { std::scoped_lock Lock(M); for (auto &[Base, Slab] : Slabs) Bases.push_back(ExecutorAddr::fromPtr(Base)); } return release(Bases); } void SimpleExecutorMemoryManager::addBootstrapSymbols( StringMap &M) { M[rt::SimpleExecutorMemoryManagerInstanceName] = ExecutorAddr::fromPtr(this); M[rt::SimpleExecutorMemoryManagerReserveWrapperName] = ExecutorAddr::fromPtr(&reserveWrapper); M[rt::SimpleExecutorMemoryManagerInitializeWrapperName] = ExecutorAddr::fromPtr(&initializeWrapper); M[rt::SimpleExecutorMemoryManagerDeinitializeWrapperName] = ExecutorAddr::fromPtr(&deinitializeWrapper); M[rt::SimpleExecutorMemoryManagerReleaseWrapperName] = ExecutorAddr::fromPtr(&releaseWrapper); } Expected SimpleExecutorMemoryManager::getSlabInfo(ExecutorAddr A, StringRef Context) { auto MakeBadSlabError = [&]() { return make_error( Context + ", address " + formatv("{0:x}", A) + " is not part of any reserved address range", inconvertibleErrorCode()); }; auto I = Slabs.upper_bound(A.toPtr()); if (I == Slabs.begin()) return MakeBadSlabError(); --I; if (!ExecutorAddrRange(ExecutorAddr::fromPtr(I->first), I->second.Size) .contains(A)) return MakeBadSlabError(); return I->second; } Expected SimpleExecutorMemoryManager::getSlabInfo(ExecutorAddrRange R, StringRef Context) { auto MakeBadSlabError = [&]() { return make_error( Context + ", range " + formatv("{0:x}", R) + " is not part of any reserved address range", inconvertibleErrorCode()); }; auto I = Slabs.upper_bound(R.Start.toPtr()); if (I == Slabs.begin()) return MakeBadSlabError(); --I; if (!ExecutorAddrRange(ExecutorAddr::fromPtr(I->first), I->second.Size) .contains(R)) return MakeBadSlabError(); return I->second; } Expected SimpleExecutorMemoryManager::createRegionInfo(ExecutorAddrRange R, StringRef Context) { auto Slab = getSlabInfo(R, Context); if (!Slab) return Slab.takeError(); auto MakeBadRegionError = [&](ExecutorAddrRange Other, bool Prev) { return make_error(Context + ", region " + formatv("{0:x}", R) + " overlaps " + (Prev ? "previous" : "following") + " region " + formatv("{0:x}", Other), inconvertibleErrorCode()); }; auto I = Slab->Regions.upper_bound(R.Start); if (I != Slab->Regions.begin()) { auto J = std::prev(I); ExecutorAddrRange PrevRange(J->first, J->second.Size); if (PrevRange.overlaps(R)) return MakeBadRegionError(PrevRange, true); } if (I != Slab->Regions.end()) { ExecutorAddrRange NextRange(I->first, I->second.Size); if (NextRange.overlaps(R)) return MakeBadRegionError(NextRange, false); } auto &RInfo = Slab->Regions[R.Start]; RInfo.Size = R.size(); return RInfo; } Expected SimpleExecutorMemoryManager::getRegionInfo(SlabInfo &Slab, ExecutorAddr A, StringRef Context) { auto I = Slab.Regions.find(A); if (I == Slab.Regions.end()) return make_error( Context + ", address " + formatv("{0:x}", A) + " does not correspond to the start of any initialized region", inconvertibleErrorCode()); return I->second; } Expected SimpleExecutorMemoryManager::getRegionInfo(ExecutorAddr A, StringRef Context) { auto Slab = getSlabInfo(A, Context); if (!Slab) return Slab.takeError(); return getRegionInfo(*Slab, A, Context); } llvm::orc::shared::CWrapperFunctionResult SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData, size_t ArgSize) { return shared::WrapperFunction:: handle(ArgData, ArgSize, shared::makeMethodWrapperHandler( &SimpleExecutorMemoryManager::reserve)) .release(); } llvm::orc::shared::CWrapperFunctionResult SimpleExecutorMemoryManager::initializeWrapper(const char *ArgData, size_t ArgSize) { return shared:: WrapperFunction::handle( ArgData, ArgSize, shared::makeMethodWrapperHandler( &SimpleExecutorMemoryManager::initialize)) .release(); } llvm::orc::shared::CWrapperFunctionResult SimpleExecutorMemoryManager::deinitializeWrapper(const char *ArgData, size_t ArgSize) { return shared::WrapperFunction< rt::SPSSimpleRemoteMemoryMapDeinitializeSignature>:: handle(ArgData, ArgSize, shared::makeMethodWrapperHandler( &SimpleExecutorMemoryManager::deinitialize)) .release(); } llvm::orc::shared::CWrapperFunctionResult SimpleExecutorMemoryManager::releaseWrapper(const char *ArgData, size_t ArgSize) { return shared::WrapperFunction:: handle(ArgData, ArgSize, shared::makeMethodWrapperHandler( &SimpleExecutorMemoryManager::release)) .release(); } } // namespace rt_bootstrap } // end namespace orc } // end namespace llvm