@@ -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
Expand All
@@ -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.
## No fold: _not_abs has the same value as _abs1{a,b}, but is not absolute.
_not_abs_ref:
movabs $_not_abs, %rdx
.globl _b2
.p2align 2
_b2:
callq _d
_dylib_ref_1:
mov ___nan@GOTPCREL(%rip ), %rax
callq ___isnan
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_2:
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!
## No fold: referent dylib symbol differs
_dylib_ref_3:
mov ___inf@GOTPCREL(%rip ), %rax
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
.globl _d
.p2align 2
_d:
callq _d
mov ___nan@GOTPCREL(%rip ), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0 , %eax
_no_alt_entry:
movq $3132 , %rax
ret
### No fold: the function body is longer
_defined_ref_with_addend_1:
callq _with_alt_entry + 4
.globl _e
.p2align 2
_e:
callq _d
mov ___nan@GOTPCREL(%rip ), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0 , %eax
ret
nop
_defined_ref_with_addend_2:
callq _with_alt_entry + 4
### No fold: GOT referent dylib symbol differs
## _recursive has the same body as its next two callers, but cannot be folded
## with them.
_recursive:
callq _recursive
.globl _f
.p2align 2
_f:
callq _d
mov ___inf@GOTPCREL(%rip ), %rax
callq ___isnan
movabs $_abs1a, %rdx
movl $0 , %eax
ret
_call_recursive_1:
callq _recursive
### No fold: call referent dylib symbol differs
_call_recursive_2:
callq _recursive
.globl _g
.p2align 2
_g:
callq _d
mov ___nan@GOTPCREL(%rip ), %rax
callq ___isinf
movabs $_abs1a, %rdx
movl $0 , %eax
ret
## Functions of different lengths should not be folded
_check_length_1:
movq $97 , %rax
_check_length_2:
movq $97 , %rax
.space 1
### 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.
_my_personality:
mov $1345 , %rax
.globl _i
.p2align 2
_i :
## 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: _k2 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 )
### Fold: Simple recursion
.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
## 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
ret
.globl _xr2
.p2align 2
_xr2:
callq _xr1 # call my twin
callq _xr2 # call myself
_asymmetric_recursive_2:
callq _asymmetric_recursive_1 # call my twin
callq _asymmetric_recursive_2 # call myself
movl $3 , %eax
ret
###
_init_1:
movq $12938 , %rax
.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
## Fold: _init_2 is in a section that gets renamed and output as __text
.section __TEXT,__StaticInit
_init_2:
movq $12938 , %rax
.globl _my_personality0
.p2align 2
_my_personality0:
movl $0 , %eax
ret
## No fold: _init_3 is in a different output section from _init_{1,2}
.section __TEXT,__foo
_init_3:
movq $12938 , %rax
.section __TEXT,__gcc_except_tab
.globl _exception0
_exception0:
.space 1
.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 _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