太阳能路灯厂家
免费服务热线

Free service

hotline

010-00000000
太阳能路灯厂家
热门搜索:
技术资讯
当前位置:首页 > 技术资讯

DSP编程技巧之20理解函数的调用过程

发布时间:2020-07-01 01:54:39 阅读: 来源:太阳能路灯厂家

在我们使用C/C++对DSP进行编程的时候,函数无疑是功能模块划分的重要组成部分,这些函数之间则通过显式地调用或者中断等方式来共同工作。除了对特定的RTS库中的函数(例如某些数学函数)的调用按照它们内置规则进行分配外,我们自定义的函数之间的调用则需要遵循一定的规则,了解这一过程对理解程序的执行和调试也是十分有帮助的,下面我们就来解读一下函数的调用过程,并且可以从其中了解到CPU寄存器、FPU寄存器以及栈(stack)在这一过程中的作用。

本文引用地址:一.父函数调用子函数

在父函数调用子函数(被调函数)时,通常会执行以下的步骤:

1.如果寄存器不是SOE类型的(入口保存,save on entry),即它的值没有被被调用函数占用,但是在被调用函数返回值之后又会用到该寄存器的值的话,则该寄存器的值被保存在栈中。

2.如果被调函数返回一个结构体,则调用函数会为结构体分配空间,并且把这段空间的地址作为第一个参数传递给被调函数,被调函数需要创建一个该结构体的本地副本。

3.传递给被调函数的参数一般情况下会保存在寄存器中,在必要的情况下则会保存在栈中,因为寄存器的数量有限;具体的规则是:

(1)如果目标器件是FPU,并且传递的有32位的浮点从那时,则前4个浮点参数被保存在R0H-R3H这4个FPU寄存器中(注意与CPU寄存器AR0H-AR3H相区别)。

(2)如果有64位的整形(longlong)参数,则第一个64位整形参数的高32位存入ACC寄存器中,低32位存入P寄存器中,其它的64位整形参数按照逆序(函数声明中参数列表里最左边的参数最后被压入栈中)保存在栈中。

此外,如果P寄存器被用于参数传递,则对该函数的装入(prolog)和排空(epilog)的提取的优化功能(通过减小性能达到减小程序尺寸)被禁止。

(3)如果参数中有任何的32位长整形或者浮点型,则第一个会放入ACC寄存器中,其它的32位参数则按照逆序保存在栈中。

(4)指针参数被放入CPU寄存器XAR4和XAR5中,其它的指针则存入栈中。

(5)剩余的16位的参数在CPU寄存器AL,AH,XAR4和XAR5可用的情况下,按照这一寄存器的顺序被保存在它们中。

4.任何没有被存入寄存器的参数都会被以逆序压入栈中,所有的32位参数在压入栈中时都会对齐到偶数地址。

如果一个函数的参数中使用了省略号,即参数个数是可变的,则最后一个显式声明的参数在压入栈中之后,它在栈中的地址可以用来定位未显式声明的参数。

5.栈指针SP必须在父函数调用子函数之前偶对齐。如果不是偶对齐,则需要把SP加1.

6.父函数使用LCR指令(使用返回程序指针寄存器RPC的方式来进行22位的长调用)来调用子函数,在调用时RPC寄存器的值会被压入栈中,从而可以把返回地址保存在RPC寄存器中。

7.最后,栈被对齐到函数的边界上。

二.子函数响应父函数

在子函数被调用时,通常会执行以下的步骤:

1.如果被调函数修改了XAR1、XAR2或者XAR3的值,则必须保存它们的值,因为在调用前后,父函数假设这3个寄存器的值在被返回之前是被保留的。如果目标是FPU,并且在被调函数中修改了R4H-R8H的值,则同样需要保存它们的值。

2.被调用的函数需要在栈中为所有的本地变量、临时存储区域已经任何被调用的参数分配足够的空间。在通过为SP寄存器加偏移量跳转到被调函数之后,这段存储空间就立刻被分配了。

3.栈被对齐到函数的边界上。

4.如果被调用的函数参数中有结构体,则它实际接收到的是该结构体的指针。如果在被调函数中对该结构体进行了写操作,则必须在栈中分配空间以创建该结构体的副本,在完成操作之后把本地结构体通过指针复制回原有的结构体。如果在被调函数中不对传入的结构体参数进行写操作,则可以通过对其指针的操作来完成参数的引用。

5.完成参数传入之后,被调函数执行它本身的代码。

6.功能执行完成之后,被调函数返回值,根据返回值的类型,它们值的保存位置分别为:

16位整数:AL寄存器

32位整数:ACC寄存器

64位整数:ACC和P寄存器

16位或者22位指针:XAR4寄存器

FPU下的32位浮点数:R0H寄存器

结构体:其指针保存在XAR4寄存器中

在返回结构体的情况下,例如s=f(x),其中s为结构体,f为函数,则可以直接用f(&s,x)的方式在父函数中调用子函数f,通过结构体指针,被调函数可以自动返回结构体的值了。

7.通过把SP中减去调用子函数时加的偏移量,SP寄存器可以重新指向父函数。

8.被调函数恢复所有在第一步中保存的建城七队值。

9.被调函数使用LRETR(使用PC指针返回)指令返回,PC寄存器的值被置为RPC寄存器中的值,即返回地址,然后RPC寄存器中的原有值被推出栈并重新保存在RPC寄存器中。

通过以上的描述,可以看出栈在函数调用前后起着非常关键的中继作用。所以,如果在调用时传递的参数非常多,例如传递了一个很长的数组,或者有多个64位的参数,则栈很有可能没有足够的空间来完成参数的暂存,造成栈的溢出,甚至造成程序运行结果的异常或者错误的输出结果,因为编译器无法检查栈的溢出错误(除非我们自己编写某些代码来检测),所以要为栈分配一个相对较大的存储空间,它的默认值是1K字。即使是非常小的程序,常用例程里栈的长度也往往能达到0x400这样的长度。

c++相关文章:c++教程

莱芜制做防静电工服

天津西服订做

滨州工服定制