[lld] [lld][COFF] Refactor ICF section equality checks, NFC (PR #206562)

Reid Kleckner via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 16 11:10:03 PDT 2026


https://github.com/rnk updated https://github.com/llvm/llvm-project/pull/206562

>From fbe6ec82fc6bad0016a09e7da925088723211052 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Tue, 23 Jun 2026 15:52:57 -0700
Subject: [PATCH 1/4] [lld][COFF] Refactor ICF section equality, NFC

This matches the pattern used in `segregate` (a constant bool
parameter).
---
 lld/COFF/ICF.cpp | 95 +++++++++++++++++++-----------------------------
 1 file changed, 38 insertions(+), 57 deletions(-)

diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index 69d339e3fc7cf..ade95482abae0 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -35,7 +35,7 @@ namespace lld::coff {
 
 class ICF {
 public:
-  ICF(COFFLinkerContext &c) : ctx(c){};
+  ICF(COFFLinkerContext &c) : ctx(c) {};
   void run();
 
 private:
@@ -43,8 +43,8 @@ class ICF {
 
   bool assocEquals(const SectionChunk *a, const SectionChunk *b);
 
-  bool equalsConstant(const SectionChunk *a, const SectionChunk *b);
-  bool equalsVariable(const SectionChunk *a, const SectionChunk *b);
+  bool sectionsEqual(const SectionChunk *a, const SectionChunk *b,
+                     bool constant);
 
   bool isEligible(SectionChunk *c);
 
@@ -75,7 +75,8 @@ class ICF {
 // of the Visual C++ linker.
 bool ICF::isEligible(SectionChunk *c) {
   // Non-comdat chunks, dead chunks, and writable chunks are not eligible.
-  bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
+  bool writable =
+      c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
   if (!c->isCOMDAT() || !c->live || writable)
     return false;
 
@@ -106,10 +107,8 @@ void ICF::segregate(size_t begin, size_t end, bool constant) {
     // Divide [Begin, End) into two. Let Mid be the start index of the
     // second group.
     auto bound = std::stable_partition(
-        chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {
-          if (constant)
-            return equalsConstant(chunks[begin], s);
-          return equalsVariable(chunks[begin], s);
+        chunks.begin() + begin + 1, chunks.begin() + end, [=](SectionChunk *s) {
+          return sectionsEqual(chunks[begin], s, constant);
         });
     size_t mid = bound - chunks.begin();
 
@@ -126,7 +125,8 @@ void ICF::segregate(size_t begin, size_t end, bool constant) {
   }
 }
 
-// Returns true if two sections' associative children are equal.
+// Returns true if two sections' associative children, i.e. exception handling
+// metadata such as .pdata and .xdata, are equal.
 bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
   // Ignore associated metadata sections that don't participate in ICF, such as
   // debug info and CFGuard metadata.
@@ -143,65 +143,46 @@ bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
                     });
 }
 
-// Compare "non-moving" part of two sections, namely everything
-// except relocation targets.
-bool ICF::equalsConstant(const SectionChunk *a, const SectionChunk *b) {
-  if (a->relocsSize != b->relocsSize)
-    return false;
-
-  // Compare relocations.
-  auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
-    if (r1.Type != r2.Type ||
-        r1.VirtualAddress != r2.VirtualAddress) {
-      return false;
-    }
-    Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
-    Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
+// Compare the "non-moving" or "moving" parts of two sections.
+bool ICF::sectionsEqual(const SectionChunk *a, const SectionChunk *b,
+                        bool constant) {
+  auto eqSym = [=](Symbol *b1, Symbol *b2) {
     if (b1 == b2)
       return true;
-    if (auto *d1 = dyn_cast<DefinedRegular>(b1))
-      if (auto *d2 = dyn_cast<DefinedRegular>(b2))
-        return d1->getValue() == d2->getValue() &&
-               d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
-    return false;
+    auto *d1 = dyn_cast<DefinedRegular>(b1);
+    auto *d2 = dyn_cast<DefinedRegular>(b2);
+    if (!d1 || !d2 || (constant && d1->getValue() != d2->getValue()))
+      return false;
+    return d1->getChunk()->eqClass[cnt % 2] ==
+           d2->getChunk()->eqClass[cnt % 2];
   };
-  if (!std::equal(a->getRelocs().begin(), a->getRelocs().end(),
-                  b->getRelocs().begin(), eq))
-    return false;
-
-  // Compare section attributes and contents.
-  return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
-         a->getSectionName() == b->getSectionName() &&
-         a->header->SizeOfRawData == b->header->SizeOfRawData &&
-         a->checksum == b->checksum && a->getContents() == b->getContents() &&
-         a->getMachine() == b->getMachine() && assocEquals(a, b);
-}
 
-// Compare "moving" part of two sections, namely relocation targets.
-bool ICF::equalsVariable(const SectionChunk *a, const SectionChunk *b) {
-  // Compare relocations.
-  auto eqSym = [&](Symbol *b1, Symbol *b2) {
-    if (b1 == b2)
-      return true;
-    if (auto *d1 = dyn_cast<DefinedRegular>(b1))
-      if (auto *d2 = dyn_cast<DefinedRegular>(b2))
-        return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
-    return false;
-  };
-  auto eq = [&](const coff_relocation &r1, const coff_relocation &r2) {
-    Symbol *b1 = a->file->getSymbol(r1.SymbolTableIndex);
-    Symbol *b2 = b->file->getSymbol(r2.SymbolTableIndex);
-    return eqSym(b1, b2);
+  auto eqReloc = [=](const coff_relocation &r1, const coff_relocation &r2) {
+    if (constant &&
+        (r1.Type != r2.Type || r1.VirtualAddress != r2.VirtualAddress))
+      return false;
+    return eqSym(a->file->getSymbol(r1.SymbolTableIndex),
+                 b->file->getSymbol(r2.SymbolTableIndex));
   };
+  if (!llvm::equal(a->getRelocs(), b->getRelocs(), eqReloc))
+    return false;
+
+  if (constant)
+    return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
+           a->getSectionName() == b->getSectionName() &&
+           a->header->SizeOfRawData == b->header->SizeOfRawData &&
+           a->checksum == b->checksum &&
+           a->getContents() == b->getContents() &&
+           a->getMachine() == b->getMachine() && assocEquals(a, b);
 
   Symbol *e1 = a->getEntryThunk();
   Symbol *e2 = b->getEntryThunk();
   if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2)))
     return false;
 
-  return std::equal(a->getRelocs().begin(), a->getRelocs().end(),
-                    b->getRelocs().begin(), eq) &&
-         assocEquals(a, b);
+  // Check associated children sections, i.e. exception handling data, for
+  // equality.
+  return assocEquals(a, b);
 }
 
 // Find the first Chunk after Begin that has a different class from Begin.

>From 39e126bec9c14cf8e671e67c9cf200e5afc8c590 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Mon, 29 Jun 2026 11:01:51 -0700
Subject: [PATCH 2/4] [lld][COFF] Specialize ICF equality passes, NFC

---
 lld/COFF/ICF.cpp | 55 ++++++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 25 deletions(-)

diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index ade95482abae0..cac053f4c562c 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -39,12 +39,12 @@ class ICF {
   void run();
 
 private:
-  void segregate(size_t begin, size_t end, bool constant);
+  template <bool constant> void segregate(size_t begin, size_t end);
 
   bool assocEquals(const SectionChunk *a, const SectionChunk *b);
 
-  bool sectionsEqual(const SectionChunk *a, const SectionChunk *b,
-                     bool constant);
+  template <bool constant>
+  bool sectionsEqual(const SectionChunk *a, const SectionChunk *b);
 
   bool isEligible(SectionChunk *c);
 
@@ -102,13 +102,14 @@ bool ICF::isEligible(SectionChunk *c) {
 }
 
 // Split an equivalence class into smaller classes.
-void ICF::segregate(size_t begin, size_t end, bool constant) {
+template <bool constant>
+void ICF::segregate(size_t begin, size_t end) {
   while (begin < end) {
     // Divide [Begin, End) into two. Let Mid be the start index of the
     // second group.
     auto bound = std::stable_partition(
-        chunks.begin() + begin + 1, chunks.begin() + end, [=](SectionChunk *s) {
-          return sectionsEqual(chunks[begin], s, constant);
+        chunks.begin() + begin + 1, chunks.begin() + end, [&](SectionChunk *s) {
+          return sectionsEqual<constant>(chunks[begin], s);
         });
     size_t mid = bound - chunks.begin();
 
@@ -144,45 +145,49 @@ bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
 }
 
 // Compare the "non-moving" or "moving" parts of two sections.
-bool ICF::sectionsEqual(const SectionChunk *a, const SectionChunk *b,
-                        bool constant) {
-  auto eqSym = [=](Symbol *b1, Symbol *b2) {
+template <bool constant>
+bool ICF::sectionsEqual(const SectionChunk *a, const SectionChunk *b) {
+  auto eqSym = [&](Symbol *b1, Symbol *b2) {
     if (b1 == b2)
       return true;
     auto *d1 = dyn_cast<DefinedRegular>(b1);
     auto *d2 = dyn_cast<DefinedRegular>(b2);
-    if (!d1 || !d2 || (constant && d1->getValue() != d2->getValue()))
+    if (!d1 || !d2)
       return false;
+    if constexpr (constant)
+      if (d1->getValue() != d2->getValue())
+        return false;
     return d1->getChunk()->eqClass[cnt % 2] ==
            d2->getChunk()->eqClass[cnt % 2];
   };
 
-  auto eqReloc = [=](const coff_relocation &r1, const coff_relocation &r2) {
-    if (constant &&
-        (r1.Type != r2.Type || r1.VirtualAddress != r2.VirtualAddress))
-      return false;
+  auto eqReloc = [&](const coff_relocation &r1, const coff_relocation &r2) {
+    if constexpr (constant)
+      if (r1.Type != r2.Type || r1.VirtualAddress != r2.VirtualAddress)
+        return false;
     return eqSym(a->file->getSymbol(r1.SymbolTableIndex),
                  b->file->getSymbol(r2.SymbolTableIndex));
   };
   if (!llvm::equal(a->getRelocs(), b->getRelocs(), eqReloc))
     return false;
 
-  if (constant)
+  if constexpr (constant) {
     return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
            a->getSectionName() == b->getSectionName() &&
            a->header->SizeOfRawData == b->header->SizeOfRawData &&
            a->checksum == b->checksum &&
            a->getContents() == b->getContents() &&
            a->getMachine() == b->getMachine() && assocEquals(a, b);
+  } else {
+    Symbol *e1 = a->getEntryThunk();
+    Symbol *e2 = b->getEntryThunk();
+    if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2)))
+      return false;
 
-  Symbol *e1 = a->getEntryThunk();
-  Symbol *e2 = b->getEntryThunk();
-  if ((e1 || e2) && (!e1 || !e2 || !eqSym(e1, e2)))
-    return false;
-
-  // Check associated children sections, i.e. exception handling data, for
-  // equality.
-  return assocEquals(a, b);
+    // Check associated children sections, i.e. exception handling data, for
+    // equality.
+    return assocEquals(a, b);
+  }
 }
 
 // Find the first Chunk after Begin that has a different class from Begin.
@@ -282,13 +287,13 @@ void ICF::run() {
   });
 
   // Compare static contents and assign unique IDs for each static content.
-  forEachClass([&](size_t begin, size_t end) { segregate(begin, end, true); });
+  forEachClass([&](size_t begin, size_t end) { segregate<true>(begin, end); });
 
   // Split groups by comparing relocations until convergence is obtained.
   do {
     repeat = false;
     forEachClass(
-        [&](size_t begin, size_t end) { segregate(begin, end, false); });
+        [&](size_t begin, size_t end) { segregate<false>(begin, end); });
   } while (repeat);
 
   Log(ctx) << "ICF needed " << Twine(cnt) << " iterations";

>From 42e04d6a119578f368b773df22336c1daa1b95d3 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Mon, 29 Jun 2026 11:38:39 -0700
Subject: [PATCH 3/4] rm stray semi

---
 lld/COFF/ICF.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index cac053f4c562c..3e3f740879ec2 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -35,7 +35,7 @@ namespace lld::coff {
 
 class ICF {
 public:
-  ICF(COFFLinkerContext &c) : ctx(c) {};
+  ICF(COFFLinkerContext &c) : ctx(c) {}
   void run();
 
 private:

>From 31733f3d464f6095c943f8c18118a537e0eaaec6 Mon Sep 17 00:00:00 2001
From: Reid Kleckner <rkleckner at nvidia.com>
Date: Mon, 29 Jun 2026 15:21:52 -0700
Subject: [PATCH 4/4] clang-format

---
 lld/COFF/ICF.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/lld/COFF/ICF.cpp b/lld/COFF/ICF.cpp
index 3e3f740879ec2..aa64cd0b15594 100644
--- a/lld/COFF/ICF.cpp
+++ b/lld/COFF/ICF.cpp
@@ -102,8 +102,7 @@ bool ICF::isEligible(SectionChunk *c) {
 }
 
 // Split an equivalence class into smaller classes.
-template <bool constant>
-void ICF::segregate(size_t begin, size_t end) {
+template <bool constant> void ICF::segregate(size_t begin, size_t end) {
   while (begin < end) {
     // Divide [Begin, End) into two. Let Mid be the start index of the
     // second group.
@@ -157,8 +156,7 @@ bool ICF::sectionsEqual(const SectionChunk *a, const SectionChunk *b) {
     if constexpr (constant)
       if (d1->getValue() != d2->getValue())
         return false;
-    return d1->getChunk()->eqClass[cnt % 2] ==
-           d2->getChunk()->eqClass[cnt % 2];
+    return d1->getChunk()->eqClass[cnt % 2] == d2->getChunk()->eqClass[cnt % 2];
   };
 
   auto eqReloc = [&](const coff_relocation &r1, const coff_relocation &r2) {
@@ -175,8 +173,7 @@ bool ICF::sectionsEqual(const SectionChunk *a, const SectionChunk *b) {
     return a->getOutputCharacteristics() == b->getOutputCharacteristics() &&
            a->getSectionName() == b->getSectionName() &&
            a->header->SizeOfRawData == b->header->SizeOfRawData &&
-           a->checksum == b->checksum &&
-           a->getContents() == b->getContents() &&
+           a->checksum == b->checksum && a->getContents() == b->getContents() &&
            a->getMachine() == b->getMachine() && assocEquals(a, b);
   } else {
     Symbol *e1 = a->getEntryThunk();



More information about the llvm-commits mailing list