LLVM 之后端篇(3):如何添加 MyRISCV 目标后端的第一条指令
Author: stormQ
Created: Sunday, 09. January 2022 11:47AM
Last Modified: Saturday, 13. August 2022 03:44PM
本文基于release/15.x
版本的 LLVM 源码,介绍了在注册目标后端MyRISCV
后如何为其添加第一条指令。从而,能够将本文的 LLVM IR 测试程序翻译成汇编代码。
测试程序 test.ll:
define i32 @test() {
ret i32 1
}
生成目标后端riscv32
的汇编代码(作为对照结果):
$ llc -march=riscv32 -filetype=asm test.ll -o test.s
test.s 的内容如下:
.text
.attribute 4, 16
.attribute 5, "rv32i2p0"
.file "test.ll"
.globl test # -- Begin function test
.p2align 2
.type test,@function
test: # @test
.cfi_startproc
# %bb.0:
addi a0, zero, 1
ret
.Lfunc_end0:
.size test, .Lfunc_end0-test
.cfi_endproc
# -- End function
.section ".note.GNU-stack","",@progbits
从上面的结果可以看出,目标后端riscv32
将 LLVM IR 语句ret i32 1
翻译成了addi a0, zero, 1
和ret
两条汇编代码。
接下来,在目标后端MyRISCV
中实现相同的功能。
LLVM 中,<Target>.td
文件用于描述目标属性,比如:支持的指令集、处理器等。同时,也作为主文件包含其它.td
文件。
对于MyRISCV
目标后端,MyRISCV.td
位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 include "llvm/Target/Target.td"
2
3 include "MyRISCVRegisterInfo.td"
4 include "MyRISCVInstrInfo.td"
5 include "MyRISCVCallingConv.td"
6
7 def : ProcessorModel<"generic-rv32", NoSchedModel, []>;
8
9 def MyRISCVInstrInfo : InstrInfo;
10
11 def MyRISCV : Target {
12 let InstructionSet = MyRISCVInstrInfo;
13 }
第 1 行,用于包含InstrInfo
、Target
等抽象记录的定义。
第 3~5 行,分别包含用于描述寄存器、指令、调用约定的.td
文件。
第 7 行,定义支持的处理器类型。抽象记录ProcessorModel
的第 1 个参数表示处理器的名称(这里为generic-rv32
,也可以是其它名称),第 2 个参数表示处理器的调度模型(为了简单起见,这里为NoSchedModel
,也可以自定义调度模型)。需要注意的是, 至少要定义一种支持的处理器类型,否则编译时会报错error: zero-size array ‘llvm::MyRISCVSubTypeKV’
。
第 9 行,定义指令的全局属性(这里使用抽象记录InstrInfo
中的默认值)。每个目标后端都必须实例化抽象记录InstrInfo
(这里为MyRISCVInstrInfo
,也可以是其它名称)。
第 11~13 行,定义目标的全局属性(这里仅指定了指令属性为MyRISCVInstrInfo
,其它字段使用抽象记录Target
中的默认值)。每个目标后端都必须实例化抽象记录Target
(这里为MyRISCV
,也可以是其它名称)。需要注意的是, 这里的实例名称MyRISCV
会影响MyRISCVGenSubtargetInfo.inc
、MyRISCVGenInstrInfo.inc
、MyRISCVGenRegisterInfo.inc
等.inc
文件的生成。比如:结构体MyRISCVGenSubtargetInfo
、MyRISCVGenInstrInfo
、MyRISCVGenRegisterInfo
等前部分的内容即为这里的实例名称。
LLVM 中,<Target>RegisterInfo.td
文件用于描述寄存器和寄存器类。寄存器类是一组具有相同大小的寄存器集合,同时也定义了寄存器分配的默认先后顺序。
对于MyRISCV
目标后端,MyRISCVRegisterInfo.td
位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 class MyRISCVReg<bits<5> enc, string name, list<string> alt = []>
2 : Register<name, alt> {
3 let HWEncoding{4-0} = enc;
4 let Namespace = "MyRISCV";
5 }
6
7 def ABIRegAltName : RegAltNameIndex;
8
9 let RegAltNameIndices = [ABIRegAltName] in {
10 def X0 : MyRISCVReg<0, "x0", ["zero"]>;
11 def X1 : MyRISCVReg<1, "x1", ["ra"]>;
12 def X2 : MyRISCVReg<2, "x2", ["sp"]>;
13 def X3 : MyRISCVReg<3, "x3", ["gp"]>;
14 def X4 : MyRISCVReg<4, "x4", ["tp"]>;
15 def X5 : MyRISCVReg<5, "x5", ["t0"]>;
16 def X6 : MyRISCVReg<6, "x6", ["t1"]>;
17 def X7 : MyRISCVReg<7, "x7", ["t2"]>;
18 def X8 : MyRISCVReg<8, "x8", ["s0", "fp"]>;
19 def X9 : MyRISCVReg<9, "x9", ["s1"]>;
20 def X10 : MyRISCVReg<10, "x10", ["a0"]>;
21 def X11 : MyRISCVReg<11, "x11", ["a1"]>;
22 def X12 : MyRISCVReg<12, "x12", ["a2"]>;
23 def X13 : MyRISCVReg<13, "x13", ["a3"]>;
24 def X14 : MyRISCVReg<14, "x14", ["a4"]>;
25 def X15 : MyRISCVReg<15, "x15", ["a5"]>;
26 def X16 : MyRISCVReg<16, "x16", ["a6"]>;
27 def X17 : MyRISCVReg<17, "x17", ["a7"]>;
28 def X18 : MyRISCVReg<18, "x18", ["s2"]>;
29 def X19 : MyRISCVReg<19, "x19", ["s3"]>;
30 def X20 : MyRISCVReg<20, "x20", ["s4"]>;
31 def X21 : MyRISCVReg<21, "x21", ["s5"]>;
32 def X22 : MyRISCVReg<22, "x22", ["s6"]>;
33 def X23 : MyRISCVReg<23, "x23", ["s7"]>;
34 def X24 : MyRISCVReg<24, "x24", ["s8"]>;
35 def X25 : MyRISCVReg<25, "x25", ["s9"]>;
36 def X26 : MyRISCVReg<26, "x26", ["s10"]>;
37 def X27 : MyRISCVReg<27, "x27", ["s11"]>;
38 def X28 : MyRISCVReg<28, "x28", ["t3"]>;
39 def X29 : MyRISCVReg<29, "x29", ["t4"]>;
40 def X30 : MyRISCVReg<30, "x30", ["t5"]>;
41 def X31 : MyRISCVReg<31, "x31", ["t6"]>;
42 }
43
44 def GPR : RegisterClass<"MyRISCV", [i32], 32, (add
45 (sequence "X%u", 10, 17),
46 (sequence "X%u", 5, 7),
47 (sequence "X%u", 28, 31),
48 (sequence "X%u", 8, 9),
49 (sequence "X%u", 18, 27),
50 (sequence "X%u", 0, 4)
51 )>;
第 1~5 行,定义抽象记录MyRISCVReg
,便于定义后面的寄存器。其第 1 个参数表示寄存器的编码(占用 5 个 bits),第 2 个参数表示寄存器的名称,第 3 个参数表示寄存器的别名列表。LLVM 中,每个寄存器定义(比如X0
)都会由 TableGen 生成唯一的标识符(比如llvm::MyRISCV::X0
,定义在MyRISCVGenRegisterInfo.inc
文件中)。而字段Namespace
的值指定了这些标识符所在的命令空间。
第 7、9、42 行(optional),定义寄存器别名选择类型ABIRegAltName
,并允许应用于X0~X31
寄存器。这意味着,生成汇编代码时,可以打印寄存器的名称(比如x0
),也可以打印寄存器的别名(比如zero
)。其实现位于llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVInstPrinter.cpp
文件中。
第 10~41 行,定义X0~X31
32 个寄存器。这些寄存器的用途可参考《riscv-spec-20191213》Table 25.1: Assembler mnemonics for RISC-V integer and floating-point registers, and their role in the first standard calling convention。
第 44~51 行,定义 32-bit 通用寄存器GPR
。抽象记录RegisterClass
的第 1 个参数表示命令空间,第 2 个参数表示该寄存器类所支持的数据类型,第 3 个参数表示对齐要求(单位:bit,通常为寄存器大小),第 4 个参数表示包含哪些寄存器以及默认的寄存器分配顺序,排放规则通常为:由调用者保存的寄存器 -> 由被调用者保存的寄存器 -> 特殊的寄存器。比如:寄存器x10~x17
用于存放函数参数,由调用者保存,所以放到靠前的位置,同时在寄存器分配时也会被优先分配。
LLVM 中,<Target>InstrInfo.td
文件用于描述指令。
对于MyRISCV
目标后端,MyRISCVInstrInfo.td
位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 class RV32 : Instruction {
2 let Size = 4;
3 let Namespace = "MyRISCV";
4 bits<32> Inst;
5 }
6
7 def MyRISCVRetFlag : SDNode<"MyRISCVISD::RET_FLAG", SDTNone,
8 [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
9
10 def simm12 : Operand<i32>, ImmLeaf<i32, [{return isInt<12>(Imm);}]>;
11
12 def ADDI : RV32 {
13 let OutOperandList = (outs GPR:$rd);
14 let InOperandList = (ins GPR:$rs1, simm12:$imm12);
15 let AsmString = "addi $rd, $rs1, $imm12";
16 let Pattern = [(set GPR:$rd, (add GPR:$rs1, simm12:$imm12))];
17 bits<5> rd;
18 bits<5> rs1;
19 bits<12> imm12;
20 let Inst{31-20} = imm12;
21 let Inst{19-15} = rs1;
22 let Inst{14-12} = 0b000;
23 let Inst{11-7} = rd;
24 let Inst{6-0} = 0b0010011;
25 }
26
27 def JALR : RV32 {
28 let OutOperandList = (outs GPR:$rd);
29 let InOperandList = (ins GPR:$rs1, simm12:$imm12);
30 let AsmString = "jalr $rd, ${imm12}(${rs1})";
31 bits<5> rd;
32 bits<5> rs1;
33 bits<12> imm12;
34 let Inst{31-20} = imm12;
35 let Inst{19-15} = rs1;
36 let Inst{14-12} = 0b000;
37 let Inst{11-7} = rd;
38 let Inst{6-0} = 0b1101111;
39 let isCall = 1;
40 }
41
42 def : InstAlias<"ret", (JALR X0, X1, 0), 4>;
43
44 def PseudoRET : RV32, PseudoInstExpansion<(JALR X0, X1, 0)> {
45 let OutOperandList = (outs);
46 let InOperandList = (ins);
47 let Pattern = [(MyRISCVRetFlag)];
48 let isPseudo = 1;
49 let isReturn = 1;
50 let isTerminator = 1;
51 }
第 1~5 行,定义抽象记录RV32
,用于定义 32-bit 指令的公共字段或值。字段Size
表示指令编码占用的字节数。字段Inst
表示指令的二进制编码。
第 7~8 行,定义SDNode
节点MyRISCVRetFlag
,其操作码为枚举值MyRISCVISD::RET_FLAG
(定义在llvm/lib/Target/MyRISCV/MyRISCVISelLowering.h
文件中),指令选择过程中需要用到。
第 10 行,定义 12-bit 的有符号立即数,数据类型为i32
。
第 12~25 行,定义具象记录ADDI
,用于表示addi
指令。
字段OutOperandList
表示指令的输出,(outs GPR:$rd)
表示指令的输出是一个寄存器类GPR
中的寄存器,即用$rd
存放addi
指令的运算结果。另外,需要定义同名的字段即bits<5> rd;
。而let Inst{11-7} = rd;
表示该寄存器在指令编码中占用低 7~11 bit(共 5 个 bits)。
字段InOperandList
表示指令的输入,(ins GPR:$rs1, simm12:$imm12)
表示指令的第 1 个操作数是寄存器类GPR
中的寄存器(用$rs1
表示),第 2 个操作数是类型为simm12
的立即数(用$imm12
表示)。同样地,也需要为这两个操作数定义同名的字段,以及在指令编码中占用哪些 bits。
字段AsmString
表示指令的汇编代码格式,比如:addi a0, zero, 1
。
字段Pattern
表示指令的匹配模式,[(set GPR:$rd, (add GPR:$rs1, simm12:$imm12))]
表示如果SDNode
节点为add
(对应的的操作码为ISD::ADD
),运算结果和第 1 个操作数都为GPR
中的寄存器,第 2 个操作数为simm12
类型的立即数,那么会匹配为addi
指令。
第 20 ~24 行表示指令编码中字段与 bit 位之间的对应关系。可参考《riscv-spec-20191213》Table 24.2: Instruction listing for RISC-V。
第 27~40 行,定义具象记录JALR
,用于表示jalr
指令。字段isCall
的值为 1 表示该指令用于函数调用。
第 42 行,定义汇编代码jalr x0, 0(x1)
的别名为ret
,并且优先打印该别名(由于抽象记录InstAlias
的第三个参数表示优先级,默认值为 1,这里值为 4,所以优先打印别名)。
第 44~52 行,定义伪指令PseudoRET
。
PseudoInstExpansion<(JALR X0, X1, 0)
表示伪指令PseudoRET
实际对应的指令为JALR X0, X1, 0
。
let Pattern = [(MyRISCVRetFlag)];
表示SDNode
节点为MyRISCVRetFlag
时会匹配到该伪指令。
let isPseudo = 1;
表示该指令是伪指令。
let isReturn = 1;
表示该指令会导致从函数返回。
let isTerminator = 1;
表示该指令必须作为基本块的最后一条指令。
LLVM 中,<Target>CallingConv.td
文件用于描述程序调用约定。
对于MyRISCV
目标后端,MyRISCVCallingConv.td
位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 def RetCC_MyRISCV : CallingConv<[
2 CCIfType<[i1, i8, i16, i32], CCAssignToReg<[X10, X11]>>,
3 CCIfType<[i32], CCAssignToStack<4, 4>>,
4 ]>;
5
6 def CC_MyRISCV : CallingConv<[
7 CCIfType<[i1, i8, i16, i32], CCAssignToReg<[X10, X11, X12, X13, X14, X15, X16, X17]>>,
8 CCIfType<[i32], CCAssignToStack<4, 4>>,
9 ]>;
10
11 def CC_Save : CalleeSavedRegs<(add X2, X8, X9, (sequence "X%u", 18, 27))>;
第 1~4 行,定义具象记录RetCC_MyRISCV
,用于描述函数返回值如何返回(对应MyRISCVGenCallingConv.inc
文件中的RetCC_MyRISCV()
函数)。
CCIfType<[i1, i8, i16, i32], CCAssignToReg<[X10, X11]>>
表示如果返回值的数据类型为i1
、i8
、i16
或i32
,那么可以将返回值保存到寄存器x10
或x11
中。
CCIfType<[i32], CCAssignToStack<4, 4>>
表示返回值需要保存到栈内存中时,存放规则为每个值占用 4 字节并以 4 字节进行内存对齐。
第 6~9 行,定义具象记录CC_MyRISCV
,用于描述函数参数如何传递(对应MyRISCVGenCallingConv.inc
文件中的CC_MyRISCV()
函数)。
CCIfType<[i1, i8, i16, i32], CCAssignToReg<[X10, X11, X12, X13, X14, X15, X16, X17]>>
表示如果函数参数的数据类型为i1
、i8
、i16
或i32
,那么可以将函数参数保存到寄存器x10~x17
中。
CCIfType<[i32], CCAssignToStack<4, 4>>
表示函数参数需要保存到栈内存中时,存放规则为每个值占用 4 字节并以 4 字节进行内存对齐。
第 11 行,定义具象记录CC_Save
,用于描述由被调用者保存的寄存器,包括x2
、x8
、x9
和x18~x27
(对应MyRISCVGenRegisterInfo.inc
文件中的CC_Save_SaveList
数组)。
step 0: 注册目标后端 MyRISCV
具体步骤参考上一篇《LLVM 之后端篇(2):如何扩展 llc 的目标后端》。
step 1: MyRISCV.h
MyRISCV.h
用于声明全局函数,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCV_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCV_H
3
4 namespace llvm {
5
6 class MyRISCVTargetMachine;
7 class FunctionPass;
8
9 FunctionPass *createMyRISCVISelDag(MyRISCVTargetMachine &TM);
10
11 } // end namespace llvm
12
13 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCV_H
第 6~7 行,前向声明MyRISCVTargetMachine
和FunctionPass
类,以减少头文件依赖。
第 9 行,全局函数llvm::createMyRISCVISelDag()
用于创建目标后端MyRISCV
的指令选择 Pass,其实现位于llvm/lib/Target/MyRISCV/MyRISCVISelDAGToDAG.cpp
文件。
step 2: MyRISCVTargetMachine.h
MyRISCVTargetMachine.h
用于声明继承自llvm::LLVMTargetMachine
的派生类MyRISCVTargetMachine
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVTARGETMACHINE_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVTARGETMACHINE_H
3
4 #include "MyRISCVSubtarget.h"
5 #include "llvm/Target/TargetMachine.h"
6
7 namespace llvm {
8
9 class MyRISCVTargetMachine : public LLVMTargetMachine {
10 MyRISCVSubtarget Subtarget;
11 std::unique_ptr<TargetLoweringObjectFile> TLOF;
12
13 public:
14 MyRISCVTargetMachine(const Target &T, const Triple &TT, StringRef CPU ,
15 StringRef FS, const TargetOptions &Options,
16 Optional<Reloc::Model> RM, Optional<CodeModel::M odel> CM,
17 CodeGenOpt::Level OL, bool JIT);
18
19 const MyRISCVSubtarget *getSubtargetImpl(const Function &F) const ove rride {
20 return &Subtarget;
21 }
22
23 const MyRISCVSubtarget *getSubtargetImpl() const { return &Subtarget; }
24
25 TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
26
27 TargetLoweringObjectFile *getObjFileLowering() const override {
28 return TLOF.get();
29 }
30 };
31
32 } // end namespace llvm
33
34 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVTARGETMACHINE_H
第 19~21、25、27~29 行,每个目标后端至少需要覆写基类llvm::LLVMTargetMachine
中的这 3 个虚函数。
第 23 行,自定义公有成员函数getSubtargetImpl()
,以便于其它类获取MyRISCVTargetMachine::Subtarget
对象。
step 3: MyRISCVTargetMachine.cpp
llvm/lib/Target/MyRISCV/MyRISCVTargetMachine.cpp 的内容如下:
1 #include "MyRISCVTargetMachine.h"
2 #include "MyRISCV.h"
3 #include "TargetInfo/MyRISCVTargetInfo.h"
4 #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
5 #include "llvm/CodeGen/TargetPassConfig.h"
6 #include "llvm/MC/TargetRegistry.h"
7
8 using namespace llvm;
9
10 namespace {
11
12 class MyRISCVPassConfig : public TargetPassConfig {
13 public:
14 MyRISCVPassConfig(MyRISCVTargetMachine &TM, PassManagerBase &PM)
15 : TargetPassConfig(TM, PM) {}
16
17 bool addInstSelector() override {
18 addPass(createMyRISCVISelDag(getMyRISCVTargetMachine()));
19 return false;
20 }
21
22 private:
23 MyRISCVTargetMachine &getMyRISCVTargetMachine() const {
24 return getTM<MyRISCVTargetMachine>();
25 }
26 };
27
28 StringRef computeDataLayout(const Triple &TT) {
29 assert(TT.isArch32Bit() && "Only RV32 is currently supported!");
30 return "e-m:e-p:32:32-i64:64-n32-S128";
31 }
32
33 Reloc::Model getEffectiveRelocModel(Optional<Reloc::Model> RM) {
34 return RM.hasValue() ? *RM : Reloc::Static;
35 }
36
37 } // end anonymous namespace
38
39 MyRISCVTargetMachine::MyRISCVTargetMachine(const Target &T, const Triple &TT,
40 StringRef CPU, StringRef FS,
41 const TargetOptions &Options,
42 Optional<Reloc::Model> RM,
43 Optional<CodeModel::Model> CM,
44 CodeGenOpt::Level OL, bool JIT)
45 : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options,
46 getEffectiveRelocModel(RM),
47 getEffectiveCodeModel(CM, CodeModel::Small), OL),
48 Subtarget(TT, CPU, CPU, FS, *this),
49 TLOF(std::make_unique<TargetLoweringObjectFileELF>()) {
50 initAsmInfo();
51 }
52
53 TargetPassConfig *MyRISCVTargetMachine::createPassConfig(PassManagerBase &PM) {
54 return new MyRISCVPassConfig(*this, PM);
55 }
56
57 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeMyRISCVTarget() {
58 RegisterTargetMachine<MyRISCVTargetMachine> X(getMyRISCV32Target());
59 }
第 12~26 行,定义继承自llvm::TargetPassConfig
的派生类MyRISCVPassConfig
,用于定制代码生成流水线中的各种 Pass。
函数addInstSelector()
用于安装目标后端的指令选择 Pass(这里为MyRISCVDAGToDAGISel
类,定义在llvm/lib/Target/MyRISCV/MyRISCVISelDAGToDAG.cpp
文件中)。
第 50 行,函数LLVMTargetMachine::initAsmInfo()
用于执行一些初始化操作。该函数所依赖的对象(比如:MCAsmInfo
)在llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVMCTargetDesc.cpp
文件中进行创建并注册。
step 4: MyRISCVSubtarget.h
MyRISCVSubtarget.h
用于声明继承自llvm::MyRISCVGenSubtargetInfo
(位于MyRISCVGenSubtargetInfo.inc
文件)的派生类MyRISCVSubtarget
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVSUBTARGET_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVSUBTARGET_H
3
4 #include "MyRISCVFrameLowering.h"
5 #include "MyRISCVISelLowering.h"
6 #include "MyRISCVInstrInfo.h"
7 #include "MyRISCVRegisterInfo.h"
8 #include "llvm/CodeGen/TargetSubtargetInfo.h"
9
10 #define GET_SUBTARGETINFO_HEADER
11 #include "MyRISCVGenSubtargetInfo.inc"
12
13 namespace llvm {
14
15 class MyRISCVTargetMachine;
16
17 class MyRISCVSubtarget : public MyRISCVGenSubtargetInfo {
18 MyRISCVTargetLowering TLInfo;
19 MyRISCVFrameLowering FrameLowering;
20 MyRISCVInstrInfo InstrInfo;
21 MyRISCVRegisterInfo RegInfo;
22
23 public:
24 MyRISCVSubtarget(const Triple &TT, StringRef CPU, StringRef TuneCPU,
25 StringRef FS, MyRISCVTargetMachine &TM);
26
27 const MyRISCVTargetLowering *getTargetLowering() const override {
28 return &TLInfo;
29 }
30
31 const MyRISCVFrameLowering *getFrameLowering() const override {
32 return &FrameLowering;
33 }
34
35 const MyRISCVInstrInfo *getInstrInfo() const override { return &InstrInfo; }
36
37 const MyRISCVRegisterInfo *getRegisterInfo() const override {
38 return &RegInfo;
39 }
40
41 private:
42 void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
43
44 MyRISCVSubtarget &initializeSubtargetDependencies(const Triple &TT,
45 StringRef CPU,
46 StringRef FS);
47 };
48
49 } // end namespace llvm
50
51 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVSUBTARGET_H
第 27~39 行,每个目标后端至少需要覆写基类llvm::TargetSubtargetInfo
中的这 4 个虚函数。
第 42 行,该函数是由 TableGen 生成的,其实现位于MyRISCVGenSubtargetInfo.inc
文件,这里需要提供其函数声明。
第 44~46 行,自定义私有成员函数initializeSubtargetDependencies()
,用于初始化MyRISCVSubtarget
。
step 5: MyRISCVSubtarget.cpp
llvm/lib/Target/MyRISCV/MyRISCVSubtarget.cpp 的内容如下:
1 #include "MyRISCVSubtarget.h"
2 #include "MyRISCVTargetMachine.h"
3
4 using namespace llvm;
5
6 #define DEBUG_TYPE "riscv-subtarget"
7
8 #define GET_SUBTARGETINFO_MC_DESC
9 #define GET_SUBTARGETINFO_TARGET_DESC
10 #define GET_SUBTARGETINFO_CTOR
11 #include "MyRISCVGenSubtargetInfo.inc"
12
13 MyRISCVSubtarget::MyRISCVSubtarget(const Triple &TT, StringRef CPU,
14 StringRef TuneCPU, StringRef FS,
15 MyRISCVTargetMachine &TM)
16 : MyRISCVGenSubtargetInfo(TT, CPU, TuneCPU, FS), TLInfo(TM),
17 FrameLowering(initializeSubtargetDependencies(TT, CPU, FS)),
18 InstrInfo(*this), RegInfo(getHwMode()) {}
19
20 MyRISCVSubtarget &
21 MyRISCVSubtarget::initializeSubtargetDependencies(const Triple &TT,
22 StringRef CPU, StringRef FS) {
23 if (CPU.empty()) {
24 assert(TT.isArch32Bit() && "Only RV32 is currently supported!");
25 CPU = "generic-rv32";
26 }
27 ParseSubtargetFeatures(CPU, CPU, FS);
28 return *this;
29 }
第 25 行,设置处理器的默认名称为generic-rv32
,对应MyRISCV.td
中某个ProcessorModel
具象记录的第一个参数的值。
第 27 行,该函数是由 TableGen 生成的,其实现位于MyRISCVGenSubtargetInfo.inc
文件。
step 6: MyRISCVRegisterInfo.h
MyRISCVRegisterInfo.h
用于声明继承自llvm::MyRISCVGenRegisterInfo
(位于MyRISCVGenRegisterInfo.inc
文件)的派生类MyRISCVRegisterInfo
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVREGISTERINFO_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVREGISTERINFO_H
3
4 #include "llvm/CodeGen/TargetRegisterInfo.h"
5
6 #define GET_REGINFO_HEADER
7 #include "MyRISCVGenRegisterInfo.inc"
8
9 namespace llvm {
10
11 class MyRISCVRegisterInfo : public MyRISCVGenRegisterInfo {
12 public:
13 explicit MyRISCVRegisterInfo(unsigned HwMode);
14
15 const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
16
17 BitVector getReservedRegs(const MachineFunction &MF) const override;
18
19 void eliminateFrameIndex(MachineBasicBlock::iterator MI, int SPAdj,
20 unsigned FIOperandNum,
21 RegScavenger *RS = nullptr) const override;
22
23 Register getFrameRegister(const MachineFunction &MF) const override;
24 };
25
26 } // end namespace llvm
27
28 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVREGISTERINFO_H
第 6~7 行,用于包含基类llvm::MyRISCVGenRegisterInfo
的声明。
第 15~23 行,每个目标后端至少需要覆写基类llvm::TargetRegisterInfo
中的这 4 个虚函数。
函数getCalleeSavedRegs()
,用于返回由被调用者保存的所有寄存器。
函数getReservedRegs()
,用于返回保留的寄存器。这些保留的寄存器是不可分配的(比如:虽然栈指针寄存器X2
属于寄存器类GPR
,但如果将X2
视作保留寄存器,那么在寄存器分配时不会分配该寄存器),并且总被认为是live
的。
函数getFrameRegister()
,用于返回保存帧指针的寄存器。
step 7: MyRISCVRegisterInfo.cpp
llvm/lib/Target/MyRISCV/MyRISCVRegisterInfo.cpp 的内容如下:
1 #include "MyRISCVRegisterInfo.h"
2 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
3 #include "MyRISCVFrameLowering.h"
4 #include "MyRISCVSubtarget.h"
5 #include "llvm/CodeGen/MachineFunction.h"
6
7 #define GET_REGINFO_TARGET_DESC
8 #include "MyRISCVGenRegisterInfo.inc"
9
10 using namespace llvm;
11
12 MyRISCVRegisterInfo::MyRISCVRegisterInfo(unsigned HwMode)
13 : MyRISCVGenRegisterInfo(MyRISCV::X1, /*DwarfFlavour*/ 0, /*EHFlavor*/ 0,
14 /*PC*/ 0, HwMode) {}
15
16 const MCPhysReg *
17 MyRISCVRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
18 return CC_Save_SaveList;
19 }
20
21 BitVector
22 MyRISCVRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
23 BitVector Reserved(getNumRegs());
24 Reserved.set(MyRISCV::X0);
25 Reserved.set(MyRISCV::X2);
26 Reserved.set(MyRISCV::X3);
27 Reserved.set(MyRISCV::X4);
28 return Reserved;
29 }
30
31 void MyRISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator MI,
32 int SPAdj, unsigned FIOperandNum,
33 RegScavenger *RS) const {}
34
35 Register
36 MyRISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
37 return MyRISCV::X8;
38 }
step 8: MyRISCVMCInstLower.h
MyRISCVMCInstLower.h
用于声明类MyRISCVMCInstLower
,该类负责将机器指令的表示形式从MachineInstr
转换为轻量级的MCInst
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVMCINSTLOWER_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVMCINSTLOWER_H
3
4 namespace llvm {
5
6 class MCContext;
7 class AsmPrinter;
8 class MachineInstr;
9 class MachineOperand;
10 class MCInst;
11 class MCOperand;
12
13 class MyRISCVMCInstLower {
14 MCContext &Ctx;
15 AsmPrinter &Printer;
16
17 public:
18 MyRISCVMCInstLower(MCContext &Ctx, AsmPrinter &Printer);
19
20 void Lower(const MachineInstr *MI, MCInst &OutMI) const;
21
22 private:
23 bool LowerOperand(const MachineOperand &MO, MCOperand &MCOp) const;
24 };
25
26 } // end namespace llvm
27
28 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVMCINSTLOWER_H
step 9: MyRISCVMCInstLower.cpp
llvm/lib/Target/MyRISCV/MyRISCVMCInstLower.cpp 的内容如下:
1 #include "MyRISCVMCInstLower.h"
2 #include "llvm/CodeGen/AsmPrinter.h"
3 #include "llvm/CodeGen/MachineInstr.h"
4 #include "llvm/CodeGen/MachineOperand.h"
5 #include "llvm/MC/MCContext.h"
6 #include "llvm/MC/MCExpr.h"
7 #include "llvm/MC/MCInst.h"
8
9 using namespace llvm;
10
11 MyRISCVMCInstLower::MyRISCVMCInstLower(MCContext &Ctx, AsmPrinter &Printer)
12 : Ctx(Ctx), Printer(Printer) {}
13
14 void MyRISCVMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
15 OutMI.setOpcode(MI->getOpcode());
16
17 for (const MachineOperand &MO : MI->operands()) {
18 MCOperand MCOp;
19 if (LowerOperand(MO, MCOp)) {
20 OutMI.addOperand(MCOp);
21 }
22 }
23 }
24
25 bool MyRISCVMCInstLower::LowerOperand(const MachineOperand &MO,
26 MCOperand &MCOp) const {
27 switch (MO.getType()) {
28 case MachineOperand::MO_Register:
29 if (MO.isImplicit()) {
30 return false;
31 }
32 MCOp = MCOperand::createReg(MO.getReg());
33 break;
34 case MachineOperand::MO_Immediate:
35 MCOp = MCOperand::createImm(MO.getImm());
36 break;
37 default:
38 llvm_unreachable("Unknown operand type!");
39 }
40 return true;
41 }
第 14~23 行,函数Lower()
用于将机器指令的表示形式从MachineInstr
转换为轻量级的MCInst
。其中,第 15 行用于设置MCInst
的操作码,第 17~22 行用于设置MCInst
的所有操作数。
第 25~41 行,函数LowerOperand()
用于将机器指令操作数的表示形式从MachineOperand
转换为MCOperand
。目前仅支持操作数为寄存器
或立即数
的情形。
step 10: MyRISCVISelLowering.h
MyRISCVRegisterInfo.h
用于声明继承自llvm::TargetLowering
的派生类MyRISCVTargetLowering
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVISELLOWERING_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVISELLOWERING_H
3
4 #include "llvm/CodeGen/SelectionDAG.h"
5 #include "llvm/CodeGen/TargetLowering.h"
6
7 namespace llvm {
8
9 class MyRISCVSubtarget;
10 class MyRISCVTargetMachine;
11
12 namespace MyRISCVISD {
13
14 enum NodeType {
15 FIRST_NUMBER = ISD::BUILTIN_OP_END,
16 RET_FLAG,
17 };
18
19 } // namespace MyRISCVISD
20
21 class MyRISCVTargetLowering : public TargetLowering {
22 const MyRISCVSubtarget &Subtarget;
23
24 public:
25 explicit MyRISCVTargetLowering(MyRISCVTargetMachine &TM);
26
27 const char *getTargetNodeName(unsigned Opcode) const override;
28
29 SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv,
30 bool IsVarArg,
31 const SmallVectorImpl<ISD::InputArg> &Ins,
32 const SDLoc &DL, SelectionDAG &DAG,
33 SmallVectorImpl<SDValue> &InVals) const override;
34
35 SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
36 const SmallVectorImpl<ISD::OutputArg> &Outs,
37 const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
38 SelectionDAG &DAG) const override;
39 };
40
41 } // end namespace llvm
42
43 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVISELLOWERING_H
第 16 行,定义目标后端MyRISCV
特有的操作码MyRISCVISD::RET_FLAG
。这意味着,在选择机器指令(即pre-isel
阶段)之前SDNode
节点的操作码可以是MyRISCVISD::RET_FLAG
。需要注意的是, 这些特定于目标的操作码的值应该大于ISD::BUILTIN_OP_END
。
第 27 行,该函数用于返回特定于目标的SDNode
节点的名称。
第 29~33 行,该函数用于 Lowering 函数参数。即将函数参数转换为何种SDNode
节点。
第 35~38 行,该函数用于 Lowering 函数返回值。即将函数返回值转换何种SDNode
节点。
step 11: MyRISCVISelLowering.cpp
llvm/lib/Target/MyRISCV/MyRISCVISelLowering.cpp 的内容如下:
1 #include "MyRISCVISelLowering.h"
2 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
3 #include "MyRISCVSubtarget.h"
4 #include "MyRISCVTargetMachine.h"
5 #include "llvm/CodeGen/CallingConvLower.h"
6
7 using namespace llvm;
8
9 #include "MyRISCVGenCallingConv.inc"
10
11 MyRISCVTargetLowering::MyRISCVTargetLowering(MyRISCVTargetMachine &TM)
12 : TargetLowering(TM), Subtarget(*TM.getSubtargetImpl()) {
13 addRegisterClass(MVT::i32, &MyRISCV::GPRRegClass);
14 computeRegisterProperties(Subtarget.getRegisterInfo());
15 }
16
17 const char *MyRISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
18 switch (Opcode) {
19 case MyRISCVISD::RET_FLAG:
20 return "MyRISCVISD::RET_FLAG";
21 default:
22 return nullptr;
23 }
24 }
25
26 SDValue MyRISCVTargetLowering::LowerFormalArguments(
27 SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
28 const SmallVectorImpl<ISD::InputArg> &Ins, const SDLoc &DL,
29 SelectionDAG &DAG, SmallVectorImpl<SDValue> &InVals) const {
30 SmallVector<CCValAssign, 16> ArgLocs;
31 MachineFunction &MF = DAG.getMachineFunction();
32 CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
33 CCInfo.AnalyzeFormalArguments(Ins, CC_MyRISCV);
34
35 for (unsigned i = 0, e = ArgLocs.size(); i < e; ++i) {
36 CCValAssign &VA = ArgLocs[i];
37 }
38
39 return Chain;
40 }
41
42 SDValue
43 MyRISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
44 bool IsVarArg,
45 const SmallVectorImpl<ISD::OutputArg> &Outs,
46 const SmallVectorImpl<SDValue> &OutVals,
47 const SDLoc &DL, SelectionDAG &DAG) const {
48 SmallVector<CCValAssign, 16> RVLocs;
49
50 CCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs,
51 *DAG.getContext());
52 CCInfo.AnalyzeReturn(Outs, RetCC_MyRISCV);
53
54 SDValue Glue;
55 SmallVector<SDValue, 4> RetOps(1, Chain);
56
57 for (unsigned i = 0, e = RVLocs.size(); i < e; ++i) {
58 CCValAssign &VA = RVLocs[i];
59 assert(VA.isRegLoc() && "Can only return in registers!");
60 Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), OutVals[i], Glue);
61 Glue = Chain.getValue(1);
62 RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
63 }
64
65 RetOps[0] = Chain;
66
67 if (Glue.getNode()) {
68 RetOps.push_back(Glue);
69 }
70
71 return DAG.getNode(MyRISCVISD::RET_FLAG, DL, MVT::Other, RetOps);
72 }
第 26~40 行,由于本文的测试程序没有函数参数,因此,这里LowerFormalArguments()
只是返回参数Chain
。
第 42~72 行,将函数返回值转换为操作码为MyRISCVISD::RET_FLAG
的SDNode
节点MyRISCVRetFlag
(定义在llvm/lib/Target/MyRISCV/MyRISCVInstrInfo.td
文件中)。该节点的第 1 个操作数总是为chain-edge
(对应第 65 行);最后 1 个操作数可能是glue-edge
(对应第 67~69 行);中间的操作数为regular-edge
(对应第 62 行),表示用于保存函数返回值的寄存器。
step 12: MyRISCVISelDAGToDAG.cpp
MyRISCVISelDAGToDAG.cpp
用于定义继承自llvm::SelectionDAGISel
的派生类MyRISCVDAGToDAGISel
,该类用于选择机器指令,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
2 #include "MyRISCV.h"
3 #include "MyRISCVTargetMachine.h"
4 #include "llvm/CodeGen/SelectionDAGISel.h"
5
6 using namespace llvm;
7
8 namespace {
9
10 class MyRISCVDAGToDAGISel : public SelectionDAGISel {
11 public:
12 explicit MyRISCVDAGToDAGISel(MyRISCVTargetMachine &TM)
13 : SelectionDAGISel(TM) {}
14
15 StringRef getPassName() const override {
16 return "MyRISCV DAG->DAG Pattern Instruction Selection";
17 }
18
19 void Select(SDNode *N) override;
20
21 private:
22 #include "MyRISCVGenDAGISel.inc"
23 };
24
25 } // end anonymous namespace
26
27 void MyRISCVDAGToDAGISel::Select(SDNode *N) {
28 SDLoc DL(N);
29
30 switch (N->getOpcode()) {
31 case ISD::Constant: {
32 int64_t Imm = cast<ConstantSDNode>(N)->getSExtValue();
33 if (-2048 <= Imm && Imm <= 2047) {
34 SDValue SDImm = CurDAG->getTargetConstant(Imm, DL, MVT::i32);
35 SDValue SrcReg = CurDAG->getRegister(MyRISCV::X0, MVT::i32);
36 SDNode *Result =
37 CurDAG->getMachineNode(MyRISCV::ADDI, DL, MVT::i32, SrcReg, SDImm);
38 ReplaceNode(N, Result);
39 return;
40 }
41 }
42 }
43
44 SelectCode(N);
45 }
46
47 FunctionPass *llvm::createMyRISCVISelDag(MyRISCVTargetMachine &TM) {
48 return new MyRISCVDAGToDAGISel(TM);
49 }
第 15~17 行,返回指令选择 Pass——MyRISCVDAGToDAGISel
的名称。
第 27~45 行,函数Select()
用于选择机器指令。其中,SelectCode()
函数是由 TableGen 根据.td
文件中定义的 Patterns 自动生成的,在调用该函数之前,我们可以对某些SDNode
节点进行特殊处理(对应第 28~40 行)。由于RISCV
中没有数据传送指令。因此,这里使用addi
指令将立即数(仅限于大小范围为[-2048, 2047]
的整数)拷贝到寄存器中。
step 13: MyRISCVInstrInfo.h
MyRISCVInstrInfo.h
用于声明继承自llvm::MyRISCVGenInstrInfo
(位于MyRISCVGenInstrInfo.inc
文件)的派生类MyRISCVInstrInfo
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVINSTRINFO_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVINSTRINFO_H
3
4 #include "llvm/CodeGen/TargetInstrInfo.h"
5
6 #define GET_INSTRINFO_HEADER
7 #include "MyRISCVGenInstrInfo.inc"
8
9 namespace llvm {
10
11 class MyRISCVSubtarget;
12
13 class MyRISCVInstrInfo : public MyRISCVGenInstrInfo {
14 const MyRISCVSubtarget &STI;
15
16 public:
17 explicit MyRISCVInstrInfo(MyRISCVSubtarget &STI);
18 };
19
20 } // end namespace llvm
21
22 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVINSTRINFO_H
step 14: MyRISCVInstrInfo.cpp
llvm/lib/Target/MyRISCV/MyRISCVInstrInfo.cpp 的内容如下:
1 #include "MyRISCVInstrInfo.h"
2 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
3 #include "MyRISCVSubtarget.h"
4
5 using namespace llvm;
6
7 #define GET_INSTRINFO_CTOR_DTOR
8 #include "MyRISCVGenInstrInfo.inc"
9
10 MyRISCVInstrInfo::MyRISCVInstrInfo(MyRISCVSubtarget &STI)
11 : MyRISCVGenInstrInfo(), STI(STI) {}
step 15: MyRISCVFrameLowering.h
MyRISCVInstrInfo.h
用于声明继承自llvm::TargetFrameLowering
的派生类MyRISCVFrameLowering
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MYRISCVFRAMELOWERING_H
2 #define LLVM_LIB_TARGET_MYRISCV_MYRISCVFRAMELOWERING_H
3
4 #include "llvm/CodeGen/TargetFrameLowering.h"
5
6 namespace llvm {
7
8 class MyRISCVSubtarget;
9
10 class MyRISCVFrameLowering : public TargetFrameLowering {
11 const MyRISCVSubtarget &STI;
12
13 public:
14 explicit MyRISCVFrameLowering(const MyRISCVSubtarget &STI)
15 : TargetFrameLowering(StackGrowsDown,
16 /*StackAlignment=*/Align(16),
17 /*LocalAreaOffset=*/0),
18 STI(STI) {}
19
20 void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
21
22 void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
23
24 bool hasFP(const MachineFunction &MF) const override;
25 };
26
27 } // end namespace llvm
28
29 #endif // LLVM_LIB_TARGET_MYRISCV_MYRISCVFRAMELOWERING_H
第 20~22 行,每个目标后端至少需要覆写基类llvm::TargetFrameLowering
中的这 2 个虚函数。
函数emitPrologue()
,用于分配栈内存。
函数emitEpilogue()
,用于回收栈内存。
第 24 行,函数hasFP()
用于返回MF
中是否应该有帧指针寄存器。
step 16: MyRISCVFrameLowering.cpp
llvm/lib/Target/MyRISCV/MyRISCVFrameLowering.cpp 的内容如下:
1 #include "MyRISCVFrameLowering.h"
2 #include "llvm/CodeGen/MachineFunction.h"
3
4 using namespace llvm;
5
6 void MyRISCVFrameLowering::emitPrologue(MachineFunction &MF,
7 MachineBasicBlock &MBB) const {}
8
9 void MyRISCVFrameLowering::emitEpilogue(MachineFunction &MF,
10 MachineBasicBlock &MBB) const {}
11
12 bool MyRISCVFrameLowering::hasFP(const MachineFunction &MF) const {
13 return false;
14 }
step 17: MyRISCVAsmPrinter.cpp
MyRISCVAsmPrinter.cpp
用于定义继承自llvm::AsmPrinter
的派生类MyRISCVAsmPrinter
,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
2 #include "MyRISCVMCInstLower.h"
3 #include "TargetInfo/MyRISCVTargetInfo.h"
4 #include "llvm/CodeGen/AsmPrinter.h"
5 #include "llvm/MC/MCStreamer.h"
6 #include "llvm/MC/TargetRegistry.h"
7
8 using namespace llvm;
9
10 namespace {
11
12 class MyRISCVAsmPrinter : public AsmPrinter {
13 MyRISCVMCInstLower MCInstLowering;
14
15 public:
16 MyRISCVAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
17 : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this) {
18 }
19
20 void emitInstruction(const MachineInstr *MI) override;
21
22 private:
23 bool emitPseudoExpansionLowering(MCStreamer &OutStreamer,
24 const MachineInstr *MI);
25 };
26
27 } // end anonymous namespace
28
29 #include "MyRISCVGenMCPseudoLowering.inc"
30
31 void MyRISCVAsmPrinter::emitInstruction(const MachineInstr *MI) {
32 if (emitPseudoExpansionLowering(*OutStreamer, MI)) {
33 return;
34 }
35
36 MCInst TmpInst;
37 MCInstLowering.Lower(MI, TmpInst);
38 EmitToStreamer(*OutStreamer, TmpInst);
39 }
40
41 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeMyRISCVAsmPrinter() {
42 RegisterAsmPrinter<MyRISCVAsmPrinter> X(getMyRISCV32Target());
43 }
第 23~24 行,函数emitPseudoExpansionLowering()
由 TableGen 生成(定义在MyRISCVGenMCPseudoLowering.inc
文件)。该函数用于处理伪指令。
第 31~39 行,每个目标后端至少需要覆写基类llvm::AsmPrinter
中的虚函数emitInstruction()
。该函数用于将机器指令的表示形式转换为MCInst
。
step 18: CMakeLists.txt
llvm/lib/Target/MyRISCV/CMakeLists.txt 的内容如下:
add_llvm_component_group(MyRISCV)
set(LLVM_TARGET_DEFINITIONS MyRISCV.td)
tablegen(LLVM MyRISCVGenAsmWriter.inc -gen-asm-writer)
tablegen(LLVM MyRISCVGenCallingConv.inc -gen-callingconv)
tablegen(LLVM MyRISCVGenDAGISel.inc -gen-dag-isel)
tablegen(LLVM MyRISCVGenInstrInfo.inc -gen-instr-info)
tablegen(LLVM MyRISCVGenMCPseudoLowering.inc -gen-pseudo-lowering)
tablegen(LLVM MyRISCVGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM MyRISCVGenSubtargetInfo.inc -gen-subtarget)
add_public_tablegen_target(MyRISCVCommonTableGen)
set(sources
MyRISCVTargetMachine.cpp
MyRISCVISelDAGToDAG.cpp
MyRISCVAsmPrinter.cpp
MyRISCVSubtarget.cpp
MyRISCVISelLowering.cpp
MyRISCVFrameLowering.cpp
MyRISCVInstrInfo.cpp
MyRISCVRegisterInfo.cpp
MyRISCVMCInstLower.cpp
)
add_llvm_target(MyRISCVCodeGen ${sources}
LINK_COMPONENTS
AsmPrinter
Core
CodeGen
MC
MyRISCVDesc
MyRISCVInfo
SelectionDAG
Support
Target
ADD_TO_COMPONENT
MyRISCV
)
add_subdirectory(MCTargetDesc)
add_subdirectory(TargetInfo)
step 19: MyRISCVMCTargetDesc.h
llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVMCTargetDesc.h 的内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCTARGETDESC_H
2 #define LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCTARGETDESC_H
3
4 #define GET_REGINFO_ENUM
5 #include "MyRISCVGenRegisterInfo.inc"
6
7 #define GET_INSTRINFO_ENUM
8 #include "MyRISCVGenInstrInfo.inc"
9
10 #define GET_SUBTARGETINFO_ENUM
11 #include "MyRISCVGenSubtargetInfo.inc"
12
13 #endif // LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCTARGETDESC_H
step 20: MyRISCVMCTargetDesc.cpp
llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVMCTargetDesc.cpp 的内容如下:
1 #include "MyRISCVMCTargetDesc.h"
2 #include "MyRISCVInstPrinter.h"
3 #include "MyRISCVMCAsmInfo.h"
4 #include "TargetInfo/MyRISCVTargetInfo.h"
5 #include "llvm/MC/MCInstrInfo.h"
6 #include "llvm/MC/MCRegisterInfo.h"
7 #include "llvm/MC/MCSubtargetInfo.h"
8 #include "llvm/MC/TargetRegistry.h"
9
10 #define GET_INSTRINFO_MC_DESC
11 #include "MyRISCVGenInstrInfo.inc"
12
13 #define GET_REGINFO_MC_DESC
14 #include "MyRISCVGenRegisterInfo.inc"
15
16 #define GET_SUBTARGETINFO_MC_DESC
17 #include "MyRISCVGenSubtargetInfo.inc"
18
19 using namespace llvm;
20
21 namespace {
22
23 MCInstrInfo *createMyRISCVMCInstrInfo() {
24 MCInstrInfo *X = new MCInstrInfo();
25 InitMyRISCVMCInstrInfo(X);
26 return X;
27 }
28
29 MCRegisterInfo *createMyRISCVMCRegisterInfo(const Triple &TT) {
30 MCRegisterInfo *X = new MCRegisterInfo();
31 InitMyRISCVMCRegisterInfo(X, MyRISCV::X1);
32 return X;
33 }
34
35 MCAsmInfo *createMyRISCVMCAsmInfo(const MCRegisterInfo &MRI, const Triple &TT,
36 const MCTargetOptions &Options) {
37 return new MyRISCVMCAsmInfo(TT);
38 }
39
40 MCSubtargetInfo *createMyRISCVMCSubtargetInfo(const Triple &TT, StringRef CPU,
41 StringRef FS) {
42 if (CPU.empty()) {
43 assert(TT.isArch32Bit() && "Only RV32 is currently supported!");
44 CPU = "generic-rv32";
45 }
46 return createMyRISCVMCSubtargetInfoImpl(TT, CPU, CPU, FS);
47 }
48
49 MCInstPrinter *createMyRISCVMCInstPrinter(const Triple &T,
50 unsigned SyntaxVariant,
51 const MCAsmInfo &MAI,
52 const MCInstrInfo &MII,
53 const MCRegisterInfo &MRI) {
54 return new MyRISCVInstPrinter(MAI, MII, MRI);
55 }
56
57 } // end anonymous namespace
58
59 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeMyRISCVTargetMC() {
60 Target &RV32 = getMyRISCV32Target();
61 TargetRegistry::RegisterMCAsmInfo(RV32, createMyRISCVMCAsmInfo);
62 TargetRegistry::RegisterMCInstrInfo(RV32, createMyRISCVMCInstrInfo);
63 TargetRegistry::RegisterMCRegInfo(RV32, createMyRISCVMCRegisterInfo);
64 TargetRegistry::RegisterMCSubtargetInfo(RV32, createMyRISCVMCSubtargetInfo);
65 TargetRegistry::RegisterMCInstPrinter(RV32, createMyRISCVMCInstPrinter);
66 }
step 21: MyRISCVMCAsmInfo.h
llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVMCAsmInfo.h 的内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCASMINFO_H
2 #define LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCASMINFO_H
3
4 #include "llvm/MC/MCAsmInfoELF.h"
5
6 namespace llvm {
7
8 class Triple;
9
10 class MyRISCVMCAsmInfo : public MCAsmInfoELF {
11 public:
12 explicit MyRISCVMCAsmInfo(const Triple &TT);
13 };
14
15 } // end namespace llvm
16
17 #endif // LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVMCASMINFO_H
step 22: MyRISCVMCAsmInfo.cpp
llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVMCAsmInfo.cpp 的内容如下:
1 #include "MyRISCVMCAsmInfo.h"
2 #include "llvm/ADT/Triple.h"
3
4 using namespace llvm;
5
6 MyRISCVMCAsmInfo::MyRISCVMCAsmInfo(const Triple &TT) {
7 assert(TT.isArch32Bit() && "Only RV32 is currently supported!");
8 CodePointerSize = CalleeSaveStackSlotSize = TT.isArch32Bit() ? 4 : 4;
9 CommentString = "#";
10 Data16bitsDirective = "\t.half\t";
11 Data32bitsDirective = "\t.word\t";
12 }
step 23: MyRISCVInstPrinter.h
MyRISCVInstPrinter.h
用于声明继承自llvm::MCInstPrinter
的派生类MyRISCVInstPrinter
,该类负责打印汇编代码,位于llvm/lib/Target/MyRISCV
目录中,其内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVINSTPRINTER_H
2 #define LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVINSTPRINTER_H
3
4 #include "llvm/MC/MCInstPrinter.h"
5
6 namespace llvm {
7
8 class MyRISCVInstPrinter : public MCInstPrinter {
9 public:
10 MyRISCVInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII,
11 const MCRegisterInfo &MRI)
12 : MCInstPrinter(MAI, MII, MRI) {}
13
14 void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
15 const MCSubtargetInfo &STI, raw_ostream &OS) override;
16
17 private:
18 std::pair<const char *, uint64_t> getMnemonic(const MCInst *MI) override;
19
20 void printInstruction(const MCInst *MI, uint64_t Address, raw_ostream &O);
21
22 bool printAliasInstr(const MCInst *MI, uint64_t Address, raw_ostream &O);
23
24 static const char *getRegisterName(unsigned RegNo, unsigned AltIdx);
25
26 void printCustomAliasOperand(const MCInst *MI, uint64_t Address,
27 unsigned OpIdx, unsigned PrintMethodIdx,
28 raw_ostream &O);
29
30 void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O);
31 };
32
33 } // end namespace llvm
34
35 #endif // LLVM_LIB_TARGET_MYRISCV_MCTARGETDESC_MYRISCVINSTPRINTER_H
第 14~15 行,每个目标后端至少需要覆写基类llvm::MCInstPrinter
中的虚函数printInst()
。该函数用于打印汇编代码。
第 20~28 行,这些函数由 TableGen 生成的,其实现位于MyRISCVGenAsmWriter.inc
文件,这里需要提供其函数声明。
第 30 行,自定义私有成员函数printOperand()
,用于打印指令的操作数。
step 24: MyRISCVInstPrinter.cpp
llvm/lib/Target/MyRISCV/MCTargetDesc/MyRISCVInstPrinter.cpp 的内容如下:
1 #include "MyRISCVInstPrinter.h"
2 #include "MCTargetDesc/MyRISCVMCTargetDesc.h"
3 #include "llvm/MC/MCExpr.h"
4 #include "llvm/MC/MCInst.h"
5 #include "llvm/Support/FormattedStream.h"
6
7 using namespace llvm;
8
9 #define PRINT_ALIAS_INSTR
10 #include "MyRISCVGenAsmWriter.inc"
11
12 void MyRISCVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
13 StringRef Annot, const MCSubtargetInfo &STI,
14 raw_ostream &OS) {
15 if (!printAliasInstr(MI, Address, OS)) {
16 printInstruction(MI, Address, OS);
17 }
18 }
19
20 void MyRISCVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
21 raw_ostream &OS) {
22 const MCOperand &MO = MI->getOperand(OpNo);
23
24 if (MO.isReg()) {
25 OS << getRegisterName(MO.getReg(), MyRISCV::ABIRegAltName);
26 return;
27 }
28
29 if (MO.isImm()) {
30 OS << MO.getImm();
31 return;
32 }
33
34 assert(MO.isExpr() && "Unknown operand kind in printOperand!");
35 MO.getExpr()->print(OS, &MAI);
36 }
第 15~17 行,优先打印指令的别名,如果失败,则根据汇编格式打印指令。
第 20~36 行,目前仅支持打印指令的操作数为寄存器
、立即数
或表达式
的情形。
step 25: MCTargetDesc/CMakeLists.txt
llvm/lib/Target/MyRISCV/MCTargetDesc/CMakeLists.txt 的内容如下:
add_llvm_component_library(LLVMMyRISCVDesc
MyRISCVMCTargetDesc.cpp
MyRISCVMCAsmInfo.cpp
MyRISCVInstPrinter.cpp
LINK_COMPONENTS
MC
MyRISCVInfo
Support
ADD_TO_COMPONENT
MyRISCV
)
step 26: MyRISCVTargetInfo.h
llvm/lib/Target/MyRISCV/TargetInfo/MyRISCVTargetInfo.h 的内容如下:
1 #ifndef LLVM_LIB_TARGET_MYRISCV_TARGETINFO_MYRISCVTARGETINFO_H
2 #define LLVM_LIB_TARGET_MYRISCV_TARGETINFO_MYRISCVTARGETINFO_H
3
4 namespace llvm {
5
6 class Target;
7
8 Target &getMyRISCV32Target();
9
10 } // end namespace llvm
11
12 #endif // LLVM_LIB_TARGET_MYRISCV_TARGETINFO_MYRISCVTARGETINFO_H
step 27: MyRISCVTargetInfo.cpp
llvm/lib/Target/MyRISCV/TargetInfo/MyRISCVTargetInfo.cpp 的内容如下:
1 #include "TargetInfo/MyRISCVTargetInfo.h"
2 #include "llvm/MC/TargetRegistry.h"
3
4 using namespace llvm;
5
6 namespace llvm {
7
8 Target &getMyRISCV32Target() {
9 static Target MyRISCV32Target;
10 return MyRISCV32Target;
11 }
12
13 } // end namespace llvm
14
15 extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeMyRISCVTargetInfo() {
16 RegisterTarget<Triple::myriscv32> X(getMyRISCV32Target(), "myriscv32",
17 "32-bit RISC-V", "RISCV");
18 }
step 28: TargetInfo/CMakeLists.txt
llvm/lib/Target/MyRISCV/TargetInfo/CMakeLists.txt 的内容如下:
add_llvm_component_library(LLVMMyRISCVInfo
MyRISCVTargetInfo.cpp
LINK_COMPONENTS
MC
ADD_TO_COMPONENT
MyRISCV
)
step 1: 编译
在构建目录中执行如下命令:
$ ninja
step 2: 运行
在构建目录的子目录bin
中执行如下命令:
$ llc -march=myriscv32 -filetype=asm test.ll -o test_my.s
test_my.s 的内容如下:
.text
.file "test.ll"
.globl test # -- Begin function test
.type test,@function
test: # @test
# %bb.0:
addi a0, zero, 1
ret
.Lfunc_end0:
.size test, .Lfunc_end0-test
# -- End function
.section ".note.GNU-stack","",@progbits
从上面的结果可以看出,目标后端MyRISCV
也将 LLVM IR 语句ret i32 1
翻译成了addi a0, zero, 1
和ret
两条汇编代码。
下一篇:LLVM 之后端篇(4):理解指令选择的 dump 输出