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