Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:50

0001 //===-- JITLinkMemoryManager.h - JITLink mem manager interface --*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // Contains the JITLinkMemoryManager interface.
0010 //
0011 //===----------------------------------------------------------------------===//
0012 
0013 #ifndef LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
0014 #define LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H
0015 
0016 #include "llvm/ADT/FunctionExtras.h"
0017 #include "llvm/ADT/SmallVector.h"
0018 #include "llvm/ExecutionEngine/JITLink/JITLinkDylib.h"
0019 #include "llvm/ExecutionEngine/Orc/Shared/AllocationActions.h"
0020 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
0021 #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
0022 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h"
0023 #include "llvm/Support/Allocator.h"
0024 #include "llvm/Support/Error.h"
0025 #include "llvm/Support/MSVCErrorWorkarounds.h"
0026 #include "llvm/Support/Memory.h"
0027 #include "llvm/Support/RecyclingAllocator.h"
0028 #include "llvm/TargetParser/Triple.h"
0029 
0030 #include <cassert>
0031 #include <cstdint>
0032 #include <future>
0033 #include <mutex>
0034 
0035 namespace llvm {
0036 namespace jitlink {
0037 
0038 class Block;
0039 class LinkGraph;
0040 class Section;
0041 
0042 /// Manages allocations of JIT memory.
0043 ///
0044 /// Instances of this class may be accessed concurrently from multiple threads
0045 /// and their implemetations should include any necessary synchronization.
0046 class JITLinkMemoryManager {
0047 public:
0048 
0049   /// Represents a finalized allocation.
0050   ///
0051   /// Finalized allocations must be passed to the
0052   /// JITLinkMemoryManager:deallocate method prior to being destroyed.
0053   ///
0054   /// The interpretation of the Address associated with the finalized allocation
0055   /// is up to the memory manager implementation. Common options are using the
0056   /// base address of the allocation, or the address of a memory management
0057   /// object that tracks the allocation.
0058   class FinalizedAlloc {
0059     friend class JITLinkMemoryManager;
0060 
0061     static constexpr auto InvalidAddr = ~uint64_t(0);
0062 
0063   public:
0064     FinalizedAlloc() = default;
0065     explicit FinalizedAlloc(orc::ExecutorAddr A) : A(A) {
0066       assert(A.getValue() != InvalidAddr &&
0067              "Explicitly creating an invalid allocation?");
0068     }
0069     FinalizedAlloc(const FinalizedAlloc &) = delete;
0070     FinalizedAlloc(FinalizedAlloc &&Other) : A(Other.A) {
0071       Other.A.setValue(InvalidAddr);
0072     }
0073     FinalizedAlloc &operator=(const FinalizedAlloc &) = delete;
0074     FinalizedAlloc &operator=(FinalizedAlloc &&Other) {
0075       assert(A.getValue() == InvalidAddr &&
0076              "Cannot overwrite active finalized allocation");
0077       std::swap(A, Other.A);
0078       return *this;
0079     }
0080     ~FinalizedAlloc() {
0081       assert(A.getValue() == InvalidAddr &&
0082              "Finalized allocation was not deallocated");
0083     }
0084 
0085     /// FinalizedAllocs convert to false for default-constructed, and
0086     /// true otherwise. Default-constructed allocs need not be deallocated.
0087     explicit operator bool() const { return A.getValue() != InvalidAddr; }
0088 
0089     /// Returns the address associated with this finalized allocation.
0090     /// The allocation is unmodified.
0091     orc::ExecutorAddr getAddress() const { return A; }
0092 
0093     /// Returns the address associated with this finalized allocation and
0094     /// resets this object to the default state.
0095     /// This should only be used by allocators when deallocating memory.
0096     orc::ExecutorAddr release() {
0097       orc::ExecutorAddr Tmp = A;
0098       A.setValue(InvalidAddr);
0099       return Tmp;
0100     }
0101 
0102   private:
0103     orc::ExecutorAddr A{InvalidAddr};
0104   };
0105 
0106   /// Represents an allocation which has not been finalized yet.
0107   ///
0108   /// InFlightAllocs manage both executor memory allocations and working
0109   /// memory allocations.
0110   ///
0111   /// On finalization, the InFlightAlloc should transfer the content of
0112   /// working memory into executor memory, apply memory protections, and
0113   /// run any finalization functions.
0114   ///
0115   /// Working memory should be kept alive at least until one of the following
0116   /// happens: (1) the InFlightAlloc instance is destroyed, (2) the
0117   /// InFlightAlloc is abandoned, (3) finalized target memory is destroyed.
0118   ///
0119   /// If abandon is called then working memory and executor memory should both
0120   /// be freed.
0121   class InFlightAlloc {
0122   public:
0123     using OnFinalizedFunction = unique_function<void(Expected<FinalizedAlloc>)>;
0124     using OnAbandonedFunction = unique_function<void(Error)>;
0125 
0126     virtual ~InFlightAlloc();
0127 
0128     /// Called prior to finalization if the allocation should be abandoned.
0129     virtual void abandon(OnAbandonedFunction OnAbandoned) = 0;
0130 
0131     /// Called to transfer working memory to the target and apply finalization.
0132     virtual void finalize(OnFinalizedFunction OnFinalized) = 0;
0133 
0134     /// Synchronous convenience version of finalize.
0135     Expected<FinalizedAlloc> finalize() {
0136       std::promise<MSVCPExpected<FinalizedAlloc>> FinalizeResultP;
0137       auto FinalizeResultF = FinalizeResultP.get_future();
0138       finalize([&](Expected<FinalizedAlloc> Result) {
0139         FinalizeResultP.set_value(std::move(Result));
0140       });
0141       return FinalizeResultF.get();
0142     }
0143   };
0144 
0145   /// Typedef for the argument to be passed to OnAllocatedFunction.
0146   using AllocResult = Expected<std::unique_ptr<InFlightAlloc>>;
0147 
0148   /// Called when allocation has been completed.
0149   using OnAllocatedFunction = unique_function<void(AllocResult)>;
0150 
0151   /// Called when deallocation has completed.
0152   using OnDeallocatedFunction = unique_function<void(Error)>;
0153 
0154   virtual ~JITLinkMemoryManager();
0155 
0156   /// Start the allocation process.
0157   ///
0158   /// If the initial allocation is successful then the OnAllocated function will
0159   /// be called with a std::unique_ptr<InFlightAlloc> value. If the assocation
0160   /// is unsuccessful then the OnAllocated function will be called with an
0161   /// Error.
0162   virtual void allocate(const JITLinkDylib *JD, LinkGraph &G,
0163                         OnAllocatedFunction OnAllocated) = 0;
0164 
0165   /// Convenience function for blocking allocation.
0166   AllocResult allocate(const JITLinkDylib *JD, LinkGraph &G) {
0167     std::promise<MSVCPExpected<std::unique_ptr<InFlightAlloc>>> AllocResultP;
0168     auto AllocResultF = AllocResultP.get_future();
0169     allocate(JD, G, [&](AllocResult Alloc) {
0170       AllocResultP.set_value(std::move(Alloc));
0171     });
0172     return AllocResultF.get();
0173   }
0174 
0175   /// Deallocate a list of allocation objects.
0176   ///
0177   /// Dealloc actions will be run in reverse order (from the end of the vector
0178   /// to the start).
0179   virtual void deallocate(std::vector<FinalizedAlloc> Allocs,
0180                           OnDeallocatedFunction OnDeallocated) = 0;
0181 
0182   /// Convenience function for deallocation of a single alloc.
0183   void deallocate(FinalizedAlloc Alloc, OnDeallocatedFunction OnDeallocated) {
0184     std::vector<FinalizedAlloc> Allocs;
0185     Allocs.push_back(std::move(Alloc));
0186     deallocate(std::move(Allocs), std::move(OnDeallocated));
0187   }
0188 
0189   /// Convenience function for blocking deallocation.
0190   Error deallocate(std::vector<FinalizedAlloc> Allocs) {
0191     std::promise<MSVCPError> DeallocResultP;
0192     auto DeallocResultF = DeallocResultP.get_future();
0193     deallocate(std::move(Allocs),
0194                [&](Error Err) { DeallocResultP.set_value(std::move(Err)); });
0195     return DeallocResultF.get();
0196   }
0197 
0198   /// Convenience function for blocking deallocation of a single alloc.
0199   Error deallocate(FinalizedAlloc Alloc) {
0200     std::vector<FinalizedAlloc> Allocs;
0201     Allocs.push_back(std::move(Alloc));
0202     return deallocate(std::move(Allocs));
0203   }
0204 };
0205 
0206 /// BasicLayout simplifies the implementation of JITLinkMemoryManagers.
0207 ///
0208 /// BasicLayout groups Sections into Segments based on their memory protection
0209 /// and deallocation policies. JITLinkMemoryManagers can construct a BasicLayout
0210 /// from a Graph, and then assign working memory and addresses to each of the
0211 /// Segments. These addreses will be mapped back onto the Graph blocks in
0212 /// the apply method.
0213 class BasicLayout {
0214 public:
0215   /// The Alignment, ContentSize and ZeroFillSize of each segment will be
0216   /// pre-filled from the Graph. Clients must set the Addr and WorkingMem fields
0217   /// prior to calling apply.
0218   //
0219   // FIXME: The C++98 initializer is an attempt to work around compile failures
0220   // due to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1397.
0221   // We should be able to switch this back to member initialization once that
0222   // issue is fixed.
0223   class Segment {
0224     friend class BasicLayout;
0225 
0226   public:
0227     Segment()
0228         : ContentSize(0), ZeroFillSize(0), Addr(0), WorkingMem(nullptr),
0229           NextWorkingMemOffset(0) {}
0230     Align Alignment;
0231     size_t ContentSize;
0232     uint64_t ZeroFillSize;
0233     orc::ExecutorAddr Addr;
0234     char *WorkingMem = nullptr;
0235 
0236   private:
0237     size_t NextWorkingMemOffset;
0238     std::vector<Block *> ContentBlocks, ZeroFillBlocks;
0239   };
0240 
0241   /// A convenience class that further groups segments based on memory
0242   /// deallocation policy. This allows clients to make two slab allocations:
0243   /// one for all standard segments, and one for all finalize segments.
0244   struct ContiguousPageBasedLayoutSizes {
0245     uint64_t StandardSegs = 0;
0246     uint64_t FinalizeSegs = 0;
0247 
0248     uint64_t total() const { return StandardSegs + FinalizeSegs; }
0249   };
0250 
0251 private:
0252   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
0253 
0254 public:
0255   BasicLayout(LinkGraph &G);
0256 
0257   /// Return a reference to the graph this allocation was created from.
0258   LinkGraph &getGraph() { return G; }
0259 
0260   /// Returns the total number of required to allocate all segments (with each
0261   /// segment padded out to page size) for all standard segments, and all
0262   /// finalize segments.
0263   ///
0264   /// This is a convenience function for the common case where the segments will
0265   /// be allocated contiguously.
0266   ///
0267   /// This function will return an error if any segment has an alignment that
0268   /// is higher than a page.
0269   Expected<ContiguousPageBasedLayoutSizes>
0270   getContiguousPageBasedLayoutSizes(uint64_t PageSize);
0271 
0272   /// Returns an iterator over the segments of the layout.
0273   iterator_range<SegmentMap::iterator> segments() {
0274     return {Segments.begin(), Segments.end()};
0275   }
0276 
0277   /// Apply the layout to the graph.
0278   Error apply();
0279 
0280   /// Returns a reference to the AllocActions in the graph.
0281   /// This convenience function saves callers from having to #include
0282   /// LinkGraph.h if all they need are allocation actions.
0283   orc::shared::AllocActions &graphAllocActions();
0284 
0285 private:
0286   LinkGraph &G;
0287   SegmentMap Segments;
0288 };
0289 
0290 /// A utility class for making simple allocations using JITLinkMemoryManager.
0291 ///
0292 /// SimpleSegementAlloc takes a mapping of AllocGroups to Segments and uses
0293 /// this to create a LinkGraph with one Section (containing one Block) per
0294 /// Segment. Clients can obtain a pointer to the working memory and executor
0295 /// address of that block using the Segment's AllocGroup. Once memory has been
0296 /// populated, clients can call finalize to finalize the memory.
0297 ///
0298 /// Note: Segments with MemLifetime::NoAlloc are not permitted, since they would
0299 /// not be useful, and their presence is likely to indicate a bug.
0300 class SimpleSegmentAlloc {
0301 public:
0302   /// Describes a segment to be allocated.
0303   struct Segment {
0304     Segment() = default;
0305     Segment(size_t ContentSize, Align ContentAlign)
0306         : ContentSize(ContentSize), ContentAlign(ContentAlign) {}
0307 
0308     size_t ContentSize = 0;
0309     Align ContentAlign;
0310   };
0311 
0312   /// Describes the segment working memory and executor address.
0313   struct SegmentInfo {
0314     orc::ExecutorAddr Addr;
0315     MutableArrayRef<char> WorkingMem;
0316   };
0317 
0318   using SegmentMap = orc::AllocGroupSmallMap<Segment>;
0319 
0320   using OnCreatedFunction = unique_function<void(Expected<SimpleSegmentAlloc>)>;
0321 
0322   using OnFinalizedFunction =
0323       JITLinkMemoryManager::InFlightAlloc::OnFinalizedFunction;
0324 
0325   static void Create(JITLinkMemoryManager &MemMgr,
0326                      std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
0327                      const JITLinkDylib *JD, SegmentMap Segments,
0328                      OnCreatedFunction OnCreated);
0329 
0330   static Expected<SimpleSegmentAlloc>
0331   Create(JITLinkMemoryManager &MemMgr,
0332          std::shared_ptr<orc::SymbolStringPool> SSP, Triple TT,
0333          const JITLinkDylib *JD, SegmentMap Segments);
0334 
0335   SimpleSegmentAlloc(SimpleSegmentAlloc &&);
0336   SimpleSegmentAlloc &operator=(SimpleSegmentAlloc &&);
0337   ~SimpleSegmentAlloc();
0338 
0339   /// Returns the SegmentInfo for the given group.
0340   SegmentInfo getSegInfo(orc::AllocGroup AG);
0341 
0342   /// Finalize all groups (async version).
0343   void finalize(OnFinalizedFunction OnFinalized) {
0344     Alloc->finalize(std::move(OnFinalized));
0345   }
0346 
0347   /// Finalize all groups.
0348   Expected<JITLinkMemoryManager::FinalizedAlloc> finalize() {
0349     return Alloc->finalize();
0350   }
0351 
0352 private:
0353   SimpleSegmentAlloc(
0354       std::unique_ptr<LinkGraph> G,
0355       orc::AllocGroupSmallMap<Block *> ContentBlocks,
0356       std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc);
0357 
0358   std::unique_ptr<LinkGraph> G;
0359   orc::AllocGroupSmallMap<Block *> ContentBlocks;
0360   std::unique_ptr<JITLinkMemoryManager::InFlightAlloc> Alloc;
0361 };
0362 
0363 /// A JITLinkMemoryManager that allocates in-process memory.
0364 class InProcessMemoryManager : public JITLinkMemoryManager {
0365 public:
0366   class IPInFlightAlloc;
0367 
0368   /// Attempts to auto-detect the host page size.
0369   static Expected<std::unique_ptr<InProcessMemoryManager>> Create();
0370 
0371   /// Create an instance using the given page size.
0372   InProcessMemoryManager(uint64_t PageSize) : PageSize(PageSize) {
0373     assert(isPowerOf2_64(PageSize) && "PageSize must be a power of 2");
0374   }
0375 
0376   void allocate(const JITLinkDylib *JD, LinkGraph &G,
0377                 OnAllocatedFunction OnAllocated) override;
0378 
0379   // Use overloads from base class.
0380   using JITLinkMemoryManager::allocate;
0381 
0382   void deallocate(std::vector<FinalizedAlloc> Alloc,
0383                   OnDeallocatedFunction OnDeallocated) override;
0384 
0385   // Use overloads from base class.
0386   using JITLinkMemoryManager::deallocate;
0387 
0388 private:
0389   // FIXME: Use an in-place array instead of a vector for DeallocActions.
0390   //        There shouldn't need to be a heap alloc for this.
0391   struct FinalizedAllocInfo {
0392     sys::MemoryBlock StandardSegments;
0393     std::vector<orc::shared::WrapperFunctionCall> DeallocActions;
0394   };
0395 
0396   FinalizedAlloc createFinalizedAlloc(
0397       sys::MemoryBlock StandardSegments,
0398       std::vector<orc::shared::WrapperFunctionCall> DeallocActions);
0399 
0400   uint64_t PageSize;
0401   std::mutex FinalizedAllocsMutex;
0402   RecyclingAllocator<BumpPtrAllocator, FinalizedAllocInfo> FinalizedAllocInfos;
0403 };
0404 
0405 } // end namespace jitlink
0406 } // end namespace llvm
0407 
0408 #endif // LLVM_EXECUTIONENGINE_JITLINK_JITLINKMEMORYMANAGER_H