GCC 优化过于复杂,这里探索 GCC 对未使用的代码和变量以及文件的优化策略。
- 3.10 Options That Control Optimization
- 6.3.3 Reducing Size of Executables with Unused Subprogram/Data Elimination
样例
如下两个文件。
$ tree
├── main.c
└── unused.c
main.c
文件内容如下。
#include <stdio.h>
// __attribute__((used, section(".linker_entry_list")))
int main_extern_unused_var = 0;
// __attribute__((used, section(".linker_entry_list")))
static int main_static_unused_var = 0;
// __attribute__((used))
static int main_static_unused_func(void) {
printf("static unused function.\n");
return 0;
}
// __attribute__((used))
int main_extern_unused_func(void) {
printf("extern unused function.\n");
return 0;
}
int main(int argc, char* argv[]) {
printf("hello world!\n");
return 0;
}
unused.c
#include <stdio.h>
// __attribute__((used, section(".linker_entry_list")))
int other_extern_unused_var = 0;
// __attribute__((used, section(".linker_entry_list")))
static int other_static_unused_var = 0;
// __attribute__((used))
static int other_static_unused_func(void) {
printf("static unused function.\n");
return 0;
}
// __attribute__((used))
int other_extern_unused_func(void) {
printf("extern unused function.\n");
return 0;
}
探索
默认编译选项,通过可执行文件确定所有变量和函数都未优化。
$ gcc -o main.app main.c unused.c readelf -a main.app |grep unused 37: 0000000000004018 4 OBJECT LOCAL DEFAULT 26 main_static_unused_var 38: 0000000000001149 27 FUNC LOCAL DEFAULT 16 main_static_unused_func 40: 0000000000004020 4 OBJECT LOCAL DEFAULT 26 other_static_unused_var 41: 00000000000011a5 27 FUNC LOCAL DEFAULT 16 other_static_unused_func 57: 0000000000001164 27 FUNC GLOBAL DEFAULT 16 main_extern_unused_func 68: 00000000000011c0 27 FUNC GLOBAL DEFAULT 16 other_extern_unused_func 69: 000000000000401c 4 OBJECT GLOBAL DEFAULT 26 other_extern_unused_var 70: 0000000000004014 4 OBJECT GLOBAL DEFAULT 26 main_extern_unused_var
-O1 -O2 -Os
选项,所有static
链接属性的变量和函数被用优化掉。$ gcc -Os -o main.app main.c unused.c $ readelf -a main.app |grep unused 53: 0000000000001169 21 FUNC GLOBAL DEFAULT 16 main_extern_unused_func 64: 000000000000117e 21 FUNC GLOBAL DEFAULT 16 other_extern_unused_func 65: 0000000000004018 4 OBJECT GLOBAL DEFAULT 26 other_extern_unused_var 66: 0000000000004014 4 OBJECT GLOBAL DEFAULT 26 main_extern_unused_var
为
static
链接属性的变量增加__attribute__((used))
属性。优化被抑制。__attribute__((used)) static int other_static_unused_var=0; __attribute__((used)) static int other_static_unused_func(void) { printf("static unused function.\n"); return 0; }
$ gcc -Os -o main.app main.c unused.c $ readelf -a main.app |grep unused 29: 0000000000001169 21 FUNC LOCAL DEFAULT 16 main_static_unused_func 30: 0000000000004018 4 OBJECT LOCAL DEFAULT 26 main_static_unused_var 40: 0000000000001193 21 FUNC LOCAL DEFAULT 16 other_static_unused_func 41: 0000000000004020 4 OBJECT LOCAL DEFAULT 26 other_static_unused_var 57: 000000000000117e 21 FUNC GLOBAL DEFAULT 16 main_extern_unused_func 68: 00000000000011a8 21 FUNC GLOBAL DEFAULT 16 other_extern_unused_func 69: 000000000000401c 4 OBJECT GLOBAL DEFAULT 26 other_extern_unused_var 70: 0000000000004014 4 OBJECT GLOBAL DEFAULT 26 main_extern_unused_var
增加
-Wl,--gc-sections
选项,所有的unused.c
变量和函数,以及main.c
中的变量全部被优化。$gcc -Wl,--gc-sections -o main.app main.c unused.c $readelf -a main.app |grep unused 37: 0000000000001149 27 FUNC LOCAL DEFAULT 16 main_static_unused_func 53: 0000000000001164 27 FUNC GLOBAL DEFAULT 16 main_extern_unused_func
同时使用
-Wl,--gc-sections
和-O1
。进一步优化掉main.c
中的static
函数,只剩下最后一个未使用的extern
函数。$ gcc -Wl,--gc-sections -O1 -o main.app main.c unused.c $ readelf -a main.app |grep unused 52: 0000000000001149 30 FUNC GLOBAL DEFAULT 16 main_extern_unused_func
同时使用
-Wl,--gc-sections
和-O2
或者-Os
。所有未使用的函数、变量全部被优化。$ gcc -Wl,--gc-sections -O2 -o main.app main.c unused.c
或者额外指定
-ffunction-sections
,进一步把未使用的函数优化掉。$gcc -Wl,--gc-sections -ffunction-sections -fdata-sections -o main.app main.c unused.c
对于
--gc-sections
,通过设置KEEP
链接抑制变量的优化。注意,并不能对函数使用属性。__attribute__((used, section(".linker_entry_list"))) int main_extern_unused_var = 0; __attribute__((used, section(".linker_entry_list"))) static int main_static_unused_var = 0;
.linker_entry_list : { KEEP(*(SORT(.linker_entry_list*))); }
$gcc -Wl,--gc-sections -ffunction-sections -fdata-sections -o main.app main.c unused.c -T x86_64.lds $ readelf -a main.app |grep unused 25: 3ffbdb64 4 OBJECT LOCAL DEFAULT 13 main_static_unused_var 27: 3ffbdb6c 4 OBJECT LOCAL DEFAULT 13 other_static_unused_var 88: 3ffbdb68 4 OBJECT GLOBAL DEFAULT 13 other_extern_unused_var 98: 3ffbdb60 4 OBJECT GLOBAL DEFAULT 13 main_extern_unused_var
结论
选项 | 结果 |
---|---|
默认选项/-O0 | 不优化 |
-O1/2/3/s | 按链接属性优化,优化未使用的 static 链接属性的变量和函数。可以通过 __attribute__((used)) 抑制该优化。 |
-Wl,--gc-sections | 按未使用的变量、函数、文件进行优化,默认优化未使用变量(-fdata-sections)和未使用文件,通过指定 (-ffunction-sections)选择未优化函数。对于 Data 可以使用 KEEP 抑制该优化。 |
还不快抢沙发