[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