llvm/lld/test/MachO/icf.s

# REQUIRES: x86
# RUN: rm -rf %t; split-file %s %t

## Check that we fold identical function bodies correctly. Note: This test
## has many different functions; each group of similarly-named functions aim
## to test one aspect of ICF's logic. To prevent accidental folding across
## groups, we use `mov` instructions with a variety of immediates, with
## different immediate values for each group.

# RUN: llvm-mc -emit-compact-unwind-non-canonical=true -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/main.s -o %t/main.o
# RUN: llvm-mc -emit-compact-unwind-non-canonical=true -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/abs.s -o %t/abs.o
# RUN: %lld -lSystem --icf=all -o %t/main %t/main.o %t/abs.o
# RUN: llvm-objdump -d --syms --dwarf=frames %t/main | FileCheck %s

# CHECK-LABEL: SYMBOL TABLE:
# CHECK: [[#%x,ABS1B_REF:]]                 l     F __TEXT,__text _abs1a_ref
# CHECK: [[#%x,ABS1B_REF]]                  l     F __TEXT,__text _abs1b_ref
# CHECK: [[#%x,ABS1B_REF_WITH_ADDEND:]]     l     F __TEXT,__text _abs1a_ref_with_addend
# CHECK: [[#%x,ABS1B_REF_WITH_ADDEND]]      l     F __TEXT,__text _abs1b_ref_with_addend
# CHECK: [[#%x,ABS2_REF:]]                  l     F __TEXT,__text _abs2_ref
# CHECK: [[#%x,NOT_ABS_REF:]]               l     F __TEXT,__text _not_abs_ref
# CHECK: [[#%x,DYLIB_REF_2:]]               l     F __TEXT,__text _dylib_ref_1
# CHECK: [[#%x,DYLIB_REF_2]]                l     F __TEXT,__text _dylib_ref_2
# CHECK: [[#%x,DYLIB_REF_3:]]               l     F __TEXT,__text _dylib_ref_3
# CHECK: [[#%x,DYLIB_REF_4:]]               l     F __TEXT,__text _dylib_ref_4
# CHECK: [[#%x,ALT:]]                       l     F __TEXT,__text _alt
# CHECK: [[#%x,WITH_ALT_ENTRY:]]            l     F __TEXT,__text _with_alt_entry
# CHECK: [[#%x,NO_ALT_ENTRY:]]              l     F __TEXT,__text _no_alt_entry
# CHECK: [[#%x,DEFINED_REF_WITH_ADDEND_2:]] l     F __TEXT,__text _defined_ref_with_addend_1
# CHECK: [[#%x,DEFINED_REF_WITH_ADDEND_2]]  l     F __TEXT,__text _defined_ref_with_addend_2
# CHECK: [[#%x,DEFINED_REF_WITH_ADDEND_3:]] l     F __TEXT,__text _defined_ref_with_addend_3
# CHECK: [[#%x,RECURSIVE:]]                 l     F __TEXT,__text _recursive
# CHECK: [[#%x,CALL_RECURSIVE_2:]]          l     F __TEXT,__text _call_recursive_1
# CHECK: [[#%x,CALL_RECURSIVE_2]]           l     F __TEXT,__text _call_recursive_2
# CHECK: [[#%x,CHECK_LENGTH_1:]]            l     F __TEXT,__text _check_length_1
# CHECK: [[#%x,CHECK_LENGTH_2:]]            l     F __TEXT,__text _check_length_2
# 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
# 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
# CHECK: [[#%x,INIT_2]]                     l     F __TEXT,__text _init_2
# CHECK: [[#%x,INIT_3:]]                    l     O __TEXT,__foo  _init_3
### 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]]
# CHECK: FDE {{.*}} pc=[[#%x,HAS_EH_FRAME_2]]
# CHECK: FDE {{.*}} pc=[[#%x,HAS_EH_FRAME_3]]

