aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp')
-rw-r--r--llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp261
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