LLVM 后端移植 寄存器定义部分代码分析

对经过前端翻译后生成的 LLVM 中间代码,通过后端代码生成器可以生成对特定后端处理器的后端代码。生成的后端代码可以两种形式存在:一种是以目标处理器的汇编代码形式,可以通过汇编器编译后得到相应的目标处理器二进制代码, 并能运行在目标处理器上;另一种是直接以二进制代码存在,不能运行在目标处理 器上,但可以使用 JIT 编译器直接在本地运行。

移植接口代码结构

有用的抽象类::TargetMachine、TargetData、TargetLowering、MRegisterInfo、TargetInstrInfo、TargetFrameInfo、TargetSubtarget、TargetJITInfo 等。

后端移植结构如下图:

LLVM 后端移植 寄存器定义部分代码分析

寄存器描述

主要从两方面来描述目标处理器的寄存器,实现关于目标处理器寄存器的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; 
上一篇:python – 构建LLVM失败,显示空错误消息


下一篇:LLVM的安装