diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -366,11 +366,16 @@ // ICF = Identical C(ode|OMDAT) Folding // -// FIXME: implement address-significance tables for MachO object files -// FIXME: exclude writable isecs -// FIXME: exclude init/fini isecs -// FIXME: fold vtables? -// FIXME: implement keep-unique attributes? +// init/fini functions should never be folded. This is already baked-into +// MachO because the __mod_init_func & __mod_term_func sections belong to +// __DATA(_CONST),__const, whereas only __TEXT,__text is eligible for ICF. +// +// Writeable sections should never be folded. Since only members of readonly +// __TEXT,__text are foldable, anything in __DATA is excluded. +// +// FIXME(gkm): implement keep-unique attributes +// FIXME(gkm) fold objects in __DATA_CONST,* __DATA,__const __TEXT,__const +// FIXME(gkm): implement address-significance tables for MachO object files static unsigned icfPass = 0; static std::atomic icfRepeat{false}; @@ -438,13 +443,22 @@ if (ra.referent.is()) { const auto *sa = ra.referent.get(); const auto *sb = rb.referent.get(); - if (const auto *da = dyn_cast(sa)) - if (const auto *db = dyn_cast(sb)) - return (!(da->isec->icfEquivalenceClass[icfPass % 2] != - db->isec->icfEquivalenceClass[icfPass % 2] && - logUnequal(nStr + "def hash"))); - // FIXME: handle DylibSymbol - logUnequal(nStr + "!defined"); + if (sa->kind() != sb->kind() && logUnequal(nStr + "sym kind")) + return false; + if (isa(sa) && isa(sb)) { + const auto *da = dyn_cast(sa); + const auto *db = dyn_cast(sb); + return !(da->isec->icfEquivalenceClass[icfPass % 2] != + db->isec->icfEquivalenceClass[icfPass % 2] && + logUnequal(nStr + "def hash")); + } else if (isa(sa) && isa(sb)) { + const auto *da = dyn_cast(sa); + const auto *db = dyn_cast(sb); + return !(da->stubsHelperIndex != db->stubsHelperIndex && + logUnequal(nStr + "stub index")); + } else { + llvm_unreachable("equalsVariable symbol kind"); + } return false; } const auto *sa = ra.referent.get(); @@ -513,10 +527,14 @@ parallelForEach(icfInputs, [&](InputSection *isec) { uint64_t hash = isec->icfEquivalenceClass[icfPass % 2]; for (const Reloc &r : isec->relocs) { - if (auto *sym = r.referent.dyn_cast()) + if (auto *sym = r.referent.dyn_cast()) { if (auto *defined = dyn_cast(sym)) - // FIXME: handle InputSection & DylibSymbol relocs hash += defined->isec->icfEquivalenceClass[icfPass % 2]; + else if (auto *dylibSym = dyn_cast(sym)) + hash += static_cast(dylibSym->stubsHelperIndex) << 32; + else + llvm_unreachable("foldIdenticalSections symbol kind"); + } } // Set MSB to 1 to avoid collisions with non-hashed classes. isec->icfEquivalenceClass[(icfPass + 1) % 2] = hash | (1ull << 63); diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -49,7 +49,7 @@ // How many symbols refer to this InputSection. uint32_t numRefs = 0; - bool isEligibleForICF() const; + bool isEligibleForICF(const OutputSection *textOutputSection) const; void hashForICF(); void foldIdentical(InputSection *redundant); diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -47,22 +47,21 @@ return sym->getVA(); } -bool InputSection::isEligibleForICF() const { +bool InputSection::isEligibleForICF( + const OutputSection *textOutputSection) const { + if (shouldOmitFromOutput()) + return false; switch (sectionType(flags)) { case S_REGULAR: - if (shouldOmitFromOutput()) - return false; - if (in.unwindInfo->funcHasPersonality(this)) - return false; - // FIXME: too permissive? - return true; + // FIXME(gkm): handle readonly sections outside __TEXT,__text + return parent == textOutputSection && + !in.unwindInfo->funcHasPersonality(this); case S_CSTRING_LITERALS: case S_4BYTE_LITERALS: case S_8BYTE_LITERALS: case S_16BYTE_LITERALS: case S_LITERAL_POINTERS: - // FIXME: too restrictive? - return false; + return true; case S_ZEROFILL: case S_GB_ZEROFILL: case S_NON_LAZY_SYMBOL_POINTERS: @@ -99,7 +98,7 @@ log("ICF: " + thisName + " replaces " + thatName + ", saves " + to_string(getSize())); align = std::max(align, copy->align); - copy->canOmitFromOutput = true; + copy->wasCoalesced = true; if (copy->defined) { numRefs++; copy->defined->isec = this; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -49,9 +49,9 @@ void scanRelocations(); void scanSymbols(); - template ConcatOutputSection *createOutputSections(); + template void createOutputSections(); template void createLoadCommands(); - void foldIdenticalSections(ConcatOutputSection *textSection); + void foldIdenticalSections(); void finalizeAddresses(); void finalizeLinkEditSegment(); void assignAddresses(OutputSegment *); @@ -76,6 +76,7 @@ LCUuid *uuidCommand = nullptr; OutputSegment *linkEditSegment = nullptr; + ConcatOutputSection *textOutputSection = nullptr; }; // LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information. @@ -840,7 +841,7 @@ return key; } -template ConcatOutputSection *Writer::createOutputSections() { +template void Writer::createOutputSections() { TimeTraceScope timeScope("Create output sections"); // First, create hidden sections stringTableSection = make(); @@ -905,11 +906,14 @@ // dyld requires __LINKEDIT segment to always exist (even if empty). linkEditSegment = getOrCreateOutputSegment(segment_names::linkEdit); - return concatOutputSections.lookup( + textOutputSection = concatOutputSections.lookup( maybeRenameSection({segment_names::text, section_names::text})); } -void Writer::foldIdenticalSections(ConcatOutputSection *textSection) { +void Writer::foldIdenticalSections() { + if (config->icfLevel == ICFLevel::none || textOutputSection == nullptr) + return; + TimeTraceScope timeScope("Fold Identical Code Sections"); // The ICF equivalence-class segregation algorithm relies on pre-computed // hashes of InputSection::data for the ConcatOutputSection::inputs and all @@ -922,13 +926,13 @@ // ID to force it into an unfoldable singleton equivalence class. uint64_t icfInvalidId = 0; for (InputSection *isec : inputSections) { - if (isec->parent == textSection && isec->isEligibleForICF()) + if (isec->isEligibleForICF(textOutputSection)) hashable.push_back(isec); else isec->icfEquivalenceClass[0] = ++icfInvalidId; } parallelForEach(hashable, [](InputSection *isec) { isec->hashForICF(); }); - textSection->foldIdenticalSections(); + textOutputSection->foldIdenticalSections(); } void Writer::finalizeAddresses() { @@ -1054,9 +1058,8 @@ if (in.stubHelper->isNeeded()) in.stubHelper->setup(); scanSymbols(); - ConcatOutputSection *textSection = createOutputSections(); - if (config->icfLevel != ICFLevel::none && textSection) - foldIdenticalSections(textSection); + createOutputSections(); + foldIdenticalSections(); // After this point, we create no new segments; HOWEVER, we might // yet create branch-range extension thunks for architectures whose // hardware call instructions have limited range, e.g., ARM(64).