《编译原理-龙书》练习第8章

代码生成:

中间表示形式+符号表 -> 指令选择,寄存器分配和指派,指令排序


8.2 目标语言

8.2.1 1) x = 1

LD   r, #1

ST   x, r

3)x = a+1

LD    r, a

ADD r, #1

ST    a, r

5)x = b*c

LD    R1, b

MUL R1, c

ST     x, R1

8.2.2 1)x=a[i] y=b[j] a[i]=y b[j]=x

LD    R0 i

MUL R0, 4

LD R1 a(R0)

LD    R0 j

MUL R0, 4

LD R2 b(R0)

ST a[i] R2

ST b[j] R1

2) x=a[i] y=b[i] z=x*y

LD    R0 i

MUL R0, 4

LD    R1 a(R0)

MUL R1 b(R0)

ST    z R1

8.2.3 y=*q q=q+4 *p=y p=p+4

LD    R1 q

LD    R2 0(R1)

ADD R1 #4

ST    q R1

LD    R1 p

ST    0(R1) R2

ADD R1 #4

ST    p R1

8.2.4 

LD     R1, x

LD     R2, y

SUB  R1-R2

BLTZ R1, M   // M为L1生成第一条指令位置

8.2.5

LD R1, s

ST R1, 0

LD R2, i

ST R1, 0

LD R3, n

L1: CMP R2, R3

JG L2

ADD R1, R2

ADD R2, #1

GOTO L1

L2:

8.2.6 1) 2+2+1+2

8.3 目标代码中的地址

8.3.1 没有理解题中return什么意思,下面没有考虑return。假设这是main中一段代码

100:  LD   SP,  #600

108:  ACTION1

128:  ADD SP, SP, #msize

136:  ST 0(SP), #152

144:  BR 300

152:  SUB SP, SP #msize

160:  ADD SP, SP #msize

168:  ST 0(SP), #184

176:  BR 400

184:  SUB SP, SP #msize

192:  ADD SP, SP #msize

200:  ST 0(SP), #216

208:  BR 500

216:  SUB SP, SP #msize

300: ACTION

320: BR *0(SP)


400: ACTION

420: BR *0(SP)


500: ACTION

520: BR *0(SP)

8.3.2与8.3.3 8.3中没有类似这样的例子,栈式分配的话重要的是地址的确定,可以将这些语句当作函数中第一条语句生成代码

8.4 基本块和流图

8.4.1 一个二重循环一个三重循环,二重循环三地址语句类似于图8-7上半部分,流图类似于图8-9B1-B4,循环包括例8.9中1和3,下面练习一下三重循环

1)

1  i = 0

2  j = 0

3  k = 0

4  t1 = n*i

5  t2 = t1 + j

6  t3 = t1 + k

7  t4 = n*k

8  t5 = t4 + j

9  t6 = a[t3] * b[t5]

10 c[t2] = c[t2] + t6

11 k = k + 1

12 if(k<n) goto 4

13 j = j + 1

14 if(j<n) goto 3

15 i = i + 1

16 if(i<n) goto 2

2)三重循环流图:

B1 1

B2 2

B3 3

B4 4-12

B5 13-14

B6 15-16

3)循环有3个:B4自身,B3B4B5,B2B3B4B5B6

8.4.2 类似,略

8.5 基本块的优化

8.5.1

              a1

       +(e)  *(d,b)

a0     b0       c0

8.5.2

1)只有a活跃,则第一行可以优化掉

2)abc活跃,则同样第一行d=b*c可以优化掉

8.5.3 B6 原理类似8.5.1,略

8.5.4 B3 原理类似8.5.1,略

8.5.5

1) a[i]=b  a设置为不活跃,b设置为活跃,影响所有对数组a的引用

2) a=b[i]  a设置为不活跃,b设置为活跃,影响所有对数组b的引用

3) a=*b   a设置为不活跃,b设置为活跃,从后往前扫描过程中,所有对b的引用都是活跃的

4) *a=b   a设置为不活跃,b设置为活跃,从后往前扫描过程中,所有对a的引用都是不活跃的

