编译-第15章-目标代码生成和优化

现代微处理器体系结构简介

指令集架构

image

存储器层次结构

硬盘(os) -> 内存(os) -> 缓存(硬件) -> 寄存器(编译器)

编译器可以管理寄存器

尽可能少的访问寄存器以外的存储器

  • 寄存器的数量有限
  • 对缓存的利用,对大型数据结构有用

地址空间

  • 代码区
    • 存放目标代码
  • 静态数据区
    • 全局变量
    • 静态变量
    • 部分常量,例如字符串
  • 动态内存区
    • 也被称为内存堆
    • 程序员管理:c,cpp
    • 程序自动管理:java,ada
  • 程序运行栈
    • 活动记录
    • 函数调用的上下文现场

寄存器的分配和指派

why?

  • 速度快
  • 某些运算只能发生在寄存器中?()
  • 优化指令的执行,更希望所有的指令都在寄存器中完成
  • 寄存器有很多好处,然后确实有限

种类

  • 通用寄存器
    • 保留寄存器(专门用途,特定功能):栈指针,返回地址寄存器
    • 临时寄存器‼️:调用方保存的寄存器
    • 全局寄存器‼️:被调用方保存的寄存器
  • 专门寄存器

全局寄存器分配

“全局”是相对于“基本块”而言的

分配原则:

  • 垮基本块仍然活跃的变量,尤其是循环体内
    局部变量参与全局寄存器分配
  • 寄存器专属于线程!为了线程安全,全局变量/静态变量一般不参与全局寄存器分配
1
2
3
4
5
void foo(int a)
{
static int s_c=0;
s_c+=a;
}

?(为什么全局变量不参与)

分配方法

  • 引用计数:根据被引用次数,赋予权重,由大到小分配寄存器
  • 着色图算法:构建变量之间的冲突图,将不同的全局寄存器分配给有冲突的变量(如果两个元素有边就要分配不同的寄存器)

引用计数

直接看次数,如果有循环体可以预先分配循环次数

‼️不再使用的变量无法及时释放寄存器

解决办法:活跃变量分析,冲突图;着色算法

图着色算法

简化版

  • 构建冲突图
  • 有k个寄存器,用k个颜色填充

活跃变量分析:看in[B]即基本块入口处的活跃变量

image

启发式着色算法

  • 找到一个连接边小于k的节点,将其从图中移除
  • 重复上面的步骤
  • 按照节点被移走的反向顺序,重新构建填充颜色

⚠️如果找不到连接边小于k的节点,就在图中选取适当的结点,将它记录为“不分配全局寄存器”的结点,并从图中移走

临时寄存器的分配

  • 基本块内使用
  • 不得跨用函数调用
  • 管理方法:维护寄存器池

指令选择(感觉不太重要)

(想补充补充补充)