0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

编译器优化教程:寄存器分配 2

jf_78858299 来源:毕昇编译 作者:王博洋 2023-01-30 16:16 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

线性扫描

接下来介绍一种不同思路的算法:线性扫描。算法描述如下[4]:

LinearScanRegisterAllocation:
	active := {}
	for i in live interval in order of increasing start point
		ExpireOldIntervals(i)
		if length(avtive) == R
			SpillAtInterval(i)
		else
			register[i] := a regsiter removed from pool of free registers
			add i to active, sorted by increasing end point
ExpireOldInterval(i)
	for interval j in active, in order of increaing end point
		if endpoint[j] >= startpoint[i]
			return
		remove j from active
		add register[j] to pool of free registers
SpillAtInterval(i)
	spill := last interval in active
	if endpoint[spill] > endpoint[i]
		register[i] := register[spill]
		location[spill] := new stack location
		remove spill from active
		add i to active, sorted by increasing end point
	else
		location[i] := new stack location

live interval其实就是变量的生命期,用活跃变量分析可以算出来。不过需要标识第一次出现和最后一次出现的时间点。

举个例子:

图片

图10

变量名 live interval
a 1,2
d 2,3,4,5
e 3,4,5,6

llvm中实现

在上文中介绍的算法都是作用在最普通的四元式上,但LLVM-IR是SSA形式,有PHI节点,但PHI节点没有机器指令表示,所以在寄存器分配前需要把PHI节点干掉,消除PHI节点的算法限于篇幅不展开,如感兴趣的话请后台留言。

llvm作为工业级编译器,有多种分配算法,可以通过llc的命令行选项-regalloc=pbqp|greedy|basic|fast来手动控制分配算法。

不同优化等级默认使用算法也不同:O2和O3默认使用greedy,其他默认使用fast。

fast算法的策略很简单,扫描代码并为出现的变量分配寄存器,寄存器不够用就溢出到内存。用途主要是 调试

basic算法以linearscan为基础并对life interval设置了溢出权重而且用优先队列来存储life interval。

greedy算法也使用优先队列,但特点是先为生命期长的变量分配寄存器,而短生命期的变量可以放在间隙中,详情可以参考[5]。

pbqp算法全称是Partitioned Boolean Quadratic Programming,限于篇幅,感兴趣的读者请查阅[6]。

至于具体实现,自顶向下依次是:

  • TargetPassConfig::addMachinePasses含有寄存器分配和其他优化
  • addOptimizedRegAlloc中是与寄存器分配密切相关的pass,比如上文提到的消除PHI节点
  • addRegAssignAndRewriteOptimized是实际的寄存器分配算法
  • 寄存器分配相关文件在lib/CodeGen下的RegAllocBase.cpp、RegAllocGreedy.cpp、RegAllocFast.cpp、RegAllocBasic.cpp和RegAllocPBQP.cpp等。
  • RegAllocBase类定义了一系列接口,重点是selectOrSplit和enqueue/dequeue方法,数据结构的重点是priority queue。selectOrSplit方法可以类比上文中提到的SpillAtInterval。priority queue类比active list。简要代码如下:
void RegAllocBase::allocatePhysRegs() {
  // 1. virtual reg其实就是变量
  while (LiveInterval *VirtReg = dequeue()) {

    // 2.selectOrSplit 会返回一个可用的物理寄存器然后返回新的live intervals列表
    using VirtRegVec = SmallVector4>;
    VirtRegVec SplitVRegs;
    MCRegister AvailablePhysReg = selectOrSplit(*VirtReg, SplitVRegs);
 // 3.分配失败检查
    if (AvailablePhysReg == ~0u) {
     ...
    }
 // 4.正式分配
    if (AvailablePhysReg)
      Matrix->assign(*VirtReg, AvailablePhysReg);
 
    for (Register Reg : SplitVRegs) {
      // 5.入队分割后的liver interval
      LiveInterval *SplitVirtReg = &LIS->getInterval(Reg);
      enqueue(SplitVirtReg);
    }
  }
}

至于这四种算法的性能对比,我们主要考虑三个指标:运行时间、编译时间和溢出次数。

图片

图11 各算法的运行时间,图源[6]

横坐标是测试集,纵坐标是以秒为单位的运行时间

图片

图12 各算法的编译时间,图源[6]

横坐标是测试集,纵坐标是编译时间

图片

图13 各算法的溢出次数,图源[6]

从这三幅图可以看出greedy算法在大多数测试集上都优于其他算法,因此greedy作为默认分配器是可行的。

小结

我们通过一个例子介绍了活跃变量分析和图着色算法。借助活跃变量分析,我们知道了变量的生命期,有了变量生命期建立干涉图,对干涉图进行着色。如果着色失败,可以选择某个变量溢出到内存中。之后在RIG的基础上介绍了寄存器合并这一变换。

然后我们简单介绍了不同思路的寄存器分配算法:linearscan。最后介绍了llvm12中算法的实现并对比了llvm中四种算法的性能差异。

