12ce73f13SLang Hames //===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===//
278b083dbSLang Hames //
378b083dbSLang Hames // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
478b083dbSLang Hames // See https://llvm.org/LICENSE.txt for license information.
578b083dbSLang Hames // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
678b083dbSLang Hames //
778b083dbSLang Hames //===----------------------------------------------------------------------===//
878b083dbSLang Hames
978b083dbSLang Hames #include "llvm/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.h"
1078b083dbSLang Hames
1178b083dbSLang Hames #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
1278b083dbSLang Hames #include "llvm/Support/FormatVariadic.h"
1378b083dbSLang Hames
1478b083dbSLang Hames #define DEBUG_TYPE "orc"
1578b083dbSLang Hames
1678b083dbSLang Hames namespace llvm {
1778b083dbSLang Hames namespace orc {
1878b083dbSLang Hames namespace rt_bootstrap {
1978b083dbSLang Hames
~SimpleExecutorMemoryManager()2078b083dbSLang Hames SimpleExecutorMemoryManager::~SimpleExecutorMemoryManager() {
2178b083dbSLang Hames assert(Allocations.empty() && "shutdown not called?");
2278b083dbSLang Hames }
2378b083dbSLang Hames
allocate(uint64_t Size)24ef391df2SLang Hames Expected<ExecutorAddr> SimpleExecutorMemoryManager::allocate(uint64_t Size) {
2578b083dbSLang Hames std::error_code EC;
2678b083dbSLang Hames auto MB = sys::Memory::allocateMappedMemory(
275a667c0eSKazu Hirata Size, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
2878b083dbSLang Hames if (EC)
2978b083dbSLang Hames return errorCodeToError(EC);
3078b083dbSLang Hames std::lock_guard<std::mutex> Lock(M);
3178b083dbSLang Hames assert(!Allocations.count(MB.base()) && "Duplicate allocation addr");
3278b083dbSLang Hames Allocations[MB.base()].Size = Size;
33ef391df2SLang Hames return ExecutorAddr::fromPtr(MB.base());
3478b083dbSLang Hames }
3578b083dbSLang Hames
finalize(tpctypes::FinalizeRequest & FR)3678b083dbSLang Hames Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) {
37ef391df2SLang Hames ExecutorAddr Base(~0ULL);
38089acf25SLang Hames std::vector<shared::WrapperFunctionCall> DeallocationActions;
3978b083dbSLang Hames size_t SuccessfulFinalizationActions = 0;
4078b083dbSLang Hames
416498b0e9SLang Hames if (FR.Segments.empty()) {
426498b0e9SLang Hames // NOTE: Finalizing nothing is currently a no-op. Should it be an error?
436498b0e9SLang Hames if (FR.Actions.empty())
446498b0e9SLang Hames return Error::success();
456498b0e9SLang Hames else
466498b0e9SLang Hames return make_error<StringError>("Finalization actions attached to empty "
476498b0e9SLang Hames "finalization request",
486498b0e9SLang Hames inconvertibleErrorCode());
496498b0e9SLang Hames }
506498b0e9SLang Hames
5178b083dbSLang Hames for (auto &Seg : FR.Segments)
5278b083dbSLang Hames Base = std::min(Base, Seg.Addr);
5378b083dbSLang Hames
5478b083dbSLang Hames for (auto &ActPair : FR.Actions)
55089acf25SLang Hames if (ActPair.Dealloc)
56089acf25SLang Hames DeallocationActions.push_back(ActPair.Dealloc);
5778b083dbSLang Hames
5878b083dbSLang Hames // Get the Allocation for this finalization.
5978b083dbSLang Hames size_t AllocSize = 0;
6078b083dbSLang Hames {
6178b083dbSLang Hames std::lock_guard<std::mutex> Lock(M);
6278b083dbSLang Hames auto I = Allocations.find(Base.toPtr<void *>());
6378b083dbSLang Hames if (I == Allocations.end())
6478b083dbSLang Hames return make_error<StringError>("Attempt to finalize unrecognized "
6578b083dbSLang Hames "allocation " +
6678b083dbSLang Hames formatv("{0:x}", Base.getValue()),
6778b083dbSLang Hames inconvertibleErrorCode());
6878b083dbSLang Hames AllocSize = I->second.Size;
6978b083dbSLang Hames I->second.DeallocationActions = std::move(DeallocationActions);
7078b083dbSLang Hames }
71ef391df2SLang Hames ExecutorAddr AllocEnd = Base + ExecutorAddrDiff(AllocSize);
7278b083dbSLang Hames
7378b083dbSLang Hames // Bail-out function: this will run deallocation actions corresponding to any
7478b083dbSLang Hames // completed finalization actions, then deallocate memory.
7578b083dbSLang Hames auto BailOut = [&](Error Err) {
7678b083dbSLang Hames std::pair<void *, Allocation> AllocToDestroy;
7778b083dbSLang Hames
78*25f4ead9SStephan T. Lavavej // Get allocation to destroy.
7978b083dbSLang Hames {
8078b083dbSLang Hames std::lock_guard<std::mutex> Lock(M);
8178b083dbSLang Hames auto I = Allocations.find(Base.toPtr<void *>());
8278b083dbSLang Hames
8378b083dbSLang Hames // Check for missing allocation (effective a double free).
8478b083dbSLang Hames if (I == Allocations.end())
8578b083dbSLang Hames return joinErrors(
8678b083dbSLang Hames std::move(Err),
8778b083dbSLang Hames make_error<StringError>("No allocation entry found "
8878b083dbSLang Hames "for " +
8978b083dbSLang Hames formatv("{0:x}", Base.getValue()),
9078b083dbSLang Hames inconvertibleErrorCode()));
9178b083dbSLang Hames AllocToDestroy = std::move(*I);
9278b083dbSLang Hames Allocations.erase(I);
9378b083dbSLang Hames }
9478b083dbSLang Hames
9578b083dbSLang Hames // Run deallocation actions for all completed finalization actions.
9678b083dbSLang Hames while (SuccessfulFinalizationActions)
9712b2cc22SLang Hames Err =
9812b2cc22SLang Hames joinErrors(std::move(Err), FR.Actions[--SuccessfulFinalizationActions]
99089acf25SLang Hames .Dealloc.runWithSPSRetErrorMerged());
10078b083dbSLang Hames
10178b083dbSLang Hames // Deallocate memory.
10278b083dbSLang Hames sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size);
10378b083dbSLang Hames if (auto EC = sys::Memory::releaseMappedMemory(MB))
10478b083dbSLang Hames Err = joinErrors(std::move(Err), errorCodeToError(EC));
10578b083dbSLang Hames
10678b083dbSLang Hames return Err;
10778b083dbSLang Hames };
10878b083dbSLang Hames
10978b083dbSLang Hames // Copy content and apply permissions.
11078b083dbSLang Hames for (auto &Seg : FR.Segments) {
11178b083dbSLang Hames
11278b083dbSLang Hames // Check segment ranges.
11378b083dbSLang Hames if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size()))
11478b083dbSLang Hames return BailOut(make_error<StringError>(
11578b083dbSLang Hames formatv("Segment {0:x} content size ({1:x} bytes) "
11678b083dbSLang Hames "exceeds segment size ({2:x} bytes)",
11778b083dbSLang Hames Seg.Addr.getValue(), Seg.Content.size(), Seg.Size),
11878b083dbSLang Hames inconvertibleErrorCode()));
119ef391df2SLang Hames ExecutorAddr SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size);
12078b083dbSLang Hames if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd))
12178b083dbSLang Hames return BailOut(make_error<StringError>(
12278b083dbSLang Hames formatv("Segment {0:x} -- {1:x} crosses boundary of "
12378b083dbSLang Hames "allocation {2:x} -- {3:x}",
12478b083dbSLang Hames Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(),
12578b083dbSLang Hames AllocEnd.getValue()),
12678b083dbSLang Hames inconvertibleErrorCode()));
12778b083dbSLang Hames
12878b083dbSLang Hames char *Mem = Seg.Addr.toPtr<char *>();
129aabc4b13SLang Hames if (!Seg.Content.empty())
13078b083dbSLang Hames memcpy(Mem, Seg.Content.data(), Seg.Content.size());
13178b083dbSLang Hames memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());
13278b083dbSLang Hames assert(Seg.Size <= std::numeric_limits<size_t>::max());
13378b083dbSLang Hames if (auto EC = sys::Memory::protectMappedMemory(
13478b083dbSLang Hames {Mem, static_cast<size_t>(Seg.Size)},
1350b7e16afSLang Hames toSysMemoryProtectionFlags(Seg.RAG.Prot)))
13678b083dbSLang Hames return BailOut(errorCodeToError(EC));
1370b7e16afSLang Hames if ((Seg.RAG.Prot & MemProt::Exec) == MemProt::Exec)
13878b083dbSLang Hames sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
13978b083dbSLang Hames }
14078b083dbSLang Hames
14178b083dbSLang Hames // Run finalization actions.
14278b083dbSLang Hames for (auto &ActPair : FR.Actions) {
143089acf25SLang Hames if (auto Err = ActPair.Finalize.runWithSPSRetErrorMerged())
14478b083dbSLang Hames return BailOut(std::move(Err));
14578b083dbSLang Hames ++SuccessfulFinalizationActions;
14678b083dbSLang Hames }
14778b083dbSLang Hames
14878b083dbSLang Hames return Error::success();
14978b083dbSLang Hames }
15078b083dbSLang Hames
deallocate(const std::vector<ExecutorAddr> & Bases)15178b083dbSLang Hames Error SimpleExecutorMemoryManager::deallocate(
152ef391df2SLang Hames const std::vector<ExecutorAddr> &Bases) {
15378b083dbSLang Hames std::vector<std::pair<void *, Allocation>> AllocPairs;
15478b083dbSLang Hames AllocPairs.reserve(Bases.size());
15578b083dbSLang Hames
156*25f4ead9SStephan T. Lavavej // Get allocation to destroy.
15778b083dbSLang Hames Error Err = Error::success();
15878b083dbSLang Hames {
15978b083dbSLang Hames std::lock_guard<std::mutex> Lock(M);
16078b083dbSLang Hames for (auto &Base : Bases) {
16178b083dbSLang Hames auto I = Allocations.find(Base.toPtr<void *>());
16278b083dbSLang Hames
16378b083dbSLang Hames // Check for missing allocation (effective a double free).
16478b083dbSLang Hames if (I != Allocations.end()) {
16578b083dbSLang Hames AllocPairs.push_back(std::move(*I));
16678b083dbSLang Hames Allocations.erase(I);
16778b083dbSLang Hames } else
16878b083dbSLang Hames Err = joinErrors(
16978b083dbSLang Hames std::move(Err),
17078b083dbSLang Hames make_error<StringError>("No allocation entry found "
17178b083dbSLang Hames "for " +
17278b083dbSLang Hames formatv("{0:x}", Base.getValue()),
17378b083dbSLang Hames inconvertibleErrorCode()));
17478b083dbSLang Hames }
17578b083dbSLang Hames }
17678b083dbSLang Hames
17778b083dbSLang Hames while (!AllocPairs.empty()) {
17878b083dbSLang Hames auto &P = AllocPairs.back();
17978b083dbSLang Hames Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second));
18078b083dbSLang Hames AllocPairs.pop_back();
18178b083dbSLang Hames }
18278b083dbSLang Hames
18378b083dbSLang Hames return Err;
18478b083dbSLang Hames }
18578b083dbSLang Hames
shutdown()18678b083dbSLang Hames Error SimpleExecutorMemoryManager::shutdown() {
18778b083dbSLang Hames
18878b083dbSLang Hames AllocationsMap AM;
18978b083dbSLang Hames {
19078b083dbSLang Hames std::lock_guard<std::mutex> Lock(M);
19178b083dbSLang Hames AM = std::move(Allocations);
19278b083dbSLang Hames }
19378b083dbSLang Hames
19478b083dbSLang Hames Error Err = Error::success();
19578b083dbSLang Hames for (auto &KV : AM)
19678b083dbSLang Hames Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second));
19778b083dbSLang Hames return Err;
19878b083dbSLang Hames }
19978b083dbSLang Hames
addBootstrapSymbols(StringMap<ExecutorAddr> & M)20078b083dbSLang Hames void SimpleExecutorMemoryManager::addBootstrapSymbols(
201ef391df2SLang Hames StringMap<ExecutorAddr> &M) {
202ef391df2SLang Hames M[rt::SimpleExecutorMemoryManagerInstanceName] = ExecutorAddr::fromPtr(this);
20378b083dbSLang Hames M[rt::SimpleExecutorMemoryManagerReserveWrapperName] =
204ef391df2SLang Hames ExecutorAddr::fromPtr(&reserveWrapper);
20578b083dbSLang Hames M[rt::SimpleExecutorMemoryManagerFinalizeWrapperName] =
206ef391df2SLang Hames ExecutorAddr::fromPtr(&finalizeWrapper);
20778b083dbSLang Hames M[rt::SimpleExecutorMemoryManagerDeallocateWrapperName] =
208ef391df2SLang Hames ExecutorAddr::fromPtr(&deallocateWrapper);
20978b083dbSLang Hames }
21078b083dbSLang Hames
deallocateImpl(void * Base,Allocation & A)21178b083dbSLang Hames Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) {
21278b083dbSLang Hames Error Err = Error::success();
21378b083dbSLang Hames
21478b083dbSLang Hames while (!A.DeallocationActions.empty()) {
21512b2cc22SLang Hames Err = joinErrors(std::move(Err),
216089acf25SLang Hames A.DeallocationActions.back().runWithSPSRetErrorMerged());
21778b083dbSLang Hames A.DeallocationActions.pop_back();
21878b083dbSLang Hames }
21978b083dbSLang Hames
22078b083dbSLang Hames sys::MemoryBlock MB(Base, A.Size);
22178b083dbSLang Hames if (auto EC = sys::Memory::releaseMappedMemory(MB))
22278b083dbSLang Hames Err = joinErrors(std::move(Err), errorCodeToError(EC));
22378b083dbSLang Hames
22478b083dbSLang Hames return Err;
22578b083dbSLang Hames }
22678b083dbSLang Hames
227213666f8SLang Hames llvm::orc::shared::CWrapperFunctionResult
reserveWrapper(const char * ArgData,size_t ArgSize)22878b083dbSLang Hames SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData,
22978b083dbSLang Hames size_t ArgSize) {
23078b083dbSLang Hames return shared::WrapperFunction<
23178b083dbSLang Hames rt::SPSSimpleExecutorMemoryManagerReserveSignature>::
23278b083dbSLang Hames handle(ArgData, ArgSize,
23378b083dbSLang Hames shared::makeMethodWrapperHandler(
23478b083dbSLang Hames &SimpleExecutorMemoryManager::allocate))
23578b083dbSLang Hames .release();
23678b083dbSLang Hames }
23778b083dbSLang Hames
238213666f8SLang Hames llvm::orc::shared::CWrapperFunctionResult
finalizeWrapper(const char * ArgData,size_t ArgSize)23978b083dbSLang Hames SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData,
24078b083dbSLang Hames size_t ArgSize) {
24178b083dbSLang Hames return shared::WrapperFunction<
24278b083dbSLang Hames rt::SPSSimpleExecutorMemoryManagerFinalizeSignature>::
24378b083dbSLang Hames handle(ArgData, ArgSize,
24478b083dbSLang Hames shared::makeMethodWrapperHandler(
24578b083dbSLang Hames &SimpleExecutorMemoryManager::finalize))
24678b083dbSLang Hames .release();
24778b083dbSLang Hames }
24878b083dbSLang Hames
249213666f8SLang Hames llvm::orc::shared::CWrapperFunctionResult
deallocateWrapper(const char * ArgData,size_t ArgSize)25078b083dbSLang Hames SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData,
25178b083dbSLang Hames size_t ArgSize) {
25278b083dbSLang Hames return shared::WrapperFunction<
25378b083dbSLang Hames rt::SPSSimpleExecutorMemoryManagerDeallocateSignature>::
25478b083dbSLang Hames handle(ArgData, ArgSize,
25578b083dbSLang Hames shared::makeMethodWrapperHandler(
25678b083dbSLang Hames &SimpleExecutorMemoryManager::deallocate))
25778b083dbSLang Hames .release();
25878b083dbSLang Hames }
25978b083dbSLang Hames
26078b083dbSLang Hames } // namespace rt_bootstrap
26178b083dbSLang Hames } // end namespace orc
26278b083dbSLang Hames } // end namespace llvm
263