# CHECK-LABEL: Disassembly of section __TEXT,__text:
# CHECK:        <_main>:
# CHECK: callq 0x[[#%x,ABS1B_REF]]                 <_abs1b_ref>
# CHECK: callq 0x[[#%x,ABS1B_REF]]                 <_abs1b_ref>
# CHECK: callq 0x[[#%x,ABS1B_REF_WITH_ADDEND]]     <_abs1b_ref_with_addend>
# CHECK: callq 0x[[#%x,ABS1B_REF_WITH_ADDEND]]     <_abs1b_ref_with_addend>
# CHECK: callq 0x[[#%x,ABS2_REF]]                  <_abs2_ref>
# CHECK: callq 0x[[#%x,NOT_ABS_REF]]               <_not_abs_ref>
# CHECK: callq 0x[[#%x,DYLIB_REF_2]]               <_dylib_ref_2>
# CHECK: callq 0x[[#%x,DYLIB_REF_2]]               <_dylib_ref_2>
# CHECK: callq 0x[[#%x,DYLIB_REF_3]]               <_dylib_ref_3>
# CHECK: callq 0x[[#%x,DYLIB_REF_4]]               <_dylib_ref_4>
# CHECK: callq 0x[[#%x,ALT]]                       <_alt>
# CHECK: callq 0x[[#%x,WITH_ALT_ENTRY]]            <_with_alt_entry>
# CHECK: callq 0x[[#%x,NO_ALT_ENTRY]]              <_no_alt_entry>
# CHECK: callq 0x[[#%x,DEFINED_REF_WITH_ADDEND_2]] <_defined_ref_with_addend_2>
# CHECK: callq 0x[[#%x,DEFINED_REF_WITH_ADDEND_2]] <_defined_ref_with_addend_2>
# CHECK: callq 0x[[#%x,DEFINED_REF_WITH_ADDEND_3]] <_defined_ref_with_addend_3>
# CHECK: callq 0x[[#%x,RECURSIVE]]                 <_recursive>
# CHECK: callq 0x[[#%x,CALL_RECURSIVE_2]]          <_call_recursive_2>
# CHECK: callq 0x[[#%x,CALL_RECURSIVE_2]]          <_call_recursive_2>
# CHECK: callq 0x[[#%x,CHECK_LENGTH_1]]            <_check_length_1>
# CHECK: callq 0x[[#%x,CHECK_LENGTH_2]]            <_check_length_2>
# 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>
# 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)
# COM:   callq 0x[[#%x,ASYMMETRIC_RECURSIVE_2]]    <_asymmetric_recursive_2>
# COM:   callq 0x[[#%x,ASYMMETRIC_RECURSIVE_2]]    <_asymmetric_recursive_2>
# CHECK: callq 0x[[#%x,INIT_2]]                    <_init_2>
# CHECK: callq 0x[[#%x,INIT_2]]                    <_init_2>
# CHECK: callq 0x[[#%x,INIT_3]]                    <_init_3>

### TODO:
### * Fold: funcs only differ in alignment
### * No fold: func is weak? preemptible?
### * Test that we hash things appropriately w/ minimal collisions

#--- abs.s
.subsections_via_symbols

.globl _abs1a, _abs1b, _abs2, _not_abs
_abs1a = 0xfac3
_abs1b = 0xfac3
_abs2 =  0xf00d

.data
.space 0xfac3
## _not_abs has the same Defined::value as _abs1{a,b}
_not_abs:

#--- main.s
.subsections_via_symbols
.text

_abs1a_ref:
  movabs $_abs1a, %rdx

_abs1b_ref:
  movabs $_abs1b, %rdx

_abs1a_ref_with_addend:
  movabs $_abs1a + 3, %rdx

_abs1b_ref_with_addend:
  movabs $_abs1b + 3, %rdx

## No fold: the absolute symbol value differs
_abs2_ref:
  movabs $_abs2, %rdx

## No fold: _not_abs has the same value as _abs1{a,b}, but is not absolute.
_not_abs_ref:
  movabs $_not_abs, %rdx

_dylib_ref_1:
  mov ___nan@GOTPCREL(%rip), %rax
  callq ___isnan

_dylib_ref_2:
  mov ___nan@GOTPCREL(%rip), %rax
  callq ___isnan

## No fold: referent dylib symbol differs
_dylib_ref_3:
  mov ___inf@GOTPCREL(%rip), %rax
  callq ___inf

