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 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; 
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值