JITLinkMemoryManager.h 14 KB

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