为何GCC在-O0编译目标文件时LTO不执行函数死代码消除?
GCC LTO死代码消除差异:-O1 vs -O0编译目标文件的区别
示例代码
notmain.c
int __attribute__ ((noinline)) notmain(int i) { return i + 1; } int notmain2(int i) { return i + 2; }
main.c
int notmain(int); int main(int argc, char **argv) { return notmain(argc); }
测试过程与现象
我用noinline属性确保现象并非notmain是否被内联带来的次生效应。
用-O1编译并反汇编
执行命令:
gcc -c -flto -O1 notmain.c gcc -flto -O3 notmain.o main.c objdump -d a.out
结果:notmain保留,notmain2被死代码消除:
0000000000001040 <main>: 1040: f3 0f 1e fa endbr64 1044: e9 f0 00 00 00 jmp 1139 <notmain> 1049: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000001139 <notmain>: 1139: 8d 47 01 lea 0x1(%rdi),%eax 113c: c3 ret
用-O0编译并反汇编
改用以下命令:
gcc -c -flto -O0 notmain.c gcc -flto -O3 notmain.o main.c objdump -d a.out
结果:两个函数都保留:
0000000000001040 <main>: 1040: f3 0f 1e fa endbr64 1044: e9 f0 00 00 00 jmp 1139 <notmain> 1049: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 0000000000001139 <notmain>: 1139: f3 0f 1e fa endbr64 113d: 55 push %rbp 113e: 48 89 e5 mov %rsp,%rbp 1141: 89 7d fc mov %edi,-0x4(%rbp) 1144: 8b 45 fc mov -0x4(%rbp),%eax 1147: 83 c0 01 add $0x1,%eax 114a: 5d pop %rbp 114b: c3 ret 000000000000114c <notmain2>: 114c: f3 0f 1e fa endbr64 1150: 55 push %rbp 1151: 48 89 e5 mov %rsp,%rbp 1154: 89 7d fc mov %edi,-0x4(%rbp) 1157: 8b 45 fc mov -0x4(%rbp),%eax 115a: 83 c0 02 add $0x2,%eax 115d: 5d pop %rbp 115e: c3 ret
核心问题
我想知道:-O1编译notmain.o时做了哪些改动,让LTO能对notmain2执行死代码消除,而-O0编译时却做不到?
排查尝试
我尝试找出-O1中哪个具体优化选项导致这个现象,把man gcc里列出的-O1启用的所有选项单独加进去编译:
gcc -c -flto -fauto-inc-dec -fbranch-count-reg -fcombine-stack-adjustments -fcompare-elim -fcprop-registers -fdce -fdefer-pop -fdelayed-branch -fdse -fforward-propagate -fguess-branch-probability -fif-conversion -fif-conversion2 -finline-functions-called-once -fipa-modref -fipa-profile -fipa-pure-const -fipa-reference -fipa-reference-addressable -fmerge-constants -fmove-loop-invariants -fmove-loop-stores -fomit-frame-pointer -freorder-blocks -fshrink-wrap -fshrink-wrap-separate -fsplit-wide-types -fssa-backprop -fssa-phiopt -ftree-bit-ccp -ftree-ccp -ftree-ch -ftree-coalesce-vars -ftree-copy-prop -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre -ftree-phiprop -ftree-pta -ftree-scev-cprop -ftree-sink -ftree-slsr -ftree-sra -ftree-ter -funit-at-a-time notmain.c gcc -flto -O3 notmain.o main.c objdump -d a.out
但此时notmain2依然保留,没被消除。
我又用lto-dump查看LTO中间代码,对比-O1和-O0编译的notmain.o里notmain2的Gimple代码:
-O1编译时的notmain2 Gimple代码
Gimple Body of Function: notmain2 int notmain2 (int i) { int _2; <bb 2> [local count: 1073741824]: _2 = i_1(D) + 2; return _2; }
-O0编译时的notmain2 Gimple代码
Gimple Body of Function: notmain2 int notmain2 (int i) { int D.4724; int _2; <bb 2> : _2 = i_1(D) + 2; <bb 3> : <L0>: return _2; }
两者没有明显差异。
测试环境:Ubuntu 23.04,GCC 12.2.0。
内容的提问来源于stack exchange,提问作者Ciro Santilli OurBigBook.com
相关产品推荐
相关产品推荐

