寄存器定义的基本语法和流程
1 寄存器类定义
所有架构都首先定义一个寄存器类,该类继承自 LLVM 的 Register 或其子类(如 RegisterWithSubRegs)。这个类定义了寄存器的通用属性。
class ArchReg<EncodingType Enc, string n, list<Register> subregs = [],
list<string> altNames = []> : Register<n, altNames> {
let HWEncoding = Enc;
let Namespace = "ArchName";
let SubRegs = subregs;
// 其他架构特定属性
}
编码(Encoding):
指定寄存器的硬件编码,类型可能是 bits<N>,其中 N 取决于架构(ARM 使用 16 位,CSKY 使用 6 位,Lanai 使用 5 位)
名称(Name):寄存器的汇编名称,类型为 string
命名空间(Namespace):
指定寄存器的命名空间,通常为架构的名称。
子寄存器(SubRegs):
指定寄存器的子寄存器,这些子寄存器将继承该寄存器的属性。类型为 list<Register>,默认值为空列表 []
别名(altNames):指定寄存器的替代名称列表,类型为 list<string>,默认值为空列表 []
其他架构特定属性:
根据架构的属性,可以添加其他属性,如子寄存器的索引等。
1.2 寄存器实例定义
定义了寄存器类后,各架构使用不同的方法创建具体的寄存器实例。
def RegName : ArchReg<Encoding, "Name", [SubRegs...]>, DwarfRegNum<[DebugNum]>;
所有寄存器实例都包含以下基本属性:
调试编号(DwarfRegNum):用于调试信息的寄存器编号,通过 DwarfRegNum<[DebugNum]> 指定
子寄存器索引(SubRegIndices):指定子寄存器的索引,通过 SubRegIndices = [sub_index1, sub_index2, ...] 指定
使用 foreach 循环:
foreach I = 0-11 in {
// 32-bit Integer (alias to low part of 64-bit register).
def W#I : Wi<I, "w"#I>, DwarfRegNum<[I]>;
// 64-bit Integer registers
def R#I : Ri<I, "r"#I, [!cast<Wi>("W"#I)]>, DwarfRegNum<[I]>;
}
特殊寄存器定义模式:
let SubRegIndices = [sub_index...] in {
def SpecialReg : ArchReg<Encoding, "Name", [SubRegs...]>, DwarfRegAlias<BaseReg>;
}
总结
不同架构的寄存器定义虽然有一些细节差异,但遵循共同的模式和流程:
- 定义架构特定的寄存器类,继承自 LLVM 的 Register 类
- 定义子寄存器索引,用于描述寄存器层次结构
- 创建寄存器实例,可以使用直接定义或 foreach 循环
- 定义寄存器类(RegisterClass),将寄存器分组用于代码生成
这种模块化的定义方式使得 LLVM 能够支持多种不同的目标架构,同时保持代码的可维护性和一致性。每个架构可以根据自己的特定需求扩展或修改基本模式,但核心概念和结构保持一致。