8.1 代码生成器和目标语言

8.1.1 代码生成器的设计

位置
notion image
notion image
设计
代码生成器的三个任务
  • 指令选择:选择适当的指令实现IR语句
  • 寄存器分配和指派:把哪个值放在哪个寄存器中
  • 指令排序:按照什么顺序安排指令执行
 
输入中间表示及符号表信息:
  • 四元式、三元式、间接三元式
  • 字节代码、堆栈机代码、后缀表示
  • 抽象语法树、DAG图、…
输出目标程序
  • RISC、CISC
  • 可重定向代码、汇编语言
 

8.1.2 目标语言

使用汇编代码作为目标语言
指令
  • 加载:LD dst, addr
    • (把地址addr中的内容加载到dst所指的位置)
  • 保存:ST x, r
    • (把寄存器r中的内容保存到x中)
  • 计算:OP dst, src1, src2
    • (把src1和src2中的值运算后将结果存放到dst中)
  • 无条件跳转:BR L
    • (控制流转向标号L的指令)
  • 条件跳转:Bcond r, L
    • (对r中的值进行测试,如果为真则转向L。如BLTZ r L是当寄存器r中的值小于0时跳转到L)
 
寻址模式
  • 变量x:指向分配x的内存位置
  • a(r):地址是a的左值加上寄存器r中的值
    • (如LD R1,a(R2) 代表 R1=contents(a+contents(R2)))
  • constant(r):寄存器r中内容加上前面的常数即其地址
    • (如LD R1,100(R2)代表R1=contents(100+contents(R2)))
  • *r:寄存器r的内容所表示的位置上存放的内容位置
  • *constant(r):寄存器r中内容加上常数所代表的位置上的内容所表示的位置
    • (如LD R1,*100(R2)代表R1=contents(contents(100+contents(R2))))
  • 常数 #constant
notion image
notion image
notion image
 
程序及指令的代价
假设每个指令有固定的代价,设定为1加上运算分 量寻址模式的代价,例:
LD R0, R1:代价为1(将contents(R1)存入R0)
LD R0, M:代价是2(将内存中M的值存入R0)
LD R1, *100(R2):代价为2(将contents(contents(100+contents(R2)))存入R1)
 
 
直接将三地址代码翻译成机器语言容易导致冗余

8.2 一个简单的代码生成器

根据三地址指令序列生成机器指令
  • 假设每个三地址指令只有一个对应的机器指令
  • 有一组寄存器用于计算基本块内部的值
主要的目标是减少加载 (LD) 和保存 (ST) 指令,即最大限度地利用寄存器
寄存器的使用方法
  • 执行运算时,运算分量必须放在寄存器中
  • 存放临时变量
  • 存放全局的值
  • 进行运行时刻管理 (比如栈顶指针)
 
依次考虑各三地址指令,尽可能把值保留在寄存 器中,以减少寄存器/内存之间的数据交换
为一个三地址指令生成机器指令时
  • 只有当运算分量不在寄存器中时,才从内存载入
  • 尽量保证只有当寄存器中值不被使用时,才覆盖掉
 
数据结构
  • 寄存器描述符:跟踪各个寄存器都存放了哪些变量的 当前值
  • 地址描述符:各个变量的当前值存放在哪些位置(包括内存位置和寄存器) 上

8.2.1 代码生成算法

逐个处理三地址指令
运算语句x = y + z
getReg(x = y + z)为x, y, z选择寄存器Rx, Ry, Rz
检查Ry的寄存器描述符,如果y不在Ry中则生成指令
  • LD Ry, y' // y'表示存放y值的当前位置
  • 类似地确定是否生成LD Rz, z'
生成指令ADD Rx, Ry, Rz
 
复制语句x = y
getReg(x = y)为xy选择相同的寄存器
如果y不在Ry中,则生成指令LD Ry, y(将y中的内容加载到寄存器Ry中)
 
基本块的收尾
如果变量x活跃,且不在内存中,则生成指令ST x, Rx
 

8.2.2 函数getReg

根据寄存器描述符和地址描述符等数据流信息,为三地址指令/选择最佳的寄存器
得到的机器指令的质量依赖于getReg函数选取寄存器的算法
 
//太抽象了没看懂
 
Loading...