Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -140,6 +140,7 @@ bool OptRemarksWithHotness; bool Pie; bool PrintGcSections; + bool PrintIcfSections; bool Relocatable; bool SaveTemps; bool SingleRoRx; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -643,6 +643,8 @@ Config->OrphanHandling = getOrphanHandling(Args); Config->OutputFile = Args.getLastArgValue(OPT_o); Config->Pie = Args.hasFlag(OPT_pie, OPT_nopie, false); + Config->PrintIcfSections = + Args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false); Config->PrintGcSections = Args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); Config->Rpath = getRpath(Args); Index: ELF/ICF.cpp =================================================================== --- ELF/ICF.cpp +++ ELF/ICF.cpp @@ -424,14 +424,28 @@ log("ICF needed " + Twine(Cnt) + " iterations"); + auto Print = [&](const Twine &Prefix, size_t I) { + if (!Config->Verbose && !Config->PrintIcfSections) + return; + std::string Filename = + Sections[I]->File ? Sections[I]->File->getName() : ""; + std::string S = (Prefix + " section '" + Sections[I]->Name + + "' from file '" + Filename + "'") + .str(); + if (Config->PrintIcfSections) + message(S); + else + log(S); + }; + // Merge sections by the equivalence class. forEachClass([&](size_t Begin, size_t End) { if (End - Begin == 1) return; - log("selected " + Sections[Begin]->Name); + Print("selected", Begin); for (size_t I = Begin + 1; I < End; ++I) { - log(" removed " + Sections[I]->Name); + Print(" removing identical", I); Sections[Begin]->replace(Sections[I]); } }); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -222,6 +222,9 @@ def no_print_gc_sections: F<"no-print-gc-sections">, HelpText<"Do not list removed unused sections">; +def no_print_icf_sections: F<"no-print-icf-sections">, + HelpText<"Do not list identical folded sections">; + def no_rosegment: F<"no-rosegment">, HelpText<"Do not put read-only non-executable sections in their own segment">; @@ -251,6 +254,9 @@ def print_gc_sections: F<"print-gc-sections">, HelpText<"List removed unused sections">; +def print_icf_sections: F<"print-icf-sections">, + HelpText<"List identical folded sections">; + def print_map: F<"print-map">, HelpText<"Print a link map to the standard output">; Index: test/ELF/Inputs/print-icf.s =================================================================== --- test/ELF/Inputs/print-icf.s +++ test/ELF/Inputs/print-icf.s @@ -0,0 +1,9 @@ +.section .text.f6, "ax" +f6: + mov $60, %rax + mov $42, %rdi + syscall + + .section .text.f7, "ax" +f7: + mov $0, %rax Index: test/ELF/icf-absolute.s =================================================================== --- test/ELF/icf-absolute.s +++ test/ELF/icf-absolute.s @@ -4,8 +4,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-absolute.s -o %t2 # RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file +# CHECK: removing identical section '.text.f2' from file .globl _start, f1, f2 _start: Index: test/ELF/icf-comdat.s =================================================================== --- test/ELF/icf-comdat.s +++ test/ELF/icf-comdat.s @@ -3,8 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file [[T:'.*']] +# CHECK: removing identical section '.text.f2' from file [[T]] .globl _start, f1, f2 _start: Index: test/ELF/icf-i386.s =================================================================== --- test/ELF/icf-i386.s +++ test/ELF/icf-i386.s @@ -4,9 +4,9 @@ # RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 -# CHECK-NOT: removed .text.f3 +# CHECK: selected section '.text.f1' from file [[T:'.*']] +# CHECK: removing identical section '.text.f2' from file [[T]] +# CHECK-NOT: removing identical section '.text.f3' from file [[T]] .globl _start, f1, f2, f3 _start: Index: test/ELF/icf-merge-sec.s =================================================================== --- test/ELF/icf-merge-sec.s +++ test/ELF/icf-merge-sec.s @@ -4,8 +4,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge-sec.s -o %t2 # RUN: ld.lld %t %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file +# CHECK: removing identical section '.text.f2' from file .section .rodata.str,"aMS",@progbits,1 .asciz "foo" Index: test/ELF/icf-merge.s =================================================================== --- test/ELF/icf-merge.s +++ test/ELF/icf-merge.s @@ -10,10 +10,10 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/icf-merge3.s -o %t3 # RUN: ld.lld %t %t3 -o %t3.out --icf=all --verbose 2>&1 | FileCheck --check-prefix=NOMERGE %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file +# CHECK: removing identical section '.text.f2' from file -# NOMERGE-NOT: selected .text.f +# NOMERGE-NOT: selected section '.text.f .section .rodata.str,"aMS",@progbits,1 foo: Index: test/ELF/icf-non-mergeable.s =================================================================== --- test/ELF/icf-non-mergeable.s +++ test/ELF/icf-non-mergeable.s @@ -10,8 +10,8 @@ // RUN: ld.lld %t1 %t2 -o %t3 --icf=all --verbose 2>&1 | FileCheck %s -// CHECK-NOT: selected .text.f1 -// CHECK-NOT: removed .text.f2 +// CHECK-NOT: selected section '.text.f1' +// CHECK-NOT: removing identical section '.text.f2' .globl _start, f1, f2, d1, d2 _start: Index: test/ELF/icf-none.s =================================================================== --- test/ELF/icf-none.s +++ test/ELF/icf-none.s @@ -3,7 +3,7 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --icf=none --verbose 2>&1 | FileCheck %s -# CHECK-NOT: selected .text.f1 +# CHECK-NOT: selected section '.text.f1' .globl _start, f1, f2 _start: Index: test/ELF/icf1.s =================================================================== --- test/ELF/icf1.s +++ test/ELF/icf1.s @@ -3,8 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file [[T:'.*']] +# CHECK: removing identical section '.text.f2' from file [[T]] .globl _start, f1, f2 _start: Index: test/ELF/icf2.s =================================================================== --- test/ELF/icf2.s +++ test/ELF/icf2.s @@ -4,8 +4,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2 # RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose 2>&1 | FileCheck %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file +# CHECK: removing identical section '.text.f2' from file .globl _start, f1, f2 _start: Index: test/ELF/icf3.s =================================================================== --- test/ELF/icf3.s +++ test/ELF/icf3.s @@ -4,8 +4,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/icf2.s -o %t2 # RUN: ld.lld %t1 %t2 -o %t --icf=all --verbose 2>&1 | FileCheck %s -# CHECK-NOT: Selected .text.f1 -# CHECK-NOT: Selected .text.f2 +# CHECK-NOT: selected section '.text.f1' from file +# CHECK-NOT: selected section '.text.f2' from file .globl _start, f1, f2 _start: Index: test/ELF/icf4.s =================================================================== --- test/ELF/icf4.s +++ test/ELF/icf4.s @@ -3,8 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK-NOT: Selected .text.f1 -# CHECK-NOT: Selected .text.f2 +# CHECK-NOT: selected section '.text.f1' +# CHECK-NOT: selected section '.text.f2' .globl _start, f1, f2 _start: Index: test/ELF/icf5.s =================================================================== --- test/ELF/icf5.s +++ test/ELF/icf5.s @@ -3,8 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK-NOT: Selected .text.f1 -# CHECK-NOT: Selected .text.f2 +# CHECK-NOT: selected section '.text.f1' +# CHECK-NOT: selected section '.text.f2' .globl _start, f1, f2 _start: Index: test/ELF/icf6.s =================================================================== --- test/ELF/icf6.s +++ test/ELF/icf6.s @@ -3,8 +3,8 @@ # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s -# CHECK-NOT: Selected .text.f1 -# CHECK-NOT: Selected .text.f2 +# CHECK-NOT: selected section '.text.f1' +# CHECK-NOT: selected section '.text.f2' .globl _start, f1, f2 _start: Index: test/ELF/icf7.s =================================================================== --- test/ELF/icf7.s +++ test/ELF/icf7.s @@ -4,8 +4,8 @@ # RUN: ld.lld %t -o %t2 --icf=all --verbose 2>&1 | FileCheck %s # RUN: llvm-objdump -t %t2 | FileCheck -check-prefix=ALIGN %s -# CHECK: selected .text.f1 -# CHECK: removed .text.f2 +# CHECK: selected section '.text.f1' from file [[T:'.*']] +# CHECK: removing identical section '.text.f2' from file [[T]] # ALIGN: 0000000000201000 .text 00000000 _start # ALIGN: 0000000000201100 .text 00000000 f1 Index: test/ELF/icf9.s =================================================================== --- test/ELF/icf9.s +++ test/ELF/icf9.s @@ -7,15 +7,15 @@ # SEC: .rodata PROGBITS 0000000000200120 000120 000002 00 A 0 0 1 -# CHECK-NOT: selected .rodata.d1 -# CHECK-NOT: selected .rodata.d2 +# CHECK-NOT: selected section '.rodata.d1' +# CHECK-NOT: selected section '.rodata.d2' # We do merge rodata if passed --icf-data # RUN: ld.lld %t -o %t2 --icf=all --verbose --ignore-data-address-equality 2>&1 | FileCheck --check-prefix=DATA %s # RUN: llvm-readelf -S -W %t2 | FileCheck --check-prefix=DATA-SEC %s -# DATA: selected .rodata.d1 -# DATA: removed .rodata.d2 +# DATA: selected section '.rodata.d1' from file [[T:'.*']] +# DATA: removing identical section '.rodata.d2' from file [[T]] # DATA-SEC: .rodata PROGBITS 0000000000200120 000120 000001 00 A 0 0 1 Index: test/ELF/print-icf.s =================================================================== --- test/ELF/print-icf.s +++ test/ELF/print-icf.s @@ -0,0 +1,48 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/print-icf.s -o %t1 +# RUN: ld.lld %t %t1 -o %t2 --icf=all --print-icf-sections | FileCheck %s +# RUN: ld.lld %t %t1 -o %t2 --icf=all --no-print-icf-sections --print-icf-sections | FileCheck %s +# RUN: ld.lld %t %t1 -o %t2 --icf=all --print-icf-sections --no-print-icf-sections | FileCheck -allow-empty -check-prefix=PRINT %s + +# CHECK: selected section '.text.f2' from file [[T:'.*']] +# CHECK: removing identical section '.text.f4' from file [[T]] +# CHECK: removing identical section '.text.f7' from file [[T1:'.*']] +# CHECK: selected section '.text.f1' from file [[T]] +# CHECK: removing identical section '.text.f3' from file [[T]] +# CHECK: removing identical section '.text.f5' from file [[T]] +# CHECK: removing identical section '.text.f6' from file [[T1]] + +# PRINT-NOT: selected +# PRINT-NOT: removing + +.globl _start, f1, f2 +_start: + ret + +.section .text.f1, "ax" +f1: + mov $60, %rax + mov $42, %rdi + syscall + + .section .text.f2, "ax" +f2: + mov $0, %rax + +.section .text.f3, "ax" +f3: + mov $60, %rax + mov $42, %rdi + syscall + +.section .text.f4, "ax" +f4: + mov $0, %rax + +.section .text.f5, "ax" +f5: + mov $60, %rax + mov $42, %rdi + syscall