[llvm] 2cc64df - [JITLink][ORC] Rename MemDeallocPolicy to MemLifetimePolicy, add NoAlloc option.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 17 12:38:48 PDT 2023


Author: Lang Hames
Date: 2023-03-17T12:35:41-07:00
New Revision: 2cc64df0bd6a802eab592dbc282463c3e4a4281c

URL: https://github.com/llvm/llvm-project/commit/2cc64df0bd6a802eab592dbc282463c3e4a4281c
DIFF: https://github.com/llvm/llvm-project/commit/2cc64df0bd6a802eab592dbc282463c3e4a4281c.diff

LOG: [JITLink][ORC] Rename MemDeallocPolicy to MemLifetimePolicy, add NoAlloc option.

The original MemDeallocPolicy had two options:
* Standard: allocated memory lives until deallocated or abandoned.
* Finalize: allocated memory lives until all finalize actions have been run,
            then is destroyed.

This patch introduces a new 'NoAlloc' option. NoAlloc indicates that the
section should be ignored by the JITLinkMemoryManager -- the memory manager
should allocate neither working memory nor executor address space to blocks in
NoAlloc sections. The NoAlloc option is intended to support metadata sections
(e.g. debug info) that we want to keep in the graph and have fixed up if
necessary, but don't want allocated or transmitted to the executor (or we want
that allocation and transmission to be managed manually by plugins).

Since NoAlloc blocks are ignored by the JITLinkMemoryManager they will not have
working memory allocated to them by default post-allocation. Clients wishing to
modify the content of a block in a NoAlloc section should call
`Block::getMutableMemory(LinkGraph&)` to get writable memory allocated on the
LinkGraph's allocator (this memory will exist for the lifetime of the graph).
If no client requests mutable memory prior to the fixup phase then the generic
link algorithm will do so when it encounters the first edge in any given block.

Addresses of blocks in NoAlloc sections are initialized by the LinkGraph
creator (a LinkGraphBuilder, if the graph is generated from an object file),
and should not be modified by the JITLinkMemoryManager. Plugins are responsible
for updating addresses if they add/remove content from these sections. The
meaning of addresses in NoAlloc-sections is backend/plugin defined, but for
fixup purposes they will be treated the same as addresses in Standard/Finalize
sections. References from Standard/Finalize sections to NoAlloc sections are
expected to be common (these represent metadata tracking executor addresses).
References from NoAlloc sections to Standard/Finalize sections are expected to
be rare/non-existent (they would represent JIT'd code / data tracking metadata
in the controller, which would be surprising). LinkGraphBuilders and specific
backends may impose additional constraints on edges between Standard/Finalize
and NoAlloc sections where required for correctness.

Differential Revision: https://reviews.llvm.org/D146183

Added: 
    

Modified: 
    llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
    llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h
    llvm/include/llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h
    llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
    llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
    llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h
    llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h
    llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp
    llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp
    llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp
    llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
    llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
    llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp
    llvm/tools/llvm-jitlink/llvm-jitlink.cpp
    llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp
    llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
