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 @@ -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 @@ #--- 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