Skip to content

算法Algorithm

活跃范围分裂

Live-range splitting · Live interval splitting

按区域为同一逻辑值改名并补齐每条边的传值,输出可执行虚拟IR,再比较分段位置和额外移动。

形式陈述 ​

分裂的输出先是一份新程序 ​

一个名字全程占同一个位置,会把互不相邻的需求绑在一起。活跃范围分裂让不同区域使用不同名字,允许以后分别选择位置。输入是良构纯标量CFG和区域边界;输出是改名后的虚拟IR及边传值,此步不必决定哪段进栈。已经给定位置以后插LOAD/STORE是另一项变换。

本页给一个完整、容易逐边证明的版本:每个原基本块是一个区域。由活跃分析计算每块入口集合In(B)。对每个块B中读、写或穿过该块的原名字x,生成全程序新鲜的区域名字xB;同一块里对x的重复赋值仍写xB,因此输出并不自动成为SSA。

块内所有读写按此映射改名。对每条CFG边A→B,插入只属于该边的适配块,执行并行复制

(xB)x∈In(B):=(xA)x∈In(B),

然后跳到B。入口input仍读取原外部输入键,只改变接收结果的虚拟名字。即使x在A中没有局部读写,只要它活跃地穿过A,也必须有xA,否则无法给后继传值。

每条边必须把同一份旧值交出去 ​

在原块B入口关联原状态σ与新状态ρ:所有x∈In(B)满足σ(x)=ρ(xB)。块内同名改写保持每次读到的值;指令先读后写,所以x:=x+1仍正确。块末在本条实际选择的边上,右侧xA等于原值,整组复制同时把它交给B。因而B入口关系重新成立。

初始input建立第一批对应;每步普通指令和每次跨块转移都保持关系,就覆盖任意有限执行前缀。每条原边只展开成有限个无循环的复制/跳转,因此既不把正常返回改成发散,也不把原无限循环吞掉。本模型原语全定义,无异常和外部事件;如果观察异常或内存效应,必须另扩展对应关系。

物理位置确定后,复制可能构成寄存器交换环。这里实际采用SSA消解的边专属并行复制顺序化工具:先做不会覆盖其他待读源的位置,遇环先保存一个旧值再断环。使用该工具不要求源程序是SSA,要求的是“多个目标同时接收原源值”的接口相同。

直觉

同一份文件可以经过几个有名字的交接点 ​

一个值从入口活到退出,不意味着它必须一直住在同一个寄存器。可以先存在一个位置,到调用前换到不会被破坏的位置,调用后再换回来。但每次换位置都要真正搬过去;只给前后两段起不同名字,不会让值自动出现在新位置。

区域边界还可能有多个来源。B的入口必须从实际来的那条边拿值,不能在另一个分支执行时也提前覆盖B需要的来源。边适配块把这份义务直接写进控制流。

例子与边界

在菱形的四条边上交接 ​

主例E产生a、t2、t3,经T或F得到t4,再到J调用double。按块分裂后,E内使用a_E、t2_E、t3_E;T/F各有自己的三个入口名字;J的入口只需要a_J与t4_J。

text
E→T: (a_T,t2_T,t3_T) := (a_E,t2_E,t3_E); goto T
E→F: (a_F,t2_F,t3_F) := (a_E,t2_E,t3_E); goto F
T→J: (a_J,t4_J) := (a_T,t4_T); goto J
F→J: (a_J,t4_J) := (a_F,t4_F); goto J
J: v_J := t4_J; t5_J := double(v_J)
   y_J := a_J + t5_J; return y_J

a_T和a_F在各自算术里没有被读写,却仍须从区域入口传到出口,因为J会用a。若仅按“本块指令里出现哪些变量”生成区域名字,这个穿过值就会丢失。flag只控制E的出边,不进入后继入口集合,所以无需传给T/F。

