您的位置:首页 > 其它

过程调用约定

2009-06-05 20:47 211 查看
过程调用约定

当程序中的各个过程(函数)被分别编译时,关键是如何规定寄存器的使用规则。当编译某个过程时,编译器必须事先知道需要用到哪些寄存器、哪些寄存器的内容需要保留给其他过程使用等信息。我们称这些寄存器的使用规则为寄存器使用约定过程调用约定。顾名思义,大多数情况下,这些规定主要是用于约束软件的,而不会受到硬件的限制。因此,大多数的编译器和程序员都必须遵守这些约定以免发生错误。

下面是GCC编译器在MIPS系统下的寄存器使用约定。



1、过程调用

下面说明一个程序(调用者)调用另一个程序(被调用者)的具体步骤。

大多数有关过程调用的记录集中在一个叫做过程调用帧(procedure call frame)的内存块中。使用这块内存区域有以下几个目的:

a. 存放传递给被调用过程的参数值

b. 保留当前过程可以修改但给过程的调用者不希望改变的寄存器值(返回前必须恢复,亦即使用前必须保存)

c. 为过程的局部变量提供足够的内存空间。

在大多数编程语言中,过程调用和返回严格遵从后进先出(LIFO)原则,因此,这些内存可以在堆栈段实现分配和释放,这也是为什么这些内存块有时被称作堆栈帧。

下图是一个典型的堆栈帧。





帧的内存区域包括帧指针$fp(指向帧的首字)寄存器和堆栈指针$sp(指向帧的尾字)寄存器值之间的范围。由于堆栈的存储遵从由高到低的存放顺序,所以帧指针总是高于堆栈指针。运行的程序利用帧指针能快速地存取堆栈帧中的内容。例如,堆栈帧中的参数能够用下面的指令送入寄存器$v0:

lw $v0, 0($fp)

堆栈帧能用多种途径实现;但无论如何,调用者和被调用者必须遵从一系列步骤。下列描述的步骤用于大多数MIPS系统上的调用协议。在过程调用中,该协议在三个时刻发挥作用:在调用者调用被调用者之前,在被调用者开始执行的时刻,以及在被调用者返回到调用者之前的时刻。首先,调用者将过程调用参数放到合适的位置,然后按下列步骤调用目的过程:

1)传递参数。根据协议,前四个参数将被送到寄存器$a0~$a3中,其他参数压到堆栈中,这些参数刚好放在被调用过程的堆栈帧的起始地址处(从而在被调用过程中通过堆栈帧寄存器$fp的值访问这些参数),达到参数传递的目的。

2)保存调用者的寄存器的值。这样,被调用过程就可以随意使用这些寄存器($a0~$a3,$t0~$t9),而无须先保存它们的值。如果调用者在调用之后还想使用这些寄存器,那么它必须在调用前保存寄存器的值)。

3)执行jal指令。该指令把控制转移到被调用者的第一条指令处,并且在寄存器$ra中保存返回地址(而不是在IA-32系统中,将返回地址压栈,遭受缓冲区溢出之攻击)。

在被调用子程序正式运行之前(指执行子程序所代表之功能之前),该程序必须执行以下步步骤(例行公事),以便设置其堆栈帧:

1)通过从当前栈指针(寄存器)中减去栈帧之大小为栈帧分配空间(即$sp = $sp - frameSize)。

2)把被调用者所用寄存器保存到帧中。被调用者在改变其值之前必须保存$s0~S7、$fp以及$ra的值,因为调用者希望调用之后寄存器没有发生改变。而每个过程中如果需要创建新的栈帧则必须保存$fp。而$ra则仅在被调用者再调用时才保存。此外,其他被调用者专用的寄存器也要保存。

3)设置栈帧指针,其值为栈帧大小减去4加上$sp,保存在$fp中(即$fp=$sp+frameSize - 4)。

最后,被调用者执行如下步骤,将控制返回给调用者:

1)如果被调用者为具有返回值的函数,则将返回值保存到寄存器$v0中。

2)恢复被调用者进入时保存的所有寄存器。

3)向$sp加上栈帧大小,将帧从栈中弹出($sp = $sp + frameSize),释放栈帧空间。

4)跳转到$ra中记录的制定地址处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: