[lld] [llvm] [LLD][COFF] Prefetch inputs early-on to improve link times (PR #169224)

Alexandre Ganea via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 28 07:13:09 PST 2025


https://github.com/aganea updated https://github.com/llvm/llvm-project/pull/169224

>From 17165b8c5ba28f636dadea1ee566420c3ec94877 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 3 Oct 2025 17:14:25 -0400
Subject: [PATCH 1/2] [LLD][COFF] Prefetch inputs early on to improve link
 times

---
 lld/COFF/Driver.cpp                      |  8 +++++++-
 llvm/include/llvm/Support/FileSystem.h   |  2 ++
 llvm/include/llvm/Support/MemoryBuffer.h |  5 +++++
 llvm/lib/Support/MemoryBuffer.cpp        |  1 +
 llvm/lib/Support/Unix/Path.inc           | 13 ++++++++++++
 llvm/lib/Support/Windows/Path.inc        | 26 ++++++++++++++++++++++++
 6 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 0e528de9c3652..e6e7194a684c1 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -164,6 +164,8 @@ static std::future<MBErrPair> createFutureForFile(std::string path) {
                                          /*RequiresNullTerminator=*/false);
     if (!mbOrErr)
       return MBErrPair{nullptr, mbOrErr.getError()};
+    // Prefetch memory pages in the background as we will need them soon enough.
+    (*mbOrErr)->willNeedIfMmap();
     return MBErrPair{std::move(*mbOrErr), std::error_code()};
   });
 }
@@ -337,8 +339,12 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
         auto retryMb = MemoryBuffer::getFile(*retryPath, /*IsText=*/false,
                                              /*RequiresNullTerminator=*/false);
         ec = retryMb.getError();
