From d87b94c2ea5fcc9fd636ab2e3bdc0604051ed415 Mon Sep 17 00:00:00 2001 From: Jez Ng Date: Sun, 26 Oct 2025 22:29:08 -0400 Subject: [PATCH] [lld][macho] Move unwind logic from equalsVariable to equalsConstant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since equalsVariable runs a lot more times, we want to minimize the work it needs to do. Anything not dependent on the icfEqClass values should get hoisted out. With this change, ICF runs ~1.7% faster when linking clang. Benchmarking approach: cbdr sample -b ~/extract-icf-time.sh ~/old/ld64.lld bin/ld64.lld --timeout=300s | cbdr analyze -s 95 `extract-icf-time.sh` runs the clang link command with the `--icf=all --time-trace` flags, then parses out the ICF duration from the resulting time trace using `jq`: jq '{ICF: (.traceEvents[] | select(.name == "Fold Identical Code Sections") | .dur)}' Output: difference (95% CI) ICF 83678.207 ± 1502.778 82234.751 ± 1290.984 [ -2.0% .. -1.4%] samples 208 225 --- lld/MachO/ICF.cpp | 58 ++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp index 7b31378c3781e..4b7919223de8e 100644 --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -179,8 +179,30 @@ bool ICF::equalsConstant(const ConcatInputSection *ia, return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend); } }; - return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), - f); + if (!llvm::equal(ia->relocs, ib->relocs, f)) + return false; + + // Check unwind info structural compatibility: if there are symbols with + // associated unwind info, check that both sections have compatible symbol + // layouts. For simplicity, we only attempt folding when all symbols are at + // offset zero within the section (which is typically the case with + // .subsections_via_symbols.) + auto hasUnwind = [](Defined *d) { return d->unwindEntry() != nullptr; }; + const auto *itA = llvm::find_if(ia->symbols, hasUnwind); + const auto *itB = llvm::find_if(ib->symbols, hasUnwind); + if (itA == ia->symbols.end()) + return itB == ib->symbols.end(); + if (itB == ib->symbols.end()) + return false; + const Defined *da = *itA; + const Defined *db = *itB; + if (da->value != 0 || db->value != 0) + return false; + auto isZero = [](Defined *d) { return d->value == 0; }; + return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) == + ia->symbols.end() && + std::find_if_not(std::next(itB), ib->symbols.end(), isZero) == + ib->symbols.end(); } // Compare the "moving" parts of two ConcatInputSections -- i.e. everything not @@ -217,31 +239,21 @@ bool ICF::equalsVariable(const ConcatInputSection *ia, } return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2]; }; - if (!std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f)) + if (!llvm::equal(ia->relocs, ib->relocs, f)) return false; - // If there are symbols with associated unwind info, check that the unwind - // info matches. For simplicity, we only handle the case where there are only - // symbols at offset zero within the section (which is typically the case with - // .subsections_via_symbols.) + // Compare unwind info equivalence classes. auto hasUnwind = [](Defined *d) { return d->unwindEntry() != nullptr; }; const auto *itA = llvm::find_if(ia->symbols, hasUnwind); - const auto *itB = llvm::find_if(ib->symbols, hasUnwind); - if (itA == ia->symbols.end()) - return itB == ib->symbols.end(); - if (itB == ib->symbols.end()) - return false; - const Defined *da = *itA; - const Defined *db = *itB; - if (da->unwindEntry()->icfEqClass[icfPass % 2] != - db->unwindEntry()->icfEqClass[icfPass % 2] || - da->value != 0 || db->value != 0) - return false; - auto isZero = [](Defined *d) { return d->value == 0; }; - return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) == - ia->symbols.end() && - std::find_if_not(std::next(itB), ib->symbols.end(), isZero) == - ib->symbols.end(); + if (itA != ia->symbols.end()) { + const Defined *da = *itA; + // equalsConstant() guarantees that both sections have unwind info. + const Defined *db = *llvm::find_if(ib->symbols, hasUnwind); + if (da->unwindEntry()->icfEqClass[icfPass % 2] != + db->unwindEntry()->icfEqClass[icfPass % 2]) + return false; + } + return true; } // Find the first InputSection after BEGIN whose equivalence class differs