index 1123b35a4cd76..fdf5ef8046a98 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLink.h
@@ -704,11 +704,11 @@ class Section {
   /// Set the protection flags for this section.
   void setMemProt(orc::MemProt Prot) { this->Prot = Prot; }
 
-  /// Get the deallocation policy for this section.
-  orc::MemDeallocPolicy getMemDeallocPolicy() const { return MDP; }
+  /// Get the memory lifetime policy for this section.
+  orc::MemLifetimePolicy getMemLifetimePolicy() const { return MLP; }
 
-  /// Set the deallocation policy for this section.
-  void setMemDeallocPolicy(orc::MemDeallocPolicy MDP) { this->MDP = MDP; }
+  /// Set the memory lifetime policy for this section.
+  void setMemLifetimePolicy(orc::MemLifetimePolicy MLP) { this->MLP = MLP; }
 
   /// Returns the ordinal for this section.
   SectionOrdinal getOrdinal() const { return SecOrdinal; }
@@ -773,7 +773,7 @@ class Section {
 
   StringRef Name;
   orc::MemProt Prot;
-  orc::MemDeallocPolicy MDP = orc::MemDeallocPolicy::Standard;
+  orc::MemLifetimePolicy MLP = orc::MemLifetimePolicy::Standard;
   SectionOrdinal SecOrdinal = 0;
   BlockSet Blocks;
   SymbolSet Symbols;

diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h b/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h
index 6ef4a0bd0c982..09e0d71cf0bd2 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h
@@ -291,6 +291,9 @@ class BasicLayout {
 /// Segment. Clients can obtain a pointer to the working memory and executor
 /// address of that block using the Segment's AllocGroup. Once memory has been
 /// populated, clients can call finalize to finalize the memory.
+///
+/// Note: Segments with MemLifetimePolicy::NoAlloc are not permitted, since
+/// they would not be useful, and their presence is likely to indicate a bug.
 class SimpleSegmentAlloc {
 public:
   /// Describes a segment to be allocated.

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h
index 2642e6c241b6e..c20366cfbb388 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h
@@ -65,26 +65,43 @@ inline MemProt fromSysMemoryProtectionFlags(sys::Memory::ProtectionFlags PF) {
   return MP;
 }
 
-/// Describes a memory deallocation policy for memory to be allocated by a
+/// Describes a memory lifetime policy for memory to be allocated by a
 /// JITLinkMemoryManager.
 ///
 /// All memory allocated by a call to JITLinkMemoryManager::allocate should be
 /// deallocated if a call is made to
 /// JITLinkMemoryManager::InFlightAllocation::abandon. The policies below apply
 /// to finalized allocations.
-enum class MemDeallocPolicy {
-  /// Standard memory should be deallocated when the deallocate method is called
-  /// for the finalized allocation.
+enum class MemLifetimePolicy {
+  /// Standard memory should be allocated by the allocator and then deallocated
+  /// when the deallocate method is called for the finalized allocation.
   Standard,
 
-  /// Finalize memory should be overwritten and then deallocated after all
-  /// finalization functions have been run.
-  Finalize
+  /// Finalize memory should be allocated by the allocator, and then be
+  /// overwritten and deallocated after all finalization functions have been
+  /// run.
+  Finalize,
+
+  /// NoAlloc memory should not be allocated by the JITLinkMemoryManager at
+  /// all. It is used for sections that don't need to be transferred to the
+  /// executor process, typically metadata sections.
+  NoAlloc
 };
 
 /// Print a MemDeallocPolicy.
-inline raw_ostream &operator<<(raw_ostream &OS, MemDeallocPolicy MDP) {
-  return OS << (MDP == MemDeallocPolicy::Standard ? "standard" : "finalize");
+inline raw_ostream &operator<<(raw_ostream &OS, MemLifetimePolicy MLP) {
+  switch (MLP) {
+  case MemLifetimePolicy::Standard:
+    OS << "standard";
+    break;
+  case MemLifetimePolicy::Finalize:
+    OS << "finalize";
+    break;
+  case MemLifetimePolicy::NoAlloc:
+    OS << "noalloc";
+    break;
+  }
+  return OS;
 }
 
 /// A pair of memory protections and allocation policies.
@@ -95,34 +112,34 @@ class AllocGroup {
 
   using underlying_type = uint8_t;
   static constexpr unsigned BitsForProt = 3;
-  static constexpr unsigned BitsForDeallocPolicy = 1;
+  static constexpr unsigned BitsForLifetimePolicy = 2;
   static constexpr unsigned MaxIdentifiers =
-      1U << (BitsForProt + BitsForDeallocPolicy);
+      1U << (BitsForProt + BitsForLifetimePolicy);
 
 public:
   static constexpr unsigned NumGroups = MaxIdentifiers;
 
   /// Create a default AllocGroup. No memory protections, standard
-  /// deallocation policy.
+  /// lifetime policy.
   AllocGroup() = default;
 
   /// Create an AllocGroup from a MemProt only -- uses
-  /// MemoryDeallocationPolicy::Standard.
+  /// MemLifetimePolicy::Standard.
   AllocGroup(MemProt MP) : Id(static_cast<underlying_type>(MP)) {}
 
-  /// Create an AllocGroup from a MemProt and a MemoryDeallocationPolicy.
-  AllocGroup(MemProt MP, MemDeallocPolicy MDP)
+  /// Create an AllocGroup from a MemProt and a MemLifetimePolicy.
+  AllocGroup(MemProt MP, MemLifetimePolicy MLP)
       : Id(static_cast<underlying_type>(MP) |
-           (static_cast<underlying_type>(MDP) << BitsForProt)) {}
+           (static_cast<underlying_type>(MLP) << BitsForProt)) {}
 
   /// Returns the MemProt for this group.
   MemProt getMemProt() const {
     return static_cast<MemProt>(Id & ((1U << BitsForProt) - 1));
   }
 
-  /// Returns the MemoryDeallocationPolicy for this group.
-  MemDeallocPolicy getMemDeallocPolicy() const {
-    return static_cast<MemDeallocPolicy>(Id >> BitsForProt);
+  /// Returns the MemLifetimePolicy for this group.
+  MemLifetimePolicy getMemLifetimePolicy() const {
+    return static_cast<MemLifetimePolicy>(Id >> BitsForProt);
   }
 
   friend bool operator==(const AllocGroup &LHS, const AllocGroup &RHS) {
@@ -186,7 +203,7 @@ template <typename T> class AllocGroupSmallMap {
 
 /// Print an AllocGroup.
 inline raw_ostream &operator<<(raw_ostream &OS, AllocGroup AG) {
-  return OS << '(' << AG.getMemProt() << ", " << AG.getMemDeallocPolicy()
+  return OS << '(' << AG.getMemProt() << ", " << AG.getMemLifetimePolicy()
             << ')';
 }
 

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
index 565fb5477c4af..09c73db44a947 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h
@@ -30,8 +30,24 @@ namespace llvm {
 namespace orc {
 namespace tpctypes {
 
+struct RemoteAllocGroup {
+  RemoteAllocGroup() = default;
+  RemoteAllocGroup(MemProt Prot) : Prot(Prot) {}
+  RemoteAllocGroup(MemProt Prot, bool FinalizeLifetime)
+      : Prot(Prot), FinalizeLifetime(FinalizeLifetime) {}
+  RemoteAllocGroup(const AllocGroup &AG) : Prot(AG.getMemProt()) {
+    assert(AG.getMemLifetimePolicy() != orc::MemLifetimePolicy::NoAlloc &&
+           "Cannot use no-alloc memory in a remote alloc request");
+    FinalizeLifetime =
+        AG.getMemLifetimePolicy() == orc::MemLifetimePolicy::Finalize;
+  }
+
+  MemProt Prot;
+  bool FinalizeLifetime = false;
+};
+
 struct SegFinalizeRequest {
-  AllocGroup AG;
+  RemoteAllocGroup RAG;
   ExecutorAddr Addr;
   uint64_t Size;
   ArrayRef<char> Content;
@@ -43,7 +59,7 @@ struct FinalizeRequest {
 };
 
 struct SharedMemorySegFinalizeRequest {
-  AllocGroup AG;
+  RemoteAllocGroup RAG;
   ExecutorAddr Addr;
   uint64_t Size;
 };
@@ -93,16 +109,16 @@ using LookupResult = std::vector<ExecutorAddr>;
 
 namespace shared {
 
-class SPSAllocGroup {};
+class SPSRemoteAllocGroup;
 
 using SPSSegFinalizeRequest =
-    SPSTuple<SPSAllocGroup, SPSExecutorAddr, uint64_t, SPSSequence<char>>;
+    SPSTuple<SPSRemoteAllocGroup, SPSExecutorAddr, uint64_t, SPSSequence<char>>;
 
 using SPSFinalizeRequest = SPSTuple<SPSSequence<SPSSegFinalizeRequest>,
                                     SPSSequence<SPSAllocActionCallPair>>;
 
 using SPSSharedMemorySegFinalizeRequest =
-    SPSTuple<SPSAllocGroup, SPSExecutorAddr, uint64_t>;
+    SPSTuple<SPSRemoteAllocGroup, SPSExecutorAddr, uint64_t>;
 
 using SPSSharedMemoryFinalizeRequest =
     SPSTuple<SPSSequence<SPSSharedMemorySegFinalizeRequest>,
@@ -118,7 +134,8 @@ using SPSMemoryAccessUInt64Write = SPSMemoryAccessUIntWrite<uint64_t>;
 
 using SPSMemoryAccessBufferWrite = SPSTuple<SPSExecutorAddr, SPSSequence<char>>;
 
-template <> class SPSSerializationTraits<SPSAllocGroup, AllocGroup> {
+template <>
+class SPSSerializationTraits<SPSRemoteAllocGroup, tpctypes::RemoteAllocGroup> {
   enum WireBits {
     ReadBit = 1 << 0,
     WriteBit = 1 << 1,
@@ -127,25 +144,26 @@ template <> class SPSSerializationTraits<SPSAllocGroup, AllocGroup> {
   };
 
 public:
-  static size_t size(const AllocGroup &AG) {
+  static size_t size(const tpctypes::RemoteAllocGroup &RAG) {
     // All AllocGroup values encode to the same size.
     return SPSArgList<uint8_t>::size(uint8_t(0));
   }
 
-  static bool serialize(SPSOutputBuffer &OB, const AllocGroup &AG) {
+  static bool serialize(SPSOutputBuffer &OB,
+                        const tpctypes::RemoteAllocGroup &RAG) {
     uint8_t WireValue = 0;
-    if ((AG.getMemProt() & MemProt::Read) != MemProt::None)
+    if ((RAG.Prot & MemProt::Read) != MemProt::None)
       WireValue |= ReadBit;
-    if ((AG.getMemProt() & MemProt::Write) != MemProt::None)
+    if ((RAG.Prot & MemProt::Write) != MemProt::None)
       WireValue |= WriteBit;
-    if ((AG.getMemProt() & MemProt::Exec) != MemProt::None)
+    if ((RAG.Prot & MemProt::Exec) != MemProt::None)
       WireValue |= ExecBit;
-    if (AG.getMemDeallocPolicy() == MemDeallocPolicy::Finalize)
+    if (RAG.FinalizeLifetime)
       WireValue |= FinalizeBit;
     return SPSArgList<uint8_t>::serialize(OB, WireValue);
   }
 
-  static bool deserialize(SPSInputBuffer &IB, AllocGroup &AG) {
+  static bool deserialize(SPSInputBuffer &IB, tpctypes::RemoteAllocGroup &RAG) {
     uint8_t Val;
     if (!SPSArgList<uint8_t>::deserialize(IB, Val))
       return false;
@@ -156,9 +174,8 @@ template <> class SPSSerializationTraits<SPSAllocGroup, AllocGroup> {
       MP |= MemProt::Write;
     if (Val & ExecBit)
       MP |= MemProt::Exec;
-    MemDeallocPolicy MDP = (Val & FinalizeBit) ? MemDeallocPolicy::Finalize
-                                               : MemDeallocPolicy::Standard;
-    AG = AllocGroup(MP, MDP);
+    bool FinalizeLifetime = (Val & FinalizeBit) ? true : false;
+    RAG = {MP, FinalizeLifetime};
     return true;
   }
 };
@@ -170,17 +187,17 @@ class SPSSerializationTraits<SPSSegFinalizeRequest,
 
 public:
   static size_t size(const tpctypes::SegFinalizeRequest &SFR) {
-    return SFRAL::size(SFR.AG, SFR.Addr, SFR.Size, SFR.Content);
+    return SFRAL::size(SFR.RAG, SFR.Addr, SFR.Size, SFR.Content);
   }
 
   static bool serialize(SPSOutputBuffer &OB,
                         const tpctypes::SegFinalizeRequest &SFR) {
-    return SFRAL::serialize(OB, SFR.AG, SFR.Addr, SFR.Size, SFR.Content);
+    return SFRAL::serialize(OB, SFR.RAG, SFR.Addr, SFR.Size, SFR.Content);
   }
 
   static bool deserialize(SPSInputBuffer &IB,
                           tpctypes::SegFinalizeRequest &SFR) {
-    return SFRAL::deserialize(IB, SFR.AG, SFR.Addr, SFR.Size, SFR.Content);
+    return SFRAL::deserialize(IB, SFR.RAG, SFR.Addr, SFR.Size, SFR.Content);
   }
 };
 
@@ -210,17 +227,17 @@ class SPSSerializationTraits<SPSSharedMemorySegFinalizeRequest,
 
 public:
   static size_t size(const tpctypes::SharedMemorySegFinalizeRequest &SFR) {
-    return SFRAL::size(SFR.AG, SFR.Addr, SFR.Size);
+    return SFRAL::size(SFR.RAG, SFR.Addr, SFR.Size);
   }
 
   static bool serialize(SPSOutputBuffer &OB,
                         const tpctypes::SharedMemorySegFinalizeRequest &SFR) {
-    return SFRAL::serialize(OB, SFR.AG, SFR.Addr, SFR.Size);
+    return SFRAL::serialize(OB, SFR.RAG, SFR.Addr, SFR.Size);
   }
 
   static bool deserialize(SPSInputBuffer &IB,
                           tpctypes::SharedMemorySegFinalizeRequest &SFR) {
-    return SFRAL::deserialize(IB, SFR.AG, SFR.Addr, SFR.Size);
+    return SFRAL::deserialize(IB, SFR.RAG, SFR.Addr, SFR.Size);
   }
 };
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
index 481d6e72b245e..fe6252f9aa1f9 100644
--- a/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/COFFLinkGraphBuilder.cpp
@@ -152,8 +152,11 @@ Error COFFLinkGraphBuilder::graphifySections() {
 
     // Look for existing sections first.
     auto *GraphSec = G->findSectionByName(SectionName);
-    if (!GraphSec)
+    if (!GraphSec) {
       GraphSec = &G->createSection(SectionName, Prot);
+      if ((*Sec)->Characteristics & COFF::IMAGE_SCN_LNK_REMOVE)
+        GraphSec->setMemLifetimePolicy(orc::MemLifetimePolicy::NoAlloc);
+    }
     if (GraphSec->getMemProt() != Prot)
       return make_error<JITLinkError>("MemProt should match");
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h b/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h
index 953a9f512784b..9393607b6796e 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h
+++ b/llvm/lib/ExecutionEngine/JITLink/ELFLinkGraphBuilder.h
@@ -319,16 +319,6 @@ template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySections() {
       continue;
     }
 
-    // Skip non-SHF_ALLOC sections
-    if (!(Sec.sh_flags & ELF::SHF_ALLOC)) {
-      LLVM_DEBUG({
-        dbgs() << "    " << SecIndex << ": \"" << *Name
-               << "\" is not an SHF_ALLOC section: "
-                  "No graph section will be created.\n";
-      });
-      continue;
-    }
-
     LLVM_DEBUG({
       dbgs() << "    " << SecIndex << ": Creating section for \"" << *Name
              << "\"\n";
@@ -343,8 +333,19 @@ template <typename ELFT> Error ELFLinkGraphBuilder<ELFT>::graphifySections() {
 
     // Look for existing sections first.
     auto *GraphSec = G->findSectionByName(*Name);
-    if (!GraphSec)
+    if (!GraphSec) {
       GraphSec = &G->createSection(*Name, Prot);
+      // Non-SHF_ALLOC sections get NoAlloc memory lifetimes.
+      if (!(Sec.sh_flags & ELF::SHF_ALLOC)) {
+        GraphSec->setMemLifetimePolicy(orc::MemLifetimePolicy::NoAlloc);
+        LLVM_DEBUG({
+          dbgs() << "    " << SecIndex << ": \"" << *Name
+                 << "\" is not a SHF_ALLOC section. Using NoAlloc lifetime";
+        });
+      }
+      continue;
+    }
+
     assert(GraphSec->getMemProt() == Prot && "MemProt should match");
 
     Block *B = nullptr;

diff  --git a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h
index 2c92445265364..e69eddd6e1194 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h
+++ b/llvm/lib/ExecutionEngine/JITLink/JITLinkGeneric.h
@@ -123,26 +123,47 @@ template <typename LinkerImpl> class JITLinker : public JITLinkerBase {
   Error fixUpBlocks(LinkGraph &G) const override {
     LLVM_DEBUG(dbgs() << "Fixing up blocks:\n");
 
-    for (auto *B : G.blocks()) {
-      LLVM_DEBUG(dbgs() << "  " << *B << ":\n");
-
-      // Copy Block data and apply fixups.
-      LLVM_DEBUG(dbgs() << "    Applying fixups.\n");
-      assert((!B->isZeroFill() || all_of(B->edges(),
-                                         [](const Edge &E) {
-                                           return E.getKind() ==
-                                                  Edge::KeepAlive;
-                                         })) &&
-             "Non-KeepAlive edges in zero-fill block?");
-      for (auto &E : B->edges()) {
-
-        // Skip non-relocation edges.
-        if (!E.isRelocation())
-          continue;
-
-        // Dispatch to LinkerImpl for fixup.
-        if (auto Err = impl().applyFixup(G, *B, E))
-          return Err;
+    for (auto &Sec : G.sections()) {
+      bool NoAllocSection =
+          Sec.getMemLifetimePolicy() == orc::MemLifetimePolicy::NoAlloc;
+
+      for (auto *B : Sec.blocks()) {
+        LLVM_DEBUG(dbgs() << "  " << *B << ":\n");
+
+        // Copy Block data and apply fixups.
+        LLVM_DEBUG(dbgs() << "    Applying fixups.\n");
+        assert((!B->isZeroFill() || all_of(B->edges(),
+                                           [](const Edge &E) {
+                                             return E.getKind() ==
+                                                    Edge::KeepAlive;
+                                           })) &&
+               "Non-KeepAlive edges in zero-fill block?");
+
+        // If this is a no-alloc section then copy the block content into
+        // memory allocated on the Graph's allocator (if it hasn't been
+        // already).
+        if (NoAllocSection)
+          (void)B->getMutableContent(G);
+
+        for (auto &E : B->edges()) {
+
+          // Skip non-relocation edges.
+          if (!E.isRelocation())
+            continue;
+
+          // If B is a block in a Standard or Finalize section then make sure
+          // that no edges point to symbols in NoAlloc sections.
+          assert(
+              (NoAllocSection || !E.getTarget().isDefined() ||
+               E.getTarget().getBlock().getSection().getMemLifetimePolicy() !=
+                   orc::MemLifetimePolicy::NoAlloc) &&
+              "Block in allocated section has edge pointing to no-alloc "
+              "section");
+
+          // Dispatch to LinkerImpl for fixup.
+          if (auto Err = impl().applyFixup(G, *B, E))
+            return Err;
+        }
       }
     }
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp b/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp
index e74aa059f4054..f481504135a5f 100644
--- a/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/JITLinkMemoryManager.cpp
@@ -24,11 +24,12 @@ JITLinkMemoryManager::InFlightAlloc::~InFlightAlloc() = default;
 BasicLayout::BasicLayout(LinkGraph &G) : G(G) {
 
   for (auto &Sec : G.sections()) {
-    // Skip empty sections.
-    if (Sec.blocks().empty())
+    // Skip empty sections, and sections with NoAlloc lifetime policies.
+    if (Sec.blocks().empty() ||
+        Sec.getMemLifetimePolicy() == orc::MemLifetimePolicy::NoAlloc)
       continue;
 
-    auto &Seg = Segments[{Sec.getMemProt(), Sec.getMemDeallocPolicy()}];
+    auto &Seg = Segments[{Sec.getMemProt(), Sec.getMemLifetimePolicy()}];
     for (auto *B : Sec.blocks())
       if (LLVM_LIKELY(!B->isZeroFill()))
         Seg.ContentBlocks.push_back(B);
@@ -89,7 +90,7 @@ BasicLayout::getContiguousPageBasedLayoutSizes(uint64_t PageSize) {
                                      inconvertibleErrorCode());
 
     uint64_t SegSize = alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize);
-    if (AG.getMemDeallocPolicy() == orc::MemDeallocPolicy::Standard)
+    if (AG.getMemLifetimePolicy() == orc::MemLifetimePolicy::Standard)
       SegsSizes.StandardSegs += SegSize;
     else
       SegsSizes.FinalizeSegs += SegSize;
@@ -146,7 +147,7 @@ void SimpleSegmentAlloc::Create(JITLinkMemoryManager &MemMgr,
                                 const JITLinkDylib *JD, SegmentMap Segments,
                                 OnCreatedFunction OnCreated) {
 
-  static_assert(orc::AllocGroup::NumGroups == 16,
+  static_assert(orc::AllocGroup::NumGroups == 32,
                 "AllocGroup has changed. Section names below must be updated");
   StringRef AGSectionNames[] = {
       "__---.standard", "__R--.standard", "__-W-.standard", "__RW-.standard",
@@ -163,12 +164,15 @@ void SimpleSegmentAlloc::Create(JITLinkMemoryManager &MemMgr,
     auto &AG = KV.first;
     auto &Seg = KV.second;
 
+    assert(AG.getMemLifetimePolicy() != orc::MemLifetimePolicy::NoAlloc &&
+           "NoAlloc segments are not supported by SimpleSegmentAlloc");
+
     auto AGSectionName =
         AGSectionNames[static_cast<unsigned>(AG.getMemProt()) |
-                       static_cast<bool>(AG.getMemDeallocPolicy()) << 3];
+                       static_cast<bool>(AG.getMemLifetimePolicy()) << 3];
 
     auto &Sec = G->createSection(AGSectionName, AG.getMemProt());
-    Sec.setMemDeallocPolicy(AG.getMemDeallocPolicy());
+    Sec.setMemLifetimePolicy(AG.getMemLifetimePolicy());
 
     if (Seg.ContentSize != 0) {
       NextAddr =
@@ -416,7 +420,7 @@ void InProcessMemoryManager::allocate(const JITLinkDylib *JD, LinkGraph &G,
     auto &Seg = KV.second;
 
     auto &SegAddr =
-        (AG.getMemDeallocPolicy() == orc::MemDeallocPolicy::Standard)
+        (AG.getMemLifetimePolicy() == orc::MemLifetimePolicy::Standard)
             ? NextStandardSegAddr
             : NextFinalizeSegAddr;
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp
index bdf0eb6012892..385230c53f914 100644
--- a/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/MachOLinkGraphBuilder.cpp
@@ -189,6 +189,10 @@ Error MachOLinkGraphBuilder::createNormalizedSections() {
     NSec.GraphSection = &G->createSection(
         StringRef(FullyQualifiedName.data(), FullyQualifiedName.size()), Prot);
 
+    // TODO: Are there any other criteria for NoAlloc lifetime?
+    if (NSec.Flags & MachO::S_ATTR_DEBUG)
+      NSec.GraphSection->setMemLifetimePolicy(orc::MemLifetimePolicy::NoAlloc);
+
     IndexToSection.insert(std::make_pair(SecIndex, std::move(NSec)));
   }
 

diff  --git a/llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp
index ec82081937e22..fbe25d70c38a2 100644
--- a/llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.cpp
@@ -235,7 +235,7 @@ bool EPCGenericRTDyldMemoryManager::finalizeMemory(std::string *ErrMsg) {
     for (unsigned I = 0; I != 3; ++I) {
       FR.Segments.push_back({});
       auto &Seg = FR.Segments.back();
-      Seg.AG = SegMemProts[I];
+      Seg.RAG = SegMemProts[I];
       Seg.Addr = RemoteAddrs[I]->Start;
       for (auto &SecAlloc : *SegSections[I]) {
         Seg.Size = alignTo(Seg.Size, SecAlloc.Align);

diff  --git a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
index b457c7297bed6..ca4950077ffe9 100644
--- a/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/MemoryMapper.cpp
@@ -322,7 +322,8 @@ void SharedMemoryMapper::initialize(MemoryMapper::AllocInfo &AI,
     std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
 
     tpctypes::SharedMemorySegFinalizeRequest SegReq;
-    SegReq.AG = Segment.AG;
+    SegReq.RAG = {Segment.AG.getMemProt(), Segment.AG.getMemLifetimePolicy() ==
+                                               MemLifetimePolicy::Finalize};
     SegReq.Addr = AI.MappingBase + Segment.Offset;
     SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
 

diff  --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
index 147f915f61d6a..3f70dbf60437a 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/ExecutorSharedMemoryMapperService.cpp
@@ -132,11 +132,11 @@ Expected<ExecutorAddr> ExecutorSharedMemoryMapperService::initialize(
 #if defined(LLVM_ON_UNIX)
 
     int NativeProt = 0;
-    if ((Segment.AG.getMemProt() & MemProt::Read) == MemProt::Read)
+    if ((Segment.RAG.Prot & MemProt::Read) == MemProt::Read)
       NativeProt |= PROT_READ;
-    if ((Segment.AG.getMemProt() & MemProt::Write) == MemProt::Write)
+    if ((Segment.RAG.Prot & MemProt::Write) == MemProt::Write)
       NativeProt |= PROT_WRITE;
-    if ((Segment.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
+    if ((Segment.RAG.Prot & MemProt::Exec) == MemProt::Exec)
       NativeProt |= PROT_EXEC;
 
     if (mprotect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt))
@@ -144,8 +144,7 @@ Expected<ExecutorAddr> ExecutorSharedMemoryMapperService::initialize(
 
 #elif defined(_WIN32)
 
-    DWORD NativeProt =
-        getWindowsProtectionFlags(Segment.AG.getMemProt());
+    DWORD NativeProt = getWindowsProtectionFlags(Segment.RAG.Prot);
 
     if (!VirtualProtect(Segment.Addr.toPtr<void *>(), Segment.Size, NativeProt,
                         &NativeProt))
@@ -153,7 +152,7 @@ Expected<ExecutorAddr> ExecutorSharedMemoryMapperService::initialize(
 
 #endif
 
-    if ((Segment.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
+    if ((Segment.RAG.Prot & MemProt::Exec) == MemProt::Exec)
       sys::Memory::InvalidateInstructionCache(Segment.Addr.toPtr<void *>(),
                                               Segment.Size);
   }

diff  --git a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp
index ce94bf1e039aa..4da031716e32a 100644
--- a/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/TargetProcess/SimpleExecutorMemoryManager.cpp
@@ -132,9 +132,9 @@ Error SimpleExecutorMemoryManager::finalize(tpctypes::FinalizeRequest &FR) {
     assert(Seg.Size <= std::numeric_limits<size_t>::max());
     if (auto EC = sys::Memory::protectMappedMemory(
             {Mem, static_cast<size_t>(Seg.Size)},
-            toSysMemoryProtectionFlags(Seg.AG.getMemProt())))
+            toSysMemoryProtectionFlags(Seg.RAG.Prot)))
       return BailOut(errorCodeToError(EC));
-    if ((Seg.AG.getMemProt() & MemProt::Exec) == MemProt::Exec)
+    if ((Seg.RAG.Prot & MemProt::Exec) == MemProt::Exec)
       sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
   }
 

diff  --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index be0fe1750a718..5e91ad068cdbc 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -511,8 +511,7 @@ class InProcessDeltaMapper final : public InProcessMemoryMapper {
     auto FixedAI = std::move(AI);
     FixedAI.MappingBase -= DeltaAddr;
     for (auto &Seg : FixedAI.Segments)
-      Seg.AG = AllocGroup(MemProt::Read | MemProt::Write,
-                          Seg.AG.getMemDeallocPolicy());
+      Seg.AG = {MemProt::Read | MemProt::Write, Seg.AG.getMemLifetimePolicy()};
     FixedAI.Actions.clear();
     InProcessMemoryMapper::initialize(
         FixedAI, [this, OnInitialized = std::move(OnInitialized)](

diff  --git a/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp b/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp
index 80f557122ec20..0146c3b4cf6e0 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/LinkGraphTests.cpp
@@ -756,3 +756,31 @@ TEST(LinkGraphTest, IsCStringBlockTest) {
   EXPECT_TRUE(isCStringBlock(SizeOneZeroFillBlock));
   EXPECT_FALSE(isCStringBlock(LargerZeroFillBlock));
 }
+
+TEST(LinkGraphTest, BasicLayoutHonorsNoAlloc) {
+  // Check that BasicLayout honors NoAlloc.
+  LinkGraph G("foo", Triple("x86_64-apple-darwin"), 8, support::little,
+              getGenericEdgeKindName);
+
+  // Create a regular section and block.
+  auto &Sec1 =
+      G.createSection("__data", orc::MemProt::Read | orc::MemProt::Write);
+  G.createContentBlock(Sec1, BlockContent.slice(0, 8), orc::ExecutorAddr(), 8,
+                       0);
+
+  // Create a NoAlloc section and block.
+  auto &Sec2 =
+      G.createSection("__metadata", orc::MemProt::Read | orc::MemProt::Write);
+  Sec2.setMemLifetimePolicy(orc::MemLifetimePolicy::NoAlloc);
+  G.createContentBlock(Sec2, BlockContent.slice(0, 8), orc::ExecutorAddr(), 8,
+                       0);
+
+  BasicLayout BL(G);
+
+  EXPECT_EQ(std::distance(BL.segments().begin(), BL.segments().end()), 1U);
+  EXPECT_EQ(BL.segments().begin()->first,
+            orc::MemProt::Read | orc::MemProt::Write);
+  auto &SegInfo = BL.segments().begin()->second;
+  EXPECT_EQ(SegInfo.Alignment, 8U);
+  EXPECT_EQ(SegInfo.ContentSize, 8U);
+}

diff  --git a/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp
index 7a34dfc9e2304..d3a66294bd571 100644
--- a/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/EPCGenericJITLinkMemoryManagerTest.cpp
@@ -46,9 +46,9 @@ class SimpleAllocator {
       assert(Seg.Size <= std::numeric_limits<size_t>::max());
       if (auto EC = sys::Memory::protectMappedMemory(
               {Mem, static_cast<size_t>(Seg.Size)},
-              toSysMemoryProtectionFlags(Seg.AG.getMemProt())))
+              toSysMemoryProtectionFlags(Seg.RAG.Prot)))
         return errorCodeToError(EC);
-      if ((Seg.AG.getMemProt() & MemProt::Exec) != MemProt::Exec)
+      if ((Seg.RAG.Prot & MemProt::Exec) != MemProt::Exec)
         sys::Memory::InvalidateInstructionCache(Mem, Seg.Size);
     }
     return Error::success();


        


More information about the llvm-commits mailing list