Identical Code Folding (ICF) folds functions that are identical into one function, and updates symbol addresses to the new address. This reduces the size of a binary, but can lead to problems. For example when function pointers are compared. This can be done either explicitly in the code or generated IR by optimization passes like Indirect Call Promotion (ICP). After ICF what used to be two different addresses become the same address. This can lead to a different code path being taken. This is where safe ICF comes in. Linker (LLD) does it using address significant section generated by clang. If symbol is in it, or an object doesn't have this section symbols are not folded. BOLT does not have the information regarding which objects do not have this section, so can't re-use this mechanism. This implementation scans code section and conservatively marks functions symbols as unsafe. It treats symbols as unsafe if they are used in non-control flow instruction. It also scans through the data relocation sections and does the same for relocations that reference a function symbol. The latter handles the case when function pointer is stored in a local or global variable, etc. If a relocation address points within a vtable these symbols are skipped.
65 lines
1.9 KiB
Plaintext
65 lines
1.9 KiB
Plaintext
## Check that BOLT handles correctly folding functions with --icf=safe that are only referenced from a .rela.data section.
|
|
|
|
# REQUIRES: system-linux, asserts
|
|
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t1.o
|
|
# RUN: %clang %cflags %t1.o -o %t.exe -Wl,-q -no-pie
|
|
# RUN: llvm-bolt --no-threads %t.exe --icf -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=ICFCHECK %s
|
|
# RUN: llvm-bolt --no-threads %t.exe --icf=safe -debug-only=bolt-icf -o %t.bolt 2>&1 | FileCheck --check-prefix=SAFEICFCHECK %s
|
|
|
|
# ICFCHECK: ICF iteration 1
|
|
# ICFCHECK-NEXT: folding barAddFunc into fooAddFunc
|
|
|
|
# SAFEICFCHECK: skipping function with reference taken fooAddFunc
|
|
# SAFEICFCHECK-NEXT: skipping function with reference taken barAddFunc
|
|
# SAFEICFCHECK-NEXT: ICF iteration 1
|
|
# SAFEICFCHECK-NEXT: ===---------
|
|
|
|
## clang++ main.cpp
|
|
## Other functions removed for brevity.
|
|
## int main(int argc, char **argv) {
|
|
## const static int (*const funcGlobalBarAdd)(int, int) = barAddHdlper;
|
|
## const int (* const funcGlobalBarMul)(int, int) = fooGlobalFuncHelper;
|
|
## helper2(funcGlobalBarAdd, funcGlobalFooAdd, 3, 4)
|
|
## }
|
|
## Extra assembly removed.
|
|
|
|
.globl fooAddFunc
|
|
.type fooAddFunc,@function
|
|
fooAddFunc:
|
|
addl -8(%rbp), %eax
|
|
retq
|
|
.size fooAddFunc, .-fooAddFunc
|
|
|
|
.globl barAddFunc
|
|
.type barAddFunc,@function
|
|
barAddFunc:
|
|
addl -8(%rbp), %eax
|
|
retq
|
|
.size barAddFunc, .-barAddFunc
|
|
|
|
.globl helperFunc
|
|
.type helperFunc,@function
|
|
helperFunc:
|
|
retq
|
|
.size helperFunc, .-helperFunc
|
|
|
|
.globl main
|
|
.type main,@function
|
|
main:
|
|
movq localStaticVarBarAdd, %rdi
|
|
movq localStaticVarFooAdd, %rsi
|
|
callq helperFunc
|
|
retq
|
|
.size main, .-main
|
|
|
|
.type localStaticVarBarAdd,@object # @localStaticVarBarAdd
|
|
.data
|
|
localStaticVarBarAdd:
|
|
.quad barAddFunc
|
|
.size localStaticVarBarAdd, 8
|
|
|
|
.type localStaticVarFooAdd,@object # @localStaticVarFooAdd
|
|
localStaticVarFooAdd:
|
|
.quad fooAddFunc
|
|
.size localStaticVarFooAdd, 8
|