📜  编译器设计中的运行时环境

📅  最后修改于: 2021-09-28 09:58:15             🧑  作者: Mango

翻译需要将程序的静态源文本与必须在运行时发生以实现程序的动态操作相关联。该程序由过程名称、标识符等组成,这些名称需要在运行时与实际内存位置进行映射。

运行时环境是目标机器的状态,可能包括软件库、环境变量等,为系统中运行的进程提供服务。

源语言问题

激活树

程序由过程组成,过程定义是以最简单的形式将标识符(过程名称)与语句(过程的主体)相关联的声明。过程的每次执行都称为过程的激活。激活的生命周期是程序执行中存在的步骤序列。如果 ‘a’ 和 ‘b’ 是两个过程,那么它们的激活将是非重叠的(当一个在另一个之后调用时)或嵌套的(嵌套过程)。如果新的激活在同一过程的较早激活结束之前开始,则该过程是递归的。激活树显示控制进入和离开激活的方式。
激活树的属性是:-

  • 每个节点代表一个过程的激活。
  • 根显示主要函数的激活。
  • 当且仅当控制从过程 x 流向过程 y 时,过程“x”的节点是过程“y”的节点的父节点。

示例 –考虑以下 Quicksort 程序

main() {

      Int n;
      readarray();
      quicksort(1,n);
}

quicksort(int m, int n) {

     Int i= partition(m,n);
     quicksort(m,i-1);
     quicksort(i+1,n);
}

该程序的激活树将是:

1

首先以 root 身份执行 main函数,然后 main 调用 readarray 和 quicksort。快速排序反过来又调用分区和快速排序。程序中的控制流对应于从根开始的激活树的深度优先遍历。

控制栈和激活记录

控制堆栈或运行时堆栈用于跟踪活动过程激活,即尚未完成执行的过程。过程名称在被调用(激活开始)时被压入堆栈,并在返回时(激活结束)弹出。使用激活记录或框架管理单个过程执行所需的信息。当一个过程被调用时,一个活动记录被推入堆栈,一旦控制返回到调用者函数,活动记录就会被弹出。

2

一般的激活记录包括以下内容:

  • 局部变量:保存过程执行的局部数据。
  • 临时值:存储在表达式求值中出现的值。
  • 机器状态:保存函数调用前机器的状态信息。
  • 访问链接(可选):指保存在其他激活记录中的非本地数据。
  • 控制链接(可选):指向来电者的激活记录。
  • 返回值:被调用过程用于向调用过程返回一个值
  • 实际参数

上述快速排序示例的控制堆栈:

3

4

运行时内存的细分

运行时存储可以细分为:

  • 目标代码-程序代码,它是静态的,因为它的大小可以在编译时确定
  • 静态数据对象
  • 动态数据对象-堆
  • 自动数据对象-堆栈

5

存储分配技术

一、静态存储分配

    • 对于任何程序,如果我们在编译时创建内存,则会在静态区域中创建内存。
    • 对于任何程序,如果我们仅在编译时创建内存,则内存只会创建一次。
    • 它不支持动态数据结构,即内存在编译时创建并在程序完成后释放。
    • 静态存储分配的缺点是不支持递归。
    • 另一个缺点是在编译时应该知道数据的大小

例如,FORTRAN 旨在允许静态存储分配。

二、堆栈存储分配

  • 存储被组织为一个堆栈,激活记录分别在激活开始和结束时被压入和弹出。局部变量包含在激活记录中,因此它们在每次激活时都绑定到新存储。
  • 堆栈分配支持递归

三、堆存储分配

  • 内存分配和释放可以根据用户的要求随时随地进行。
  • 堆分配用于为变量动态分配内存,并在不再需要变量时将其收回。
  • 支持递归。

激活_5

参数传递

过程之间的通信媒介称为参数传递。来自调用过程的变量值通过某种机制传输到被调用过程。

基本术语:

  • R 值:表达式的值称为其 r 值。如果单个变量中包含的值出现在赋值运算符的右侧,它也会成为 r 值。 R 值总是可以分配给其他一些变量。
  • L 值:存储表达式的内存(地址)的位置称为该表达式的左值。如果赋值运算符,它总是出现在左侧。
    激活_6

    i.Formal Parameter:接受调用者过程传递的信息的变量称为形参。这些变量在被调用函数的定义中声明。

    ii. 实参:将值和函数传递给被调用函数的变量称为实参。这些变量在函数调用中被指定为参数。

将参数传递给过程的不同方式

  • 按价值调用
    在按值调用中,调用过程传递实际参数的 r 值,编译器将其放入被调用过程的活动记录中。形式参数保存调用过程传递的值,因此对形式参数所做的任何更改都不会影响实际参数。 激活_7
  • 通过引用调用 在通过引用调用中,形参和实参引用相同的内存位置。实参的左值被复制到被调用函数的活动记录中。因此被调用的函数具有实际参数的地址。如果实际参数没有 l 值(例如-i+3),则在新的临时位置对其进行评估,并传递该位置的地址。形式参数中所做的任何更改都会反映在实际参数中(因为更改是在地址处进行的)。

    激活_8

  • 通过复制恢复调用
    在按复制调用中,编译器在调用过程时复制形式参数中的值,并在控制返回到被调用函数时将它们复制回实际参数中。传递 r 值,并在返回时将形式的 r 值复制到实际值的 l 值中。
    激活_9
  • 按姓名呼叫
    在按名称调用中,在过程中所有形式出现的地方用实际参数代替形式。它也称为惰性求值,因为仅在需要时才对参数进行求值。 激活_10