LLVM 20.0.0git
SimpleExecutorMemoryManager.cpp
Go to the documentation of this file.
1//===- SimpleExecuorMemoryManagare.cpp - Simple executor-side memory mgmt -===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10
13
14#define DEBUG_TYPE "orc"
15
16namespace llvm {
17namespace orc {
18namespace rt_bootstrap {
19
21 assert(Allocations.empty() && "shutdown not called?");
22}
23
25 std::error_code EC;
28 if (EC)
29 return errorCodeToError(EC);
30 std::lock_guard<std::mutex> Lock(M);
31 assert(!Allocations.count(MB.base()) && "Duplicate allocation addr");
32 Allocations[MB.base()].Size = Size;
33 return ExecutorAddr::fromPtr(MB.base());
34}
35
37 ExecutorAddr Base(~0ULL);
38 std::vector<shared::WrapperFunctionCall> DeallocationActions;
39 size_t SuccessfulFinalizationActions = 0;
40
41 if (FR.Segments.empty()) {
42 // NOTE: Finalizing nothing is currently a no-op. Should it be an error?
43 if (FR.Actions.empty())
44 return Error::success();
45 else
46 return make_error<StringError>("Finalization actions attached to empty "
47 "finalization request",
49 }
50
51 for (auto &Seg : FR.Segments)
52 Base = std::min(Base, Seg.Addr);
53
54 for (auto &ActPair : FR.Actions)
55 if (ActPair.Dealloc)
56 DeallocationActions.push_back(ActPair.Dealloc);
57
58 // Get the Allocation for this finalization.
59 size_t AllocSize = 0;
60 {
61 std::lock_guard<std::mutex> Lock(M);
62 auto I = Allocations.find(Base.toPtr<void *>());
63 if (I == Allocations.end())
64 return make_error<StringError>("Attempt to finalize unrecognized "
65 "allocation " +
66 formatv("{0:x}", Base.getValue()),
68 AllocSize = I->second.Size;
69 I->second.DeallocationActions = std::move(DeallocationActions);
70 }
71 ExecutorAddr AllocEnd = Base + ExecutorAddrDiff(AllocSize);
72
73 // Bail-out function: this will run deallocation actions corresponding to any
74 // completed finalization actions, then deallocate memory.
75 auto BailOut = [&](Error Err) {
76 std::pair<void *, Allocation> AllocToDestroy;
77
78 // Get allocation to destroy.
79 {
80 std::lock_guard<std::mutex> Lock(M);
81 auto I = Allocations.find(Base.toPtr<void *>());
82
83 // Check for missing allocation (effective a double free).
84 if (I == Allocations.end())
85 return joinErrors(
86 std::move(Err),
87 make_error<StringError>("No allocation entry found "
88 "for " +
89 formatv("{0:x}", Base.getValue()),
91 AllocToDestroy = std::move(*I);
92 Allocations.erase(I);
93 }
94
95 // Run deallocation actions for all completed finalization actions.
96 while (SuccessfulFinalizationActions)
97 Err =
98 joinErrors(std::move(Err), FR.Actions[--SuccessfulFinalizationActions]
99 .Dealloc.runWithSPSRetErrorMerged());
100
101 // Deallocate memory.
102 sys::MemoryBlock MB(AllocToDestroy.first, AllocToDestroy.second.Size);
103 if (auto EC = sys::Memory::releaseMappedMemory(MB))
104 Err = joinErrors(std::move(Err), errorCodeToError(EC));
105
106 return Err;
107 };
108
109 // Copy content and apply permissions.
110 for (auto &Seg : FR.Segments) {
111
112 // Check segment ranges.
113 if (LLVM_UNLIKELY(Seg.Size < Seg.Content.size()))
114 return BailOut(make_error<StringError>(
115 formatv("Segment {0:x} content size ({1:x} bytes) "
116 "exceeds segment size ({2:x} bytes)",
117 Seg.Addr.getValue(), Seg.Content.size(), Seg.Size),
119 ExecutorAddr SegEnd = Seg.Addr + ExecutorAddrDiff(Seg.Size);
120 if (LLVM_UNLIKELY(Seg.Addr < Base || SegEnd > AllocEnd))
121 return BailOut(make_error<StringError>(
122 formatv("Segment {0:x} -- {1:x} crosses boundary of "
123 "allocation {2:x} -- {3:x}",
124 Seg.Addr.getValue(), SegEnd.getValue(), Base.getValue(),
125 AllocEnd.getValue()),
127
128 char *Mem = Seg.Addr.toPtr<char *>();
129 if (!Seg.Content.empty())
130 memcpy(Mem, Seg.Content.data(), Seg.Content.size());
131 memset(Mem + Seg.Content.size(), 0, Seg.Size - Seg.Content.size());
132 assert(Seg.Size <= std::numeric_limits<size_t>::max());
134 {Mem, static_cast<size_t>(Seg.Size)},
135 toSysMemoryProtectionFlags(Seg.RAG.Prot)))
136 return BailOut(errorCodeToError(EC));
137 if ((Seg.RAG.Prot & MemProt::Exec) == MemProt::Exec)
139 }
140
141 // Run finalization actions.
142 for (auto &ActPair : FR.Actions) {
143 if (auto Err = ActPair.Finalize.runWithSPSRetErrorMerged())
144 return BailOut(std::move(Err));
145 ++SuccessfulFinalizationActions;
146 }
147
148 return Error::success();
149}
150
152 const std::vector<ExecutorAddr> &Bases) {
153 std::vector<std::pair<void *, Allocation>> AllocPairs;
154 AllocPairs.reserve(Bases.size());
155
156 // Get allocation to destroy.
157 Error Err = Error::success();
158 {
159 std::lock_guard<std::mutex> Lock(M);
160 for (auto &Base : Bases) {
161 auto I = Allocations.find(Base.toPtr<void *>());
162
163 // Check for missing allocation (effective a double free).
164 if (I != Allocations.end()) {
165 AllocPairs.push_back(std::move(*I));
166 Allocations.erase(I);
167 } else
168 Err = joinErrors(
169 std::move(Err),
170 make_error<StringError>("No allocation entry found "
171 "for " +
172 formatv("{0:x}", Base.getValue()),
174 }
175 }
176
177 while (!AllocPairs.empty()) {
178 auto &P = AllocPairs.back();
179 Err = joinErrors(std::move(Err), deallocateImpl(P.first, P.second));
180 AllocPairs.pop_back();
181 }
182
183 return Err;
184}
185
187
189 {
190 std::lock_guard<std::mutex> Lock(M);
191 AM = std::move(Allocations);
192 }
193
194 Error Err = Error::success();
195 for (auto &KV : AM)
196 Err = joinErrors(std::move(Err), deallocateImpl(KV.first, KV.second));
197 return Err;
198}
199
204 ExecutorAddr::fromPtr(&reserveWrapper);
206 ExecutorAddr::fromPtr(&finalizeWrapper);
208 ExecutorAddr::fromPtr(&deallocateWrapper);
209}
210
211Error SimpleExecutorMemoryManager::deallocateImpl(void *Base, Allocation &A) {
212 Error Err = Error::success();
213
214 while (!A.DeallocationActions.empty()) {
215 Err = joinErrors(std::move(Err),
216 A.DeallocationActions.back().runWithSPSRetErrorMerged());
217 A.DeallocationActions.pop_back();
218 }
219
220 sys::MemoryBlock MB(Base, A.Size);
221 if (auto EC = sys::Memory::releaseMappedMemory(MB))
222 Err = joinErrors(std::move(Err), errorCodeToError(EC));
223
224 return Err;
225}
226
228SimpleExecutorMemoryManager::reserveWrapper(const char *ArgData,
229 size_t ArgSize) {
230 return shared::WrapperFunction<
232 handle(ArgData, ArgSize,
235 .release();
236}
237
239SimpleExecutorMemoryManager::finalizeWrapper(const char *ArgData,
240 size_t ArgSize) {
241 return shared::WrapperFunction<
243 handle(ArgData, ArgSize,
246 .release();
247}
248
250SimpleExecutorMemoryManager::deallocateWrapper(const char *ArgData,
251 size_t ArgSize) {
252 return shared::WrapperFunction<
254 handle(ArgData, ArgSize,
257 .release();
258}
259
260} // namespace rt_bootstrap
261} // end namespace orc
262} // end namespace llvm
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
#define LLVM_UNLIKELY(EXPR)
Definition: Compiler.h:237
uint64_t Size
#define I(x, y, z)
Definition: MD5.cpp:58
#define P(N)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
iterator find(const_arg_type_t< KeyT > Val)
Definition: DenseMap.h:155
bool erase(const KeyT &Val)
Definition: DenseMap.h:336
bool empty() const
Definition: DenseMap.h:98
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
Definition: DenseMap.h:151
iterator end()
Definition: DenseMap.h:84
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
StringMap - This is an unconventional map that is specialized for handling keys that are "strings",...
Definition: StringMap.h:128
Represents an address in the executor process.
uint64_t getValue() const
static ExecutorAddr fromPtr(T *Ptr, UnwrapFn &&Unwrap=UnwrapFn())
Create an ExecutorAddr from the given pointer.
Error deallocate(const std::vector< ExecutorAddr > &Bases)
void addBootstrapSymbols(StringMap< ExecutorAddr > &M) override
This class encapsulates the notion of a memory block which has an address and a size.
Definition: Memory.h:31
static std::error_code releaseMappedMemory(MemoryBlock &Block)
This method releases a block of memory that was allocated with the allocateMappedMemory method.
static MemoryBlock allocateMappedMemory(size_t NumBytes, const MemoryBlock *const NearBlock, unsigned Flags, std::error_code &EC)
This method allocates a block of memory that is suitable for loading dynamically generated code (e....
static void InvalidateInstructionCache(const void *Addr, size_t Len)
InvalidateInstructionCache - Before the JIT can run a block of code that has been emitted it must inv...
static std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags)
This method sets the protection flags for a block of memory to the state specified by /p Flags.
shared::SPSExpected< shared::SPSExecutorAddr >(shared::SPSExecutorAddr, uint64_t) SPSSimpleExecutorMemoryManagerReserveSignature
Definition: OrcRTBridge.h:64
const char * SimpleExecutorMemoryManagerFinalizeWrapperName
Definition: OrcRTBridge.cpp:26
const char * SimpleExecutorMemoryManagerDeallocateWrapperName
Definition: OrcRTBridge.cpp:28
const char * SimpleExecutorMemoryManagerReserveWrapperName
Definition: OrcRTBridge.cpp:24
shared::SPSError(shared::SPSExecutorAddr, shared::SPSFinalizeRequest) SPSSimpleExecutorMemoryManagerFinalizeSignature
Definition: OrcRTBridge.h:66
const char * SimpleExecutorMemoryManagerInstanceName
Definition: OrcRTBridge.cpp:22
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSSimpleExecutorMemoryManagerDeallocateSignature
Definition: OrcRTBridge.h:68
MethodWrapperHandler< RetT, ClassT, ArgTs... > makeMethodWrapperHandler(RetT(ClassT::*Method)(ArgTs...))
Create a MethodWrapperHandler object from the given method pointer.
uint64_t ExecutorAddrDiff
sys::Memory::ProtectionFlags toSysMemoryProtectionFlags(MemProt MP)
Convert a MemProt value to a corresponding sys::Memory::ProtectionFlags value.
Definition: MemoryFlags.h:44
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:98
auto formatv(const char *Fmt, Ts &&...Vals) -> formatv_object< decltype(std::make_tuple(support::detail::build_format_adapter(std::forward< Ts >(Vals))...))>
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:438
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:111
std::vector< SegFinalizeRequest > Segments