-        if (!ec)
+        if (!ec) {
           mb = std::move(*retryMb);
+          // Prefetch memory pages in the background as we will need them soon
+          // enough.
+          mb->willNeedIfMmap();
+        }
       } else {
         // We've already handled this file.
         return;
diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h
index cf2a8104ac813..547d732dc3053 100644
--- a/llvm/include/llvm/Support/FileSystem.h
+++ b/llvm/include/llvm/Support/FileSystem.h
@@ -1310,6 +1310,7 @@ class mapped_file_region {
 
   LLVM_ABI void unmapImpl();
   LLVM_ABI void dontNeedImpl();
+  LLVM_ABI void willNeedImpl();
 
   LLVM_ABI std::error_code init(sys::fs::file_t FD, uint64_t Offset,
                                 mapmode Mode);
@@ -1341,6 +1342,7 @@ class mapped_file_region {
     copyFrom(mapped_file_region());
   }
   void dontNeed() { dontNeedImpl(); }
+  void willNeed() { willNeedImpl(); }
 
   LLVM_ABI size_t size() const;
   LLVM_ABI char *data() const;
diff --git a/llvm/include/llvm/Support/MemoryBuffer.h b/llvm/include/llvm/Support/MemoryBuffer.h
index f092c67265a31..9b0f1f0f7d34e 100644
--- a/llvm/include/llvm/Support/MemoryBuffer.h
+++ b/llvm/include/llvm/Support/MemoryBuffer.h
@@ -83,6 +83,11 @@ class LLVM_ABI MemoryBuffer {
   /// function should not be called on a writable buffer.
   virtual void dontNeedIfMmap() {}
 
+  /// Mark the buffer as to-be-used in a near future. This shall trigger OS
+  /// prefetching from the storage device and into memory, if possible.
+  /// This should be use purely as an read optimization.
+  virtual void willNeedIfMmap() {}
+
   /// Open the specified file as a MemoryBuffer, returning a new MemoryBuffer
   /// if successful, otherwise returning null.
   ///
diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp
index 23b9f8c5790d2..3ef4cd9086b95 100644
--- a/llvm/lib/Support/MemoryBuffer.cpp
+++ b/llvm/lib/Support/MemoryBuffer.cpp
@@ -243,6 +243,7 @@ class MemoryBufferMMapFile : public MB {
   }
 
   void dontNeedIfMmap() override { MFR.dontNeed(); }
+  void willNeedIfMmap() override { MFR.willNeed(); }
 };
 } // namespace
 
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 0d991ead72416..0937adcd1bc18 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -900,6 +900,19 @@ void mapped_file_region::dontNeedImpl() {
 #endif
 }
 
+void mapped_file_region::willNeedImpl() {
+  assert(Mode == mapped_file_region::readonly);
+  if (!Mapping)
+    return;
+#if defined(__MVS__) || defined(_AIX)
+  // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
+#elif defined(POSIX_MADV_WILLNEED)
+  ::posix_madvise(Mapping, Size, POSIX_MADV_WILLNEED);
+#else
+  ::madvise(Mapping, Size, MADV_WILLNEED);
+#endif
+}
+
 int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); }
 
 std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index be007b7abdb51..8be4105ba6bd8 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -1023,6 +1023,32 @@ void mapped_file_region::unmapImpl() {
 
 void mapped_file_region::dontNeedImpl() {}
 
+void mapped_file_region::willNeedImpl() {
+#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
+  typedef struct _WIN32_MEMORY_RANGE_ENTRY {
+    PVOID VirtualAddress;
+    SIZE_T NumberOfBytes;
+  } WIN32_MEMORY_RANGE_ENTRY, *PWIN32_MEMORY_RANGE_ENTRY;
+#endif
+
+  HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
+  if (kernelM) {
+    // PrefetchVirtualMemory is only available on Windows 8 and later. Since we
+    // still support compilation on Windows 7, we load the function dynamically.
+    typedef BOOL(WINAPI * PrefetchVirtualMemory_t)(
+        HANDLE hProcess, ULONG_PTR NumberOfEntries,
+        _In_reads_(NumberOfEntries) PWIN32_MEMORY_RANGE_ENTRY VirtualAddresses,
+        ULONG Flags);
+    static const auto pfnPrefetchVirtualMemory =
+        (PrefetchVirtualMemory_t)::GetProcAddress(kernelM,
+                                                  "PrefetchVirtualMemory");
+    if (pfnPrefetchVirtualMemory) {
+      WIN32_MEMORY_RANGE_ENTRY Range{Mapping, Size};
+      pfnPrefetchVirtualMemory(::GetCurrentProcess(), 1, &Range, 0);
+    }
+  }
+}
+
 std::error_code mapped_file_region::sync() const {
   if (!::FlushViewOfFile(Mapping, Size))
     return mapWindowsError(GetLastError());

>From f06825747c6d65d2879dcbe418701cb66eb08c4d Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Fri, 28 Nov 2025 10:12:17 -0500
Subject: [PATCH 2/2] Unconditionally define struct

---
 llvm/lib/Support/Windows/Path.inc | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index 8be4105ba6bd8..0267336c5f1e8 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -1024,26 +1024,24 @@ void mapped_file_region::unmapImpl() {
 void mapped_file_region::dontNeedImpl() {}
 
 void mapped_file_region::willNeedImpl() {
-#if (_WIN32_WINNT < _WIN32_WINNT_WIN8)
-  typedef struct _WIN32_MEMORY_RANGE_ENTRY {
-    PVOID VirtualAddress;
-    SIZE_T NumberOfBytes;
-  } WIN32_MEMORY_RANGE_ENTRY, *PWIN32_MEMORY_RANGE_ENTRY;
-#endif
-
   HMODULE kernelM = llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
   if (kernelM) {
+    struct MEMORY_RANGE_ENTRY {
+      PVOID VirtualAddress;
+      SIZE_T NumberOfBytes;
+    };
+
     // PrefetchVirtualMemory is only available on Windows 8 and later. Since we
     // still support compilation on Windows 7, we load the function dynamically.
     typedef BOOL(WINAPI * PrefetchVirtualMemory_t)(
         HANDLE hProcess, ULONG_PTR NumberOfEntries,
-        _In_reads_(NumberOfEntries) PWIN32_MEMORY_RANGE_ENTRY VirtualAddresses,
+        _In_reads_(NumberOfEntries) MEMORY_RANGE_ENTRY * VirtualAddresses,
         ULONG Flags);
     static const auto pfnPrefetchVirtualMemory =
         (PrefetchVirtualMemory_t)::GetProcAddress(kernelM,
                                                   "PrefetchVirtualMemory");
     if (pfnPrefetchVirtualMemory) {
-      WIN32_MEMORY_RANGE_ENTRY Range{Mapping, Size};
+      MEMORY_RANGE_ENTRY Range{Mapping, Size};
       pfnPrefetchVirtualMemory(::GetCurrentProcess(), 1, &Range, 0);
     }
   }



More information about the llvm-commits mailing list