LLVM-GreedyRegAlloc

寄存器分配是编译器后端非常重要的一个Pass,其作用是将无限的虚拟寄存器分配给有限的物理寄存器。寄存器分配负责把程序中使用的变量存放到机器的物理寄存器中,如果无法分配成功,则先存放到栈(内存)中,待寄存器可用时,再伺机分配寄存器。

1、前言
LLVM支持多种寄存器分配算法:pbqp,basic,greedy,fast。可以通过命令选项clang -mllvm -regalloc=pbqp|basic|greedy|fast,或者使用llc -regalloc=pbqp|basic|greedy|fast 来选择寄存器分配算法,默认使用greedy寄存器分配算法。
编译时间短:basic,fast;运行时间短:greedy

2、寄存器分配约束

  1. 硬件限制,物理寄存器数量有限
  2. 调用约定,也和硬件有关,某些寄存器作为入参变量使用,哪些寄存器作为返回值使用,哪些寄存器作为callee saved使用,都有明文规范

3、greedy 寄存器分配算法 pass pipeline:
Live Variable , Phi Elimination,Two Address Instruction, Slot Index, Live Intervals,Simple Register Coaslescer,Machine Scheduler,Greedy Register Allocator,Virtual Register Rewriter。

4、Machine IR
如果想要学习LLVM中的寄存器分配,必须掌握Machine IR基础,LLVM 官方链接:https://llvm.org/docs/MIRLangRef.html
需要了解Machine IR中物理寄存器如何表示,虚拟寄存器如何表示,以及寄存器类型。因为在实施寄存器分配之前,会先把IR转换为Machine IR(这属于指令选择(Instruction selection)范畴),然后才会执行寄存器分配。

5、寄存器分配前的工作
转换为Machine IR后,会执行slot Index ,活跃变量分析,生命周期分析。Slot Index 主要作用是对machine ir中的指令进行编号,方便后续的活跃变量分析以及生命周期分析。活跃变量分析,是对machine IR中的变量进行分析,有哪些变量;生命周期分析,是对活跃变量的生命周期进行分析。
如果想要对上面的流程有更深刻的理解,我们可以通过日志来了解。

clang -mllvm -debug-only=regalloc test.c 或者使用
llc -debug-only=regalloc test.ll

例子:
slot index
上图中第一列为指令编号,通过指令编号来获取变量的生命周期。
生命周期例子:
在这里插入图片描述
6、Greedy 寄存器分配流程

在这里插入图片描述

  1. 上图中Live Interval Analysis可以看做是生命周期分析;
  2. Spill Weight Calculation 是权重计算,根据变量生命周期以及活跃性,计算出权重值,放入优先队列中
  3. 然后根据优先队列中的顺序来进行寄存器分配,如果寄存器分配成功,则停止,否则进行Eviction
  4. Eviction,根据权重,将已经分配了权重较低的寄存器收回,分配给当前变量,被收回的变量重新放入到优先队列中;如果没有找到合适的寄存器,则进行生命周期切分(spilt)
  5. 切分后如果有合适的寄存器,则进行分配,如果没有合适的寄存器,则进行溢出操作(spill)
  6. spill,没有找到合适的寄存器,将变量保存到栈内存,然后重新进行寄存器分配操作

上述寄存器分配流程会重复进行,直到所有变量分配成功。

7、RegAllocGreedy:selectOrSplit()
在LLVM代码中,该函数为寄存器分配的关键函数,感兴趣的同学可以去阅读下源码,比较有趣,已经整理好的函数调用关系可以参考:https://www.xmind.net/m/feef/

8、Calling Convention

调用约定是一种规范:描述了函数的入参或者函数的返回值该如何在函数调用之间传递。不同的架构或者硬件存在不同的调用约定。
以AArch64架构为例,在LLVM中存在AArch64CallingConvention.td文件,该文件中定义了所有有关AArch64架构的调用确定,这些调用约定会以RegMask的形式去影响寄存器分配。
源码分析:在上述文件中,你会发现CC_AArch64_AAPCS和RetCC_AArch64_AAPCS两个定义。RetCC_AArch64_AAPCS描述了函数的返回值会优先使用那些寄存器;CC_AArch64_AAPCS描述了函数的入参会优先使用哪些寄存器,当函数的入参超过规定的寄存器数量时,会将入参存到栈中进行传递。该文件种还存在CSR_AArch64_AAPCS定义,CSR_AArch64_AAPCS描述了Callee-saved register会优先使用哪些寄存器,called-saved register表示寄存器中的值在函数调用的过程中需要保留,该寄存器中的值会在函数调用过程中自动保存和重新加载。例子:函数funA调用函数funB,在调用函数funB前,已经使用了寄存器Q12,则在调用函数funB时,会先保存Q12寄存器中的值,然后调用funB,funB执行完成后,会将保存的值加载到Q12中。

9、参考资料

  • https://zhuanlan.zhihu.com/p/56409890
  • https://blog.llvm.org/2011/09/greedy-register-allocation-in-llvm-30.html
  • https://www.cnblogs.com/zhouronghua/p/16413471.html
  • https://llvm.liuxfe.com/post/61283
  • https://llvm.org/docs/MIRLangRef.html
  • https://www.youtube.com/watch?v=hf8kD-eAaxg
  • https://www.youtube.com/watch?v=IK8TMJf3G6U
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值