【编译原理】8—运行时刻环境Runtime Environments(存储组织、栈式分配和非局部数据访问)

8 运行时刻环境Runtime Environments

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
项目链接👉https://github.com/A-BigTree/college_assignment
⭐⭐⭐⭐⭐⭐

编译器必须准确地实现源程序语言中包含的各个抽象概念。这些抽象概念通常包括名字、作用域、绑定、数据类型、运算符、过程、参数以及控制流构造等概念。编译器还必须和操作系统以及其他系统软件协作,在目标机上支持这些抽象概念。

为了做到这一点,编译器创建并管理一个运行时刻环境(run-time environment),它编译得到的目标程序运行在这个环境中。这个环境处理很多事务,包括为在源程序中命名的对象分配和安排存储位置,确定目标程序访问变量时使用的机制,过程间的连接,参数传递机制,以及与操作系统、输入输出设备及其他程序的接口。

8.1 存储组织

Storage Organization

从编译器的角度看,正在执行的目标程序在它自己的逻辑地址空间内运行,其中每个程序值都在这个空间中有一个地址。对这个逻辑地址空间的管理和组织是由编译器、操作系统和目标机共同完成的。操作系统将逻辑地址映射为物理地址,而物理地址对整个内存空间编制。

运行时刻内存被划分成代码区(code areas)和数据区(data areas)的典型方式如下:

在这里插入图片描述

  • 代码区(Code):生成的目标代码的大小在编译时刻就已经固定下来了,因此编译器可以将可执行目标代码放在一个静态确定的区域,代码区通常位于存储的低端;
  • 静态区(Static):程序的某些数据对象的大小可以在编译时刻知道,它们可以放置在静态区中,该区域可以被静态确定,该区域数据对象包括全局变量和编译器产生的数据,比如用于支持垃圾回收的信息等;
  • 栈(Stack)和堆(Heap):为了将运行时刻的空间利用率最大化,堆和栈放在剩余地址空间的相对两端,这些区域是动态的,它们的 大小会随着程序运行而改变。这两个区域根据需要向对方增长。栈区用来存放称为活动记录的数据结构,这些活动记录在函数调用过程中生成在实践中,栈向较低地址方向增长,而堆向较高地址增长。

静态和动态存储分配

Static and Dynamic allocation

  • 静态分配(Static Allocation):在编译时刻布局分配数据对象;
  • 动态分配(Dynamic Allocation):
    • 栈式存储(Stack Allocation):一个过程的局部名字在栈中分配空间,运行时刻栈支持通常的过程调用/返回策略;
    • 堆存储(Heap Allocation):有些数据的生命周期要比创造它的某次过程调用更长,这些数据通常被分配在一个可复用存储的“堆”中。堆是虚拟内存的一个区域,它允许对象或其他数据元素被创建时获得存储空间,并在数据变得无效时释放该内存空间;

8.2 空间的栈式分配

Stack Allocation

8.2.1 活动树

Activation Tree

一个快速排序程序概要如下图:

在这里插入图片描述

对于该程序可能的活动序列如下:

在这里插入图片描述

下图为quicksort的某次运行中的调用活动树:

在这里插入图片描述

在活动树和程序行为之间存在下列多种有用的对应关系,正是因为这些关系使我们可以使用运行时刻栈:

  1. 过程调用的序列和活动树的前序遍历相对应;
  2. 过程返回的序列和活动树的后续遍历相对应;
  3. 假设控制流位于某个过程的特定活动中,且该过程活动对应于活动树上的某个结点N。那么当前尚未结束的(即活跃的)活动就是结点N及其祖先结点对应的活动。这些活动被调用的顺序就是它们在根结点到N的路径上的出现顺序。这些活动将按照这个顺序的反序返回;

8.2.2 活动记录

Activation Records

过程调用和返回通常由一个称为控制栈(control stack)的运行时刻栈进行管理。每个活跃的活动都有一个位于这个控制栈中的活动记录(activation record,有时也称为帧(frame))。活动树的根位于栈底,栈中全部活动记录的序列对应于在活动树中到达当前控制所在的活动结点的路径。程序控制所在的活动的记录位于栈顶。

