[llvm] [llvm-rtdyld] Precalculate required memory upfront (PR #72254)

Sjoerd Meijer via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 14 05:46:25 PST 2023


https://github.com/sjoerdmeijer created https://github.com/llvm/llvm-project/pull/72254

This changes the behaviour of llvm-rtdyld to calculate and allocate all required memory for objects and its data/code section upfront as opposed to doing this on-demand.

Allocation of the memory upfront avoids fragmentation of the memory, which is a workaround for relocations getting out of range as explained and discussed here:

https://discourse.llvm.org/t/problems-with-code-model-large-and-relocations/70511

>From 8442c0fd63c42558534f9b33f4f6efdc8305aca7 Mon Sep 17 00:00:00 2001
From: Sjoerd Meijer <smeijer at nvidia.com>
Date: Wed, 8 Nov 2023 18:57:30 +0530
Subject: [PATCH] [llvm-rtdyld] Precalculate required memory upfront

This changes the behaviour of llvm-rtdyld to calculate and allocate all
required memory for objects and its data/code section upfront as opposed
to doing this on-demand.

Allocation of the memory upfront avoids fragmentation of the memory,
which is a workaround for relocations getting out of range as explained
and discussed here:

https://discourse.llvm.org/t/problems-with-code-model-large-and-relocations/70511
---
 .../llvm/ExecutionEngine/RuntimeDyld.h        |  5 ++
 .../RuntimeDyld/RuntimeDyld.cpp               | 24 ++++++++++
 .../RuntimeDyld/RuntimeDyldImpl.h             | 18 +++++---
 llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp        | 46 ++++++++++++++++---
 4 files changed, 80 insertions(+), 13 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/RuntimeDyld.h b/llvm/include/llvm/ExecutionEngine/RuntimeDyld.h
index 468296ac1523344..423715453b7c9d1 100644
--- a/llvm/include/llvm/ExecutionEngine/RuntimeDyld.h
+++ b/llvm/include/llvm/ExecutionEngine/RuntimeDyld.h
@@ -194,6 +194,11 @@ class RuntimeDyld {
   RuntimeDyld &operator=(const RuntimeDyld &) = delete;
   ~RuntimeDyld();
 
+  /// TODO
+  Error precalculateMemorySize(const object::ObjectFile &Obj,
+    uint64_t &CodeSize, Align &CodeAlign, uint64_t &RODataSize, Align
+    &RODataAlign, uint64_t &RWDataSize, Align &RWDataAlign);
+
   /// Add the referenced object file to the list of objects to be loaded and
   /// relocated.
   std::unique_ptr<LoadedObjectInfo> loadObject(const object::ObjectFile &O);
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
index a9aaff42433f655..83cafa2d4e7a768 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
@@ -541,6 +541,8 @@ Error RuntimeDyldImpl::computeTotalAllocSize(
   std::vector<uint64_t> ROSectionSizes;
   std::vector<uint64_t> RWSectionSizes;
 
+  Arch = (Triple::ArchType)Obj.getArch();
+
   // Collect sizes of all sections to be loaded;
   // also determine the max alignment of all sections
   for (section_iterator SI = Obj.section_begin(), SE = Obj.section_end();
@@ -1343,6 +1345,28 @@ createRuntimeDyldMachO(
   return Dyld;
 }
 
+Error RuntimeDyld::precalculateMemorySize(
+    const ObjectFile &Obj, uint64_t &CodeSize, Align &CodeAlign,
+    uint64_t &RODataSize, Align &RODataAlign, uint64_t &RWDataSize,
+    Align &RWDataAlign) {
+  if (!Dyld) {
+    if (!Obj.isELF())
+      report_fatal_error("Incompatible object format!");
+
+      Dyld =
+          createRuntimeDyldELF(static_cast<Triple::ArchType>(Obj.getArch()),
+                               MemMgr, Resolver, ProcessAllSections,
+                               std::move(NotifyStubEmitted));
+  }
+  if (!Dyld->isCompatibleFile(Obj))
+    report_fatal_error("Incompatible object format!");
+
+  auto Err = Dyld->computeTotalAllocSize(Obj, CodeSize, CodeAlign, RODataSize,
+                                         RODataAlign, RWDataSize, RWDataAlign);
+  return Err;
+}
+
+
 std::unique_ptr<RuntimeDyld::LoadedObjectInfo>
 RuntimeDyld::loadObject(const ObjectFile &Obj) {
   if (!Dyld) {
diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
index 73e2b365f109a99..75caf019551019a 100644
--- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
+++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
@@ -421,13 +421,6 @@ class RuntimeDyldImpl {
   /// Resolve relocations to external symbols.
   Error resolveExternalSymbols();
 
-  // Compute an upper bound of the memory that is required to load all
-  // sections
-  Error computeTotalAllocSize(const ObjectFile &Obj, uint64_t &CodeSize,
-                              Align &CodeAlign, uint64_t &RODataSize,
-                              Align &RODataAlign, uint64_t &RWDataSize,
-                              Align &RWDataAlign);
-
   // Compute GOT size
   unsigned computeGOTSize(const ObjectFile &Obj);
 
@@ -435,6 +428,9 @@ class RuntimeDyldImpl {
   unsigned computeSectionStubBufSize(const ObjectFile &Obj,
                                      const SectionRef &Section);
 
+
+
+
   // Implementation of the generic part of the loadObject algorithm.
   Expected<ObjSectionToIDMap> loadObjectImpl(const object::ObjectFile &Obj);
 
@@ -464,6 +460,14 @@ class RuntimeDyldImpl {
 
   virtual ~RuntimeDyldImpl();
 
+// Compute an upper bound of the memory that is required to load all
+  // sections
+  Error computeTotalAllocSize(const ObjectFile &Obj, uint64_t &CodeSize,
+                              Align &CodeAlign, uint64_t &RODataSize,
+                              Align &RODataAlign, uint64_t &RWDataSize,
+                              Align &RWDataAlign);
+
+
   void setProcessAllSections(bool ProcessAllSections) {
     this->ProcessAllSections = ProcessAllSections;
   }
diff --git a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
index 107b555a99faa40..a89c9ce4863cf33 100644
--- a/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -14,6 +14,7 @@
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
+
 #include "llvm/ExecutionEngine/RuntimeDyld.h"
 #include "llvm/ExecutionEngine/RuntimeDyldChecker.h"
 #include "llvm/MC/MCAsmInfo.h"
@@ -549,20 +550,30 @@ static int executeInput() {
 
   // Instantiate a dynamic linker.
   TrivialMemoryManager MemMgr;
-  doPreallocation(MemMgr);
   RuntimeDyld Dyld(MemMgr, MemMgr);
 
+  uint64_t TotalCodeSize = 0;
+  uint64_t TotalRODataSize = 0;
+  uint64_t TotalRWDataSize = 0;
+
+
   // If we don't have any input files, read from stdin.
-  if (!InputFileList.size())
+  if (!InputFileList.size()) {
     InputFileList.push_back("-");
+  }
+
   {
     TimeRegion TR(Timers ? &Timers->LoadObjectsTimer : nullptr);
+    // First, iterate over the list of objects to calculate the object sizes
+    // before we actually allocate the memory.
     for (auto &File : InputFileList) {
       // Load the input memory buffer.
       ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
           MemoryBuffer::getFileOrSTDIN(File);
+
       if (std::error_code EC = InputBuffer.getError())
         ErrorAndExit("unable to read input: '" + EC.message() + "'");
+
       Expected<std::unique_ptr<ObjectFile>> MaybeObj(
           ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
 
@@ -576,11 +587,34 @@ static int executeInput() {
 
       ObjectFile &Obj = **MaybeObj;
 
-      // Load the object file
-      Dyld.loadObject(Obj);
-      if (Dyld.hasError()) {
+      uint64_t CodeSize, RODataSize, RWDataSize;
+      Align CodeAlign, RODataAlign, RWDataAlign;
+
+      Error Err = Dyld.precalculateMemorySize(Obj, CodeSize, CodeAlign,
+		      RODataSize, RODataAlign, RWDataSize, RWDataAlign);
+      if (Err)
+        ErrorAndExit("Can't compute total size");
+
+      TotalCodeSize += CodeSize;
+      TotalRODataSize += RODataSize;
+      TotalRWDataSize += RWDataSize;
+    }
+
+    if  (!PreallocMemory)
+      PreallocMemory = TotalCodeSize + TotalRODataSize + TotalRWDataSize;
+    doPreallocation(MemMgr);
+
+    // Now, iterate over the list again to actually load the objects.
+    for (auto &File : InputFileList) {
+      ErrorOr<std::unique_ptr<MemoryBuffer>> InputBuffer =
+          MemoryBuffer::getFileOrSTDIN(File);
+      Expected<std::unique_ptr<ObjectFile>> MaybeObj(
+          ObjectFile::createObjectFile((*InputBuffer)->getMemBufferRef()));
+      assert(MaybeObj);
+
+      Dyld.loadObject(**MaybeObj);
+      if (Dyld.hasError())
         ErrorAndExit(Dyld.getErrorString());
-      }
     }
   }
 



More information about the llvm-commits mailing list