《编译原理与实践》第七章
名词:
活动记录(Activation Record):包括参数,状态信息,local data,临时数据
stack frames:存储在stack上的AR
frame pointer:专门用于指向AR的寄存器
argument pointer(ap):专门用于指向AR中参数部分的寄存器
调用序列(calling sequence):包括AR的内存分配,参数的计算和存储,印象调用的必要的寄存器的存储和设置
返回序列(return sequence):返回值的设置,寄存器的调整,AR内存的释放
设计调用序列的重要性在于:
1)在调用者和被调用者之间划分调用操作的范围,即有多少调用序列的代码放在调用的时候,有多少代码放在被调用者开始
这一点是很重要的,一般来说在调用点插入代码较为容易,但是相同的代码被复制多次会导致生成的代码量的提高
2)多大程度上依赖于处理器的支持,而不是为调用序列的每一步显式的生成代码
【笔记】由以上知,AR中的各项数据,他们分别由谁来存储,需要在调用者(caller)和被调用者(callee)之间协调和分配
一般来说有三种主要的运行时环境,静态,基于栈,动态,这里主要说基于栈的运行时环境,因为这是C的方式
一般来说,这种基于栈的运行时环境,需要包括当前的AR和前一个AR,前一个AR用于在退出当前调用的时候使用。当前AR指针一般保存在fp中,前一个AR的指针则保存在当前AR中,一般在AR的中部,参数区和局部变量之间,该指针也被称为控制链(control link)或者动态链(dynamic link),它指向前一个AR的控制链。
活动记录(Activation Record):包括参数,状态信息,local data,临时数据
stack frames:存储在stack上的AR
frame pointer:专门用于指向AR的寄存器
argument pointer(ap):专门用于指向AR中参数部分的寄存器
调用序列(calling sequence):包括AR的内存分配,参数的计算和存储,印象调用的必要的寄存器的存储和设置
返回序列(return sequence):返回值的设置,寄存器的调整,AR内存的释放
设计调用序列的重要性在于:
1)在调用者和被调用者之间划分调用操作的范围,即有多少调用序列的代码放在调用的时候,有多少代码放在被调用者开始
这一点是很重要的,一般来说在调用点插入代码较为容易,但是相同的代码被复制多次会导致生成的代码量的提高
2)多大程度上依赖于处理器的支持,而不是为调用序列的每一步显式的生成代码
一般来说,这种基于栈的运行时环境,需要包括当前的AR和前一个AR,前一个AR用于在退出当前调用的时候使用。当前AR指针一般保存在fp中,前一个AR的指针则保存在当前AR中,一般在AR的中部,参数区和局部变量之间,该指针也被称为控制链(control link)或者动态链(dynamic link),它指向前一个AR的控制链。
![](https://app.yinxiang.com/shard/s14/res/35e94511-f8da-428c-b666-e581f55f8d36/ENIMAGE1367809004656.jpg?resizeSmall&width=721)
上图给出了AR的结构,其中m为参数,y为局部变量,由于控制链在AR的中部,可以通过正负偏移量的方式,计算出各个参数和局部变量的地址
call sequence:
1.计算参数,并且把它们存储在正确的位置(通过入栈的方式,参数从右向左或者从左向右入栈)
2.把fp存储在新的AR的控制链中
3.把fp指向新的AR的控制链,通常就是把sp赋值给fp,因为此时,sp指向栈顶,也就是控制链的位置
4.把返回地址存储在新的AR中
5.跳转到被调用过程中
return sequence:
1.把fp赋值到sp中
2.把控制链加载到fp中(现在fp指向上一个AR)
3.根据返回地址进行跳转(可以通过fp找到返回地址的偏移)
4.sp popup 参数
如何处理变长参数
Nested Declaration
嵌套声明,在C中就是在一个block中,嵌套了另外一个block,对于这种情况,一般的处理方法是在进入block的时候,在栈中分配临时数据,在退出block的时候,释放栈中的临时数据
Stack-Based Environments with Local Procedures
对于在一个procedure中包含有另外一个procedure的情况,需要在AR中加入access link,指向定义procedure的AR,control link构建了calling environments,access link构建了defining environments。在访问变量的时候,通过access link可以逐层查找定义,为了计算递归查找的次数,为每一种declaration都加入了netsting level的概念,即嵌套层数,处于最外层的global 变量或者procedure的nesting level是0,嵌套一层该值加一。在m层如果需要访问n层的变量的话,只需要递归m-n次就可以查找到。
![](https://app.yinxiang.com/shard/s14/res/ec06cf1d-50c8-4052-b24d-b3fa5606c789/ENIMAGE1367820373813.jpg?resizeSmall&width=721)
Stack-Based Environments with Procedure Parameters
在处理把Procedure当做参数的情况中,需要传递两个参数,一个是指令指针ip(指向代码地址),一个是环境指针ep(指向定义代码的AR),他们一起构成了closure(也就是access link close the hole caused by nonlocal reference)。如下图所示的调用过程:procedure p的参数是procedure r,procedure r在procedure q中定义
![](https://app.yinxiang.com/shard/s14/res/431724db-d608-4fa6-8e62-70827e80173c/ENIMAGE1367821344321.jpg?resizeSmall&width=721)
如下图:在调用p的AR中,参数a:<ip,ep>的ip是procedure r,ep是procedure r所在的procuedure q的AR,这是r的定义环境,而在procedure r被调用的时候,其AR中的access link就是这个ep所指向的access link
![](https://app.yinxiang.com/shard/s14/res/81bab898-41a8-414a-98fe-53ef83e2bb1f/ENIMAGE1367821211134.jpg?resizeSmall&width=721)
根据以上分析,最终的AR如下:
![](https://app.yinxiang.com/shard/s14/res/39f19d0b-f52e-4250-abb5-94825435d622.png?resizeSmall&width=721)
其中当参数为procedure的时候,需要传递procedure code和context作为参数,其中context就是access link,它指向caller