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

    xiaoxiao2022-07-03  177

    对经过前端翻译后生成的 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;
    最新回复(0)