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)>;