LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。
Chapter-3のサイズの大きなスタックフレームの処理に関して、どうにも解決できずに悩んでいる。 とりあえず最初から全体的に見直すために、いろいろ見直す場所もあるので最初から作り直している。
せっかくなのでMYRISCVX(LLVMに追加している独自RISC-V実装)の命令フィールドの定義も作り直すことにした。
ディレクトリ構成
$ tree lib/Target/MYRISCVX
lib/Target/MYRISCVX
├── CMakeLists.txt
├── LLVMBuild.txt
├── MCTargetDesc
│ ├── CMakeLists.txt
│ ├── LLVMBuild.txt
│ └── MYRISCVXMCTargetDesc.cpp
├── MYRISCVX.h
├── MYRISCVXInstrInfo.td
├── MYRISCVXOther.td
├── MYRISCVXRegisterInfo.td
├── MYRISCVXTargetMachine.cpp
└── TargetInfo
├── CMakeLists.txt
├── LLVMBuild.txt
└── MYRISCVXTargetInfo.cpp
MYRISCVXInstrInfo.tdの見直し
MYRISCXVはRISC-V命令セットと全く同一で作るつもりなので、別に変な加工をする必要はない。なるべく自力で作ってみるというだけである。
まず、RISC-V命令セットの命令タイプは大きく分けて以下の4種類に分けられる。

R, I, S, Uの4種類が定義されており、それぞれにクラスを定義すれば良さそうだ。
def Pseudo : Format<0>; def FrmR : Format<1>; def FrmI : Format<2>; def FrmS : Format<3>; def FrmU : Format<4>; // Instruction w/ a custom format
まずはベースとなるMYRISCVXInstクラスを定義する。
// Generic MYRISCVX Format
class MYRISCVXInst<dag outs, dag ins, string asmstr, list<dag> pattern,
InstrItinClass itin, Format f>: Instruction
{
// Inst and Size: for tablegen(... -gen-emitter) and
// tablegen(... -gen-disassembler) in CMakeLists.txt
field bits<32> Inst;
Format Form = f;
let Namespace = "MYRISCVX";
let Size = 4;
bits<7> Opcode = 0;
// Bottom 7 bits are the 'opcode' field
let Inst{6-0} = Opcode;
...
}
次に、R型の命令を定義する。Opcodeの部分はすでにフィールドに割り付け済みなので、それ以外を作っていく。
class FR<bits<7> opcode, bits<7> funct7, bits<3> funct3,
dag outs, dag ins, string asmstr,
list<dag> pattern, InstrItinClass itin>:
MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmR>
{
bits<5> rs2;
bits<5> rs1;
bits<5> rd;
let Opcode = opcode;
let Inst{31-25} = funct7;
let Inst{24-20} = rs2;
let Inst{19-15} = rs1;
let Inst{14-12} = funct3;
let Inst{11-7} = rd;
}
こんな感じでいいのだろうか。次にI型、S型を作る。
class FI<bits<7> opcode, bits<3> funct3,
dag outs, dag ins, string asmstr, list<dag> pattern,
InstrItinClass itin>: MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmI>
{
bits<5> rs1;
bits<5> rd;
bits<12> imm12;
let Opcode = opcode;
let Inst{31-20} = imm12;
let Inst{19-15} = rs1;
let Inst{14-12} = funct3;
let Inst{11-7} = rd;
}
class FS<bits<7> opcode, bits<3> funct3,
dag outs, dag ins, string asmstr, list<dag> pattern,
InstrItinClass itin>: MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmS>
{
bits<5> rs1;
bits<5> rs2;
bits<12> imm12;
let Opcode = opcode;
let Inst{31-25} = imm12{11-5};
let Inst{19-15} = rs1;
let Inst{24-20} = rs2;
let Inst{14-12} = funct3;
let Inst{11-7} = imm12{4-0};
}

ここに、各種命令を登録していった。
class LoadM<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode, RegisterClass RC,
Operand MemOpnd, bit Pseudo>:
FI<opcode, funct3, (outs RC:$ra), (ins MemOpnd:$addr),
!strconcat(instr_asm, "\t$ra, $addr"),
[(set RC:$ra, (OpNode addr:$addr))], IILoad> {
let isPseudo = Pseudo;
}
class StoreM<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode, RegisterClass RC,
Operand MemOpnd, bit Pseudo>:
FI<opcode, funct3, (outs), (ins RC:$ra, MemOpnd:$addr),
!strconcat(instr_asm, "\t$ra, $addr"),
[(OpNode RC:$ra, addr:$addr)], IIStore> {
let isPseudo = Pseudo;
}
//@ 32-bit load.
multiclass LoadM32<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode,
bit Pseudo = 0> {
def #NAME# : LoadM<opcode, funct3, instr_asm, OpNode, GPR, mem, Pseudo>;
}
// 32-bit store.
multiclass StoreM32<bits<7> opcode, bits<3> funct3, string instr_asm, PatFrag OpNode,
bit Pseudo = 0> {
def #NAME# : StoreM<opcode, funct3, instr_asm, OpNode, GPR, mem, Pseudo>;
}
//@JumpFR {
let isBranch=1, isTerminator=1, isBarrier=1, imm12=0, hasDelaySlot = 0,
isIndirectBranch = 1 in
class JumpFR<bits<7> opcode, bits<3> funct3, string instr_asm, RegisterClass RC>:
FI<opcode, funct3, (outs), (ins RC:$ra),
!strconcat(instr_asm, "\t$ra"), [(brind RC:$ra)], IIBranch> {
let rs1 = 1; // RA
let imm12 = 0;
}
//@JumpFR }
defm LW : LoadM32 <0b0000011, 0b010, "lw", load_a >;
defm SW : StoreM32<0b0100011, 0b010, "sw", store_a>;
/// Arithmetic Instructions (ALU Immediate)
// IR "add" defined in include/llvm/Target/TargetSelectionDAG.td, line 315 (def add).
def ADDI : ArithLogicI<0b0010011, 0b000, "addi", add, simm12, immSExt12, GPR>;
/// Arithmetic Instructions (3-Operand, R-Type)
def JALR : JumpFR<0b1100111, 0b000, "jalr", GPR>;
def : InstAlias<"jr $rs1", (JALR ZERO, GPR:$rs1, 0)>;
def RET : RetBase<GPR>;
/// No operation
def : InstAlias<"nop", (ADDI ZERO, ZERO, 0)>;