实现关于目标处理器指令集的 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 cond> { string CondString = cond; } class Requires<list<Predicate> preds> { list<Predicate> Predicates = preds; }FuncUnit 类 用于建立目标处理器的功能单元,对每一个功能单元需指定一个 值来代表该单元。这些功能单元将被视为可以分配使用的受限资源,即在一定时间 内只能有一条指令在执行时占用该单元。例如,“def ICU : FuncUnit;”及“def FCU : FuncUnit;”分别建立了一个整数运算单元和一个浮点数运算单元。 InstrStage 类 用于描述指令执行中的某一阶段,包括完成该阶段可以选择的功 能单元列表以及完成所需的目标处理器运行周期,其结构如下:
class InstrStage<int cycles, list<FuncUnit> units> { // 完成该指令执行阶段所需的目标处理器周期 int Cycles = cycles; // 完成该指令执行阶段可以使用的功能单元列表 list<FuncUnit> Units = units; }InstrItinClass 类 用于建立这些不同的指令执行类别。 例如, def IntSimp : InstrItinClass;和def IntComp : InstrItinClass; 建立了两个指令执行类别,可分别用于描述简单整型运算和复杂整型运算。
InstrItinData 类* 代表指令执行绑定,即对指令执行类别和指令执行阶段的绑定,其结构如下: class InstrItinData<InstrItinClass Class, list<InstrStage> stages> { InstrItinClass TheClass = Class; // 指令执行类别 list<InstrStage> Stages = stages; // 指令执行阶段列表 }下面给出了一个建立绑定的例子,设定指令执行类别 IntComplex 需要两个执行周期,可用功能单元 IntComp1 或 IntComp2 完成:
InstrItinData<IntComplex, [InstrStage<2, [IntComp1, IntComp2]>]>,ProcessorItineraries 类用于对目标处理器的所有指令执行绑定,其结构如下:
class ProcessorItineraries<list<InstrItinData> iid> { list<InstrItinData> 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<string> TSFlagsFields = []; list<int> 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;