Skip to content

Commit accec8b

Browse files
authored
[lld][macho] Move unwind logic from equalsVariable to equalsConstant (#165325)
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: </Users/jezng/extract-icf-time.sh ["/Users/jezng/old/ld64.lld"]> </Users/jezng/extract-icf-time.sh ["bin/ld64.lld"]> difference (95% CI) ICF 83678.207 ± 1502.778 82234.751 ± 1290.984 [ -2.0% .. -1.4%] samples 208 225
1 parent 9decb10 commit accec8b

File tree

1 file changed

+37
-26
lines changed

1 file changed

+37
-26
lines changed

lld/MachO/ICF.cpp

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,37 @@ bool ICF::equalsConstant(const ConcatInputSection *ia,
173173
// a valid offset in the literal section.
174174
return isecA->getOffset(valueA) == isecB->getOffset(valueB) &&
175175
ra.addend == rb.addend;
176-
else {
177-
assert(valueA == 0 && valueB == 0);
178-
// For section relocs, we compare the content at the section offset.
179-
return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend);
180-
}
176+
assert(valueA == 0 && valueB == 0);
177+
// For section relocs, we compare the content at the section offset.
178+
return isecA->getOffset(ra.addend) == isecB->getOffset(rb.addend);
181179
};
182-
return std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(),
183-
f);
180+
if (!llvm::equal(ia->relocs, ib->relocs, f))
181+
return false;
182+
183+
// Check unwind info structural compatibility: if there are symbols with
184+
// associated unwind info, check that both sections have compatible symbol
185+
// layouts. For simplicity, we only attempt folding when all symbols are at
186+
// offset zero within the section (which is typically the case with
187+
// .subsections_via_symbols.)
188+
auto hasUnwind = [](Defined *d) { return d->unwindEntry() != nullptr; };
189+
const auto *itA = llvm::find_if(ia->symbols, hasUnwind);
190+
const auto *itB = llvm::find_if(ib->symbols, hasUnwind);
191+
if (itA == ia->symbols.end())
192+
return itB == ib->symbols.end();
193+
if (itB == ib->symbols.end())
194+
return false;
195+
const Defined *da = *itA;
196+
const Defined *db = *itB;
197+
if (da->value != 0 || db->value != 0)
198+
return false;
199+
auto isZero = [](Defined *d) { return d->value == 0; };
200+
// Since symbols are stored in order of value, and since we have already
201+
// checked that da/db have value zero, we just need to do the isZero check on
202+
// the subsequent symbols.
203+
return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) ==
204+
ia->symbols.end() &&
205+
std::find_if_not(std::next(itB), ib->symbols.end(), isZero) ==
206+
ib->symbols.end();
184207
}
185208

186209
// Compare the "moving" parts of two ConcatInputSections -- i.e. everything not
@@ -217,31 +240,19 @@ bool ICF::equalsVariable(const ConcatInputSection *ia,
217240
}
218241
return isecA->icfEqClass[icfPass % 2] == isecB->icfEqClass[icfPass % 2];
219242
};
220-
if (!std::equal(ia->relocs.begin(), ia->relocs.end(), ib->relocs.begin(), f))
243+
if (!llvm::equal(ia->relocs, ib->relocs, f))
221244
return false;
222245

223-
// If there are symbols with associated unwind info, check that the unwind
224-
// info matches. For simplicity, we only handle the case where there are only
225-
// symbols at offset zero within the section (which is typically the case with
226-
// .subsections_via_symbols.)
246+
// Compare unwind info equivalence classes.
227247
auto hasUnwind = [](Defined *d) { return d->unwindEntry() != nullptr; };
228248
const auto *itA = llvm::find_if(ia->symbols, hasUnwind);
229-
const auto *itB = llvm::find_if(ib->symbols, hasUnwind);
230249
if (itA == ia->symbols.end())
231-
return itB == ib->symbols.end();
232-
if (itB == ib->symbols.end())
233-
return false;
250+
return true;
234251
const Defined *da = *itA;
235-
const Defined *db = *itB;
236-
if (da->unwindEntry()->icfEqClass[icfPass % 2] !=
237-
db->unwindEntry()->icfEqClass[icfPass % 2] ||
238-
da->value != 0 || db->value != 0)
239-
return false;
240-
auto isZero = [](Defined *d) { return d->value == 0; };
241-
return std::find_if_not(std::next(itA), ia->symbols.end(), isZero) ==
242-
ia->symbols.end() &&
243-
std::find_if_not(std::next(itB), ib->symbols.end(), isZero) ==
244-
ib->symbols.end();
252+
// equalsConstant() guarantees that both sections have unwind info.
253+
const Defined *db = *llvm::find_if(ib->symbols, hasUnwind);
254+
return da->unwindEntry()->icfEqClass[icfPass % 2] ==
255+
db->unwindEntry()->icfEqClass[icfPass % 2];
245256
}
246257

247258
// Find the first InputSection after BEGIN whose equivalence class differs

0 commit comments

Comments
 (0)