编译 第7章 运行时环境
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
不允许指针或动态分配 – 不允许递归调用过程 – 典型的例子是 典型的例子是FORTRAN77
–
例如: 例如 FORTRAN 程序 Program CNSUME …… Character function PRDUCE() …… CNSUME的目标代码 的目标代码 PRDUCE的目标代码 的目标代码 CNSUME的数据区 的数据区 PRDUCE的数据区 的数据区 全局/静态区 全局 静态区 代码区 //子程序段 子程序段 //主程序段 主程序段
参数传递机制
– 值传递 (值调用 值调用) 值调用 – 引用传递 (引用调用 引用调用) 引用调用 – 值结果传递 (复制进-复制出) (复制进 复制出) 复制进-复制出 – 名字传递 (延迟赋值 延迟赋值) 延迟赋值
1 传值
参数 参数是表达式 参数传递 – 参数在调用时计算,它们的值存储在活动 参数在调用时计算, 记录中参数对应的位置 – 在被调用程序执行时,代码将直接访问这 在被调用程序执行时, 些最终值
结果 参数变成了自变量的别ห้องสมุดไป่ตู้ (不要求复制 不要求复制 被传递的值) 被传递的值), 在参数上发生的任何变 化都会出现在自变量上
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i的地址 的地址) 的地址 procedure SWAP(n, m: real); 调用者的数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; 被调用者的数据区 add_n add_m add_j add_I add_K[i]
–
堆管理和完全动态运行时环境
– 堆管理使用 分配操作和释放操作处理指针 分配和重新分配, 这是管理堆的手工方法, 分配和重新分配 这是管理堆的手工方法, 因为程序员必须编写出分配和释放存储器 的明确的调用 – 在一种需要完全动态的运行时环境的语言 中,堆必须自动管理
7.3 参数传递机制
过程活动记录
结果 值参数被看作是初始化的局部变量, 值参数被看作是初始化的局部变量,对 它的改变不会影响任何非局部变量的变 化
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i的地址 的地址) 的地址 procedure SWAP(n, m: real); 调用者的数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; i:=5; k[i]:=6; SWAP(i, k[i]); 被调用者的数据区 add_n 5 add_m 6 add_j
① 参数传递 (add_i 表示i的地址 ) 表示 的地址 调用者的数据区 add_i add_K[i] 5 6 被调用者的数据区 add_n 5 add_m 6 add_j
② 执行了过程语句后 “j:=n;n:=m;m:=j” 调用者数据区 add_i add_K[i] 5 6 被调用者数据区 add_n 6 add_m 5 add_j 5
运行时环境 指的是目标计算机的寄存器以及存储器的结 构,用来管理存储器并保存指导执行过程所 需的信息。 需的信息。 – 寄存器和存储器分配是在运行阶段进行的 – 编译阶段设计运行时环境只能间接地维护 环境 – 在程序执行期间它必须生成代码进行必要 的维护操作
三种运行时环境
– 完全静态环境 – 基于栈的环境 – 完全动态环境
在执行之前,将数据固定在存储器中, 在执行之前,将数据固定在存储器中,包括 了程序的全局和静态数据 这些数据通常都在一个固定区域内并以类似 的风格单独分配给代码
2) 栈区
栈区域用于其分配发生在后进先出 LIFO风格中的数据 风格中的数据 3) 堆区 堆区域用于不符合LIFO协议的动态分 堆区域用于不符合 协议的动态分 配 例如指针的分配与回收: 例如指针的分配与回收 C++的new 和 的 delete ,C的 malloc 和 free 的
结果 :i=5,k[5]=6 非局部变量没有变化 ,
2 引用传递 参数 参数必须与分配的地址一起变化 参数传递
– 引用传递传递的是变量的地址(存储在局 引用传递传递的是变量的地址( 部活动记录中) 部活动记录中) – 在调用过程中,编译程序必须将对引用参 在调用过程中, 数的局部访问转为间接访问, 数的局部访问转为间接访问,因为局部 “值”实际上是环境中的别处的地址
7.1 程序运行时的存储器组织 7.2 三种运行时环境 7.3 参数传递机制
7.1 程序运行时的存储器组织
典型的计算机存储器可分为: 典型的计算机存储器可分为
– 寄存器区域 – 较慢的直接编址的随机访问存储器 (RAM)
RAM 区域还可以再分为代码区和数据区 – 代码区 代码区在执行之前是固定的, 代码区在执行之前是固定的,在编译时所 有代码的地址都是可计算的
代码区 全局变量数据区 全局 静态区 全局/静态区 Main的数据 的数据 Q的数据 的数据 Q的数据 的数据 栈区
3. 完全动态运行时环境
完全动态运行时环境
数据空间在执行时可以任意次地分配与回收。 数据空间在执行时可以任意次地分配与回收。 活动数据仅在对它们所有的引用都消失了才 再重新分配 – 完全动态运行时环境比基于栈的运行环境要 复杂许多, 复杂许多,因为它包含了要在执行时跟踪引 用,以及在执行时任意次地找寻和重新分配 存储器的不可访问区域(这种处理称为垃圾 存储器的不可访问区域(这种处理称为垃圾 回收) 回收)
一般的运行时存储器组织
栈和堆占有相同的 区域 箭头表示栈和堆的 生长方向
代码区域 全局/静态数据区域 全局 静态数据区域 栈 自由空间 堆
7.2 三类运行时环境
1. 完全静态运行时环境
所有的数据是静态的, 所有的数据是静态的,且执行程序期 间在存储器中保持固定 适用的语言有以下特点: 适用的语言有以下特点:
结果是 i=6,k[6]=5 且 k[5] 没有改变
解释 名字传递被看作是把被调用段的过程体 抄到调用出现的位置, 抄到调用出现的位置,但把其中自变量 名换成相应的参数名。 名换成相应的参数名。
例如 procedure SWAP(n, m: real); var j: real; begin j:=n; n:=m; m:=j end; 传名 主程序的 SWAP(i, k[i]) 被替换为 j:=i; i:=k[i]; k[i]:=j; j=i=5 i=k[5]=6 k[6]=j=5
//主程序 主程序 i:=5; k[i]:=6; SWAP(i, k[i]); …
词法分析, 词法分析 语法分析和静态语义分析 是 编译器中实现源语言静态分析的编译程 序各阶段, 序各阶段,这些分析仅取决于源语言的 特性 代码生成 的任务依赖于具体的目标机器。 的任务依赖于具体的目标机器。 然而同样地代码生成的一般特征在体系 结构上仍保留了很大的变化, 结构上仍保留了很大的变化 例如 运行 时环境
2. 基于栈的运行时环境
当进行一个新的过程调用时, 当进行一个新的过程调用时,每个新的 活动记录部分都分配在栈顶, 活动记录部分都分配在栈顶,当调用退 出时则再次解除分配 标准命令式语言( 标准命令式语言(如C,Pascal)中常 , ) 见的运行时环境格式,这种语言: 见的运行时环境格式,这种语言
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i 的地址 的地址) procedure SWAP(n, m: real); 调用者数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; 被调用者数据区 add_n i:=5; k[i]:=6; SWAP(i, k[i]); 5 add_m 6 add_j
结果 :i=6,k[5]=5 , 参数发生改变 ,
4 名字传递 又称为 延迟赋值 名字传递的思想是直到被调用的程序真 正使用了自变量之后才对这个自变量赋 值 参数传递 在调用点上的自变量的文本被看作是它 自己右边的函数, 自己右边的函数,每当在被调用过程的 代码中到达相应的参数名时, 代码中到达相应的参数名时,就要计算 它
允许递归调用 – 每一个调用都重新分配局部变量
–
Program main; 全局变量定义; 全局变量定义 Procedure R; … End(R); Procedure Q; … End(Q); 主程序执行体 End.(main)
若主程序调用Q, 递归调用自己 递归调用自己, 若主程序调用 ,Q递归调用自己,在Q 第二次进入运行后的存储结构为: 第二次进入运行后的存储结构为:
3 值结果传递 除了未建立真正的别名之外, 除了未建立真正的别名之外,这个机制 得到的结果与引用传递相似 参数传递 在过程中复制和使用自变量的值, 在过程中复制和使用自变量的值 然后 当过程退出时, 当过程退出时,再将参数的最终值复制 回自变量的地址 复制进, 这个方法也称为 复制进,复制出 (或 复 或 制存储) 制存储
① 参数传递 (add_i 表示 i 的地址) 的地址 data area of caller add_i add_K[i] 5 6 data area of callee add_n 5 add_m 6 add_j
② 过程代码执行后 “j:=n;n:=m;m:=j” 调用者数据区 add_i add_K[i] 6 5 被调用者数据区 add_n 6 add_m 5 add_j 5
i:=5; k[i]:=6; SWAP(i, k[i]);
① 参数传递(add_i 表示 i的地址 参数传递 的地址) 的地址 调用者的数据区 add_i add_K[i] 5 6 被调用者的数据区 add_n add_m add_j ② 过程代码执行后“j:=n;n:=m;m:=j” 过程代码执行后“ 调用者的数据区 add_i add_K[i] 6 5 被调用者的数据区 add_n add_i add_m add_k[i] add_j 结果 :i=6,k[5]=5 ,参数发生改变 , 参数发生改变 5 add_i add_K[i]
过程1的入口点 过程 的入口点 过程2的入口点 过程 的入口点
过程1的代码 过程 的代码 过程 2的代码 的代码 …
过程 n的入口点 的入口点
过程 n的代码 的代码
代码存储器
– 数据区
运行阶段, 运行阶段,代码访问的数据分配的存储区 域 全局/静态区 全局 静态区 栈区 动态数据区 堆区
数据区的分类 1) 全局 静态区 全局/静态区
– 过程或函数调用时的内存分配 – 活动记录必须包含下面的部分
参数的空间 过程或函数空间, 过程或函数空间,包括返回地址 局部数据空间 局部临时变量空间
参数传递
– 在过程调用中,参数是如何通过调用程序 在过程调用中, 在跳到被调用过程的代码之前与活动记录 中的位置相对应的 – 参数的值是如何由过程代码解释依赖于源 程序采用的特定 参数传递机制
第 7 章 运行时环境
学习目标: 学习目标 掌握 参数传递的机制 理解 完全静态运行时环境, 完全静态运行时环境,基于栈的运行时 环境和完全动态运行时环境的主要思想
源程序 词法分析 记号Tokens 记号 语法分析 语法树 语义分析 注释树 中间代码优化 中间代码 代码生成 目标代码 目标代码优化 目标代码
–
例如: 例如 FORTRAN 程序 Program CNSUME …… Character function PRDUCE() …… CNSUME的目标代码 的目标代码 PRDUCE的目标代码 的目标代码 CNSUME的数据区 的数据区 PRDUCE的数据区 的数据区 全局/静态区 全局 静态区 代码区 //子程序段 子程序段 //主程序段 主程序段
参数传递机制
– 值传递 (值调用 值调用) 值调用 – 引用传递 (引用调用 引用调用) 引用调用 – 值结果传递 (复制进-复制出) (复制进 复制出) 复制进-复制出 – 名字传递 (延迟赋值 延迟赋值) 延迟赋值
1 传值
参数 参数是表达式 参数传递 – 参数在调用时计算,它们的值存储在活动 参数在调用时计算, 记录中参数对应的位置 – 在被调用程序执行时,代码将直接访问这 在被调用程序执行时, 些最终值
结果 参数变成了自变量的别ห้องสมุดไป่ตู้ (不要求复制 不要求复制 被传递的值) 被传递的值), 在参数上发生的任何变 化都会出现在自变量上
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i的地址 的地址) 的地址 procedure SWAP(n, m: real); 调用者的数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; 被调用者的数据区 add_n add_m add_j add_I add_K[i]
–
堆管理和完全动态运行时环境
– 堆管理使用 分配操作和释放操作处理指针 分配和重新分配, 这是管理堆的手工方法, 分配和重新分配 这是管理堆的手工方法, 因为程序员必须编写出分配和释放存储器 的明确的调用 – 在一种需要完全动态的运行时环境的语言 中,堆必须自动管理
7.3 参数传递机制
过程活动记录
结果 值参数被看作是初始化的局部变量, 值参数被看作是初始化的局部变量,对 它的改变不会影响任何非局部变量的变 化
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i的地址 的地址) 的地址 procedure SWAP(n, m: real); 调用者的数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; i:=5; k[i]:=6; SWAP(i, k[i]); 被调用者的数据区 add_n 5 add_m 6 add_j
① 参数传递 (add_i 表示i的地址 ) 表示 的地址 调用者的数据区 add_i add_K[i] 5 6 被调用者的数据区 add_n 5 add_m 6 add_j
② 执行了过程语句后 “j:=n;n:=m;m:=j” 调用者数据区 add_i add_K[i] 5 6 被调用者数据区 add_n 6 add_m 5 add_j 5
运行时环境 指的是目标计算机的寄存器以及存储器的结 构,用来管理存储器并保存指导执行过程所 需的信息。 需的信息。 – 寄存器和存储器分配是在运行阶段进行的 – 编译阶段设计运行时环境只能间接地维护 环境 – 在程序执行期间它必须生成代码进行必要 的维护操作
三种运行时环境
– 完全静态环境 – 基于栈的环境 – 完全动态环境
在执行之前,将数据固定在存储器中, 在执行之前,将数据固定在存储器中,包括 了程序的全局和静态数据 这些数据通常都在一个固定区域内并以类似 的风格单独分配给代码
2) 栈区
栈区域用于其分配发生在后进先出 LIFO风格中的数据 风格中的数据 3) 堆区 堆区域用于不符合LIFO协议的动态分 堆区域用于不符合 协议的动态分 配 例如指针的分配与回收: 例如指针的分配与回收 C++的new 和 的 delete ,C的 malloc 和 free 的
结果 :i=5,k[5]=6 非局部变量没有变化 ,
2 引用传递 参数 参数必须与分配的地址一起变化 参数传递
– 引用传递传递的是变量的地址(存储在局 引用传递传递的是变量的地址( 部活动记录中) 部活动记录中) – 在调用过程中,编译程序必须将对引用参 在调用过程中, 数的局部访问转为间接访问, 数的局部访问转为间接访问,因为局部 “值”实际上是环境中的别处的地址
7.1 程序运行时的存储器组织 7.2 三种运行时环境 7.3 参数传递机制
7.1 程序运行时的存储器组织
典型的计算机存储器可分为: 典型的计算机存储器可分为
– 寄存器区域 – 较慢的直接编址的随机访问存储器 (RAM)
RAM 区域还可以再分为代码区和数据区 – 代码区 代码区在执行之前是固定的, 代码区在执行之前是固定的,在编译时所 有代码的地址都是可计算的
代码区 全局变量数据区 全局 静态区 全局/静态区 Main的数据 的数据 Q的数据 的数据 Q的数据 的数据 栈区
3. 完全动态运行时环境
完全动态运行时环境
数据空间在执行时可以任意次地分配与回收。 数据空间在执行时可以任意次地分配与回收。 活动数据仅在对它们所有的引用都消失了才 再重新分配 – 完全动态运行时环境比基于栈的运行环境要 复杂许多, 复杂许多,因为它包含了要在执行时跟踪引 用,以及在执行时任意次地找寻和重新分配 存储器的不可访问区域(这种处理称为垃圾 存储器的不可访问区域(这种处理称为垃圾 回收) 回收)
一般的运行时存储器组织
栈和堆占有相同的 区域 箭头表示栈和堆的 生长方向
代码区域 全局/静态数据区域 全局 静态数据区域 栈 自由空间 堆
7.2 三类运行时环境
1. 完全静态运行时环境
所有的数据是静态的, 所有的数据是静态的,且执行程序期 间在存储器中保持固定 适用的语言有以下特点: 适用的语言有以下特点:
结果是 i=6,k[6]=5 且 k[5] 没有改变
解释 名字传递被看作是把被调用段的过程体 抄到调用出现的位置, 抄到调用出现的位置,但把其中自变量 名换成相应的参数名。 名换成相应的参数名。
例如 procedure SWAP(n, m: real); var j: real; begin j:=n; n:=m; m:=j end; 传名 主程序的 SWAP(i, k[i]) 被替换为 j:=i; i:=k[i]; k[i]:=j; j=i=5 i=k[5]=6 k[6]=j=5
//主程序 主程序 i:=5; k[i]:=6; SWAP(i, k[i]); …
词法分析, 词法分析 语法分析和静态语义分析 是 编译器中实现源语言静态分析的编译程 序各阶段, 序各阶段,这些分析仅取决于源语言的 特性 代码生成 的任务依赖于具体的目标机器。 的任务依赖于具体的目标机器。 然而同样地代码生成的一般特征在体系 结构上仍保留了很大的变化, 结构上仍保留了很大的变化 例如 运行 时环境
2. 基于栈的运行时环境
当进行一个新的过程调用时, 当进行一个新的过程调用时,每个新的 活动记录部分都分配在栈顶, 活动记录部分都分配在栈顶,当调用退 出时则再次解除分配 标准命令式语言( 标准命令式语言(如C,Pascal)中常 , ) 见的运行时环境格式,这种语言: 见的运行时环境格式,这种语言
① 参数传递 例如 //过程定义 过程定义 (add_i 表示 i 的地址 的地址) procedure SWAP(n, m: real); 调用者数据区 var j: real; add_i 5 begin j:=n; n:=m; m:=j add_K[i] 6 end; //主程序 主程序 int i; int k[10]; 被调用者数据区 add_n i:=5; k[i]:=6; SWAP(i, k[i]); 5 add_m 6 add_j
结果 :i=6,k[5]=5 , 参数发生改变 ,
4 名字传递 又称为 延迟赋值 名字传递的思想是直到被调用的程序真 正使用了自变量之后才对这个自变量赋 值 参数传递 在调用点上的自变量的文本被看作是它 自己右边的函数, 自己右边的函数,每当在被调用过程的 代码中到达相应的参数名时, 代码中到达相应的参数名时,就要计算 它
允许递归调用 – 每一个调用都重新分配局部变量
–
Program main; 全局变量定义; 全局变量定义 Procedure R; … End(R); Procedure Q; … End(Q); 主程序执行体 End.(main)
若主程序调用Q, 递归调用自己 递归调用自己, 若主程序调用 ,Q递归调用自己,在Q 第二次进入运行后的存储结构为: 第二次进入运行后的存储结构为:
3 值结果传递 除了未建立真正的别名之外, 除了未建立真正的别名之外,这个机制 得到的结果与引用传递相似 参数传递 在过程中复制和使用自变量的值, 在过程中复制和使用自变量的值 然后 当过程退出时, 当过程退出时,再将参数的最终值复制 回自变量的地址 复制进, 这个方法也称为 复制进,复制出 (或 复 或 制存储) 制存储
① 参数传递 (add_i 表示 i 的地址) 的地址 data area of caller add_i add_K[i] 5 6 data area of callee add_n 5 add_m 6 add_j
② 过程代码执行后 “j:=n;n:=m;m:=j” 调用者数据区 add_i add_K[i] 6 5 被调用者数据区 add_n 6 add_m 5 add_j 5
i:=5; k[i]:=6; SWAP(i, k[i]);
① 参数传递(add_i 表示 i的地址 参数传递 的地址) 的地址 调用者的数据区 add_i add_K[i] 5 6 被调用者的数据区 add_n add_m add_j ② 过程代码执行后“j:=n;n:=m;m:=j” 过程代码执行后“ 调用者的数据区 add_i add_K[i] 6 5 被调用者的数据区 add_n add_i add_m add_k[i] add_j 结果 :i=6,k[5]=5 ,参数发生改变 , 参数发生改变 5 add_i add_K[i]
过程1的入口点 过程 的入口点 过程2的入口点 过程 的入口点
过程1的代码 过程 的代码 过程 2的代码 的代码 …
过程 n的入口点 的入口点
过程 n的代码 的代码
代码存储器
– 数据区
运行阶段, 运行阶段,代码访问的数据分配的存储区 域 全局/静态区 全局 静态区 栈区 动态数据区 堆区
数据区的分类 1) 全局 静态区 全局/静态区
– 过程或函数调用时的内存分配 – 活动记录必须包含下面的部分
参数的空间 过程或函数空间, 过程或函数空间,包括返回地址 局部数据空间 局部临时变量空间
参数传递
– 在过程调用中,参数是如何通过调用程序 在过程调用中, 在跳到被调用过程的代码之前与活动记录 中的位置相对应的 – 参数的值是如何由过程代码解释依赖于源 程序采用的特定 参数传递机制
第 7 章 运行时环境
学习目标: 学习目标 掌握 参数传递的机制 理解 完全静态运行时环境, 完全静态运行时环境,基于栈的运行时 环境和完全动态运行时环境的主要思想
源程序 词法分析 记号Tokens 记号 语法分析 语法树 语义分析 注释树 中间代码优化 中间代码 代码生成 目标代码 目标代码优化 目标代码