对经过前端翻译后生成的 LLVM 中间代码,通过后端代码生成器可以生成对特定后端处理器的后端代码。生成的后端代码可以两种形式存在:一种是以目标处理器的汇编代码形式,可以通过汇编器编译后得到相应的目标处理器二进制代码, 并能运行在目标处理器上;另一种是直接以二进制代码存在,不能运行在目标处理 器上,但可以使用 JIT 编译器直接在本地运行。
移植接口代码结构
有用的抽象类::TargetMachine、TargetData、TargetLowering、MRegisterInfo、TargetInstrInfo、TargetFrameInfo、TargetSubtarget、TargetJITInfo 等。
后端移植结构如下图:
寄存器描述
主要从两方面来描述目标处理器的寄存器,实现关于目标处理器寄存器的TableGen 描述以及继承并实现 MRegisterInfo 类,也即需要实现文件件“XXXRegisterInfo.td”、“XXXRegisterInfo.h”和“XXXRegisterInfo.cpp”。
在文件“XXXRegisterInfo.td”中,需要描述目标处理器每个寄存器的属性, 寄存器之间的别名关系以及程序运行时的寄存器分配方案等,这通过 TableGen 提 供的描述寄存器的四个记录类实现:Register、RegisterGroup、RegisterClass、 DwarfRegNum
Register 类 用于描述每个寄存器的属性,该类包括的数据有:
数据 | 解释 |
---|---|
string Namespace | 指定所属的命名空间 |
string Name | 寄存器的名字 |
int SpillSize | 寄存器溢出时保存寄存器所需的位的个数 |
int SpillAlignment | 寄存器溢出时保存寄存器要求的对齐 |
list Aliases | 读/写本寄存器将读/写的其它寄存器的列表(别名) |
int DwarfNumber gcc/gdb | 内部用于识别寄存器的编号 |
例如,下面建立了一个名为 GPR 的寄存器类,属于命名空间“XXX”,有一 个长度为 2 的位串类型数据 num.。用 GPR 可以建立 4 个记录定义 R0~R3,描述 了 4 个属于命名空间“XXX”的寄存器。
class GPR< bits<2> num, string n > :
Register<n>{
field bits<2> Num;
let Namespace = "XXX";
Num = num;
}
def R0 : GPR< 0, "R0">;
def R1 : GPR< 1, "R1">;
def R2 : GPR< 2, "R2">;
def R3 : GPR< 3, "R3">;
RegisterGroup 类 继承了 Register 类,用于描述有别名的寄存器,其结构如下:
class RegisterGroup<string n, list<Register> aliases> :
Register<n>{
let Aliases = aliases; }
例如,“def S0 : RegisterGroup<“S0”, [R0]>;”说明了 S0 与 R0 之间的别名关系。
RegisterClass 类 帮助对寄存器进行分类,将不同功能的寄存器分为不同的寄存器类,并规定不同类型的寄存器在进行寄存器分配时的分配顺序。
类包括的数据有:
数据 | 解释 |
---|---|
string Namespace = namespace | 指定所属的命名空间 |
list RegTypes | 指令该类寄存器类型 |
int Size | 寄存器溢出时保存寄存器所需的位的个数 |
int Alignment | 当寄存器被写入内存时需要的对齐 |
list MemberList | 列出所有属于本寄存器类的寄存器,并作为没有提供寄存器分配方案 allocation_order_*时默认的寄存器分配顺序 |
code MethodProtos | 提供寄存器分配方案 allocation_order_*的函数原型 |
code MethodBodies | 实现寄存器分配方案 allocation_order_*函数 |
以下给出了一个寄存器类的记录定义:
def GPRC : RegisterClass< "XXX", [i32], 32, [R0, R1, R2, R3] >
{ let MethodProtos = [{
iterator allocation_order_begin(MachineFunction &MF) const;
iterator allocation_order_end(MachineFunction &MF) const;
}];
let MethodBodies = [{
GPRCClass::iterator GPRCClass::allocation_order_begin(
MachineFunction &MF) const
{ return begin() + 1; }
GPRCClass::iterator GPRCClass::allocation_order_end(
MachineFunction &MF) const {
return end() - 1; }
}];
}
上面定义的寄存器类 GPRC 包含 4 个寄存器 R0、R1、R2、R3,该寄存器类中的寄存器的类型为 i32,对齐为 32,所属命名空间的名字为“XXX”,并且在allocation_order_begin 和 allocation_order_end 的函数实现中分别规定 R0 和 R3 不参与寄存器分配。
DwarfRegNum 类 提供了 llvm 寄存器与 gcc/gdb 寄存器编号之间的映射,其结构如下:
class DwarfRegNum<int N>{
int DwarfNumber = N;
}
例如,“def R0 : GPR< 0, “R0”>, DwarfRegNum<0>;”将 R0 映射到寄存器编号 0。 对需移植的目标处理器,通过以上这四个记录类来描述寄存器,就可完成文件“XXXRegisterInfo.td”的实现。
在文件 “ XXXRegisterInfo.h ” 和 “ XXXRegisterInfo.cpp ” 中 , 则 需 继 承MRegisterInfo 类,并实现 MRegisterInfo 类中的虚函数。这些虚函数接口主要有:
- 提供将寄存器中的值放入栈槽的目标处理器指令
virtual void storeRegToStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI, unsigned SrcReg,
int FrameIndex, const TargetRegisterClass *RC) const = 0;
- 提供从栈槽取出值放入寄存器中的目标处理器指令
virtual void loadRegFromStackSlot(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI, unsigned DestReg,
int FrameIndex, const TargetRegisterClass *RC) const = 0;
- 提供寄存器拷贝的目标处理器指令
virtual void copyRegToReg(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI, unsigned DestReg,
unsigned SrcReg, const TargetRegisterClass *RC) const = 0;
- 将对栈槽进行读写的中间代码转为目标处理器的访存指令
virtual MachineInstr* foldMemoryOperand(MachineInstr* MI,
unsigned OpNum, int FrameIndex) const;
- 在进行序言和尾声代码插入时,调用这个接口函数去除建立和销毁帧的伪代码
virtual void eliminateCallFramePseudoInstr(MachineFunction &MF,
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) cons;
- 实现本函数接口以在函数帧布局结束之前增加其它处理
virtual void processFunctionBeforeFrameFinalized(MachineFunction &MF) const;
- 实现本函数接口以消除 LLVM 的虚拟帧索引
virtual void eliminateFrameIndex(MachineBasicBlock::iterator MI) const;
- 添加进入函数之前运行的序言代码
virtual void emitPrologue(MachineFunction &MF) const = 0;
- 添加退出函数之前运行的尾声代码
virtual void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const = 0;
- 将目标处理器的寄存器映射到 dwarf 的寄存器编号
virtual int getDwarfRegNum(unsigned RegNum) const = 0;
- 返回当前使用的帧指针所在的寄存器
virtual unsigned getFrameRegister(MachineFunction &MF) const = 0;
- 返回目标处理器的链接寄存器
virtual unsigned getRARegister() const = 0;
- 对给定的帧索引偏移 index 返回实际位置
ML virtual void getLocation(MachineFunction &MF, unsigned Index, MachineLocation &ML) const;
- 返回出现在所有函数入口的目标处理器移动指令
virtual void getInitialFrameState(std::vector<MachineMove *> &Moves) const;