## No fold: referent dylib addend differs
_dylib_ref_4:
  mov ___nan + 1@GOTPCREL(%rip), %rax
  callq ___inf + 1

## Sections with alt entries cannot be merged.
.alt_entry _alt
_with_alt_entry:
  movq $3132, %rax
_alt:
  ret

_no_alt_entry:
  movq $3132, %rax
  ret

_defined_ref_with_addend_1:
  callq _with_alt_entry + 4

_defined_ref_with_addend_2:
  callq _with_alt_entry + 4

# No fold: addend differs
_defined_ref_with_addend_3:
  callq _with_alt_entry + 8

## _recursive has the same body as its next two callers, but cannot be folded
## with them.
_recursive:
  callq _recursive

_call_recursive_1:
  callq _recursive

_call_recursive_2:
  callq _recursive

## Functions of different lengths should not be folded
_check_length_1:
  movq $97, %rax

_check_length_2:
  movq $97, %rax
  .space 1

_my_personality:
  mov $1345, %rax

## Functions with identical unwind info should be folded.
_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 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:
  .cfi_startproc
  .cfi_def_cfa_offset 8
  ## cfi_escape cannot be encoded in compact unwind
  .cfi_escape 0x2e, 0x10
  ret
  .cfi_endproc

_has_eh_frame_2:
  .cfi_startproc
  .cfi_def_cfa_offset 8
  ## cfi_escape cannot be encoded in compact unwind
  .cfi_escape 0x2e, 0x10
  ret
  .cfi_endproc

## The nop in this function body means that it cannot be folded with the
## previous two, even though the unwind info is otherwise identical.
_has_eh_frame_3:
  .cfi_startproc
  .cfi_def_cfa_offset 8
  ## cfi_escape cannot be encoded in compact unwind
  .cfi_escape 0x2e, 0x10
  nop
  ret
  .cfi_endproc

## Fold: Mutually-recursive functions with symmetric bodies
_mutually_recursive_1:
  callq _mutually_recursive_1 # call myself
  callq _mutually_recursive_2 # call my twin

_mutually_recursive_2:
  callq _mutually_recursive_2 # call myself
  callq _mutually_recursive_1 # call my twin

## Fold: Mutually-recursive functions with identical bodies
##
## FIXME: This test is currently broken. Recursive call sites have no relocs
## and the non-zero displacement field is already written to the section
## data, while non-recursive call sites use symbol relocs and section data
## contains zeros in the displacement field. Thus, ICF's equalsConstant()
## finds that the section data doesn't match.
##
## ELF folds this case properly because it emits symbol relocs for all calls,
## even recursive ones.

_asymmetric_recursive_1:
  callq _asymmetric_recursive_1 # call myself
  callq _asymmetric_recursive_2 # call my twin
  movl $3, %eax

_asymmetric_recursive_2:
  callq _asymmetric_recursive_1 # call my twin
  callq _asymmetric_recursive_2 # call myself
  movl $3, %eax

_init_1:
  movq $12938, %rax

## Fold: _init_2 is in a section that gets renamed and output as __text
.section __TEXT,__StaticInit
_init_2:
  movq $12938, %rax

## No fold: _init_3 is in a different output section from _init_{1,2}
.section __TEXT,__foo
_init_3:
  movq $12938, %rax

.text
.globl _main
_main:
  callq _abs1a_ref
  callq _abs1b_ref
  callq _abs1a_ref_with_addend
  callq _abs1b_ref_with_addend
  callq _abs2_ref
  callq _not_abs_ref
  callq _dylib_ref_1
  callq _dylib_ref_2
  callq _dylib_ref_3
  callq _dylib_ref_4
  callq _alt
  callq _with_alt_entry
  callq _no_alt_entry
  callq _defined_ref_with_addend_1
  callq _defined_ref_with_addend_2
  callq _defined_ref_with_addend_3
  callq _recursive
  callq _call_recursive_1
  callq _call_recursive_2
  callq _check_length_1
  callq _check_length_2
  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
  callq _mutually_recursive_1
  callq _mutually_recursive_2
  callq _asymmetric_recursive_1
  callq _asymmetric_recursive_2
  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