8.5.6 在这儿画树太麻烦了

 1)p可以指向任何地方,则*p=...使得构造出来全部节点都被杀死每一步构造结果为:

a[i] = b

     []=

i0      b0

*p=c 节点[]=、i0、b0被杀死

        []=                             *=  p

i0(k)      b0(k)                 c0(k)

d=a[j]

      =[](d)

a0            j0

        []=                             *=  p

i0(k)      b0(k)                 c0(k)

e=*p

      =[](d)

a0            j0                       =* e

        []=                             *=  p

i0(k)      b0(k)                 c0(k)

*p = a[i] 转为:t=a[i]   *p=t

t=a[i]

    =[](t)         =[](d)

t0            a0            j0       =* e

        []=                             *=  p

i0(k)      b0(k)                 c0(k)

*p=t 上面所有节点都被杀死

2)p可以指向b或d,则*p=...使得构造出来以b或d为变量的节点杀死

8.5.7 e=*p -> e=c

8.5.8

t0 = a+c

t1 = t0+e

y= t0+t1

t2 = y+b

t3 = y+d

x = t3+f

8.6 一个简单的代码生成器

选择3个进行练习:1)5)(假设数组大小为x[i][10])

  x=a+b*c a[i][j]=b[i][k]+c[k][j]  
8.6.1 t0=b*c
x=a+t0
t0=i*10
t1=t0+k
t2=b[t1]
t3=k*10
t4=t3+j
t5=c[t4]
t6=t0+j
t7=t2+t5
a[t6]=t7
 
8.6.2 ...    
8.6.3 LD R1, b
LD R2, c
MUL R1, R1, R2
LD R2, a
ADD R1, R1, R2
// load b[i][k]
LD R1, i
MUL R1, R1, 10
LD R2, k
ADD R3, R1, R2
LD R4, pb
ADD R4, R4, R3
//load c[k][j]
MUL R2, R2, 10
LD R3, j
ADD R2, R2, R3
LD R5, pc
ADD R5, R5, R2
// add
ADD R6, 0(R4), 0(R5)
ADD R1, R1, R3
LD R2, pc
ADD R2, R2, R1
ST 0(R2), R6
 
8.6.4 .. L  
8.6.5 ..    
8.6.4 这道题与8.6.5寄存器分配是个大问题,8.8节有相关介绍

  R1 R2 R3 i j k pa pb pc  
LD R1, i i     i,R1 j k pa pb pc  
MUL R1, R1, 10 t0     i            
LD R2, k   k       k,R2        
ADD R3, R1, R2     t1              
LD R1, pb pb             pb,R1    
ADD R1, R1, R3 t2 k t1 i j k,R2 pa pb pc  
MUL R2, R2, 10   t3       k        
LD R3,j     j   j,R3          
ADD R2, R2, R3   t4                
LD R3, pc     pc   j       pc,R3  
ADD R3, R2, R3 t2 t4 t5 i j k pa pb pc  
ADD R1, R1, R3 t7                  
LD R2, i   i   i,R1            
MUL R2, R2, 10   t0   i            
LD R3, j     j   j,R3          
ADD R2, R2, R3   t6                
LD R3, pa     pa   j   pa,R3      
ADD R2, R2, R3 t7 a[i][j]                
ST R2, R1                    

8.6.5 只有2个寄存器需要将值保存到栈中或临时变量中,原理类似,只是数据来回倒腾多点

8.7 窥孔优化

8.8 寄存器分配和指派

例8.17计算式8.1:

  use(x,B) live(x,B)
a/4 B2,B3 B1
b/5 B1 B3,B4
c/3 B1,B3,B4 --
d/6 B1,B2,B3,B4 B1
e/4   B1,B3

书中只介绍了算法没有例子,只能靠自己了,8.8.4中举例有误,考虑的两个节点应该是a和d,不是a和b

8.8.1 只考虑B1

a=b+c

LD R0, b

LD R1, c

ADD R2, R0, R1

ST a R2

b,c活跃
d=d-b

LD R3, d

SUB R3, R3, R0

