编译原理(十九)——运行时存储空间管理(变量访问环境)

一、变量访问环境在这里插入图片描述

嵌套型语言的地址映射就复杂一些,抽象地址是l off,如果l等于当前层,那就是说他所对应的变量是处于当前的活动记录中,如果l不等于当前层,那么我们就要找到他所对应的活动记录的首地址+off,那他的首地址怎么着,我们后面会给介绍,我们要构造一个display表,把每一个活动记录要把它活跃的静态外层的首地址都存在display表里,然后找到他所对应的那一层的首地址加上off就可以了~

二、调用链、动态链在这里插入图片描述

当前的活动没有结束,所以前面的函数仍然处于活动状态,所以才会压入AR

三、活跃活动记录(LAR)

  1. LiveAR(LAR):
    一个过程S在动态链中可有多个AR,但其中只有最新AR(S)是可访问的,称此AR(S)为S的活跃活动记录,并记为LiveAR(S),简写为LAR(S)。

例:假设有当前调用链是(M,P1,P2,Q1, R1,R2,R3 )
则当前动态AR链为[AR(M),AR(P1),AR(P2),AR(Q1),AR(R1),AR(R2),AR(R3)]
活跃活动记录LAR为:
LAR( M ) = AR(M) LAR( P ) = AR(P2)
LAR( Q ) = AR(Q1) LAR( R ) = AR(R3)

int P(){
  int x;
  int Q1(){}
  ...
  int Q2(){}
}
调用链:P P Q1 Q2 Q1 Q2
Q1可以访问P中的变量,Q1可以访问父类中定义的局部变量
Q2中若也是用变量x,是在最近的一个P中被访问的,因为后面所有的调用都是从第二个P引发的

四、声明链和变量访问环境

  1. 过程声明链(DeclaChain):
    过程名序列(M)是过程声明链,M是主程序名;若(M,…,P)是过程声明链,且P中有过程Q的声明,则(M,…,P,Q)也是过程声明链。记为:DeclaChain(Q)=( M,…,P,Q )【A声明B按照顺序写下来就行,声明链和调用链不同,调用链是在实际函数运行的时候进行的具体调用,声明是比如在一个函数A中定义了另一个函数B,则声明链就是(…A,B.,)】
  2. 当前变量访问环境VarVisitEnv:(声明链的过程活动记录按照顺序写下)
    若DeclaChain(Q)= [M,…,P,Q],则VarVisitEnv(LAR(Q))=[LAR(M),…,LAR(P),LAR(Q)]
    记录当前活跃活动的初始地址

例:(M,P,Q,R)为R的声明链,假设有当前调用链是(M,P1,P2,Q1,R1,R2,R3 )
则当前动态链为:
[AR(M),AR(P1),AR(P2),AR(Q1),AR(R1),AR(R2),AR(R3)]
R的当前变量访问环境:
VarVisitEnv(LAR®)=[AR(M), AR(P2),AR(Q1),AR(R3)]

关于各种链表的对比

链表区别
调用链函数之间前后存在调用关系,在A中调用了函数B,则有(…A,B…)
动态链对应着调用链的过程活动记录的链表即(…,AR(A),AR(B),…)
声明链在一个程序中函数声明的顺序,比如在函数A中先声明了函数B然后后面有函数B的具体定义,则有(…A,B…)
变量访问环境对应声明链的活跃过程记录,即(…,LAR(A),LAR(B)…),然后再去动态链中找LAR(A),LAR(B)对应的具体的值
活跃活动记录(LAR)指的是在一个调用链对应的动态链中相同的函数最新的访问部分,比如A1A2BC,LAR(A)=AR(A1)

声明链中可以重复吗?

非局部变量的地址计算

在这里插入图片描述
知道了初始地址和段内偏移就可以知道所有属于该段的变量位置
使用<>来表示具体的起始地址,offsetx等是偏移,只要是看其定义在哪个过程中确定其起始地址。
局部变量在它内部自己访问,非局部变量可能定义在其父类函数中,所以如果知道了当前AR的变量访问环境就可以知道在他前面执行的那些函数中定义的现在可以用到的变量的具体访问位置。

五、如何计算当前过程的变量访问环境

在这里插入图片描述
主要是声明链的计算,声明链和调用链生成LAR配合生成变量访问环境。
⚠️BEGIN/END代表当前的函数对其它函数的调用关系,PROC xxx即是声明函数


情况一:

P调用P——P的层数等于P的层数N

PROC P;
... ...
BEGIN
  P
END

调用链:(...P,P...)
声明链:(...P...)
变量访问环境:(...LAR(P)...)

情况二:

P调用Q,P的层数等于Q的层数N

PROC Q;
  BEGIN
  ...
  END
  ... ...
PROC P;
  BEGIN 
   Q
  END

声明的位置决定了其层数;
DeclaChain(P)=(M,P1,P2,...,Pn-1,P)
DeclaChain(Q)=(M,P1,P2,...,Pn-1,Q)

情况三:

P调用Q,P层数N-1,小于Q的层数N

PROC P;
... ...
  PROC Q;
  BEGIN
  END
... ...
BEGIN
Q
END

DeclaChain(P)=(M,P1,P2,...,Pn-1,P)
DeclaChain(Q)=(M,P1,P2,...,Pn-1,P,Q)