参考

  1. http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15745-s18/www/lectures/L5-Intro-to-Dataflow-pre-class.pdf
  2. http://web.cecs.pdx.edu/~mperkows/temp/register-allocation.pdf
  3. http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15745-s18/www/lectures/L12-Register-Allocation.pdf http://www.cs.cmu.edu/afs/cs.cmu.edu/academic/class/15745-s18/www/lectures/L13-Register-Coalescing.pdf
  4. http://web.cs.ucla.edu/~palsberg/course/cs132/linearscan.pdf
  5. http://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html
  6. T. C. d. S. Xavier, G. S. Oliveira, E. D. d. Lima and A. F. d. Silva, "A Detailed Analysis of the LLVM's Register Allocators," 2012 31st International Conference of the Chilean Computer Science Society, 2012, pp. 190-198, doi: 10.1109/SCCC.2012.29.
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5590

    浏览量

    129143
  • 代码
    +关注

    关注

    30

    文章

    4942

    浏览量

    73193
  • 编译器
    +关注

    关注

    1

    文章

    1670

    浏览量

    51108
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    编译器优化那些事儿(5):寄存器分配

    编译器,有多种分配算法,可以通过llc的命令行选项-regalloc=pbqp|greedy|basic|fast来手动控制分配算法。不同优化等级默认使用算法也不同:O
    发表于 08-24 14:41

    编译器优化的静态调度介绍

    约束条件进行联合求解得到的解决方案是相对更优的,但由于无论是指令调度还是寄存器分配,都是很复杂的NP完全问题,综合考虑下,编译器一般会分别处理二者。  在LLVM编译器的设计中,
    发表于 03-17 17:07

    寄存器组网络处理上的寄存器分配技术

    本内容提供了多寄存器组网络处理上的寄存器分配技术
    发表于 06-28 15:26 28次下载
    多<b class='flag-5'>寄存器</b>组网络处理<b class='flag-5'>器</b>上的<b class='flag-5'>寄存器</b><b class='flag-5'>分配</b>技术

    编译器_keil的优化选项问题

    keil编译器优化选项针对ARM,对STM32编译的一些优化的问题
    发表于 02-25 14:18 3次下载

    高效的C编程之寄存器分配

    14.7 寄存器分配 编译器一项很重要的优化功能就是对寄存器分配。与
    发表于 10-17 17:17 4次下载

    C编译器及其优化

    本章将帮助读者在ARM处理上编写高效的C代码。本章涉及的一些技术不仅适用于ARM处理,也适用于其他RISC处理。本章首先从ARM编译器及其优化
    发表于 10-17 17:22 2次下载

    静态变量、自动变量与寄存器变量的存储

    register限定词通知编译器--程序中的变量将频繁使用。它的意思是建议编译器将程序中用register限定的变量放置在计算机的内部寄存其中,这样可能得到更小更快的程序。但是,编译器
    发表于 06-03 11:27 3782次阅读
    静态变量、自动变量与<b class='flag-5'>寄存器</b>变量的存储

    编译器优化对函数的影响

    编译器如gcc,可以指定不同的优化参数,在某些条件下,有些函数可能会被优化掉。
    的头像 发表于 06-22 14:58 3339次阅读
    <b class='flag-5'>编译器</b><b class='flag-5'>优化</b>对函数的影响

    基于C++编译器的节点融合优化方法

    节点,减少诸如指令、寄存器、时钟周期和访存等开销,以达到减少程序运行时间,提升访存效率等目的。为了提升LLVM编译器的性能,文中在LLVM编译流程的中间表示阶段和DAG合并阶段、指令选择阶段提岀了节点融合
    发表于 06-15 14:29 19次下载

    什么是编译器算法之寄存器分配

    寄存器是CPU中的稀有资源,如何高效的分配这一资源是一个至关重要的问题。本文介绍了基于图着色的寄存器分配算法。
    的头像 发表于 03-02 16:11 1989次阅读
    什么是<b class='flag-5'>编译器</b>算法之<b class='flag-5'>寄存器</b><b class='flag-5'>分配</b>

    怎么给D寄存器输入数值 三菱plc寄存器D怎么读取

    在单片机编程中,给D寄存器输入数值的方法取决于所使用的编程语言和编译器
    发表于 04-12 13:33 2.3w次阅读

    编译器优化选项

    一个程序首先要保证正确性,在保证正确性的基础上,性能也是一个重要的考量。要编写高性能的程序,第一,必须选择合适的算法和数据结构;第二,应该编写编译器能够有效优化以转换成高效可执行代码的源代码,要做到
    的头像 发表于 11-24 15:37 1905次阅读
    <b class='flag-5'>编译器</b>的<b class='flag-5'>优化</b>选项

    Keil编译器优化方法

    我们都知道,代码是可以通过编译器优化的,有的时候,为了提高运行速度或者减少代码尺寸,会开启优化选项。
    的头像 发表于 10-23 16:35 3211次阅读
    Keil<b class='flag-5'>编译器</b><b class='flag-5'>优化</b>方法

    Triton编译器与其他编译器的比较

    Triton编译器与其他编译器的比较主要体现在以下几个方面: 一、定位与目标 Triton编译器 : 定位:专注于深度学习中最核心、最耗时的张量运算的优化。 目标:提供一个高度抽象、灵
    的头像 发表于 12-24 17:25 1613次阅读

    Triton编译器优化技巧

    在现代计算环境中,编译器的性能对于软件的运行效率至关重要。Triton 编译器作为一个先进的编译器框架,提供了一系列的优化技术,以确保生成的代码既高效又适应不同的硬件架构。 1. 指令
    的头像 发表于 12-25 09:09 1906次阅读