ST d, R3

d,b活跃
e=a+f

LD R4, f

ADD R5 R2, R4

ST e, R5

a,f活跃,e不活跃

k=6,边包括:

d-a, d-f

a-f, a-d, a-b

a  --  d

|    \   |

b       f

a,b,d各一种颜色,b可以跟f一种颜色

8.8.2 保存全局变量的寄存器可以不覆盖?

8.9 通过树重写来选择指令

8.9.1 1)
               =
        x          +
            *             *
        a     b     c     d

8.10 表达式的优化代码的生成

例8.25

t4 t1 t3        base=1 k=3

    t3 e t2     base=2, k=2 m=1

         t2 c d  LD R3, d; LD R2, c; ADD R3, R2, R3

    LD R2, e

    MUL R3, R2, R3

    t2 a b      base=1 k=2

    LD R2, b

    LD R1, a

    SUB R2, R1, R2

ADD R3, R2, R3

8.10.1 1)a/(b+c)-d*(e+f)

t5=t2-t4

t4=d*t3

t3=e+f

t2=a/t1

t1=b+c

                     t5(3)

         t2(2)             t4(2)

   a1        t1(2)  d1        t3(2)

            b1   c1            e1    f1

2)a+b*(c*(d+e))

t4=a+t3

t3=b*t2

t2=c*t1

t1=d+e

          t4(2)

   a1       t3(2)

           b1       t2(2)

               c1       t1(2)

                      d1      e1

3)(-a+*p)*((b-*q)/(-c+*r))

t10 = t3*t9

t9=t5/t8

t8=t6+t7

t7=*r

t6=-c

t5=b-t4

t4=*q

t3=t1+t2

t2=*p

t1=-a

                                  t10(3)

                   t3(2)                    t9(3)

   t1(1)        t2(1)        t5(2)               t8(2)

  -    a1      *    p1    b1  t4(1)       t6(1)   t7(1)

                                      *   q1      -   c1   *   r1

8.10.2 对2),类似例8.25,对例1)和3)类似例8.27

8.10.3 类似例8.25

8.10.4 对于一个节点,假设有n个子节点,n<=2类似8.10.1中描述,如果n>=2,假设每个子节点标号为a1< a2 < ... < an,

计算节点标号值A方法如下:

初始化 A=an, k=n, x=0

for(i=n; i>1; --i)

   if(ai-a(i-1))>0)

      x+=(ai-a(i-1));

   else if(x>0)

      x--;

   else

     A++;

解释如下:

假设子节点从左到右标号升序排列,则从右到左检查,如果象邻两个子节点标号相同,则需要增加一个寄存器存储中间结果,

否则,如果这两个相邻子节点标号相差为1,则无需增加寄存器

否则,如果这两个相邻子节点标号相差>1,则不仅无需增加寄存器,还可以空余(差值-1)个寄存器在检查扫描过程中使用

8.10.5 可以采用8.10.4的方法

8.10.6 

如果两个子节点标号相同的情况,节点标号等于子节点标号

如果两个子节点标号不同,节点标号等于左子节点标号

8.10.7 在加载值的时候保证符合要求即可(*和/的两个变量在相邻寄存器中)

8.11 使用动态规划的代码生成

例8.28代价向量说明,只说明-/(3,2,2),其余略

  1个寄存器保存到内存 1个寄存器保存到寄存器 2个寄存器保存到寄存器
abcde(0,1,1) 0:保存到内存中 1个寄存器保存到寄存器 2个寄存器保存到寄存器
-/(3,2,2) LD R0 a
SUB R0 b
ST a R0
LD R0 a
SUB R0 b
LD R0 a
SUB R0 b
*(5,5,4)      
+(8,8,7)      

8.11.1

1) Ca(0, 1, 1)

2) Mx(0, 1, 1)

3) =(3, 2, 2)

4) =(1, 1, 1)

5) ind(3, 2, 2)

6) +(4, 2, 2)

7) +(2, 1, 1)

8) +(2, 1, 1)










你可能感兴趣的:(《编译原理-龙书》练习第8章)