实现关于目标处理器指令集的 TableGen 描述 以及继承并实现 TargetInstrInfo 类,也即需要实现文件“XXXInstrInfo.td”、 “XXXInstrInfo.h”和“XXXInstrInfo.cpp”。
在文件“XXXInstrInfo.td”中,需要描述目标处理器的指令集,指令功能,指令的寻址方式,指令操作数,指令编码,指令汇编代码的输出格式,以及指令与 LLVM 虚拟指令集的匹配关系等,这些描述可以由以下几个 TableGen 提供的记录类实现:
Operand 类用于描述指令操作数,包含的数据有:
类型 | 说明 |
---|---|
ValueType Type | 操作数的数据类型 |
string PrintMethod | 打印输出操作数的函数的名字 |
int NumMIOperands | 操作数对应的目标处理器的操作数的个数 |
dag MIOperandInfo | 操作数对应的目标处理器的操作数的信息 |
记录定义 ops,用于标识指令的操作数列表。例如:
ops GPRC:$dst, GPRC:$src
,表明指令有两个操作数 dst 和 src,且都是 GPRC 类型的。
Predicate 类 用于描述指令进行匹配选择时所需的特定条件,Requires 类为这些 特定条件的组合,它是一个以 Predicate 类为元素的列表。这两个记录类的结构如下:
class Predicate {
string CondString = cond;
}
class Requires preds> {
list Predicates = preds;
}
FuncUnit 类 用于建立目标处理器的功能单元,对每一个功能单元需指定一个 值来代表该单元。这些功能单元将被视为可以分配使用的受限资源,即在一定时间 内只能有一条指令在执行时占用该单元。例如,“def ICU : FuncUnit;”
及“def FCU : FuncUnit;”
分别建立了一个整数运算单元和一个浮点数运算单元。
InstrStage 类 用于描述指令执行中的某一阶段,包括完成该阶段可以选择的功 能单元列表以及完成所需的目标处理器运行周期,其结构如下:
class InstrStage units> {
// 完成该指令执行阶段所需的目标处理器周期
int Cycles = cycles;
// 完成该指令执行阶段可以使用的功能单元列表
list Units = units; }
InstrItinClass 类 用于建立这些不同的指令执行类别。
例如, def IntSimp : InstrItinClass;
和def IntComp : InstrItinClass;
建立了两个指令执行类别,可分别用于描述简单整型运算和复杂整型运算。
class InstrItinData stages> {
InstrItinClass TheClass = Class; // 指令执行类别
list Stages = stages; // 指令执行阶段列表
}
下面给出了一个建立绑定的例子,设定指令执行类别 IntComplex 需要两个执行周期,可用功能单元 IntComp1 或 IntComp2 完成:
InstrItinData]>,
ProcessorItineraries 类用于对目标处理器的所有指令执行绑定,其结构如下:
class ProcessorItineraries iid> {
list IID = iid;
}
Instruction 类 用于描述处理器指令,包含的数据有:
类型 | 说明 |
---|---|
string Name | 指定指令名 |
string Namespace | 指定所属的命名空间 |
dag OperandList | 指定指令的操作数列表 |
string AsmString | 指定指令的汇编代码字符串 |
list Pattern | 设定指令所匹配的 LLVM 虚拟指令模式 |
list Uses | 设定将使用的非操作数寄存器 |
list Defs | 设定将修改的非操作数寄存器 |
list Predicates | 设定指令进行模式匹配时所需的特定条件列表 |
// 以下为描述指令高级语法信息的标志位 | |
bit isReturn | 判断指令是否为返回指令的标志位 |
bit isBranch | 判断指令是否为条件跳转指令的标志位 |
bit isBarrier | 判断控制流能否穿过本指令的标志位 |
bit isCall | 判断指令是否为函数调用指令的标志位 |
bit isLoad | 判断指令是否为读内存指令的标志位 |
bit isStore | 判断指令是否为写内存指令的标志位 |
bit isTwoAddress | 判断指令是否为两地址指令的标志位 |
bit isConvertibleToThreeAddress | 判断指令是否能转化为三地址指令的标志位 |
bit isCommutable | 判断指令是否为可交换指令的标志位 |
bit isTerminator | 判断指令是否为某个基本块的结束部分的标志位 |
bit hasDelaySlot | 判断指令是否有延时槽的标志位 |
bit usesCustomDAGSchedInserter | 判断指令是否为需客制处理的伪指令的标志位 |
bit hasCtrlDep | 判断指令是否读或写控制流的标志位 |
bit noResults | 判断指令是否产生结果的标志位 |
InstrItinClass Itinerary | 设定指令执行时的调度和执行阶段 |
InstrInfo 类 用于提供目标处理器的全局参数,其结构如下:
class InstrInfo {
// 当目标处理器需要给指令设定一些目标处理器相关的信息时,列表
// TSFlagsFields 给出了这些信息的字符串列表,而列表 TSFlagsShifts
// 给出了将这些字符串对应到可用的 32 个位的位置信息列表
list TSFlagsFields = [];
list TSFlagsShifts = [];
// 指定目标处理器是否是小端编码的
bit isLittleEndianEncoding = 0;
}
在文件“XXXInstrInfo.h”和“XXXInstrInfo.cpp”中,则需继承 TargetInstrInfo类,并实现 TargetInstrInfo 类中的虚函数。下面列出部分重要的虚函数接口:
virtual bool isMoveInstr(const MachineInstr& MI,
unsigned& sourceReg, unsigned& destReg) const;
virtual unsigned isLoadFromStackSlot(MachineInstr *MI
, int &FrameIndex) const;
virtual unsigned isStoreToStackSlot(MachineInstr *MI,
int &FrameIndex) const;
virtual MachineInstr *convertToThreeAddress(MachineInstr *TA) const;
virtual MachineInstr *commuteInstruction(MachineInstr *MI) const;
virtual void insertNoop(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const;