clang 编译器下的inline探索
今天看到一篇文章说到inline内联函数的介绍文章,里面提到内联函数的作用以及适用场景,刚好最近看了clang编译器的一些资料,正好发了点时间探索下
这篇文章的介绍如下 内联函数(https://www.cnblogs.com/spock12345/p/11551147.html) inline函数的作用:不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处,适用于功能简单,规模较小又使用频繁的函数。
准备
代码
使用的示例代码如下,我这里把文件命名为a.c
#include <stdio.h>
int inlinecalc(int a, int b, int fac);
int inline_loopFunc();
void inline_recCallFunc(int count);
inline int inline_loopFunc(){
//实现
int count = 1000;
for (int i = 0; i < count; ++i)
{
printf("%d\n", i);
}
return 0;
}
inline void inline_recCallFunc(int count){
if (count > 0) {
inline_recCallFunc(count-1);
}
printf("%d\n", count);
}
int recCallFunc(){
inline_recCallFunc(10);
return 0;
}
int loopFunc(){
inline_loopFunc();
return 0;
}
inline int inlinecalc(int a, int b, int fac) {
if (fac > 2)
{
return a * 2 + b;
}
return a + b;
}
int calc(int a, int b, int fac) {
int tmp = inlinecalc(a, b, fac);
return tmp * 1.2 / fac;
}
int main(int argc, char const *argv[])
{
int res = inlinecalc(111, 222, argc);
printf("result = %d\n", res);
loopFunc();
recCallFunc();
return 0;
}
clang的优化级别
这个知识点也先介绍下,clang编译器有多个优化选项,不同的优化选项下生成的汇编代码是不一样的,在Xcode中的配置是如下的,使用clang命令行工具对应的则是-O0
、-O1
、-O2
等不同的优化选项,后面会使用到。因为clang会做优化,所以在代码准备阶段我也适当的调整了代码的复杂度,避免了不同级别的优化选项产生的汇编代码是一样的,导致分析的结果不准确。

分析
接下来就是分析步骤了,借住clang的命令行工具,clang可以把源文件转换为汇编代码
clang a.c -O0 -S -o -
-O0
表示优化级别,上面有介绍过了-S
表示生成汇编代码-o -
表示把结果输出到控制台
O0/O1级别
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, %eax
movl $111, %edi
movl $222, %esi
movl %eax, %edx
callq _inlinecalc
movl %eax, %ecx
leaq L_.str.1(%rip), %rdi
xorl %eax, %eax
movl %ecx, %esi
callq _printf
callq _loopFunc
callq _recCallFunc
xorl %eax, %eax
popq %rbp
retq
.cfi_endproc
## -- End function
这个级别做了很少的优化,源码中的三个inline方法都是当做方法调用,没有嵌入到调用的地方
O2级别
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
cmpl $2, %edi
setg %cl
movl $111, %esi
shll %cl, %esi
addl $222, %esi
leaq L_.str.1(%rip), %rdi
xorl %eax, %eax
callq _printf
callq _loopFunc
movl $10, %edi
callq _inline_recCallFunc
xorl %eax, %eax
popq %rbp
retq
.cfi_endproc
## -- End function
O1级别中可以看到有两点的变化
_inlinecalc
方法的调用已经没有了,被嵌入到了调用的地方了,对应的代码是从cmpl $2, %edi
到callq _printf
结束
O3级别
.globl _main ## -- Begin function main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## %bb.0:
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
pushq %r14
pushq %rbx
.cfi_offset %rbx, -32
.cfi_offset %r14, -24
cmpl $2, %edi
setg %cl
movl $111, %esi
shll %cl, %esi
addl $222, %esi
leaq L_.str.1(%rip), %rdi
xorl %eax, %eax
callq _printf
leaq L_.str(%rip), %r14
xorl %ebx, %ebx
.p2align 4, 0x90
LBB6_1: ## =>This Inner Loop Header: Depth=1
xorl %eax, %eax
movq %r14, %rdi
movl %ebx, %esi
callq _printf
incl %ebx
cmpl $1000, %ebx ## imm = 0x3E8
jne LBB6_1
## %bb.2:
movl $10, %edi
callq _inline_recCallFunc
xorl %eax, %eax
popq %rbx
popq %r14
popq %rbp
retq
.cfi_endproc
## -- End function
这个级别包含中有for循环的内联方法也被嵌入到了调用的地方,inline也是生效的,另外根据我自己的验证,包含有switch语句的inline也会被内嵌到调用的地方,而唯一不会生效的只有递归调用的方法了。