[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
Mon Dec 1 07:28:41 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/3] [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/3] 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);
     }
   }

>From 8a21564f7ce0dc50c88d528f13909a45515e83b8 Mon Sep 17 00:00:00 2001
From: Alexandre Ganea <aganea at havenstudios.com>
Date: Mon, 1 Dec 2025 10:27:37 -0500
Subject: [PATCH 3/3] loadSystemModuleSecure and GetProcAddress made static in
 a lambda

---
 llvm/lib/Support/Windows/Path.inc | 41 ++++++++++++++++---------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index 0267336c5f1e8..7a3ddd1e5336f 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -1024,26 +1024,27 @@ void mapped_file_region::unmapImpl() {
 void mapped_file_region::dontNeedImpl() {}
 
 void mapped_file_region::willNeedImpl() {
-  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) MEMORY_RANGE_ENTRY * VirtualAddresses,
-        ULONG Flags);
-    static const auto pfnPrefetchVirtualMemory =
-        (PrefetchVirtualMemory_t)::GetProcAddress(kernelM,
-                                                  "PrefetchVirtualMemory");
-    if (pfnPrefetchVirtualMemory) {
-      MEMORY_RANGE_ENTRY Range{Mapping, Size};
-      pfnPrefetchVirtualMemory(::GetCurrentProcess(), 1, &Range, 0);
-    }
+  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,
+      MEMORY_RANGE_ENTRY * VirtualAddresses, ULONG Flags);
+
+  static auto pfnPrefetchVirtualMemory = []() -> PrefetchVirtualMemory_t {
+    HMODULE kernelM =
+        llvm::sys::windows::loadSystemModuleSecure(L"kernel32.dll");
+    if (!kernelM)
+      return nullptr;
+    return (PrefetchVirtualMemory_t)::GetProcAddress(kernelM,
+                                                     "PrefetchVirtualMemory");
+  }();
+  if (pfnPrefetchVirtualMemory) {
+    MEMORY_RANGE_ENTRY Range{Mapping, Size};
+    pfnPrefetchVirtualMemory(::GetCurrentProcess(), 1, &Range, 0);
   }
 }
 



More information about the llvm-commits mailing list