LLVM  14.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 
19 #include "llvm/Support/Allocator.h"
20 #include "llvm/Support/Error.h"
22 #include "llvm/Support/Memory.h"
24 
25 #include <cstdint>
26 #include <future>
27 #include <mutex>
28 
29 namespace llvm {
30 namespace jitlink {
31 
32 class Block;
33 class LinkGraph;
34 class Section;
35 
36 /// Manages allocations of JIT memory.
37 ///
38 /// Instances of this class may be accessed concurrently from multiple threads
39 /// and their implemetations should include any necessary synchronization.
41 public:
42  /// Represents a call to a graph-memory-management support function in the
43  /// executor.
44  ///
45  /// Support functions are called as:
46  ///
47  /// auto *Result =
48  /// ((char*(*)(const void*, size_t))FnAddr)(
49  /// (const void*)CtxAddr, (size_t)CtxSize)
50  ///
51  /// A null result is interpreted as success.
52  ///
53  /// A non-null result is interpreted as a heap-allocated string containing
54  /// an error message to report to the allocator (the allocator's
55  /// executor-side implementation code is responsible for freeing the error
56  /// string).
57  struct AllocActionCall {
61  };
62 
63  /// A pair of AllocActionCalls, one to be run at finalization time, one to be
64  /// run at deallocation time.
65  ///
66  /// AllocActionCallPairs should be constructed for paired operations (e.g.
67  /// __register_ehframe and __deregister_ehframe for eh-frame registration).
68  /// See comments for AllocActions for execution ordering.
69  ///
70  /// For unpaired operations one or the other member can be left unused, as
71  /// AllocationActionCalls with an FnAddr of zero will be skipped.
75  };
76 
77  /// A vector of allocation actions to be run for this allocation.
78  ///
79  /// Finalize allocations will be run in order at finalize time. Dealloc
80  /// actions will be run in reverse order at deallocation time.
81  using AllocActions = std::vector<AllocActionCallPair>;
82 
83  /// Represents a finalized allocation.
84  ///
85  /// Finalized allocations must be passed to the
86  /// JITLinkMemoryManager:deallocate method prior to being destroyed.
87  ///
88  /// The interpretation of the Address associated with the finalized allocation
89  /// is up to the memory manager implementation. Common options are using the
90  /// base address of the allocation, or the address of a memory management
91  /// object that tracks the allocation.
93  friend class JITLinkMemoryManager;
94 
95  public:
97 
98  FinalizedAlloc() = default;
100  assert(A != 0 && "Explicitly creating an invalid allocation?");
101  }
102  FinalizedAlloc(const FinalizedAlloc &) = delete;
104  Other.A = InvalidAddr;
105  }
106  FinalizedAlloc &operator=(const FinalizedAlloc &) = delete;
108  assert(A == InvalidAddr &&
109  "Cannot overwrite active finalized allocation");
110  std::swap(A, Other.A);
111  return *this;
112  }
114  assert(A == InvalidAddr && "Finalized allocation was not deallocated");
115  }
116 
117  /// FinalizedAllocs convert to false for default-constructed, and
118  /// true otherwise. Default-constructed allocs need not be deallocated.
119  explicit operator bool() const { return A != InvalidAddr; }
120 
121  /// Returns the address associated with this finalized allocation.
122  /// The allocation is unmodified.
123  JITTargetAddress getAddress() const { return A; }
124 
125  /// Returns the address associated with this finalized allocation and
126  /// resets this object to the default state.
127  /// This should only be used by allocators when deallocating memory.
129  JITTargetAddress Tmp = A;
130  A = InvalidAddr;
131  return Tmp;
132  }
133 
134  private:
136  };
137 
138  /// Represents an allocation which has not been finalized yet.
139  ///
140  /// InFlightAllocs manage both executor memory allocations and working
141  /// memory allocations.
142  ///
143  /// On finalization, the InFlightAlloc should transfer the content of
144  /// working memory into executor memory, apply memory protections, and
145  /// run any finalization functions.
146  ///
147  /// Working memory should be kept alive at least until one of the following
148  /// happens: (1) the InFlightAlloc instance is destroyed, (2) the
149  /// InFlightAlloc is abandoned, (3) finalized target memory is destroyed.
150  ///
151  /// If abandon is called then working memory and executor memory should both
152  /// be freed.
154  public:
157 
158  virtual ~InFlightAlloc();
159 
160  /// Called prior to finalization if the allocation should be abandoned.
161  virtual void abandon(OnAbandonedFunction OnAbandoned) = 0;
162 
163  /// Called to transfer working memory to the target and apply finalization.
164  virtual void finalize(OnFinalizedFunction OnFinalized) = 0;
165 
166  /// Synchronous convenience version of finalize.
168  std::promise<MSVCPExpected<FinalizedAlloc>> FinalizeResultP;
169  auto FinalizeResultF = FinalizeResultP.get_future();
170  finalize([&](Expected<FinalizedAlloc> Result) {
171  FinalizeResultP.set_value(std::move(Result));
172  });
173  return FinalizeResultF.get();
174  }
175  };
176 
177  /// Typedef for the argument to be passed to OnAllocatedFunction.
179 
180  /// Called when allocation has been completed.
182 
183  /// Called when deallocation has completed.
185 
186  virtual ~JITLinkMemoryManager();
187 
188  /// Start the allocation process.
189  ///
190  /// If the initial allocation is successful then the OnAllocated function will
191  /// be called with a std::unique_ptr<InFlightAlloc> value. If the assocation
192  /// is unsuccessful then the OnAllocated function will be called with an
193  /// Error.
194  virtual void allocate(const JITLinkDylib *JD, LinkGraph &G,
195  OnAllocatedFunction OnAllocated) = 0;
196 
197  /// Convenience function for blocking allocation.
199  std::promise<MSVCPExpected<std::unique_ptr<InFlightAlloc>>> AllocResultP;
200  auto AllocResultF = AllocResultP.get_future();
201  allocate(JD, G, [&](AllocResult Alloc) {
202  AllocResultP.set_value(std::move(Alloc));
203  });
204  return AllocResultF.get();
205  }
206 
207  /// Deallocate a list of allocation objects.
208  ///
209  /// Dealloc actions will be run in reverse order (from the end of the vector
210  /// to the start).
211  virtual void deallocate(std::vector<FinalizedAlloc> Allocs,
212  OnDeallocatedFunction OnDeallocated) = 0;
213 
214  /// Convenience function for deallocation of a single alloc.
215  void deallocate(FinalizedAlloc Alloc, OnDeallocatedFunction OnDeallocated) {
216  std::vector<FinalizedAlloc> Allocs;
217  Allocs.push_back(std::move(Alloc));
218  deallocate(std::move(Allocs), std::move(OnDeallocated));
219  }
220 
221  /// Convenience function for blocking deallocation.
222  Error deallocate(std::vector<FinalizedAlloc> Allocs) {
223  std::promise<MSVCPError> DeallocResultP;
224  auto DeallocResultF = DeallocResultP.get_future();
225  deallocate(std::move(Allocs),
226  [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });
227  return DeallocResultF.get();
228  }
229 
230  /// Convenience function for blocking deallocation of a single alloc.
232  std::vector<FinalizedAlloc> Allocs;
233  Allocs.push_back(std::move(Alloc));
234  return deallocate(std::move(Allocs));
235  }
236 };
237 
238 /// BasicLayout simplifies the implementation of JITLinkMemoryManagers.
239 ///
240 /// BasicLayout groups Sections into Segments based on their memory protection
241 /// and deallocation policies. JITLinkMemoryManagers can construct a BasicLayout
242 /// from a Graph, and then assign working memory and addresses to each of the
243 /// Segments. These addreses will be mapped back onto the Graph blocks in
244 /// the apply method.
245 class BasicLayout {
246 public:
247  /// The Alignment, ContentSize and ZeroFillSize of each segment will be
248  /// pre-filled from the Graph. Clients must set the Addr and WorkingMem fields
249  /// prior to calling apply.
250  //
251  // FIXME: The C++98 initializer is an attempt to work around compile failures
252  // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397.
253  // We should be able to switch this back to member initialization once that
254  // issue is fixed.
255  class Segment {
256  friend class BasicLayout;
257 
258  public:
260  : ContentSize(0), ZeroFillSize(0), Addr(0), WorkingMem(nullptr),
261  NextWorkingMemOffset(0) {}
263  size_t ContentSize;
266  char *WorkingMem = nullptr;
267 
268  private:
269  size_t NextWorkingMemOffset;
270  std::vector<Block *> ContentBlocks, ZeroFillBlocks;
271  };
272 
273  /// A convenience class that further groups segments based on memory
274  /// deallocation policy. This allows clients to make two slab allocations:
275  /// one for all standard segments, and one for all finalize segments.
279 
280  uint64_t total() const { return StandardSegs + FinalizeSegs; }
281  };
282 
283 private:
284  using SegmentMap = AllocGroupSmallMap<Segment>;
285 
286 public:
288 
289  /// Return a reference to the graph this allocation was created from.
290  LinkGraph &getGraph() { return G; }
291 
292  /// Returns the total number of required to allocate all segments (with each
293  /// segment padded out to page size) for all standard segments, and all
294  /// finalize segments.
295  ///
296  /// This is a convenience function for the common case where the segments will
297  /// be allocated contiguously.
298  ///
299  /// This function will return an error if any segment has an alignment that
300  /// is higher than a page.
303 
304  /// Returns an iterator over the segments of the layout.
306  return {Segments.begin(), Segments.end()};
307  }
308 
309  /// Apply the layout to the graph.
310  Error apply();
311 
312  /// Returns a reference to the AllocActions in the graph.
313  /// This convenience function saves callers from having to #include
314  /// LinkGraph.h if all they need are allocation actions.
316 
317 private:
318  LinkGraph &G;
319  SegmentMap Segments;
320 };
321 
322 /// A utility class for making simple allocations using JITLinkMemoryManager.
323 ///
324 /// SimpleSegementAlloc takes a mapping of AllocGroups to Segments and uses
325 /// this to create a LinkGraph with one Section (containing one Block) per
326 /// Segment. Clients can obtain a pointer to the working memory and executor
327 /// address of that block using the Segment's AllocGroup. Once memory has been
328 /// populated, clients can call finalize to finalize the memory.
330 public:
331  /// Describes a segment to be allocated.
332  struct Segment {
333  Segment() = default;
336 
337  size_t ContentSize = 0;
339  };
340 
341  /// Describes the segment working memory and executor address.
342  struct SegmentInfo {
345  };
346 
348 
350 
351  using OnFinalizedFunction =
353 
354  static void Create(JITLinkMemoryManager &MemMgr, const JITLinkDylib *JD,
355  SegmentMap Segments, OnCreatedFunction OnCreated);
356 
358  const JITLinkDylib *JD,
359  SegmentMap Segments);
360 
364 
365  /// Returns the SegmentInfo for the given group.
367 
368  /// Finalize all groups (async version).
369  void finalize(OnFinalizedFunction OnFinalized) {
370  Alloc->finalize(std::move(OnFinalized));
371  }
372 
373  /// Finalize all groups.
375  return Alloc->finalize();
376  }
377 
378 private:
380  std::unique_ptr<LinkGraph> G, AllocGroupSmallMap<Block *> ContentBlocks,
381  std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc);
382 
383  std::unique_ptr<LinkGraph> G;
384  AllocGroupSmallMap<Block *> ContentBlocks;
385  std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc;
386 };
387 
388 /// A JITLinkMemoryManager that allocates in-process memory.
390 public:
391  class IPInFlightAlloc;
392 
393  /// Attempts to auto-detect the host page size.
395 
396  /// Create an instance using the given page size.
398 
399  void allocate(const JITLinkDylib *JD, LinkGraph &G,
400  OnAllocatedFunction OnAllocated) override;
401 
402  // Use overloads from base class.
404 
405  void deallocate(std::vector<FinalizedAlloc> Alloc,
406  OnDeallocatedFunction OnDeallocated) override;
407 
408  // Use overloads from base class.
410 
411 private:
412  // FIXME: Use an in-place array instead of a vector for DeallocActions.
413  // There shouldn't need to be a heap alloc for this.
414  struct FinalizedAllocInfo {
415  sys::MemoryBlock StandardSegments;
416  std::vector<AllocActionCall> DeallocActions;
417  };
418 
419  FinalizedAlloc
420  createFinalizedAlloc(sys::MemoryBlock StandardSegments,
421  std::vector<AllocActionCall> DeallocActions);
422 
423  uint64_t PageSize;
424  std::mutex FinalizedAllocsMutex;
426 };
427 
428 } // end namespace jitlink
429 } // end namespace llvm
430 
431 #endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AllocatorList.h:23
JITSymbol.h
MemoryFlags.h
llvm::unique_function
unique_function is a type-erasing functor similar to std::function.
Definition: FunctionExtras.h:56
Allocator.h
Error.h
llvm::sys::MemoryBlock
This class encapsulates the notion of a memory block which has an address and a size.
Definition: Memory.h:31
MSVCErrorWorkarounds.h
llvm::Expected
Tagged union holding either a T or a Error.
Definition: APFloat.h:42
llvm::ARMBuildAttrs::Section
@ Section
Legacy Tags.
Definition: ARMBuildAttributes.h:78
PageSize
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)
llvm::MutableArrayRef< char >
llvm::Align
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39
G
const DataFlowGraph & G
Definition: RDFGraph.cpp:202
uint64_t
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
std::swap
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition: BitVector.h:840
A
* A
Definition: README_ALTIVEC.txt:89
Memory.h
llvm::RecyclingAllocator
RecyclingAllocator - This class wraps an Allocator, adding the functionality of recycling deleted obj...
Definition: RecyclingAllocator.h:26
llvm::Error
Lightweight error class with error context and mandatory checking.
Definition: Error.h:157
RecyclingAllocator.h
JITLinkDylib.h
llvm::iterator_range
A range adaptor for a pair of iterators.
Definition: iterator_range.h:30
llvm::pdb::PDB_SymType::Block
@ Block
Other
Optional< std::vector< StOtherPiece > > Other
Definition: ELFYAML.cpp:1191