📜  代码生成中的寄存器分配

📅  最后修改于: 2021-09-27 22:49:41             🧑  作者: Mango

寄存器是存储器层次结构中最快的位置。但不幸的是,这个资源是有限的。它处于目标处理器最受限制的资源之下。寄存器分配是一个 NP 完全问题。但是,这个问题可以归结为图着色来实现分配和赋值。因此,一个好的寄存器分配器可以计算一个困难问题的有效近似解。

图 –输入输出

寄存器分配器确定哪些值将驻留在寄存器中以及哪个寄存器将保存这些值中的每一个。它将具有任意数量寄存器的程序作为其输入,并生成一个具有有限寄存器集的程序,该程序可以适合目标机器。 (见图)

分配与分配:

分配——
将无限命名空间映射到目标机器的该寄存器集。

  • 注册。注册。模型:将虚拟寄存器映射到物理寄存器,但将多余的量溢出到内存中。
  • 嗯。给我。模型:将内存位置的某个子集映射到一组模拟物理寄存器集的名称。

分配确保代码适合目标机器的注册。设置在每个指令。

任务 –
将分配的名称集映射到目标机器的物理寄存器集。

  • 假设分配已经完成,以便代码适合物理寄存器集。
  • 不超过‘k’ 个值被指定到寄存器中,其中 ‘k’ 是编号。物理寄存器。

一般寄存器分配是一个NP完全问题:

  • 在多项式时间内求解,当(所需寄存器数)<=(可用物理寄存器数)。
  • 可以使用区间图着色在线性时间内生成分配。

本地寄存器分配和分配:
基本块内的分配称为本地注册。分配。本地注册的两种方法。分配:自顶向下法和自底向上法。

自上而下方法是一种基于“频率计数”的简单方法。确定哪些值应该保存在寄存器中,哪些应该保存在内存中。

算法:

  1. 计算每个虚拟寄存器的优先级。
  2. 将寄存器按优先级排序。
  3. 按优先级顺序分配寄存器。
  4. 重写代码。

超越单个块:

  • 更复杂,因为控制流进入画面。
  • 活动和活动范围:活动范围由一组相互关联的定义和用途组成,因为它们即在这对指令/数据中没有一个寄存器可以是通用的。

以下是找出块中的实时范围的方法。有效范围表示为区间 [i,j],其中 i 是定义,j 是最后一次使用。

全局寄存器分配和分配:
1. 寄存器分配器的主要问题是最小化溢出代码的影响;

  • 溢出代码的执行时间。
  • 溢出操作的代码空间。
  • 溢出值的数据空间。

2. 全局分配不能保证溢出代码执行时间的最优解。
3. 本地分配和全局分配的主要区别:

  • 全局范围的结构自然比本地范围更复杂。
  • 在全局有效范围内,不同的引用可能会执行不同的次数。 (当基本块形成循环时)

4. 全局分配器为了做出分配和分配的决定,主要通过构建干扰图来使用图着色。
5. 寄存器分配器然后尝试为该图构建 k 着色,其中“k”是编号。物理寄存器。

  • 如果编译器无法直接为该图构造 k 着色,它会通过将一些值溢出到内存并重试来修改底层代码。
  • 溢出实际上简化了确保算法停止的图形。

6. Global Allocator 使用多种方法,但是,我们将看到自顶向下和自底向上的分配策略。与上述方法相关的子问题。

  • 发现全球生活范围。
  • 估计溢出成本。
  • 构建干涉图。

发现全球实时范围:
如何发现变量的实时范围?

图 –在单个块中发现生命周期

上图很好地说明了一切。以Rarp为例,它在程序点1初始化,最后一次使用是在程序点11。因此,Rarp即Larp的实时范围是[1,11]。同样,其他人也跟进。

图 –发现实时范围

估计全球泄漏成本:

  • 进行溢出决策必不可少,其中包括地址计算、内存操作成本和估计执行频率。
  • 为了提高性能,这些溢出的值通常保留用于激活记录。
  • 一些嵌入式处理器提供 ScratchPad Memory 来保存这些溢出的值。
  • 负溢出成本:需要删除单个地址的连续加载存储,因为它会增加负担,因此会产生负溢出成本。
  • 无限溢出成本:如果在定义和使用之间没有其他生命范围结束,则生命范围应该具有无限溢出成本。

干涉和干涉图:

图 –从实时范围构建干扰图

从上图可以看出,生命范围LRa从第一个基本块开始,到最后一个基本块结束。因此,它将与所有其他实时范围(即 Lrb、Lrc、Lrd)共享一条边。但是,Lrb,Lrc,Lrd 不与除 Lra 之外的任何其他有效范围重叠,因此它们仅与 Lra 共享一条边。

构建分配器:

  • 请注意,查找 k 可着色图是一个 NP 完全问题,因此我们需要对此进行近似。
  • 尝试将实时范围拆分为一些重要的块(最常用的块)。

自上而下着色

  1. 尝试按照由某些排名功能(即基于优先级)确定的顺序为实时范围着色。
  2. 如果生命范围没有颜色可用,分配器调用溢出或拆分来处理未着色的颜色。
  3. 具有 k 个或更多邻居的有效范围称为受限节点,并且难以处理。
  4. 无约束节点比较容易处理。
  5. 处理溢出:当在某些生命范围内找不到颜色时,需要进行溢出,但这当然可能不是最终/最终的解决方案。
  6. 实时范围拆分:对于未着色的,将实时范围拆分为子范围,这些子范围可能比原始范围的干扰少,因此至少可以对其中的一些进行着色。

Chaitin的想法:

  • 选择 ( degree < k ) 的任意节点并将其放入堆栈中。
  • 从图中删除该节点及其所有边。 (这可能会降低一些其他节点的度数,并导致更多节点的度数 = k,某些节点必须溢出。
  • 如果不需要溢出顶点,则连续地从堆栈中弹出顶点并用邻居未使用的颜色为它们着色。 (尽可能重复使用颜色)。

合并副本以降低程度:
编译器可以使用干扰图来合并两个有效范围。那么通过合并,您可以获得什么类型的好处?

图 –合并实时范围

比较自顶向下和自底向上分配器:

  • 自顶向下分配器可以采用自底向上分配器中使用的“溢出和迭代”哲学。
  • “溢出并迭代”为可能使用较少溢出代码的分配交换额外的编译时间。
  • 自上而下使用优先级排序对所有受限节点进行排序。 (但是,它以任意顺序为不受约束的节点着色)
  • 自下而上构造一个顺序,其中大多数节点在不受约束的图中被着色。

图 –合并实时范围