[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