1、9.1.4 地址分配,处理完所有说明性语句后,类型信息、EQ、OFFSET等均已知。换言之,在名表和COMLIST建立之后,可以对变量、数组名进行地址分配,即填DA、ADDR。 对于易碎的个体,地址分配无什么要求,但COMMON 的公共块B1要求其成员必须保持其顺序,而EQUIVALENCE中的等价片元必须使等价元共享存储。等价片和公共块谁先处理? 决定于前者的处理不要给后者的处理设置障碍。先等价、后公共,问题较多。先公共、后等价比较容易处理,只有一点限制(冒头),所以我们先处理公共语句的块。,先分配COMMON的原因,COMMON A1,B,C,A,D DEMENSION A(10,10)
2、EQUIVALANCE(I,J,A1,2,K) (X, A2,3),1.公用块中公用元分配,A1.EQ = NULL, 非等价元,即可 分配地址a, a后移 size(A1) B,C 同理,1,2,3,104,4103,14,14,14,25,公共区长度: S = MAX(fi-f1+size(Ni), i 从1到m 这时候的局部区长度len = a + S,分配到数组 A, A1,2 A2,3 为等价元 等价片中其他元的地址为 ADDR( NI )=a + (fI fA), 当前a=4,a,D应该分配到公共区之后, 故ADDR(D) = a + S = 4 + 100 = 104,等价片内部
3、是一个整体,不论片中哪一个元作为 公共元出现,内部相对队形不变,但是遇到不同 的元,在公共块中等价片的位置要变化,若先遇 到I,那么A1,1 就要跑到A1之前,可能出现冒头, 这是错误.使用EQ链和OFFSET(NI)可以计算出 ADDR(NI),因此各NI的地址可填,剩下2个问题: 1.D分到哪里? 2.公共区长度如何算?,3,公用区冒头一例:,A1,2,A10,1,A9,1,A8,1,A1,1,A2,2,A2,3,A10,10,D,冒头!,公用区地址分配算法,a: 地址计数器; len: 长度计数器; d 公用区编号 FOR COMLIST 中每个 FT 不为 null 的项 i , DO
4、 BEGIN a := 0; len := 0; d := i + 127; N := FT i ; WHILE N != null DOBEGINIF DA N != 0 THEN IF DA N != d OR ADDR N != a THEN ERROR (冲突)ELSE IF EQ N = null THEN DA N := d; ADDR N := a ELSE BEGIN N1 := N; f := OFFSET N ;,REPEATa1 := a + ( OFFSET N1 - f);IF a1 0 THEN ERROR (冒头);IF DA N1 != 0 AND ( DA N
5、1 != d OR ADDR N1 != a1)THEN ERROR (非法等价);ELSE BEGINDA N1 := d; ADDR N1 := a1 ;len := MAX ( len, a1 + size( N1 );N1 := EQ ( N1 ) ;END UNTIL N1 = N END; a := a + size N ; len := MAX ( len, a );,REPEAT N := CMP N ; END OF WHILE; LENGTH i := MAX ( LENGTH i , len ); FT i := null; LT i := null ; END OF F
6、OR LOOP;,2. 局部区地址分配,例子:DIMENSION A(10,10), B(4)REAL YEQUIVALANCE(X, A2,3 ), REAL Z,Y,Z,a,假定 f2 最小,N2 的地址为 a 其他, ADDR(Ni)=a + (fi f2), 特别地, 对X有 f1 = 0, f2 = -21ADDR(N1)=a + (f1 f2)= a + 21,N1 X f1 N2 A f2 Nm fm,Y 已经分配,X f1=0,f2 = -21,算法: 1. 环内求 Min offset(Ni) = f; 2. 环内每个元 Ni 做 DA 栏为现行程序区号ADDR栏 = a +
7、 offset(Ni) - f局部区长度:len = MAX(len, (offset(Ni)-f) + size(Ni); 3. 处理完本等价环后地址移动:a = a + len为改等价环后地变量指出分配地址,3.临时变量的地址分配,若临时变量的作用域不相交,则可以公用一个存储单元。,大部分临时变量名用来存放表达式的中间结果,这类变量名有一个特点,它们均只被定值一次、被引用一次。它们的作用域是层次嵌套的 - 栈。,例子: 对赋值语句 X := A * B C * D + E * F 产生的临时变量的处理。,四元式,地址,临时变量名,( 1 ) * A B T1,T1,a,( 2 ) * C
8、D T2,T2,a+1,( 3 ) - T1 T2 T3,( 4 ) * E F T4,( 5 ) + T3 T4 T5,( 6 ) := T5 X,T3,T4,T5,a,a+1,a,T1、T2 不再使用,它们的存储单元可以回收。,T3、T4 不再使用,它们的存储单元可以回收。,最后只是用了一个单元 : a。,“代真”后的四元式序列,(1) * A B $a,四元式,K(初始值为 a),(2) * C D $(a+1),(3) - $a $(a+1) $a,(4) * E F $(a+1),(5) + $a $(a+1) $a,(6) := $a x,a + 1,a + 2,a + 1,a +
9、 2,a + 1,a,a、a + 1 存储单元不再使用,可以回收。,a、a + 1 存储单元不再使用,可以回收。,最后只使用了单元 a。,9.2 动态存储分配 - 简单栈式,特点: 1. 没有分程序结构; 2. 过程定义不允许嵌套; 3. 允许过程递归调用; 4. 允许过程含有可变数组(但不允许全局可变数组)。,主程序全局数据区,Q 的活动记录,Q 的数组区,R 的活动记录,R 的数组区,调 用 方 向,C 语言程序的存储组织,主程序调用 Q,分配数组区完成,Q 调用 R,分配数组区完成,最 终 情 形,9.2.1 C 过程的活动记录,2 1 0,15,C 的过程调用、过程进入、数组空间分配和
10、过程返回,过程调用的四元式序列 par T1 par T2 . . . par Tn call P, n,(i + 3)TOP := Ti 或者 (i + 3)TOP := addr(Ti) 其中:x SP 表示变址访问 地址 x + SP,16,call P, n,1 TOP := SP (保护现行SP),3 TOP := n (传送参数个数),JSR P (转子指令,转向 P 的第一条指令),SP := TOP + 1 (定义新SP),1SP:= 返回地址 (保护返回地址),TOP := TOP + L (定义新TOP),1. 计算各维上下限; 2. 调用数组空间分配子程序。,17,ret
11、urn (E),T 送寄存器(等待调用段取走),TOP := SP 1 SP := 0SP X := 2TOP UJ 0X,过程通过 end 自动返回的方法: 如果过程是函数过程,则按同样的办法传送结果值,否则直接执行上述返回指令。,18,9.3 嵌套过程语言的栈式实现,重点:嵌套层次的概念和处理方法,19,引入 DISPLAY 表以后的活动记录:,20,活动记录表: AR,. .引用 Py, Qx .,call R,call Q,( Py,Qx 为参数 ),21,建立 DISPLAY 的方法:,DISPLAY 的结构:,1. 表项和层次有关,底层若从 0 层开始,表项 = 层次+1; 2.
12、与调用层有关。,22,DISPLAY 的建立方法(从Q2 调用 R),进入新过程时1. 应把它的外层的 D 表地址作为连接数据传过来,即 应把d SP1.2 传到 ARR 的全局 D 表;2. 进入 R 时,根据 SP1.2 从 Q 的活动记录中截取 d SP1.2 (d + lR) SP1.2 (l 为层数), 再添加进入 R 时新建立的 SP2 。,23,现行过程中引用了某一外层过程(层数为k),获得 X 值的方法:,LD R1, ( d + k ) SP (获得第 k 层过程的最新活动记录) LD R2, X R1 (把 X 的值传送到 R2),当过程 P1 调用过程 P2 而进入了 P
13、2 后,P2 建立自己的DISPLAY 的方法:,1. 若 P2 是一个真实过程,分2 种情形。,P1,P2,CALL P2,P0,P2,P1,CALL P2,做法: 只需要把 P1 的 DISPLAY 地址作为连接数据之一传给 P2。 WHY?,24,25,( i + 4) TOP := Ti; 或者 ( i + 4) TOP := ADDR( Ti ),call P, n,1 TOP := SP; 3 TOP := SP + d; 4 TOP := n; JSR P,定义新的 SP , TOP; 保护返回地址。,26,按第三项连接数据所提供的全局 DISPLAY 的地址,自低向上抄录 l 个单元的内容(l 为P 的层数),再添上新的 SP 以形成新的DISPLAY ( l + 1 个单元) 。,TOP := SP -1 ; SP := 0 SP; X := 2 TOP; UJ 0 X;,