Preliminaries
在lib/Target/下创建你的目标,如lib/Target/MIps;
创建CMakeLIsts.txt;
Target Machine
继承LLVMTargetMachine实现MipsTargetMachine.h MipsTargetMachine.cpp,需要包含get*INfo的方法;
Register
定义一个寄存器类:
// We have banks of 32 registers each.
class MipsReg<bits<16> Enc, string n> : Register<n> {
let HWEncoding = Enc;
let Namespace = "Mips";
}
...
MipsRegisterInfo.td中有浮点寄存器, Register Classes(注意)
.td完成后就需要手写.h和.cpp文件,以实现相应的接口.
Instruction
在代码生成的早期阶段,LLVM IR代码被转换为SelectionDAG,其中节点是包含目标指令的SDNode类的实例。 SDNode具有操作码,操作数,类型要求和操作属性。 例如,是一个交换操作,从内存中执行一个操作加载。 include / llvm / CodeGen / SelectionDAGNodes.h文件(ISD命名空间中的NodeType枚举的值)中描述了各种操作节点类型。
Target.td - 指令,操作数,InstrInfo和其他基本类的定义。
TargetSelectionDAG.td - 由SelectionDAG指令选择生成器使用,包含SDTC *类(选择DAG类型约束),SelectionDAG节点的定义(例如imm,cond,bb,add,fadd,sub)和模式支持(Pattern,Pat, PatFrag,PatLeaf,ComplexPattern。
MipsInstrFormats.td - 用于定义特定于目标的指令的模式。
MipsInstrInfo.td - 指令模板,条件代码和指令集指令的特定于目标的定义。对于架构修改,可以使用不同的文件名。
要描述一个具体的目标特定类XXXInstrInfo,它表示目标机器支持的机器指令。 XXXInstrInfo包含一个XXXInstrDescriptor对象数组,每个对象描述一条指令。 指令描述符定义:
操作码助记符
操作数的数量
隐式寄存器定义和用途列表
与目标无关的属性(如内存访问,是可交换的)
特定于目标的标志
指令类(在Target.td中定义)主要用作更复杂指令类的基础:
class Instruction {
string Namespace = "";
dag OutOperandList; // A dag containing the MI def operand list.
dag InOperandList; // A dag containing the MI use operand list.
string AsmString = ""; // The .s format to print the instruction with.
list<dag> Pattern; // Set to the DAG pattern for this instruction.
list<Register> Uses = [];
list<Register> Defs = [];
list<Predicate> Predicates = []; // predicates turned into isel match code
... remainder not shown for space ...
}
SelectionDAG节点(SDNode)包含一个对象,该对象表示在MipsInstrInfo.td中定义的特定于目标的指令。指令对象应代表来自目标机器的体系结构手册的指令.
体系结构手册中的单个指令通常建模为多个目标指令,具体取决于其操作数。例如,手册可能会描述一个带有寄存器或立即操作数的加法指令。 LLVM目标可以使用名为ADDri和ADDrr的两条指令对其进行建模。
应该为每个指令类别定义一个类,并使用适当的参数(例如操作码和扩展操作码的固定二进制编码)将每个操作码定义为该类别的子类。应该将寄存器位映射到它们编码的指令位(对于JIT)。还应该指定在使用自动装配打印机时如何打印指令。
Mips通用寄存器+指令这是一篇介绍Mips寄存器和指令的博客,写的很好.其中:
指令长度和寄存器个数
MIPS的所有指令都是32位的,指令格式简单。
32 个通用寄存器,寄存器数量跟编译器的的要求有关。寄存器分配在编译优化中是最重要的优化之一(也许是做重要的)。现在的寄存器分配算法都是基于图着色的技 术。其基本思想是构造一个图,用以代表分配寄存器的各个方案,然后用此图来分配寄存器。粗略说来就是使用有限的颜色使图中相临的节点着以不同的颜色,图着 色问题是个图大小的指数函数,有些启发式算法产生近乎线形时间运行的分配。全局分配中如果有16个通用寄存器用于整型变量,同时另有额外的寄存器用于浮点 数,那么图着色会很好的工作。在寄存器数教少时候图着色并不能很好的工作。
指令格式
所有MIPS指令长度相同,都是32位,但为了让指令的格式刚好合适,于是设计者做了一个折衷:所有指令定长,但是不同的指令有不同的格式。MIPS指令有三种格式:R格式,I格式,J格式。每种格式都由若干字段(filed)组成,图示如下:
I型指令
6 5 5 16
——|—–|—–|——————|
| op | rs | rt | 立即数操作 |
——|—–|—–|——————|
加载/存储字节,半字,字,双字
条件分支,跳转,跳转并链接寄存器
R型指令
6 5 5 5 5 6
——|—–|—–|—–|—–|——--|
|op | rs | rt | rd |shamt|funct |
——|—–|—–|—–|—–|——---|
寄存器-寄存器ALU操作
读写专用寄存器
J型指令
6 26
——|——————————|
|op | 跳转地址 |
——|——————————|
跳转,跳转并链接
陷阱和从异常中返回
各字段含义:
op:指令基本操作,称为操作码。
rs:第一个源操作数寄存器。
rt:第二个源操作数寄存器。
rd:存放操作结果的目的操作数。
shamt:位移量
funct:函数,这个字段选择op操作的某个特定变体。
所有指令都按照着三种类型之一来编码,通用字段在每种格式中的位置都是相同的。
这种定长和简单格式的指令编码很规则,很容易看出其机器码,例如:
add
t0,
t
0
,
s0,
s1表示
s
1
表
示
t0=
s0+
s
0
+
s1,即16号寄存器(s0)的内容和17号寄存器(s1)的内容相加,结果放到8号寄存器(t0)。
指令各字段的十进制表示为
——|—–|—–|—–|—–|——|
| 0 | 16 | 17 | 8 | 0 | 32 |
——|—–|—–|—–|—–|——|
op=0和funct=32表示这是加法,16=
s0表示第一个源操作数(rs)在16号寄存器里,17=
s
0
表
示
第
一
个
源
操
作
数
(
r
s
)
在
16
号
寄
存
器
里
,
17
=
s1表示第二个源操作数(rt)在17号寄存器里,8=$t0表示目的操作数(rd)在8号寄存器里。
把各字段写成二进制,为
——|—–|—–|—–|—–|——|
|000000|10000|10001|01000|00000|100000|
——|—–|—–|—–|—–|——|
这就是上述指令的机器码(machine code),可以看出是很有规则性的
这些格式中的每一种在SparcInstrFormat.td中都有相应的类。