diff --git a/lld/MachO/ICF.cpp b/lld/MachO/ICF.cpp --- a/lld/MachO/ICF.cpp +++ b/lld/MachO/ICF.cpp @@ -395,6 +395,27 @@ } } +// We can ignore embedded addends when doing ICF because we have already mapped +// their raw values to Reloc structs during the parsing phase. We usually leave +// the embedded addends in InputSection::data instead of zeroing them out, as +// this is expensive. However, these embedded addends can cause ICF to miss +// folding opportunities, because while different addends can resolve to the +// same output VA, ICF can't see that while doing its naive hashing / equality +// checks. +// +// We compromise by zeroing out embedded addends for sections where it is likely +// to expose a lot more folding opportunities. +static void clearEmbeddedAddends(InputSection *isec) { + // We have to do this copying serially as the BumpPtrAllocator is not + // thread-safe. FIXME: Make a thread-safe allocator. + MutableArrayRef copy = isec->data.copy(bAlloc()); + for (const Reloc &r : isec->relocs) { + target->relocateOne(copy.data() + r.offset, r, /*va=*/0, + /*relocVA=*/0); + } + isec->data = copy; +} + void macho::foldIdenticalSections() { TimeTraceScope timeScope("Fold Identical Code Sections"); // The ICF equivalence-class segregation algorithm relies on pre-computed @@ -415,30 +436,21 @@ uint64_t icfUniqueID = inputSections.size(); for (ConcatInputSection *isec : inputSections) { // FIXME: consider non-code __text sections as hashable? - bool isHashable = (isCodeSection(isec) || isCfStringSection(isec) || - isClassRefsSection(isec)) && - !isec->keepUnique && !isec->shouldOmitFromOutput() && - sectionType(isec->getFlags()) == MachO::S_REGULAR; + bool isHashable = + (isCodeSection(isec) || isCfStringSection(isec) || + isClassRefsSection(isec) || isGccExceptTabSection(isec)) && + !isec->keepUnique && !isec->shouldOmitFromOutput() && + sectionType(isec->getFlags()) == MachO::S_REGULAR; if (isHashable) { hashable.push_back(isec); for (Defined *d : isec->symbols) - if (d->unwindEntry) + if (d->unwindEntry) { + clearEmbeddedAddends(d->unwindEntry); hashable.push_back(d->unwindEntry); + } - // __cfstring has embedded addends that foil ICF's hashing / equality - // checks. (We can ignore embedded addends when doing ICF because the same - // information gets recorded in our Reloc structs.) We therefore create a - // mutable copy of the CFString and zero out the embedded addends before - // performing any hashing / equality checks. - if (isCfStringSection(isec) || isClassRefsSection(isec)) { - // We have to do this copying serially as the BumpPtrAllocator is not - // thread-safe. FIXME: Make a thread-safe allocator. - MutableArrayRef copy = isec->data.copy(bAlloc()); - for (const Reloc &r : isec->relocs) - target->relocateOne(copy.data() + r.offset, r, /*va=*/0, - /*relocVA=*/0); - isec->data = copy; - } + if (isCfStringSection(isec) || isClassRefsSection(isec)) + clearEmbeddedAddends(isec); } else if (!isEhFrameSection(isec)) { // EH frames are gathered as hashables from unwindEntry above; give a // unique ID to everything else. diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -281,6 +281,7 @@ bool isCfStringSection(const InputSection *); bool isClassRefsSection(const InputSection *); bool isEhFrameSection(const InputSection *); +bool isGccExceptTabSection(const InputSection *); extern std::vector inputSections; diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -345,6 +345,11 @@ isec->getSegName() == segment_names::text; } +bool macho::isGccExceptTabSection(const InputSection *isec) { + return isec->getName() == section_names:: gccExceptTab && + isec->getSegName() == segment_names::text; +} + std::string lld::toString(const InputSection *isec) { return (toString(isec->getFile()) + ":(" + isec->getName() + ")").str(); } diff --git a/lld/test/MachO/icf.s b/lld/test/MachO/icf.s --- a/lld/test/MachO/icf.s +++ b/lld/test/MachO/icf.s @@ -37,6 +37,7 @@ # CHECK: [[#%x,HAS_UNWIND_2:]] l F __TEXT,__text _has_unwind_1 # CHECK: [[#%x,HAS_UNWIND_2:]] l F __TEXT,__text _has_unwind_2 # CHECK: [[#%x,HAS_UNWIND_3:]] l F __TEXT,__text _has_unwind_3 +# CHECK: [[#%x,HAS_UNWIND_4:]] l F __TEXT,__text _has_unwind_4 # CHECK: [[#%x,MUTALLY_RECURSIVE_2:]] l F __TEXT,__text _mutually_recursive_1 # CHECK: [[#%x,MUTALLY_RECURSIVE_2:]] l F __TEXT,__text _mutually_recursive_2 # CHECK: [[#%x,INIT_2:]] l F __TEXT,__text _init_1 @@ -45,6 +46,9 @@ ### FIXME: Mutually-recursive functions with identical bodies (see below) # COM: [[#%x,ASYMMETRIC_RECURSIVE_2:]] l F __TEXT,__text _asymmetric_recursive_1 # COM: [[#%x,ASYMMETRIC_RECURSIVE_2]] l F __TEXT,__text _asymmetric_recursive_2 +# CHECK: [[#%x,GCC_EXCEPT_0:]] l O __TEXT,__gcc_except_tab GCC_except_table0 +# CHECK: [[#%x,GCC_EXCEPT_0]] l O __TEXT,__gcc_except_tab GCC_except_table1 +# CHECK: [[#%x,GCC_EXCEPT_2:]] l O __TEXT,__gcc_except_tab GCC_except_table2 # CHECK-LABEL: Disassembly of section __TEXT,__text: # CHECK: <_main>: @@ -72,6 +76,7 @@ # CHECK: callq 0x[[#%x,HAS_UNWIND_2:]] <_has_unwind_2> # CHECK: callq 0x[[#%x,HAS_UNWIND_2:]] <_has_unwind_2> # CHECK: callq 0x[[#%x,HAS_UNWIND_3:]] <_has_unwind_3> +# CHECK: callq 0x[[#%x,HAS_UNWIND_4]] <_has_unwind_4> # CHECK: callq 0x[[#%x,MUTALLY_RECURSIVE_2:]] <_mutually_recursive_2> # CHECK: callq 0x[[#%x,MUTALLY_RECURSIVE_2:]] <_mutually_recursive_2> ## FIXME: Mutually-recursive functions with identical bodies (see below) @@ -189,6 +194,7 @@ _has_unwind_1: .cfi_startproc .cfi_personality 155, _my_personality + .cfi_lsda 16, Lexception0 .cfi_def_cfa_offset 16 ret .cfi_endproc @@ -196,19 +202,31 @@ _has_unwind_2: .cfi_startproc .cfi_personality 155, _my_personality + .cfi_lsda 16, Lexception1 .cfi_def_cfa_offset 16 ret .cfi_endproc -## This function has different unwind info from the preceding two, and therefore +## This function has a different cfa_offset from the first two, and therefore ## should not be folded. _has_unwind_3: .cfi_startproc .cfi_personality 155, _my_personality + .cfi_lsda 16, Lexception1 .cfi_def_cfa_offset 8 ret .cfi_endproc +## This function has a different LSDA from the first two, and therefore should +## not be folded. +_has_unwind_4: + .cfi_startproc + .cfi_personality 155, _my_personality + .cfi_lsda 16, Lexception2 + .cfi_def_cfa_offset 16 + ret + .cfi_endproc + ## Fold: Mutually-recursive functions with symmetric bodies _mutually_recursive_1: callq _mutually_recursive_1 # call myself @@ -279,6 +297,7 @@ callq _has_unwind_1 callq _has_unwind_2 callq _has_unwind_3 + callq _has_unwind_4 callq _mutually_recursive_1 callq _mutually_recursive_2 callq _asymmetric_recursive_1 @@ -286,3 +305,16 @@ callq _init_1 callq _init_2 callq _init_3 + +.section __TEXT,__gcc_except_tab +GCC_except_table0: +Lexception0: + .byte 255 + +GCC_except_table1: +Lexception1: + .byte 255 + +GCC_except_table2: +Lexception2: + .byte 254