xref: /llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h (revision 4eaff6c58ae2f130ac8d63cf2c87bbb483114876)
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 
16 #include "llvm/ADT/FunctionExtras.h"
17 #include "llvm/ADT/SmallVector.h"
18 #include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
19 #include "llvm/ExecutionEngine/Orc/Shared/AllocationActions.h"
20 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
21 #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
22 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
23 #include "llvm/Support/Allocator.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/MSVCErrorWorkarounds.h"
26 #include "llvm/Support/Memory.h"
27 #include "llvm/Support/RecyclingAllocator.h"
28 #include "llvm/TargetParser/Triple.h"
29 
30 #include <cassert>
31 #include <cstdint>
32 #include <future>
33 #include <mutex>
34 
35 namespace llvm {
36 namespace jitlink {
37 
38 class Block;
39 class LinkGraph;
40 class 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.
46 class JITLinkMemoryManager {
47 public:
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.
58   class FinalizedAlloc {
59     friend class JITLinkMemoryManager;
60 
61     static constexpr auto InvalidAddr = ~uint64_t(0);
62 
63   public:
64     FinalizedAlloc() = default;
65     explicit FinalizedAlloc(orc::ExecutorAddr A) : A(A) {
66       assert(A.getValue() != InvalidAddr &&
67              "Explicitly creating an invalid allocation?");
68     }
69     FinalizedAlloc(const FinalizedAlloc &) = delete;
70     FinalizedAlloc(FinalizedAlloc &&Other) : A(Other.A) {
71       Other.A.setValue(InvalidAddr);
72     }
73     FinalizedAlloc &operator=(const FinalizedAlloc &) = delete;
74     FinalizedAlloc &operator=(FinalizedAlloc &&Other) {
75       assert(A.getValue() == InvalidAddr &&
76              "Cannot overwrite active finalized allocation");
77       std::swap(A, Other.A);
78       return *this;
79     }
80     ~FinalizedAlloc() {
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.
96     orc::ExecutorAddr release() {
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.
121   class InFlightAlloc {
122   public:
123     using OnFinalizedFunction = unique_function<void(Expected<FinalizedAlloc>)>;
124     using OnAbandonedFunction = unique_function<void(Error)>;
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.
135     Expected<FinalizedAlloc> finalize() {
136       std::promise<MSVCPExpected<FinalizedAlloc>> FinalizeResultP;
137       auto FinalizeResultF = FinalizeResultP.get_future();
138       finalize([&](Expected<FinalizedAlloc> Result) {
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.
146   using AllocResult = Expected<std::unique_ptr<InFlightAlloc>>;
147 
148   /// Called when allocation has been completed.
149   using OnAllocatedFunction = unique_function<void(AllocResult)>;
150 
151   /// Called when deallocation has completed.
152   using OnDeallocatedFunction = unique_function<void(Error)>;
153 
154   virtual ~JITLinkMemoryManager();
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.
166   AllocResult allocate(const JITLinkDylib *JD, LinkGraph &G) {
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.
183   void deallocate(FinalizedAlloc Alloc, OnDeallocatedFunction OnDeallocated) {
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.
199   Error deallocate(FinalizedAlloc 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.
213 class BasicLayout {
214 public:
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:
227     Segment()
228         : ContentSize(0), ZeroFillSize(0), Addr(0), WorkingMem(nullptr),
229           NextWorkingMemOffset(0) {}
230     Align Alignment;
231     size_t ContentSize;
232     uint64_t ZeroFillSize;
233     orc::ExecutorAddr Addr;
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.
244   struct ContiguousPageBasedLayoutSizes {
245     uint64_t StandardSegs = 0;
246     uint64_t FinalizeSegs = 0;
247 
248     uint64_t total() const { return StandardSegs + FinalizeSegs; }
249   };
250 
251 private:
252   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
253 
254 public:
255   BasicLayout(LinkGraph &G);
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.
269   Expected<ContiguousPageBasedLayoutSizes>
270   getContiguousPageBasedLayoutSizes(uint64_t PageSize);
271 
272   /// Returns an iterator over the segments of the layout.
273   iterator_range<SegmentMap::iterator> segments() {
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.
283   orc::shared::AllocActions &graphAllocActions();
284 
285 private:
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.
300 class SimpleSegmentAlloc {
301 public:
302   /// Describes a segment to be allocated.
303   struct Segment {
304     Segment() = default;
305     Segment(size_t ContentSize, Align ContentAlign)
306         : ContentSize(ContentSize), ContentAlign(ContentAlign) {}
307 
308     size_t ContentSize = 0;
309     Align ContentAlign;
310   };
311 
312   /// Describes the segment working memory and executor address.
313   struct SegmentInfo {
314     orc::ExecutorAddr Addr;
315     MutableArrayRef<char> WorkingMem;
316   };
317 
318   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
319 
320   using OnCreatedFunction = unique_function<void(Expected<SimpleSegmentAlloc>)>;
321 
322   using OnFinalizedFunction =
323       JITLinkMemoryManager::InFlightAlloc::OnFinalizedFunction;
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 
330   static Expected<SimpleSegmentAlloc>
331   Create(JITLinkMemoryManager &MemMgr,
332          std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
333          const JITLinkDylib *JD, SegmentMap Segments);
334 
335   SimpleSegmentAlloc(SimpleSegmentAlloc &&);
336   SimpleSegmentAlloc &operator=(SimpleSegmentAlloc &&);
337   ~SimpleSegmentAlloc();
338 
339   /// Returns the SegmentInfo for the given group.
340   SegmentInfo getSegInfo(orc::AllocGroup AG);
341 
342   /// Finalize all groups (async version).
343   void finalize(OnFinalizedFunction OnFinalized) {
344     Alloc->finalize(std::move(OnFinalized));
345   }
346 
347   /// Finalize all groups.
348   Expected<JITLinkMemoryManager::FinalizedAlloc> finalize() {
349     return Alloc->finalize();
350   }
351 
352 private:
353   SimpleSegmentAlloc(
354       std::unique_ptr<LinkGraph> G,
355       orc::AllocGroupSmallMap<Block *> ContentBlocks,
356       std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc);
357 
358   std::unique_ptr<LinkGraph> G;
359   orc::AllocGroupSmallMap<Block *> ContentBlocks;
360   std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc;
361 };
362 
363 /// A JITLinkMemoryManager that allocates in-process memory.
364 class InProcessMemoryManager : public JITLinkMemoryManager {
365 public:
366   class IPInFlightAlloc;
367 
368   /// Attempts to auto-detect the host page size.
369   static Expected<std::unique_ptr<InProcessMemoryManager>> Create();
370 
371   /// Create an instance using the given page size.
372   InProcessMemoryManager(uint64_t PageSize) : PageSize(PageSize) {
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.
380   using JITLinkMemoryManager::allocate;
381 
382   void deallocate(std::vector<FinalizedAlloc> Alloc,
383                   OnDeallocatedFunction OnDeallocated) override;
384 
385   // Use overloads from base class.
386   using JITLinkMemoryManager::deallocate;
387 
388 private:
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;
402   RecyclingAllocator<BumpPtrAllocator, FinalizedAllocInfo> FinalizedAllocInfos;
403 };
404 
405 } // end namespace jitlink
406 } // end namespace llvm
407 
408 #endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
409