新建后端的七大步骤,在前文《LLVM后端文档解析》中进行过介绍。我们在《创建一个LLVM新后端的第一步中文档与代码的差异》和《Target Registration的更正》中,已经列出了现有文档中第一步“Create a subclass of the TargetMachine class that describes characteristics of your target machine”和实际代码中的差别。当然,现有文档使用的是Sparc后端作为示例,而我分析代码时候采用的是RISCV的后端代码。两者对比起来稍微有点别扭,但是不影响对比效果。我计划在后端文档分析和对比系列写完之后,能写一个中文版本的以RISCV为实例的《Writing An LLVM Backend》文档,方便有需要熟悉后端的同学阅读。之所以选择RISCV,也是因为最近风投正盛,而且我个人对其发展持乐观态度;同时,也可以和原有文档的Sparc示例形成差异。
新建后端第二步的文档,主要内容是:Describe the register set of the target.官方文档的介绍基本上没有什么问题,但是文档有些内容没有跟上代码的更新。在此做简单的总结:
1、主要就是在llvm/lib/Target/Sparc/为目标平台建立SparcRegisterInfo.td、SparcRegisterInfo.h和SparcRegisterInfo.cpp。
2、在SparcRegisterInfo.td中要为Register<n>建立子类:
class SparcReg<bits<16> Enc, string n> : Register<n> {
在之后,要为每个Sparc的寄存器建立一个这个类的对象。同时,在SparcRegisterInfo.td中还要建立一系列RegisterClass类的对象。The RegisterClass class (specified in Target.td) is used to define an object that represents a group of related registers and also defines the default allocation order of the registers. 所以这个类的对象是为了表达一组相关的寄存器和寄存器分配的默认顺序。
其实,Register和RegisterClass这两个类都是在llvm/include/llvm/Target/Target.td之中定义的。
3、SparcRegisterInfo.h和SparcRegisterInfo.cpp的实现。这两个文件主要是为了手动实现一些RISCVRegisterInfo类的代码,它是SparcRegisterInfo.td生成的文件中的RISCVGenRegisterInfo的子类:
struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
所以,在SparcRegisterInfo.h除了一些直接可以返回值的成员函数之外,在SparcRegisterInfo.cpp之中还要重载一些函数,文档描述需要重载的函数主要有:
- getCalleeSavedRegs — Returns a list of callee-saved registers in the order of the desired callee-save stack frame offset.
- getReservedRegs — Returns a bitset indexed by physical register numbers, indicating if a particular register is unavailable.
- hasFP — Return a Boolean indicating if a function should have a dedicated frame pointer register.
- eliminateCallFramePseudoInstr — If call frame setup or destroy pseudo instructions are used, this can be called to eliminate them.
- eliminateFrameIndex — Eliminate abstract frame indices from instructions that may use them.
- emitPrologue — Insert prologue code into the function.
- emitEpilogue — Insert epilogue code into the function.
在这个环节,文档和代码出现了一些差异,文档没有跟上代码的更新实际代码内容可以参见:SparcRegisterInfo.cpp 。
在RISCV后端中,前面1、2也和文档一致,只不过具体内容换成了RISCV特征的。但是RISCV的最新代码在这个步骤上,也和文档差异较大,实际重载实现的成员函数为:
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID) const override;
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
BitVector getReservedRegs(const MachineFunction &MF) const override;
bool isConstantPhysReg(unsigned PhysReg) const override;
const uint32_t *getNoPreservedMask() const override;
void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,
unsigned FIOperandNum,
RegScavenger *RS = nullptr) const override;
unsigned getFrameRegister(const MachineFunction &MF) const override;
RISCV的具体代码参见:RISCVRegisterInfo.h 、 RISCVRegisterInfo.cpp 和 RISCVRegisterInfo.td .
总体而言,这一部分的文档和代码的差异不大,主要是在3中重载成员函数部分有差异,而且这个差异并不影响对文档和代码的理解。比之前的差异要好接受的多。
参考代码:llvm/llvm-project Latest commit9ed325e
相关内容:
小乖他爹:LLVM每日谈之三十七 LLVM后端简介(杭州分享PPT)
小乖他爹:LLVM每日谈之五十 LLVM 后端文档解析3 —目标描述类
小乖他爹:LLVM每日谈之五十一 TargetMachine
小乖他爹:LLVM每日谈之五十二 创建一个LLVM新后端的第一步中文档与代码的差异
小乖他爹:LLVM每日谈之五十三 Target Registration的更正
编辑于 2019-06-03