情况四:

P调用Q,P的层数大于Q的层数

PROC Q;
... ...
 PROC P;
 BEGIN 
 Q
 END
BEGIN END

DeclaChain(P)=(M,P1,P2,...,Pn-1,P)
DeclaChain(Q)=(M,P1,P2,...,Pn-1,P,...,Q)
变量访问环境的实现方法
  1. Display表方法
    全局表法(不需要掌握,了解即可)、局部表法(需要掌握)
  2. 静态链方法(需要掌握)

六、Display方法

在这里插入图片描述
Display表就是把变量访问环境中的AR写成小写的ar
Display表对应的是当前变量的访问环境(以声明表为顺序,以动态链作为LAR的查找构建)
在这里插入图片描述
在这里插入图片描述
上面给的过程是函数的调用过程;上面这个是AR(S)的Display表的计算过程:ar1,ar2加上新的newsp;对应的函数定义大致如下:

PROC M;//0
  PROC Q;//1
    PROC S;//2
    BEGIN
    R(S)
    END  
  PROC R;//1    
  • 访问局部变量和非局部变量的过程:
    访问S中的局部变量直接就是sp指针【指向AR(S)的起始地址】加上局部变量的偏移;访问S中使用的非局部变量就利用到了Display表进行计算,利用sp指针和对应的Display表中变量访问环境中的指向非局部变量所在的函数的AR起始地址的指针确定非局部变量的起始地址,再加上其在其对应的AR中的偏移就可以访问这个非局部变量。

  • 如何从R调用的S?
    R函数具有一个函数型参数,Q调用R的时候将S作为参数传递过去了,即Q{R(S)}同时,s作为实参传递时,其参数有两个,一个是其入口地址,一个是当前活动记录的display表即 Q的display表,这样S在实际被调用的时候抄的display表是从Q里面抄,而不是从R里面抄。

局部Display表时变量的访问

对一个变量X(L, off),地址为:
当L= CurrentAR.level时:addr(X)=sp+off
否则:
addr(X)=CurrentAR.Display[L]+ off
即[sp+D+L]+off

七、全局Display表

每个程序设置一个总的Display表,其长度为最大嵌套层数(最长声明链的长度),其中Display[i]存放第i层最新AR的指针,用D[i]表示。
该方法的理论依据:在程序的任何一点,相同层数的过程声明只能有一个有效。
在AR中设置一个Resume单元,用来临时保存某D[i]在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

八、静态链方法

在这里插入图片描述

声明过程:
PROC M//0
  PROC G//1
  	PROC H//2
  	  PROC R//3
  	  PROC S//4

原Display表部分变成一个单元,称为静态链单元,存放静态链指针。

  • 静态链指针的确定:
    若 k= CurrentAR.level+1-NewAR.level,则 NewAR.StaticChainPointer=Indir(sp,k)
    其中Indir(sp,k)表示sp的k次间接内容。
  • 变量X(L,off)的地址:
    若L= CurrentAR.Level,则addr(X)= sp+ off
    若L= CurrentAR.Level +1 -k,则
    addr(X)= Indir(sp,k)+ off
    就是沿着链表指针部分往前遍历k个
    在这里插入图片描述

总结

  1. 静态链方法是用链表表示变量访问环境
  2. 静态链方法实际上是一种共享化的局部Display表方法。其主要优点同全局Display表方法是能节省存储单元。产生需要花时间,但返回时不需要为恢复变量访问环境做任何事情。
  3. 具体采用哪种方法,取决于机器条件:如果寄存器较少,则使用Display表方法可能合适些;如果机器能提供较好的间接操作,则可选用静态链方法。

在这里插入图片描述

proc P(proc P1);//1
   int x
   //Q的声明和Q对其它函数的调用
   proc Q;//2
   int y
   begin R/P(R)/Q end
   //R的声明和其调用
   proc R;//2
     int i
    //S的声明和调用
     proc S;//3
     int j
     begin
     R/S/T/P
     end
    //T的声明和调用
     proc T;//3
     int k
     begin R/S/T/P end
  //R的调用部分
  begin  R/P/Q/S  end
//P的调用部分
begin Q/R/P1 end

局部Display表:(对于每个调用链来说,但是找的是声明链)
根据调用链给每个procedure编号

AR0.Display(P) = [ar0]
AR1.Display(Q) = [ar0,ar1]
AR2.Display(R) = [ar0,ar2]
AR3.Display(R) = [ar0,ar3]
AR4.Display(T) = [ar0,ar3,ar4]
AR5.Display(Q) = [ar0,ar5]
AR6.Display(Q) = [ar0,ar6]

构造静态链指针:(实线动态链,虚线静态链)
在这里插入图片描述

确定T中的x y i k的地址:(y无法访问,题目有错)

/*
k = currentAR.level+1-newAR.level
newAR.StaticChainPointer=Indir(sp,k)
变量的地址:
if(L = currentAR.level)
   addr(x)=sp+off
if(L = currentAR.level+1-k)
   addr[x]=Indir(sp,k)+off
*/
addr(x) = Indir(sp,3)+offx;
//addr(y) = Indir(sp,2)+offy;
addr(i) = Indir(sp,2)+offi;
addr(k) = sp+offk;
  • 7
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值