为何LLVM未优化掉尾调用中不必要的跳转指令?
为何LLVM未消除f到g的冗余jmp指令?
先看给出的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目标代码:
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
关键原因在于以下几点:
noinline属性的硬限制:函数g标记了noinline,这是编译器必须遵守的指令——它明确禁止将g内联到任何调用方中,哪怕f用了musttail要求尾优化。要消除这个jmp,最直接的方式是把g的逻辑合并到f里,但noinline直接堵死了这条路,编译器必须保留g作为独立的函数实体,所以f只能通过跳转完成尾调用。目标文件的符号独立性要求:编译生成
.o目标文件时,每个函数符号都得保持独立,方便后续链接器处理(比如g可能被其他模块引用)。哪怕两个函数在文件里挨在一起,编译器也不能随意合并它们,否则会破坏符号的可访问性和链接逻辑。optsize属性的辅助影响:g还带了optsize,这会让编译器优先选代码尺寸更小的方案,但noinline的优先级更高,而且保留独立函数加jmp的方案,代码量并没有明显增加,所以编译器不会违反noinline去做合并。
如果去掉g的noinline属性,再用-O3编译,编译器就会把g内联到f里,自然也就不会有这条jmp指令了。
内容的提问来源于stack exchange,提问作者user22808285
相关产品推荐
相关产品推荐