对分裂后IR重新运行图分配器,下载实现使a_J住R1,早先的a_E、a_T、a_F分别住私有槽。于是进入J时确实有一次槽到R1的搬移;R1跨double保存值。输入a=3、flag=1,仍返回21,目标执行3次ST、5次LD,另有3次JMP和1次MOV。未分裂图分配是2次ST、4次LD;本次全块切分反而增加成本。

分裂扩大可选择位置的集合,也增加交接义务。要改善性能,还需选择值得切的边界和好的位置;不能将“名字更多”直接等同“更快”。固定布局把全部适配块排在最后,还可能让单区间线性扫描产生很长的保守跨度。

交换必须读取两份旧值 ​

假设一条边上的两个值从(R0,R1)交到(R1,R0)。顺写R1:=R0; R0:=R1会丢掉旧R1。下载lowering用私有临时槽保存一个旧寄存器值,再按无环顺序搬移;从(1,2)得到(2,1),而不是(1,1)。它只使用四个显式物理寄存器及私有槽。

若一个循环体每轮交换a、b,执行n轮后a-b应在n为偶数时为−1,奇数时为1。改名和边复制必须在回边上每轮交付当前值;仅在第一次进入循环头时复制会让第二轮开始读旧版本。

推论与应用

边界可以选,传值不能省 ​

只在调用前后、循环入口或热/冷区域之间切分,可能减少全块切分的额外移动。无论如何选择,穿过边界且后面还要用的每个值都需要一条对应关系。若两段最后分到同一位置,物理自复制可删除;删除依据是最终位置一致,不是源名字看起来相似。

真实分裂式线性扫描会根据未来使用和固定寄存器冲突选择切点,并在所有CFG边上解析位置不一致。[1, §3.3] 本页直接按块区域构造完整新IR,用来把“切区间”背后的程序变换义务显露出来。

规模与终点任务 ​

令L为原IR大小,C=∑A→B|In(B)|为全部边需要传递的值数,rB为块B所需的区域名字数。把名字视为可常数比较的标识,映射查找取期望常数,并假定活跃事实已给定。不要求稳定顺序且已取得新鲜名字时,区域改名和边适配构造需O(1+L+C)工作及输出空间;每块自己的使用/定义,以及穿入穿出的活跃名,合计受L与C控制。

下载版为可重复结果同时排序每块区域名与每条边的传值名,额外花O(∑BrBlog⁡(rB+1)+∑A→B|In(B)|log⁡(|In(B)|+1))。它用带后缀的名字逐个试探碰撞;若总共探查F次,另计O(F),不能把任意已有名字集合下的碰撞搜索忽略。实际字符串长度的读取与拼接也另计。重新活跃分析、分配和物理复制排序不包含在这个构造成本中。

对一条含k个位置复制的边,本文反复扫描未解决源/目标,朴素顺序化为O((k+1)2);每次安全删除减少一项,断环后至少可删除一项,所以输出长度为O(k+1)。临时槽只属于这次过程,不暴露给源程序。

终点先补齐菱形四条边,再故意漏掉F→J上的a交接,选择假分支应暴露错误。迁移到带交换的循环,逐轮核(1,2)和(2,1);最后选择只在J前切a,不切t2/t3,比较新IR大小与完整块切分。任何候选都须同时提交改名表、边复制表和实际目标代码。

参考资料

[1] Christian Wimmer、Hanspeter Mössenböck,Optimized Interval Splitting in a Linear Scan Register Allocator,VEE2005,§§3.1–3.3 pp.134–136:切分子区间、重载与边上位置resolution。本文按块区域改名是独立教学方案。

[2] Laurence Rideau、Bernard Paul Serpette、Xavier Leroy,Tilting at Windmills with Coq: Formal Verification of a Compilation Algorithm for Parallel Moves,JAR40,2008,pp.307–326,§§2–4:并行位置赋值、临时位置和顺序化不变量;相应工具的完整教学算法见本页引用的旧SSA消解页。

关系图谱7 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系