高效的C编程之: 函数调用
14.9.4嵌套优化
注意 | 嵌套优化(Tail-Calloptimization)只适用于armcc。编译时如果使用-g或-debug选项,编译器自动关闭该功能。 |
一个函数如果在其结束时调用了另一个函数,则编译器使用B指令调转到被调用函数,而非BL指令。这样就避免了一级不必要的函数返回。图14.3显示了嵌套优化的调用过程。
图14.3嵌套优化函数调用过程
当编译时使用-O1或-O2选项时,编译器都执行这种嵌套优化。需要注意的是,当函数中引用了局部变量地址,由于指针别名问题的影响,即使函数在返回时调用了其他函数,编译器也不会使用嵌套优化。
下面通过一个例子来分析嵌套优化是如何提高代码执行效率的。
externintfunc2(int);
intfunc1(inta,intb)
{if(a>b)
return(func2(a-b));
else
return(func2(b-a));
}
编译后的代码如下所示。
func1
CMPa1,a2
SUBLEa1,a2,a1
SUBGTa1,a1,a2
Bfunc2
首先,func1中使用B指令代替BL指令,不用担心lr寄存器被破坏,减少了对寄存器压栈保护操作。另外,程序直接从func2返回到调用func1的函数,减少一次函数返回。如果说正常的指令调用过程为:
BL+BL+MOVpc,lr+MOVpc,lr
那么经过嵌套优化的函数调用过程就可以表示为:
BL+BL+MOVpc,lr
这样,总的开销将减少25%。
14.9.5单纯子函数
所谓单纯子函数(PureFunctions)是指那些函数返回值只和调用参数有关。换句话说,就是如果调用函数的参数相同,那么函数的返回结果也相同。如果程序中存在这样的函数,可以在函数定义时使用_pure进行声明,这样在程序编译时编译器会根据函数的调用情况对其进行优化。
下面的例子显示了当函数用_pure声明时,编译器对其所做的优化。
程序源码文件如下。
intsquare(intx)
{
returnx*x;
}
intf(intn)
{
returnsquare(n)+square(n)
}
编译后的结果如下。
square
MOVa2,a1
MULa1,a2,a2
MOVpc,lr
f
STMDBsp!,{lr}
MOVa3,a1
BLsquare
MOVa4,a1
MOVa1,a3
BLsquare
ADDa1,a4,a1
LDMIAsp!,{pc}
上面的程序中,square函数为“单纯子函数”,当使用_pure声明该函数时编译器在调用该函数时,将对程序进行优化。
声明的方法和编译后的结果如下所示。
__pureintsquare(intx)
{
returnx*x;
}
f
STMDBsp!,{lr}
BLsquare
MOVa1,a1,LSL#1
LDMIAsp!,{pc}
从编译后的代码中可以看到,用_pure声明的函数在f函数中只调用了一次。
虽然“单纯子函数”可以提高代码执行效率,但同时也会带来一些负面影响。比如,在“单纯子函数”中,不能直接或间接访问内存地址。所以在程序中使用“单纯子函数”时要特别小心。
另外,还可以使用#pragma声明“单纯子函数”,下面的代码显示了它的声明过程。
#pragmano_side_effects
/*functiondefinition*/
#pragmaside_effects
评论