一个概括性的活动记录如下图:

在这里插入图片描述

  • 临时值(Temporary values):当表达式求值过程中产生的中间结果无法存放在寄存器中时,就会产生这些临时值;
  • 局部数据(Local data):对应于这个活动记录的过程的局部数据;
  • 保存的机器状态(A saved machine status):其中包括对此过程的此次调用之前的机器状态信息。这些信息通常包括返回地址(程序计数器的值,被调用过程必须返回到该值所指位置)和一些寄存器中的内容(调用过程会使用这些内容,被调用过程必须在返回时恢复这些内容);
  • 访问链(Access link):当被调用过程需要其他地方(比如另一个活动记录)的某个数据时需要使用访问链进行定位;
  • 控制链(Control link):指向调用者的活动记录;
  • 返回值(Returned values):当被调用函数有返回值时,要有一个用于存放这个返回值的空间。不是所有的被调用过程都有返回值,即使有,我们也可能倾向于将该值放在寄存器中以提高效率;
  • 实在参数(Actual parameters):这些值通常将尽可能地放在寄存器中,而不是放在活动记录中,因为放在寄存器中会得到更好的效率。然而,我们仍然为它们预留了相应的空间,使得我们的活动记录具有完全的通用性;

上例中快速排序程序的向下增长的活动记录栈如下图:

在这里插入图片描述

8.2.3 调用代码序列

Calling Sequences

实现过程调用的代码段称为调用代码序列(calling sequence)。这个代码序列为一个活动记录在栈中分配空间,并在此记录的字段中填写信息。返回代码序列(return sequence)是一段类似的代码,它恢复机器语言状态,使得调用过程能够在调用结束之后继续执行。

寄存器top_sp指向当前的顶层活动记录中机器状态字段的末端。

调用代码序列(calling sequence)

调用者(Caller)和被调用者(Callee)之间的任务划分如下:

在这里插入图片描述

调用代码序列描述如下:

  1. 调用者计算实在参数的值;
  2. 调用者将返回地址和原来的top_sp值存放到被调用者的活动记录中。然后,调用者增加top_sp的值,使之指向上图所示位置。也就是说,top_sp越过了 调用者的局部数据和临时变量以及被调用者的参数和机器状态字段
  3. 被调用者保存寄存器值和其他状态信息;
  4. 被调用者初始化其局部数据并开始执行;
返回代码序列(return sequence)
  1. 被调用者将返回值放到与参数相邻的位置;
  2. 使用机器状态字段中的信息,被调用者恢复top_sp和其他寄存器,然后跳转到由调用者放在机器状态字段中的返回地址;
  3. 尽管top_sp已经被减少,但调用者仍然知道返回值对于当前top_sp值的位置。因此,调用者可以使用那个返回值;

8.3 栈中非局部数据访问

Access to Nonlocal Data on the Stack

8.3.1 嵌套深度

Nesting Depth

对于不内嵌在任何其他过程中的过程,我们设定其嵌套深度为1,然而,如果一个过程p在一个嵌套深度i的过程中定义,那么我们设定p的嵌套深度为i+1。

8.3.2 访问链

Access Links

如果过程p在源代码中直接嵌套在过程q中,那么p的任何活动中访问链都指向最近的q的活动。q的嵌套深度一定比p的嵌套深度恰巧少1。

用来查找非局部数据的访问连如下图:

在这里插入图片描述

8.3.3 显示表

Displays

如果嵌套深度变大,我们必须沿着一段很长的访问链路才能找到需要的数据。一个更高效的实现方法是使用一个称为显示表的辅助数组d,它为每个嵌套深度保存一个指针。我们设法使得在任何时刻,指针d[i]指向栈中最高的对应于某个嵌套深度i的过程的活动记录。

下图为维护显示表的过程:

在这里插入图片描述

⭐⭐⭐⭐⭐⭐
Github主页👉https://github.com/A-BigTree
项目链接👉https://github.com/A-BigTree/college_assignment
⭐⭐⭐⭐⭐⭐

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一棵___大树

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值