Skip to content

Commit

Permalink
[lld-macho] Support folding of functions with identical LSDAs
Browse files Browse the repository at this point in the history
To do this, we need to slice away the LSDA pointer, just like we are
slicing away the functionAddress pointer.

No observable difference in perf on chromium_framework:

             base           diff           difference (95% CI)
  sys_time   1.769 ± 0.068  1.761 ± 0.065  [  -2.7% ..   +1.8%]
  user_time  9.517 ± 0.110  9.528 ± 0.116  [  -0.6% ..   +0.8%]
  wall_time  8.291 ± 0.174  8.307 ± 0.183  [  -1.1% ..   +1.5%]
  samples    21             25

Reviewed By: #lld-macho, thakis

Differential Revision: https://reviews.llvm.org/D129830
  • Loading branch information
int3 committed Jul 19, 2022
1 parent 2d05418 commit f6017ab
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 13 deletions.
9 changes: 5 additions & 4 deletions lld/MachO/ICF.cpp
Expand Up @@ -420,10 +420,11 @@ void macho::foldIdenticalSections() {
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)
Expand Down
29 changes: 21 additions & 8 deletions lld/MachO/InputFiles.cpp
Expand Up @@ -1186,14 +1186,27 @@ ArrayRef<data_in_code_entry> ObjFile::getDataInCode() const {
void ObjFile::registerCompactUnwind(Section &compactUnwindSection) {
for (const Subsection &subsection : compactUnwindSection.subsections) {
ConcatInputSection *isec = cast<ConcatInputSection>(subsection.isec);
// Hack!! Since each CUE contains a different function address, if ICF
// operated naively and compared the entire contents of each CUE, entries
// with identical unwind info but belonging to different functions would
// never be considered equivalent. To work around this problem, we slice
// away the function address here. (Note that we do not adjust the offsets
// of the corresponding relocations.) We rely on `relocateCompactUnwind()`
// to correctly handle these truncated input sections.
isec->data = isec->data.slice(target->wordSize);
// Hack!! Each compact unwind entry (CUE) has its UNSIGNED relocations embed
// their addends in its data. Thus if ICF operated naively and compared the
// entire contents of each CUE, entries with identical unwind info but e.g.
// belonging to different functions would never be considered equivalent. To
// work around this problem, we remove some parts of the data containing the
// embedded addends. In particular, we remove the function address and LSDA
// pointers. Since these locations are at the start and end of the entry,
// we can do this using a simple, efficient slice rather than performing a
// copy. We are not losing any information here because the embedded
// addends have already been parsed in the corresponding Reloc structs.
//
// Removing these pointers would not be safe if they were pointers to
// absolute symbols. In that case, there would be no corresponding
// relocation. However, (AFAIK) MC cannot emit references to absolute
// symbols for either the function address or the LSDA. However, it *can* do
// so for the personality pointer, so we are not slicing that field away.
//
// Note that we do not adjust the offsets of the corresponding relocations;
// instead, we rely on `relocateCompactUnwind()` to correctly handle these
// truncated input sections.
isec->data = isec->data.slice(target->wordSize, 8 + target->wordSize);
uint32_t encoding = read32le(isec->data.data() + sizeof(uint32_t));
// llvm-mc omits CU entries for functions that need DWARF encoding, but
// `ld -r` doesn't. We can ignore them because we will re-synthesize these
Expand Down
5 changes: 5 additions & 0 deletions lld/MachO/InputSection.cpp
Expand Up @@ -345,6 +345,11 @@ bool macho::isEhFrameSection(const InputSection *isec) {
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();
}
1 change: 1 addition & 0 deletions lld/MachO/InputSection.h
Expand Up @@ -281,6 +281,7 @@ bool isCodeSection(const InputSection *);
bool isCfStringSection(const InputSection *);
bool isClassRefsSection(const InputSection *);
bool isEhFrameSection(const InputSection *);
bool isGccExceptTabSection(const InputSection *);

extern std::vector<ConcatInputSection *> inputSections;

Expand Down
60 changes: 59 additions & 1 deletion lld/test/MachO/icf.s
Expand Up @@ -37,6 +37,9 @@
# 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,HAS_ABS_PERSONALITY_1:]] l F __TEXT,__text _has_abs_personality_1
# CHECK: [[#%x,HAS_ABS_PERSONALITY_2:]] l F __TEXT,__text _has_abs_personality_2
# CHECK: [[#%x,HAS_EH_FRAME_1:]] l F __TEXT,__text _has_eh_frame_1
# CHECK: [[#%x,HAS_EH_FRAME_2:]] l F __TEXT,__text _has_eh_frame_2
# CHECK: [[#%x,HAS_EH_FRAME_3:]] l F __TEXT,__text _has_eh_frame_3
Expand All @@ -48,6 +51,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 that we don't accidentally dedup distinct EH frames.
# CHECK: FDE {{.*}} pc=[[#%x,HAS_EH_FRAME_1]]
Expand Down Expand Up @@ -80,6 +86,9 @@
# 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,HAS_ABS_PERSONALITY_1]] <_has_abs_personality_1>
# CHECK: callq 0x[[#%x,HAS_ABS_PERSONALITY_2]] <_has_abs_personality_2>
# CHECK: callq 0x[[#%x,HAS_EH_FRAME_1]] <_has_eh_frame_1>
# CHECK: callq 0x[[#%x,HAS_EH_FRAME_2]] <_has_eh_frame_2>
# CHECK: callq 0x[[#%x,HAS_EH_FRAME_3]] <_has_eh_frame_3>
Expand Down Expand Up @@ -200,26 +209,59 @@ _my_personality:
_has_unwind_1:
.cfi_startproc
.cfi_personality 155, _my_personality
.cfi_lsda 16, Lexception0
.cfi_def_cfa_offset 16
ret
.cfi_endproc

_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

## The next two functions should not be folded as they refer to personalities
## at different absolute addresses. This verifies that we are doing the right
## thing in our "data slicing hack" for compact unwind.
_has_abs_personality_1:
.cfi_startproc
.cfi_personality 155, _abs_personality_1
.cfi_def_cfa_offset 16
ret
.cfi_endproc

_has_abs_personality_2:
.cfi_startproc
.cfi_personality 155, _abs_personality_2
.cfi_def_cfa_offset 16
ret
.cfi_endproc

_abs_personality_1 = 0x1
_abs_personality_2 = 0x2

## In theory _has_eh_frame_{1, 2} can be dedup'ed, but we don't support this
## yet.
_has_eh_frame_1:
Expand Down Expand Up @@ -319,6 +361,9 @@ _main:
callq _has_unwind_1
callq _has_unwind_2
callq _has_unwind_3
callq _has_unwind_4
callq _has_abs_personality_1
callq _has_abs_personality_2
callq _has_eh_frame_1
callq _has_eh_frame_2
callq _has_eh_frame_3
Expand All @@ -329,3 +374,16 @@ _main:
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

0 comments on commit f6017ab

Please sign in to comment.