LLVM 后端移植 指令集代码学习笔记

实现关于目标处理器指令集的 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;
建立了两个指令执行类别,可分别用于描述简单整型运算和复杂整型运算。

  • InstrItinData 类* 代表指令执行绑定,即对指令执行类别和指令执行阶段的绑定,其结构如下:
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; 

你可能感兴趣的:(硬件编程语言,嵌入式学习,计算机系统)