xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
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/Compiler.h"
25 #include "llvm/Support/Error.h"
26 #include "llvm/Support/MSVCErrorWorkarounds.h"
27 #include "llvm/Support/Memory.h"
28 #include "llvm/Support/RecyclingAllocator.h"
29 #include "llvm/TargetParser/Triple.h"
30 
31 #include <cassert>
32 #include <cstdint>
33 #include <future>
34 #include <mutex>
35 
36 namespace llvm {
37 namespace jitlink {
38 
39 class Block;
40 class LinkGraph;
41 class Section;
42 
43 /// Manages allocations of JIT memory.
44 ///
45 /// Instances of this class may be accessed concurrently from multiple threads
46 /// and their implemetations should include any necessary synchronization.
47 class LLVM_ABI JITLinkMemoryManager {
48 public:
49 
50   /// Represents a finalized allocation.
51   ///
52   /// Finalized allocations must be passed to the
53   /// JITLinkMemoryManager:deallocate method prior to being destroyed.
54   ///
55   /// The interpretation of the Address associated with the finalized allocation
56   /// is up to the memory manager implementation. Common options are using the
57   /// base address of the allocation, or the address of a memory management
58   /// object that tracks the allocation.
59   class FinalizedAlloc {
60     friend class JITLinkMemoryManager;
61 
62     static constexpr auto InvalidAddr = ~uint64_t(0);
63 
64   public:
65     FinalizedAlloc() = default;
FinalizedAlloc(orc::ExecutorAddr A)66     explicit FinalizedAlloc(orc::ExecutorAddr A) : A(A) {
67       assert(A.getValue() != InvalidAddr &&
68              "Explicitly creating an invalid allocation?");
69     }
70     FinalizedAlloc(const FinalizedAlloc &) = delete;
FinalizedAlloc(FinalizedAlloc && Other)71     FinalizedAlloc(FinalizedAlloc &&Other) : A(Other.A) {
72       Other.A.setValue(InvalidAddr);
73     }
74     FinalizedAlloc &operator=(const FinalizedAlloc &) = delete;
75     FinalizedAlloc &operator=(FinalizedAlloc &&Other) {
76       assert(A.getValue() == InvalidAddr &&
77              "Cannot overwrite active finalized allocation");
78       std::swap(A, Other.A);
79       return *this;
80     }
~FinalizedAlloc()81     ~FinalizedAlloc() {
82       assert(A.getValue() == InvalidAddr &&
83              "Finalized allocation was not deallocated");
84     }
85 
86     /// FinalizedAllocs convert to false for default-constructed, and
87     /// true otherwise. Default-constructed allocs need not be deallocated.
88     explicit operator bool() const { return A.getValue() != InvalidAddr; }
89 
90     /// Returns the address associated with this finalized allocation.
91     /// The allocation is unmodified.
getAddress()92     orc::ExecutorAddr getAddress() const { return A; }
93 
94     /// Returns the address associated with this finalized allocation and
95     /// resets this object to the default state.
96     /// This should only be used by allocators when deallocating memory.
release()97     orc::ExecutorAddr release() {
98       orc::ExecutorAddr Tmp = A;
99       A.setValue(InvalidAddr);
100       return Tmp;
101     }
102 
103   private:
104     orc::ExecutorAddr A{InvalidAddr};
105   };
106 
107   /// Represents an allocation which has not been finalized yet.
108   ///
109   /// InFlightAllocs manage both executor memory allocations and working
110   /// memory allocations.
111   ///
112   /// On finalization, the InFlightAlloc should transfer the content of
113   /// working memory into executor memory, apply memory protections, and
114   /// run any finalization functions.
115   ///
116   /// Working memory should be kept alive at least until one of the following
117   /// happens: (1) the InFlightAlloc instance is destroyed, (2) the
118   /// InFlightAlloc is abandoned, (3) finalized target memory is destroyed.
119   ///
120   /// If abandon is called then working memory and executor memory should both
121   /// be freed.
122   class LLVM_ABI InFlightAlloc {
123   public:
124     using OnFinalizedFunction = unique_function<void(Expected<FinalizedAlloc>)>;
125     using OnAbandonedFunction = unique_function<void(Error)>;
126 
127     virtual ~InFlightAlloc();
128 
129     /// Called prior to finalization if the allocation should be abandoned.
130     virtual void abandon(OnAbandonedFunction OnAbandoned) = 0;
131 
132     /// Called to transfer working memory to the target and apply finalization.
133     virtual void finalize(OnFinalizedFunction OnFinalized) = 0;
134 
135     /// Synchronous convenience version of finalize.
finalize()136     Expected<FinalizedAlloc> finalize() {
137       std::promise<MSVCPExpected<FinalizedAlloc>> FinalizeResultP;
138       auto FinalizeResultF = FinalizeResultP.get_future();
139       finalize([&](Expected<FinalizedAlloc> Result) {
140         FinalizeResultP.set_value(std::move(Result));
141       });
142       return FinalizeResultF.get();
143     }
144   };
145 
146   /// Typedef for the argument to be passed to OnAllocatedFunction.
147   using AllocResult = Expected<std::unique_ptr<InFlightAlloc>>;
148 
149   /// Called when allocation has been completed.
150   using OnAllocatedFunction = unique_function<void(AllocResult)>;
151 
152   /// Called when deallocation has completed.
153   using OnDeallocatedFunction = unique_function<void(Error)>;
154 
155   virtual ~JITLinkMemoryManager();
156 
157   /// Start the allocation process.
158   ///
159   /// If the initial allocation is successful then the OnAllocated function will
160   /// be called with a std::unique_ptr<InFlightAlloc> value. If the assocation
161   /// is unsuccessful then the OnAllocated function will be called with an
162   /// Error.
163   virtual void allocate(const JITLinkDylib *JD, LinkGraph &G,
164                         OnAllocatedFunction OnAllocated) = 0;
165 
166   /// Convenience function for blocking allocation.
allocate(const JITLinkDylib * JD,LinkGraph & G)167   AllocResult allocate(const JITLinkDylib *JD, LinkGraph &G) {
168     std::promise<MSVCPExpected<std::unique_ptr<InFlightAlloc>>> AllocResultP;
169     auto AllocResultF = AllocResultP.get_future();
170     allocate(JD, G, [&](AllocResult Alloc) {
171       AllocResultP.set_value(std::move(Alloc));
172     });
173     return AllocResultF.get();
174   }
175 
176   /// Deallocate a list of allocation objects.
177   ///
178   /// Dealloc actions will be run in reverse order (from the end of the vector
179   /// to the start).
180   virtual void deallocate(std::vector<FinalizedAlloc> Allocs,
181                           OnDeallocatedFunction OnDeallocated) = 0;
182 
183   /// Convenience function for deallocation of a single alloc.
deallocate(FinalizedAlloc Alloc,OnDeallocatedFunction OnDeallocated)184   void deallocate(FinalizedAlloc Alloc, OnDeallocatedFunction OnDeallocated) {
185     std::vector<FinalizedAlloc> Allocs;
186     Allocs.push_back(std::move(Alloc));
187     deallocate(std::move(Allocs), std::move(OnDeallocated));
188   }
189 
190   /// Convenience function for blocking deallocation.
deallocate(std::vector<FinalizedAlloc> Allocs)191   Error deallocate(std::vector<FinalizedAlloc> Allocs) {
192     std::promise<MSVCPError> DeallocResultP;
193     auto DeallocResultF = DeallocResultP.get_future();
194     deallocate(std::move(Allocs),
195                [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });
196     return DeallocResultF.get();
197   }
198 
199   /// Convenience function for blocking deallocation of a single alloc.
deallocate(FinalizedAlloc Alloc)200   Error deallocate(FinalizedAlloc Alloc) {
201     std::vector<FinalizedAlloc> Allocs;
202     Allocs.push_back(std::move(Alloc));
203     return deallocate(std::move(Allocs));
204   }
205 };
206 
207 /// BasicLayout simplifies the implementation of JITLinkMemoryManagers.
208 ///
209 /// BasicLayout groups Sections into Segments based on their memory protection
210 /// and deallocation policies. JITLinkMemoryManagers can construct a BasicLayout
211 /// from a Graph, and then assign working memory and addresses to each of the
212 /// Segments. These addreses will be mapped back onto the Graph blocks in
213 /// the apply method.
214 class BasicLayout {
215 public:
216   /// The Alignment, ContentSize and ZeroFillSize of each segment will be
217   /// pre-filled from the Graph. Clients must set the Addr and WorkingMem fields
218   /// prior to calling apply.
219   //
220   // FIXME: The C++98 initializer is an attempt to work around compile failures
221   // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397.
222   // We should be able to switch this back to member initialization once that
223   // issue is fixed.
224   class Segment {
225     friend class BasicLayout;
226 
227   public:
228     Segment() = default;
229     Align Alignment;
230     size_t ContentSize = 0;
231     uint64_t ZeroFillSize = 0;
232     orc::ExecutorAddr Addr;
233     char *WorkingMem = nullptr;
234 
235   private:
236     size_t NextWorkingMemOffset = 0;
237     std::vector<Block *> ContentBlocks, ZeroFillBlocks;
238   };
239 
240   /// A convenience class that further groups segments based on memory
241   /// deallocation policy. This allows clients to make two slab allocations:
242   /// one for all standard segments, and one for all finalize segments.
243   struct ContiguousPageBasedLayoutSizes {
244     uint64_t StandardSegs = 0;
245     uint64_t FinalizeSegs = 0;
246 
totalContiguousPageBasedLayoutSizes247     uint64_t total() const { return StandardSegs + FinalizeSegs; }
248   };
249 
250 private:
251   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
252 
253 public:
254   LLVM_ABI BasicLayout(LinkGraph &G);
255 
256   /// Return a reference to the graph this allocation was created from.
getGraph()257   LinkGraph &getGraph() { return G; }
258 
259   /// Returns the total number of required to allocate all segments (with each
260   /// segment padded out to page size) for all standard segments, and all
261   /// finalize segments.
262   ///
263   /// This is a convenience function for the common case where the segments will
264   /// be allocated contiguously.
265   ///
266   /// This function will return an error if any segment has an alignment that
267   /// is higher than a page.
268   LLVM_ABI Expected<ContiguousPageBasedLayoutSizes>
269   getContiguousPageBasedLayoutSizes(uint64_t PageSize);
270 
271   /// Returns an iterator over the segments of the layout.
segments()272   iterator_range<SegmentMap::iterator> segments() {
273     return {Segments.begin(), Segments.end()};
274   }
275 
276   /// Apply the layout to the graph.
277   LLVM_ABI Error apply();
278 
279   /// Returns a reference to the AllocActions in the graph.
280   /// This convenience function saves callers from having to #include
281   /// LinkGraph.h if all they need are allocation actions.
282   LLVM_ABI orc::shared::AllocActions &graphAllocActions();
283 
284 private:
285   LinkGraph &G;
286   SegmentMap Segments;
287 };
288 
289 /// A utility class for making simple allocations using JITLinkMemoryManager.
290 ///
291 /// SimpleSegementAlloc takes a mapping of AllocGroups to Segments and uses
292 /// this to create a LinkGraph with one Section (containing one Block) per
293 /// Segment. Clients can obtain a pointer to the working memory and executor
294 /// address of that block using the Segment's AllocGroup. Once memory has been
295 /// populated, clients can call finalize to finalize the memory.
296 ///
297 /// Note: Segments with MemLifetime::NoAlloc are not permitted, since they would
298 /// not be useful, and their presence is likely to indicate a bug.
299 class SimpleSegmentAlloc {
300 public:
301   /// Describes a segment to be allocated.
302   struct Segment {
303     Segment() = default;
SegmentSegment304     Segment(size_t ContentSize, Align ContentAlign)
305         : ContentSize(ContentSize), ContentAlign(ContentAlign) {}
306 
307     size_t ContentSize = 0;
308     Align ContentAlign;
309   };
310 
311   /// Describes the segment working memory and executor address.
312   struct SegmentInfo {
313     orc::ExecutorAddr Addr;
314     MutableArrayRef<char> WorkingMem;
315   };
316 
317   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
318 
319   using OnCreatedFunction = unique_function<void(Expected<SimpleSegmentAlloc>)>;
320 
321   using OnFinalizedFunction =
322       JITLinkMemoryManager::InFlightAlloc::OnFinalizedFunction;
323 
324   LLVM_ABI static void Create(JITLinkMemoryManager &MemMgr,
325                               std::shared_ptr<orc::SymbolStringPool> SSP,
326                               Triple TT, const JITLinkDylib *JD,
327                               SegmentMap Segments, OnCreatedFunction OnCreated);
328 
329   LLVM_ABI static Expected<SimpleSegmentAlloc>
330   Create(JITLinkMemoryManager &MemMgr,
331          std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
332          const JITLinkDylib *JD, SegmentMap Segments);
333 
334   LLVM_ABI SimpleSegmentAlloc(SimpleSegmentAlloc &&);
335   LLVM_ABI SimpleSegmentAlloc &operator=(SimpleSegmentAlloc &&);
336   LLVM_ABI ~SimpleSegmentAlloc();
337 
338   /// Returns the SegmentInfo for the given group.
339   LLVM_ABI SegmentInfo getSegInfo(orc::AllocGroup AG);
340 
341   /// Finalize all groups (async version).
finalize(OnFinalizedFunction OnFinalized)342   void finalize(OnFinalizedFunction OnFinalized) {
343     Alloc->finalize(std::move(OnFinalized));
344   }
345 
346   /// Finalize all groups.
finalize()347   Expected<JITLinkMemoryManager::FinalizedAlloc> finalize() {
348     return Alloc->finalize();
349   }
350 
351 private:
352   SimpleSegmentAlloc(
353       std::unique_ptr<LinkGraph> G,
354       orc::AllocGroupSmallMap<Block *> ContentBlocks,
355       std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc);
356 
357   std::unique_ptr<LinkGraph> G;
358   orc::AllocGroupSmallMap<Block *> ContentBlocks;
359   std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc;
360 };
361 
362 /// A JITLinkMemoryManager that allocates in-process memory.
363 class LLVM_ABI InProcessMemoryManager : public JITLinkMemoryManager {
364 public:
365   class IPInFlightAlloc;
366 
367   /// Attempts to auto-detect the host page size.
368   static Expected<std::unique_ptr<InProcessMemoryManager>> Create();
369 
370   /// Create an instance using the given page size.
InProcessMemoryManager(uint64_t PageSize)371   InProcessMemoryManager(uint64_t PageSize) : PageSize(PageSize) {
372     assert(isPowerOf2_64(PageSize) && "PageSize must be a power of 2");
373   }
374 
375   void allocate(const JITLinkDylib *JD, LinkGraph &G,
376                 OnAllocatedFunction OnAllocated) override;
377 
378   // Use overloads from base class.
379   using JITLinkMemoryManager::allocate;
380 
381   void deallocate(std::vector<FinalizedAlloc> Alloc,
382                   OnDeallocatedFunction OnDeallocated) override;
383 
384   // Use overloads from base class.
385   using JITLinkMemoryManager::deallocate;
386 
387 private:
388   // FIXME: Use an in-place array instead of a vector for DeallocActions.
389   //        There shouldn't need to be a heap alloc for this.
390   struct FinalizedAllocInfo {
391     sys::MemoryBlock StandardSegments;
392     std::vector<orc::shared::WrapperFunctionCall> DeallocActions;
393   };
394 
395   FinalizedAlloc createFinalizedAlloc(
396       sys::MemoryBlock StandardSegments,
397       std::vector<orc::shared::WrapperFunctionCall> DeallocActions);
398 
399   uint64_t PageSize;
400   std::mutex FinalizedAllocsMutex;
401   RecyclingAllocator<BumpPtrAllocator, FinalizedAllocInfo> FinalizedAllocInfos;
402 };
403 
404 } // end namespace jitlink
405 } // end namespace llvm
406 
407 #endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
408