Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Blame | Last modification | View Log | Download | RSS feed

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