[lld-macho] Reorganize + extend ICF test

I found icf.s a bit hard to work with as it was not possible to
extend any of the functions `_a` ... `_k` to test new relocation /
referent types without modifying every single one of them. Additionally,
their one-letter names were not descriptive (though the comments
helped).

I've renamed all the functions to reflect the feature they are testing,
and shrunk them so that they contain just enough to test that one
feature.

I've also added tests for non-zero addends (via the
`_abs1a_ref_with_addend` and `_defined_ref_with_addend_1` functions).

Reviewed By: #lld-macho, gkm

Differential Revision: https://reviews.llvm.org/D106211
This commit is contained in:
Jez Ng 2021-07-23 11:33:26 -04:00
parent 0ad562b48b
commit 8eac5dcb36

View File

@ -1,60 +1,79 @@
# 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 -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/main.s -o %t/main.o
# RUN: llvm-mc -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 %t/main | FileCheck %s
# CHECK-LABEL: SYMBOL TABLE:
# CHECK: [[#%x,MAIN:]] g F __TEXT,__text _main
# CHECK: [[#%x,A:]] g F __TEXT,__text _a1
# CHECK: [[#%x,H:]] g F __TEXT,__text _h
# CHECK: [[#%x,A]] g F __TEXT,__text _a2
# CHECK: [[#%x,A]] g F __TEXT,__text _a3
# CHECK: [[#%x,B:]] g F __TEXT,__text _b
# CHECK: [[#%x,B2:]] g F __TEXT,__text _b2
# CHECK: [[#%x,C:]] g F __TEXT,__text _c
# CHECK: [[#%x,D:]] g F __TEXT,__text _d
# CHECK: [[#%x,E:]] g F __TEXT,__text _e
# CHECK: [[#%x,F:]] g F __TEXT,__text _f
# CHECK: [[#%x,G:]] g F __TEXT,__text _g
# CHECK: [[#%x,I:]] g F __TEXT,__text _i
# CHECK: [[#%x,J:]] g F __TEXT,__text _j
# CHECK: [[#%x,SR:]] g F __TEXT,__text _sr1
# CHECK: [[#%x,SR]] g F __TEXT,__text _sr2
# CHECK: [[#%x,MR:]] g F __TEXT,__text _mr1
# CHECK: [[#%x,MR]] g F __TEXT,__text _mr2
# CHECK: [[#%x,K1:]] g O __TEXT,__foo _k1
# CHECK: [[#%x,A:]] g F __TEXT,__text _k2
# 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,ALT:]] l F __TEXT,__text _alt
# CHECK: [[#%x,WITH_ALT_ENTRY:]] l F __TEXT,__text _with_alt_entry
# CHECK: [[#%x,WITH_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,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_1:]] l F __TEXT,__text _has_unwind_1
# CHECK: [[#%x,HAS_UNWIND_2:]] l F __TEXT,__text _has_unwind_2
# 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,XR:]] g F __TEXT,__text _xr1
# COM: [[#%x,XR]] g F __TEXT,__text _xr2
# 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-LABEL: Disassembly of section __TEXT,__text:
# CHECK: [[#%x,MAIN]] <_main>:
# CHECK-NEXT: callq 0x[[#%x,A]] <_k2>
# CHECK-NEXT: callq 0x[[#%x,A]] <_k2>
# CHECK-NEXT: callq 0x[[#%x,A]] <_k2>
# CHECK-NEXT: callq 0x[[#%x,B]] <_b>
# CHECK-NEXT: callq 0x[[#%x,B2]] <_b2>
# CHECK-NEXT: callq 0x[[#%x,C]] <_c>
# CHECK-NEXT: callq 0x[[#%x,D]] <_d>
# CHECK-NEXT: callq 0x[[#%x,E]] <_e>
# CHECK-NEXT: callq 0x[[#%x,F]] <_f>
# CHECK-NEXT: callq 0x[[#%x,G]] <_g>
# CHECK-NEXT: callq 0x[[#%x,H]] <_h>
# CHECK-NEXT: callq 0x[[#%x,I]] <_i>
# CHECK-NEXT: callq 0x[[#%x,J]] <_j>
# CHECK-NEXT: callq 0x[[#%x,K1]] <_k1>
# CHECK-NEXT: callq 0x[[#%x,A]] <_k2>
# CHECK-NEXT: callq 0x[[#%x,SR]] <_sr2>
# CHECK-NEXT: callq 0x[[#%x,SR]] <_sr2>
# CHECK-NEXT: callq 0x[[#%x,MR]] <_mr2>
# CHECK-NEXT: callq 0x[[#%x,MR]] <_mr2>
### FIXME: Mutually-recursive functions with identical bodies (see below)
# COM-NEXT: callq 0x[[#%x,XR]] <_xr2>
# COM-NEXT: callq 0x[[#%x,XR]] <_xr2>
# 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,ALT:]] <_alt>
# CHECK: callq 0x[[#%x,WITH_ALT_ENTRY:]] <_with_alt_entry>
# CHECK: callq 0x[[#%x,WITH_ALT_ENTRY:]] <_with_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,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_1:]] <_has_unwind_1>
# CHECK: callq 0x[[#%x,HAS_UNWIND_2:]] <_has_unwind_2>
# 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
@ -77,291 +96,167 @@ _not_abs:
#--- main.s
.subsections_via_symbols
.text
.globl _h
.alt_entry _h
### Fold: _a1 & _a2 have identical bodies, flags, relocs
.globl _a1
.p2align 2
_a1:
callq _d
### No fold: _h is an alt entry past _a1
_h:
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
_abs1a_ref:
movabs $_abs1a, %rdx
movl $0, %eax
ret
.globl _a2
.p2align 2
_a2:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
### Fold: reference to absolute symbol with different name but identical value
.globl _a3
.p2align 2
_a3:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
_abs1b_ref:
movabs $_abs1b, %rdx
movl $0, %eax
ret
### No fold: the absolute symbol value differs
_abs1a_ref_with_addend:
movabs $_abs1a + 3, %rdx
.globl _b
.p2align 2
_b:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
_abs1b_ref_with_addend:
movabs $_abs1b + 3, %rdx
## No fold: the absolute symbol value differs
_abs2_ref:
movabs $_abs2, %rdx
movl $0, %eax
ret
### No fold: _not_abs has the same value as _abs1{a,b}, but is not absolute.
.globl _b2
.p2align 2
_b2:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
## No fold: _not_abs has the same value as _abs1{a,b}, but is not absolute.
_not_abs_ref:
movabs $_not_abs, %rdx
movl $0, %eax
ret
### No fold: _c has slightly different body from _a1 & _a2
.globl _c
.p2align 2
_c:
callq _d
_dylib_ref_1:
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $1, %eax
ret
### No fold: _d has the same body as _a1 & _a2, but _d is recursive!
.globl _d
.p2align 2
_d:
callq _d
_dylib_ref_2:
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
### No fold: the function body is longer
.globl _e
.p2align 2
_e:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
nop
### No fold: GOT referent dylib symbol differs
.globl _f
.p2align 2
_f:
callq _d
## No fold: referent dylib symbol differs
_dylib_ref_3:
mov ___inf@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
callq ___inf
## We can merge two sections even if one of them has an alt entry. Just make
## sure we don't merge the alt entry symbol with a regular symbol.
.alt_entry _alt
_with_alt_entry:
movq $3132, %rax
_alt:
ret
### No fold: call referent dylib symbol differs
.globl _g
.p2align 2
_g:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isinf
movabs $_abs1a, %rdx
movl $0, %eax
_no_alt_entry:
movq $3132, %rax
ret
### No fold: functions have personality and/or LSDA
### Mere presence of personality and/or LSDA isolates a function into its own
### equivalence class. We don't care if two functions happen to have identical
### personality & LSDA.
_defined_ref_with_addend_1:
callq _with_alt_entry + 4
.globl _i
.p2align 2
_i:
_defined_ref_with_addend_2:
callq _with_alt_entry + 4
## _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
## No fold: functions have unwind info.
## FIXME: Fold functions with identical unwind info.
_has_unwind_1:
.cfi_startproc
.cfi_personality 155, _my_personality0
.cfi_lsda 16, _exception0
.cfi_personality 155, _my_personality
.cfi_def_cfa_offset 16
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
.cfi_endproc
.globl _j
.p2align 2
_j:
_has_unwind_2:
.cfi_startproc
.cfi_personality 155, _my_personality0
.cfi_lsda 16, _exception0
.cfi_personality 155, _my_personality
.cfi_def_cfa_offset 16
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
.cfi_endproc
### No fold: _k1 is in a different section from _a1
.section __TEXT,__foo
.globl _k1
.p2align 2
_k1:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
nopl (%rax)
## Fold: Mutually-recursive functions with symmetric bodies
_mutually_recursive_1:
callq _mutually_recursive_1 # call myself
callq _mutually_recursive_2 # call my twin
### Fold: _k2 is in a section that gets renamed and output as __text
_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
.globl _k2
.p2align 2
_k2:
callq _d
mov ___nan@GOTPCREL(%rip), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0, %eax
ret
## For some reason, llvm-mc generates different nop encodings when adding
## padding for __StaticInit vs __text functions. So we explicitly specify the
## nop here to make sure this function can be folded with _a1.
nopl (%rax)
_init_2:
movq $12938, %rax
### Fold: Simple recursion
## No fold: _init_3 is in a different output section from _init_{1,2}
.section __TEXT,__foo
_init_3:
movq $12938, %rax
.text
.globl _sr1
.p2align 2
_sr1:
callq _sr1
movl $0, %eax
ret
.globl _sr2
.p2align 2
_sr2:
callq _sr2
movl $0, %eax
ret
### Fold: Mutually-recursive functions with symmetric bodies
.globl _mr1
.p2align 2
_mr1:
callq _mr1 # call myself
callq _mr2 # call my twin
movl $0, %eax
ret
.globl _mr2
.p2align 2
_mr2:
callq _mr2 # call myself
callq _mr1 # call my twin
movl $0, %eax
ret
### 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.
.globl _xr1
.p2align 2
_xr1:
callq _xr1 # call myself
callq _xr2 # call my twin
movl $3, %eax
ret
.globl _xr2
.p2align 2
_xr2:
callq _xr1 # call my twin
callq _xr2 # call myself
movl $3, %eax
ret
###
.globl _main
.p2align 2
_main:
callq _a1
callq _a2
callq _a3
callq _b
callq _b2
callq _c
callq _d
callq _e
callq _f
callq _g
callq _h
callq _i
callq _j
callq _k1
callq _k2
callq _sr1
callq _sr2
callq _mr1
callq _mr2
callq _xr1
callq _xr2
ret
.globl _my_personality0
.p2align 2
_my_personality0:
movl $0, %eax
ret
.section __TEXT,__gcc_except_tab
.globl _exception0
_exception0:
.space 1
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 _alt
callq _with_alt_entry
callq _no_alt_entry
callq _defined_ref_with_addend_1
callq _defined_ref_with_addend_2
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 _mutually_recursive_1
callq _mutually_recursive_2
callq _asymmetric_recursive_1
callq _asymmetric_recursive_2
callq _init_1
callq _init_2
callq _init_3