diff options
Diffstat (limited to 'llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp')
-rw-r--r-- | llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp new file mode 100644 index 000000000000..232340c22a32 --- /dev/null +++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp @@ -0,0 +1,261 @@ +//===- 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/ExecutionEngine/Orc/Shared/OrcRTBridge.h" +#include "llvm/Support/FormatVariadic.h" + +#define DEBUG_TYPE "orc" + +namespace llvm { +namespace orc { +namespace rt_bootstrap { + +SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() { + assert(Allocations.empty() && "shutdown not called?"); +} + +Expected<ExecutorAddr> SimpleExecutorMemoryManager::allocate(uint64_t Size) { + std::error_code EC; + auto MB = sys::Memory::allocateMappedMemory( + Size, 0, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC); + if (EC) + return errorCodeToError(EC); + std::lock_guard<std::mutex> Lock(M); + assert(!Allocations.count(MB.base()) && "Duplicate allocation addr"); + Allocations[MB.base()].Size = Size; + return ExecutorAddr::fromPtr(MB.base()); +} + +Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) { + ExecutorAddr Base(~0ULL); + std::vector<tpctypes::WrapperFunctionCall> DeallocationActions; + size_t SuccessfulFinalizationActions = 0; + + if (FR.Segments.empty()) { + // NOTE: Finalizing nothing is currently a no-op. Should it be an error? + if (FR.Actions.empty()) + return Error::success(); + else + return make_error<StringError>("Finalization actions attached to empty " + "finalization request", + inconvertibleErrorCode()); + } + + for (auto &Seg : FR.Segments) + Base = std::min(Base, Seg.Addr); + + for (auto &ActPair : FR.Actions) + if (ActPair.Deallocate.Func) + DeallocationActions.push_back(ActPair.Deallocate); + + // Get the Allocation for this finalization. + size_t AllocSize = 0; + { + std::lock_guard<std::mutex> Lock(M); + auto I = Allocations.find(Base.toPtr<void *>()); + if (I == Allocations.end()) + return make_error<StringError>("Attempt to finalize unrecognized " + "allocation " + + formatv("{0:x}", Base.getValue()), + inconvertibleErrorCode()); + AllocSize = I->second.Size; + I->second.DeallocationActions = std::move(DeallocationActions); + } + ExecutorAddr AllocEnd = Base + ExecutorAddrDiff(AllocSize); + + // Bail-out function: this will run deallocation actions corresponding to any + // completed finalization actions, then deallocate memory. + auto BailOut = [&](Error Err) { + std::pair<void *, Allocation> AllocToDestroy; + + // Get allocation to destory. + { + std::lock_guard<std::mutex> Lock(M); + auto I = Allocations.find(Base.toPtr<void *>()); + + // Check for missing allocation (effective a double free). + if (I == Allocations.end()) + return joinErrors( + std::move(Err), + make_error<StringError>("No allocation entry found " + "for " + + formatv("{0:x}", Base.getValue()), + inconvertibleErrorCode())); + AllocToDestroy = std::move(*I); + Allocations.erase(I); + } + + // Run deallocation actions for all completed finalization actions. + while (SuccessfulFinalizationActions) + Err = + joinErrors(std::move(Err), FR.Actions[--SuccessfulFinalizationActions] + .Deallocate.runWithSPSRet()); + + // Deallocate memory. + sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size); + if (auto EC = sys::Memory::releaseMappedMemory(MB)) + Err = joinErrors(std::move(Err), errorCodeToError(EC)); + + return Err; + }; + + // Copy content and apply permissions. + for (auto &Seg : FR.Segments) { + + // Check segment ranges. + if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size())) + return BailOut(make_error<StringError>( + 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 < Base || SegEnd > AllocEnd)) + return BailOut(make_error<StringError>( + formatv("Segment {0:x} -- {1:x} crosses boundary of " + "allocation {2:x} -- {3:x}", + Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(), + AllocEnd.getValue()), + inconvertibleErrorCode())); + + char *Mem = Seg.Addr.toPtr<char *>(); + 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<size_t>::max()); + if (auto EC = sys::Memory::protectMappedMemory( + {Mem, static_cast<size_t>(Seg.Size)}, + tpctypes::fromWireProtectionFlags(Seg.Prot))) + return BailOut(errorCodeToError(EC)); + if (Seg.Prot & tpctypes::WPF_Exec) + sys::Memory::InvalidateInstructionCache(Mem, Seg.Size); + } + + // Run finalization actions. + for (auto &ActPair : FR.Actions) { + if (auto Err = ActPair.Finalize.runWithSPSRet()) + return BailOut(std::move(Err)); + ++SuccessfulFinalizationActions; + } + + return Error::success(); +} + +Error SimpleExecutorMemoryManager::deallocate( + const std::vector<ExecutorAddr> &Bases) { + std::vector<std::pair<void *, Allocation>> AllocPairs; + AllocPairs.reserve(Bases.size()); + + // Get allocation to destory. + Error Err = Error::success(); + { + std::lock_guard<std::mutex> Lock(M); + for (auto &Base : Bases) { + auto I = Allocations.find(Base.toPtr<void *>()); + + // Check for missing allocation (effective a double free). + if (I != Allocations.end()) { + AllocPairs.push_back(std::move(*I)); + Allocations.erase(I); + } else + Err = joinErrors( + std::move(Err), + make_error<StringError>("No allocation entry found " + "for " + + formatv("{0:x}", Base.getValue()), + inconvertibleErrorCode())); + } + } + + while (!AllocPairs.empty()) { + auto &P = AllocPairs.back(); + Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second)); + AllocPairs.pop_back(); + } + + return Err; +} + +Error SimpleExecutorMemoryManager::shutdown() { + + AllocationsMap AM; + { + std::lock_guard<std::mutex> Lock(M); + AM = std::move(Allocations); + } + + Error Err = Error::success(); + for (auto &KV : AM) + Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second)); + return Err; +} + +void SimpleExecutorMemoryManager::addBootstrapSymbols( + StringMap<ExecutorAddr> &M) { + M[rt::SimpleExecutorMemoryManagerInstanceName] = ExecutorAddr::fromPtr(this); + M[rt::SimpleExecutorMemoryManagerReserveWrapperName] = + ExecutorAddr::fromPtr(&reserveWrapper); + M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] = + ExecutorAddr::fromPtr(&finalizeWrapper); + M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] = + ExecutorAddr::fromPtr(&deallocateWrapper); +} + +Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) { + Error Err = Error::success(); + + while (!A.DeallocationActions.empty()) { + Err = joinErrors(std::move(Err), + A.DeallocationActions.back().runWithSPSRet()); + A.DeallocationActions.pop_back(); + } + + sys::MemoryBlock MB(Base, A.Size); + if (auto EC = sys::Memory::releaseMappedMemory(MB)) + Err = joinErrors(std::move(Err), errorCodeToError(EC)); + + return Err; +} + +llvm::orc::shared::CWrapperFunctionResult +SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData, + size_t ArgSize) { + return shared::WrapperFunction< + rt::SPSSimpleExecutorMemoryManagerReserveSignature>:: + handle(ArgData, ArgSize, + shared::makeMethodWrapperHandler( + &SimpleExecutorMemoryManager::allocate)) + .release(); +} + +llvm::orc::shared::CWrapperFunctionResult +SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData, + size_t ArgSize) { + return shared::WrapperFunction< + rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>:: + handle(ArgData, ArgSize, + shared::makeMethodWrapperHandler( + &SimpleExecutorMemoryManager::finalize)) + .release(); +} + +llvm::orc::shared::CWrapperFunctionResult +SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData, + size_t ArgSize) { + return shared::WrapperFunction< + rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>:: + handle(ArgData, ArgSize, + shared::makeMethodWrapperHandler( + &SimpleExecutorMemoryManager::deallocate)) + .release(); +} + +} // namespace rt_bootstrap +} // end namespace orc +} // end namespace llvm |