以下 LLVM IR:
define tailcc i64 @f() {
%1 = musttail call tailcc i64 @g(i64 10)
ret i64 %1
}
define tailcc i64 @g(i64 %0) align 1 optsize noinline {
ret i64 %0
}
生成此 X86 目标代码 (
clang-18 -O3 test.ll -o test.o && objdump -d test.o
):
0000000000000000 <f>:
0: bf 0a 00 00 00 mov $0xa,%edi
5: e9 00 00 00 00 jmp a <g>
000000000000000a <g>:
a: 48 89 f8 mov %rdi,%rax
d: c2 08 00 ret $0x8
为什么从
jmp
到f
的g
没有优化掉?不需要,因为 g
紧邻 f
下面。
LLVM一般使用pass进行优化,pass共有三种,对函数进行操作的pass是其中一种。
LLVM 试图使编写和组合通道变得容易。事实上,编写一个 pass 来执行您所建议的操作非常容易,但这样做也会做其他事情:对所有后续的 pass 添加新的要求。
比如有了这样一个新的pass,其他pass就不能再分析
call
和invoke
指令来找出调用了哪些函数,因为这个新pass增加了一种新的函数调用方式,即落空。
这种效应值得让世界变得复杂吗?我不得不说不,LLVM 的简单界面是一项很有价值的功能,比省略一条汇编指令更有价值。