编译原理与技术
2)A将可选的访问链放入(push)B的活动记录;
3)A将返回地址放入B的活动记录并跳转到过程B 的代码入口,开始B的执行;(一般通过A中的 代码指令-“call B” 来完成这一步)
4)
2019/11/8
《编译原理与技术》-运行环境
14
栈式分配下的过程调用与返回 -过程A 调用 过程B 的过程调用序列: B的入口代码:4)~ 6) 4)在B自己的AR中保存A的活动记录的基址
mov bp,sp spbp
pop bp
//恢复调用者A的bp
ret
// B执行结束并返回
注:X86-Linux中的leave 和ret组合亦能达到目的。
至此,B的AR中除了参数/返回值区域,其余区域全部 回收(撤销)了。
2019/11/8
《编译原理与技术》-运行环境
16
栈式分配下的过程调用与返回 -过程A 调用 过程B 的过程返回序列: A的“调用”善后代码:3)~ 4) 3)A取回返回值以及引用型参数(有的话) 4)A调整栈顶sp(主要根据参数个数以及可 能有的访问链和可能有的返回值)。对应操 作: add para_size, sp 即spsp + para_size
“最接近嵌套” 序)
vs. 现行单元活动(调用次
分程序
- 含有声明和语句;如C的{…}
- 分程序具有以下特征:
-- 可以嵌套;
-- 没有参数;
-- 控制流沿静态文本进入、流出
-- 采用“最接近嵌套”的规则
2019/11/8
《编译原理与技术》-运行环境
24
非局部变量访问
e.g.6 分程序 main() { int a = 0 ; int b =0; //local VAR of main
(即当前bp,对应操作 push bp)并且将这 个位置作为B自己AR的基址(对应操作)
mov sp,bp 即 bpsp) 5)保存可选的机器状态(寄存器) 6)为局部数据分配空间,(对应操作) sub local_size,sp,即sp sp – local_size 7)“开始”B的执行。(至此,B的AR建好)
2019/11/8
《编译原理与技术》-运行环境
15
栈式分配下的过程调用与返回
- 过程返回:回收分配的被调过程的AR,并将程 序控制转移回调用过程继续执行;
-过程A 调用 过程B 的过程返回序列:
B的出口代码:1)~ 2)
1)B回收局部数据空间;恢复原先保存的机器状态 2)B恢复A的AR基址;取出返回地址将程序控制交 回到A。对应操作:
AR中的内容 - 临时区域。用以保存临时计算结果
- 局部数据区。源程序中程序单元声明的局 部变量对应在此区域。
- 机器状态保存区。存有机器的寄存器,程 序指令计数器 ip(返回地址)等。
2019/11/8
《编译原理与技术》-运行环境
5
活动记录
AR中的内容 - 访问链(静态链)。当前程序单元可以访 问的(静态程序中)外围程序单元的活动记 录链。 - 控制链(动态链)。程序单元的活动记录 按它们的生成(或调用)次序串成链。 - 实在参数 - 返回值
2019/11/8
《编译原理与技术》-运行环境
22
e.g.5 悬空引用
有C程序如下: main() {
int * p = dangle(); … }
int * dangle() {
int i; return &i; }
2019/11/8
《编译原理与技术》-运行环境
23
非局部变量访问
静态作用域规则 vs. 动态作用域规则
2019/11/8
《编译原理与技术》-运行环境
12
动态存储分配
栈式分配下的AR内容布局
高地址
返回值 实在参数
可选的访问链
参数/返回值区域放在AR 高端-靠近调用者过程的 活动记录,既便于双方存 取,又适应参数可变情况
返回地址
bp
调用者 bp
长度固定的区 域放在AR中间
可选机器状态
低地址
局部数据
至此,B的AR区域全部回收(撤销)了。
2019/11/8
《编译原理与技术》-运行环境
17
e.g.2 栈式存储分配
有C程序如下: void g() { int a ; a = 10 ; } void h() { int a ; a = 100; g(); } main() { int a = 1000; h(); }
主程序 : 0
嵌套深度:1
A:1
var j : integer; procedure C ;
嵌套深度:2
i: 2
begin
B;
C
B:2
end; begin
j := i;
BA 主程序
嵌套深度:3
j: 3
C;
C:3
end;
begin
B;
2019/11/8
《编译原理与技术》-运行环境
18
e.g.2 栈式存储分配
过程g被调用时,活动记录栈的(大致)内容
2019/11/8
返回地址
old bp
a : 1000 返回地址
old bp
a : 100
返回地址
bp
old bp
a : 10 sp
main程序的AR 函数h的AR 函数g的AR
《编译原理与技术》-运行环境
a0 = 0 b0 = 0 b1 = 1
a0 = 0 b0 = 0
a)进入block 1, b)进入block 2, c)退出block 2, d)退出block 3, e)退出block 1,
未进入block 2 未进入block 3 进入block 3
进入block 1
进入main
2019/11/8
19
e.g.3 有参函数的活动记录
参数 b 参数 a 返回地址 老bp 局部变量c 局部变量d
bp+12 bp+8
bp bp-4 bp-8
.file "ar.c"
void func( int a , int b )
.text
{
.globl func
int c , d;
.type func,@function
2019/11/8
《编译原理与技术》-运行环境
6
活动记录的内容
返回值(return value) 实在参数(actual parameter) 控制链(control link)
可选的访问链(optional access/static link)
机器状态(saved machine status) 局部数据(local data) 临时区(temporaries)
编译原理与技术
运行环境
2019/11/8
《编译原理与技术》-运行环境
1
运行环境
存储组织与分配
程序单元、运行时内存划分与活动记录 静态/动态存储分配 动态栈式的过程调用/返回 非局部名字的访问
参数传递
参数传递的方式及其实现
2019/11/8
《编译原理与技术》-运行环境
2
存储组织与分配
2019/11/8
《编译原理与技术》-运行环境
7
活动记录内容的存取
AR的基地址bp
返回值 实在参数 控制链 (old bp)
可选的访问链
机器状态 局部数据 临时区
2019/11/8
《编译原理与技术》-运行环境
8
活动记录内容的存取
返回值
地址:Βιβλιοθήκη 实在参数bp+d1
偏 移
控制链(old bp)
d1
可选的访问链
c = a;
func:
d = b;
pushl %ebp
}
movl %esp, %ebp
subl $8, %esp
movl 8(%ebp), %eax
movl %eax, -4(%ebp)
movl 12(%ebp), %eax
movl %eax, -8(%ebp)
leave
ret
2019/11/8
《编译原理与技术》-运行环境
栈顶sp 临时区
长度可变的区 域放在AR低端
2019/11/8
《编译原理与技术》-运行环境
13
栈式分配下的过程调用与返回
- 过程调用:分配被调过程的AR并填入相关信息, 然后程序控制转移到被调过程入口;
-过程A 调用 过程B 的过程调用序列:
A的“调用”准备操作:1)~ 3)
1)A 计算实在参数并放入(对应栈操作-push) B的活动记录中;(亦可考虑返回值存放空间)
《编译原理与技术》-运行环境
21
e.g.4. scanf的可变参数
&f &i 格式串1指针 返回地址 老bp … …
bp+8 bp
&j 格式串2指针
返回地址 老bp … …
scanf的第一次调用时AR scanf的第二次调用时AR
由于C语言采用 逆序传递参数, 格式串参数将被 放在AR中的“固 定”位置,即 bp+8。而由此参 数即可确定待输 入值的参数(变 量)的个数。从 而适应参数个数 变化的情况。
d1、d2谁正谁负?
bp
机器状态
偏移d2 局部数据
临时区
地址: bp+d2
2019/11/8
《编译原理与技术》-运行环境
9
静态存储分配
- 全局变量的存储绑定、AR均在编译时确定 且在整个程序执行中保持。