LLVM 20.0.0git
JITLinkMemoryManager.h
Go to the documentation of this file.
1//===-- JITLinkMemoryManager.h - JITLink mem manager interface --*- C++ -*-===//
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//
9// Contains the JITLinkMemoryManager interface.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
14#define LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
15
24#include "llvm/Support/Error.h"
26#include "llvm/Support/Memory.h"
29
30#include <cassert>
31#include <cstdint>
32#include <future>
33#include <mutex>
34
35namespace llvm {
36namespace jitlink {
37
38class Block;
39class LinkGraph;
40class Section;
41
42/// Manages allocations of JIT memory.
43///
44/// Instances of this class may be accessed concurrently from multiple threads
45/// and their implemetations should include any necessary synchronization.
47public:
48
49 /// Represents a finalized allocation.
50 ///
51 /// Finalized allocations must be passed to the
52 /// JITLinkMemoryManager:deallocate method prior to being destroyed.
53 ///
54 /// The interpretation of the Address associated with the finalized allocation
55 /// is up to the memory manager implementation. Common options are using the
56 /// base address of the allocation, or the address of a memory management
57 /// object that tracks the allocation.
60
61 static constexpr auto InvalidAddr = ~uint64_t(0);
62
63 public:
64 FinalizedAlloc() = default;
66 assert(A.getValue() != InvalidAddr &&
67 "Explicitly creating an invalid allocation?");
68 }
69 FinalizedAlloc(const FinalizedAlloc &) = delete;
71 Other.A.setValue(InvalidAddr);
72 }
75 assert(A.getValue() == InvalidAddr &&
76 "Cannot overwrite active finalized allocation");
77 std::swap(A, Other.A);
78 return *this;
79 }
81 assert(A.getValue() == InvalidAddr &&
82 "Finalized allocation was not deallocated");
83 }
84
85 /// FinalizedAllocs convert to false for default-constructed, and
86 /// true otherwise. Default-constructed allocs need not be deallocated.
87 explicit operator bool() const { return A.getValue() != InvalidAddr; }
88
89 /// Returns the address associated with this finalized allocation.
90 /// The allocation is unmodified.
91 orc::ExecutorAddr getAddress() const { return A; }
92
93 /// Returns the address associated with this finalized allocation and
94 /// resets this object to the default state.
95 /// This should only be used by allocators when deallocating memory.
97 orc::ExecutorAddr Tmp = A;
98 A.setValue(InvalidAddr);
99 return Tmp;
100 }
101
102 private:
103 orc::ExecutorAddr A{InvalidAddr};
104 };
105
106 /// Represents an allocation which has not been finalized yet.
107 ///
108 /// InFlightAllocs manage both executor memory allocations and working
109 /// memory allocations.
110 ///
111 /// On finalization, the InFlightAlloc should transfer the content of
112 /// working memory into executor memory, apply memory protections, and
113 /// run any finalization functions.
114 ///
115 /// Working memory should be kept alive at least until one of the following
116 /// happens: (1) the InFlightAlloc instance is destroyed, (2) the
117 /// InFlightAlloc is abandoned, (3) finalized target memory is destroyed.
118 ///
119 /// If abandon is called then working memory and executor memory should both
120 /// be freed.
122 public:
125
126 virtual ~InFlightAlloc();
127
128 /// Called prior to finalization if the allocation should be abandoned.
129 virtual void abandon(OnAbandonedFunction OnAbandoned) = 0;
130
131 /// Called to transfer working memory to the target and apply finalization.
132 virtual void finalize(OnFinalizedFunction OnFinalized) = 0;
133
134 /// Synchronous convenience version of finalize.
136 std::promise<MSVCPExpected<FinalizedAlloc>> FinalizeResultP;
137 auto FinalizeResultF = FinalizeResultP.get_future();
139 FinalizeResultP.set_value(std::move(Result));
140 });
141 return FinalizeResultF.get();
142 }
143 };
144
145 /// Typedef for the argument to be passed to OnAllocatedFunction.
147
148 /// Called when allocation has been completed.
150
151 /// Called when deallocation has completed.
153
155
156 /// Start the allocation process.
157 ///
158 /// If the initial allocation is successful then the OnAllocated function will
159 /// be called with a std::unique_ptr<InFlightAlloc> value. If the assocation
160 /// is unsuccessful then the OnAllocated function will be called with an
161 /// Error.
162 virtual void allocate(const JITLinkDylib *JD, LinkGraph &G,
163 OnAllocatedFunction OnAllocated) = 0;
164
165 /// Convenience function for blocking allocation.
167 std::promise<MSVCPExpected<std::unique_ptr<InFlightAlloc>>> AllocResultP;
168 auto AllocResultF = AllocResultP.get_future();
169 allocate(JD, G, [&](AllocResult Alloc) {
170 AllocResultP.set_value(std::move(Alloc));
171 });
172 return AllocResultF.get();
173 }
174
175 /// Deallocate a list of allocation objects.
176 ///
177 /// Dealloc actions will be run in reverse order (from the end of the vector
178 /// to the start).
179 virtual void deallocate(std::vector<FinalizedAlloc> Allocs,
180 OnDeallocatedFunction OnDeallocated) = 0;
181
182 /// Convenience function for deallocation of a single alloc.
184 std::vector<FinalizedAlloc> Allocs;
185 Allocs.push_back(std::move(Alloc));
186 deallocate(std::move(Allocs), std::move(OnDeallocated));
187 }
188
189 /// Convenience function for blocking deallocation.
190 Error deallocate(std::vector<FinalizedAlloc> Allocs) {
191 std::promise<MSVCPError> DeallocResultP;
192 auto DeallocResultF = DeallocResultP.get_future();
193 deallocate(std::move(Allocs),
194 [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });
195 return DeallocResultF.get();
196 }
197
198 /// Convenience function for blocking deallocation of a single alloc.
200 std::vector<FinalizedAlloc> Allocs;
201 Allocs.push_back(std::move(Alloc));
202 return deallocate(std::move(Allocs));
203 }
204};
205
206/// BasicLayout simplifies the implementation of JITLinkMemoryManagers.
207///
208/// BasicLayout groups Sections into Segments based on their memory protection
209/// and deallocation policies. JITLinkMemoryManagers can construct a BasicLayout
210/// from a Graph, and then assign working memory and addresses to each of the
211/// Segments. These addreses will be mapped back onto the Graph blocks in
212/// the apply method.
214public:
215 /// The Alignment, ContentSize and ZeroFillSize of each segment will be
216 /// pre-filled from the Graph. Clients must set the Addr and WorkingMem fields
217 /// prior to calling apply.
218 //
219 // FIXME: The C++98 initializer is an attempt to work around compile failures
220 // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397.
221 // We should be able to switch this back to member initialization once that
222 // issue is fixed.
223 class Segment {
224 friend class BasicLayout;
225
226 public:
228 : ContentSize(0), ZeroFillSize(0), Addr(0), WorkingMem(nullptr),
229 NextWorkingMemOffset(0) {}
234 char *WorkingMem = nullptr;
235
236 private:
237 size_t NextWorkingMemOffset;
238 std::vector<Block *> ContentBlocks, ZeroFillBlocks;
239 };
240
241 /// A convenience class that further groups segments based on memory
242 /// deallocation policy. This allows clients to make two slab allocations:
243 /// one for all standard segments, and one for all finalize segments.
247
249 };
250
251private:
252 using SegmentMap = orc::AllocGroupSmallMap<Segment>;
253
254public:
256
257 /// Return a reference to the graph this allocation was created from.
258 LinkGraph &getGraph() { return G; }
259
260 /// Returns the total number of required to allocate all segments (with each
261 /// segment padded out to page size) for all standard segments, and all
262 /// finalize segments.
263 ///
264 /// This is a convenience function for the common case where the segments will
265 /// be allocated contiguously.
266 ///
267 /// This function will return an error if any segment has an alignment that
268 /// is higher than a page.
271
272 /// Returns an iterator over the segments of the layout.
274 return {Segments.begin(), Segments.end()};
275 }
276
277 /// Apply the layout to the graph.
278 Error apply();
279
280 /// Returns a reference to the AllocActions in the graph.
281 /// This convenience function saves callers from having to #include
282 /// LinkGraph.h if all they need are allocation actions.
284
285private:
286 LinkGraph &G;
287 SegmentMap Segments;
288};
289
290/// A utility class for making simple allocations using JITLinkMemoryManager.
291///
292/// SimpleSegementAlloc takes a mapping of AllocGroups to Segments and uses
293/// this to create a LinkGraph with one Section (containing one Block) per
294/// Segment. Clients can obtain a pointer to the working memory and executor
295/// address of that block using the Segment's AllocGroup. Once memory has been
296/// populated, clients can call finalize to finalize the memory.
297///
298/// Note: Segments with MemLifetime::NoAlloc are not permitted, since they would
299/// not be useful, and their presence is likely to indicate a bug.
301public:
302 /// Describes a segment to be allocated.
303 struct Segment {
304 Segment() = default;
307
308 size_t ContentSize = 0;
310 };
311
312 /// Describes the segment working memory and executor address.
313 struct SegmentInfo {
316 };
317
319
321
324
325 static void Create(JITLinkMemoryManager &MemMgr,
326 std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
327 const JITLinkDylib *JD, SegmentMap Segments,
328 OnCreatedFunction OnCreated);
329
332 std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
333 const JITLinkDylib *JD, SegmentMap Segments);
334
338
339 /// Returns the SegmentInfo for the given group.
341
342 /// Finalize all groups (async version).
343 void finalize(OnFinalizedFunction OnFinalized) {
344 Alloc->finalize(std::move(OnFinalized));
345 }
346
347 /// Finalize all groups.
349 return Alloc->finalize();
350 }
351
352private:
354 std::unique_ptr<LinkGraph> G,
356 std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc);
357
358 std::unique_ptr<LinkGraph> G;
360 std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc;
361};
362
363/// A JITLinkMemoryManager that allocates in-process memory.
365public:
366 class IPInFlightAlloc;
367
368 /// Attempts to auto-detect the host page size.
370
371 /// Create an instance using the given page size.
373 assert(isPowerOf2_64(PageSize) && "PageSize must be a power of 2");
374 }
375
376 void allocate(const JITLinkDylib *JD, LinkGraph &G,
377 OnAllocatedFunction OnAllocated) override;
378
379 // Use overloads from base class.
381
382 void deallocate(std::vector<FinalizedAlloc> Alloc,
383 OnDeallocatedFunction OnDeallocated) override;
384
385 // Use overloads from base class.
387
388private:
389 // FIXME: Use an in-place array instead of a vector for DeallocActions.
390 // There shouldn't need to be a heap alloc for this.
391 struct FinalizedAllocInfo {
392 sys::MemoryBlock StandardSegments;
393 std::vector<orc::shared::WrapperFunctionCall> DeallocActions;
394 };
395
396 FinalizedAlloc createFinalizedAlloc(
397 sys::MemoryBlock StandardSegments,
398 std::vector<orc::shared::WrapperFunctionCall> DeallocActions);
399
400 uint64_t PageSize;
401 std::mutex FinalizedAllocsMutex;
403};
404
405} // end namespace jitlink
406} // end namespace llvm
407
408#endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
This file defines the BumpPtrAllocator interface.
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file provides a collection of function (or more generally, callable) type erasure utilities supp...
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
This file defines the SmallVector class.
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
Tagged union holding either a T or a Error.
Definition: Error.h:481
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition: ArrayRef.h:310
RecyclingAllocator - This class wraps an Allocator, adding the functionality of recycling deleted obj...
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
A range adaptor for a pair of iterators.
A specialized small-map for AllocGroups.
Definition: MemoryFlags.h:165
A pair of memory protections and allocation policies.
Definition: MemoryFlags.h:110
Represents an address in the executor process.
This class encapsulates the notion of a memory block which has an address and a size.
Definition: Memory.h:32
unique_function is a type-erasing functor similar to std::function.
std::vector< AllocActionCallPair > AllocActions
A vector of allocation actions to be run for this allocation.
NodeAddr< BlockNode * > Block
Definition: RDFGraph.h:392
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Definition: MathExtras.h:296
@ Other
Any other memory.
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition: BitVector.h:860
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39