LLVMバックエンドを追加するにあたり、MYRISCVXアーキテクチャを定義するためのtdファイルを作成する必要がある。 tdファイルはLLVMのバックエンドを定義するためのDSLで、バックエンドを理解するためには避けては通れないものだ。
MYRISCVXInstrInfo.td
次に追加すべきは、MYRISCVXの命令を定義するためのMYRISCVXInstrInfo.td
だ。
MYRISCVXInstrInfo.td
内には命令フォーマットを定義するためのMYRISCVXInstrFormats.td
を用意する必要がある。
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
// 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; let OutOperandList = outs; let InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; let Itinerary = itin; // // Attributes specific to MYRISCVX instructions... // bits<4> FormBits = Form.Value; // TSFlags layout should be kept in sync with MYRISCVXInstrInfo.h. let TSFlags{3-0} = FormBits; let DecoderNamespace = "MYRISCVX"; field bits<32> SoftFail = 0; }
MYRISCVXInst
がベースのクラスになる。32ビットのフィールドInst
を定義し、これが命令フィールドそのものになる。
OutOperandList
はこの命令で書き込まれるレジスタのリスト、InOperandList
はこの命令で読み込まれるレジスタのリストだ。
このオペランドリストを使用してデータフローを構築したり、レジスタ割り付けの解析を行う。
さらに、命令のニーモニックを格納するためにAsmString
を定義し、Pattern
はLLVM IRから命令を生成するために必要なパターンを格納する。
さて、ここから先はいよいよ命令フォーマットの定義氏をしていく。RISC-Vには、基本として以下の命令フォーマットが決められている。
ここでは、上記の6つの命令フォーマットについてクラスを作ればよいことになる。
それぞれ、上記で定義したMYRISCVXInst
クラスを継承して定義する。
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
//===----------------------------------------------------------------------===// // R-Type instruction class in MYRISCVX : <|opcode|funct7|funct3|rd|rs1|rs2|> //===----------------------------------------------------------------------===// class MYRISCVX_R<bits<7> opcode, bits<3> funct3, bits<7> funct7, dag outs, dag ins, string asmstr, list<dag> pattern, InstrItinClass itin>: MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmR> { bits<5> rd; bits<5> rs1; bits<5> rs2; 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; let Inst{6-0} = opcode; }
MYRISCVXInst
を継承して、MYRISCVX_R
クラスを作成した。
引数としてopcode, funct3, func7
を取る。
また、outs, ins
は使用すレジスタを指定し、データフローを構築する。
asmstr
には命令のニーモニックを定義する。
rd, rs1, rs2
を定義して、オペランドとして指定されるレジスタオペランドを作成する。
そして、MYRISCVXInst
でインスタンスしたInst
変数に対して、ビットフィールドを指定する形で値を設定する。
Inst{31-25}
ならば、Inst
変数の31ビットから25ビットまでの7ビットにfunct7
が代入されるという意味になる。
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
MYRISCVX_I
は即値命令を扱うためのフォーマットだ。
//===----------------------------------------------------------------------===// // I-Type instruction class in MYRISCVX : <|opcode|funct3|rd|rs1|imm12|> //===----------------------------------------------------------------------===// class MYRISCVX_I<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> rd; bits<5> rs1; bits<12> imm12; let Inst{31-20} = imm12; let Inst{19-15} = rs1; let Inst{14-12} = funct3; let Inst{11-7} = rd; let Inst{6-0} = opcode; }
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
MYRISCVX_S
はメモリストア命令を扱うためのフォーマットだ。
//===----------------------------------------------------------------------===// // S-Type instruction class in MYRISCVX : <|opcode|rs2|rs1|width|offset> //===----------------------------------------------------------------------===// class MYRISCVX_S<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> rs2; bits<5> rs1; bits<12> simm12; let Inst{31-25} = simm12{11-5}; let Inst{19-15} = rs1; let Inst{24-20} = rs2; let Inst{14-12} = funct3; let Inst{11-7} = simm12{4-0}; let Inst{6-0} = opcode; let DecoderMethod = "DecodeStore"; }
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
MYRISCVX_U
は即値生成命令(LUI
命令やAUIPC
命令など)を扱うためのフォーマットだ。
//===----------------------------------------------------------------------===// // U-Type instruction class in MYRISCVX : <|opcode|rd|imm31-12> //===----------------------------------------------------------------------===// class MYRISCVX_U<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern, InstrItinClass itin>: MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmI> { bits<5> rd; bits<20> imm20; let Inst{31-12} = imm20; let Inst{11-7} = rd; let Inst{6-0} = opcode; }
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
MYRISCVX_B
は条件分岐命令を扱うためのフォーマットだ。
//===----------------------------------------------------------------------===// // B-Type instruction class in MYRISCVX : <|opcode|funct3|rs1|rs2|imm12|> //===----------------------------------------------------------------------===// class MYRISCVX_B<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<12> imm12; bits<5> rs2; bits<5> rs1; let Inst{31} = imm12{11}; let Inst{30-25} = imm12{9-4}; let Inst{14-12} = funct3; let Inst{11-8} = imm12{3-0}; let Inst{7} = imm12{10}; let Inst{6-0} = opcode; }
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
MYRISCVX_J
は即値ジャンプ命令を扱うためのフォーマットだ。
//===----------------------------------------------------------------------===// // J-Type instruction class in MYRISCVX : <|opcode|imm20|> //===----------------------------------------------------------------------===// class MYRISCVX_J<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern, InstrItinClass itin>: MYRISCVXInst<outs, ins, asmstr, pattern, itin, FrmI> { bits<20> imm20; let Inst{31} = imm20{19}; let Inst{30-21} = imm20{9-0}; let Inst{20} = imm20{10}; let Inst{19-12} = imm20{18-11}; let Inst{6-0} = opcode; }
Formatsを定義する
上記の命令区分に応じて、命令を区別するためのFormatsを定義する。
これはMYRISCVXInst
内でTSFlags
に格納される。
lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
// Format specifies the encoding used by the instruction. This is part of the // ad-hoc solution used to emit machine instruction encodings by our machine // code emitter. class Format<bits<3> val> { bits<3> Value = val; } def Pseudo : Format<0>; def FrmR : Format<1>; def FrmI : Format<2>; def FrmS : Format<3>; def FrmU : Format<4>; def FrmB : Format<5>; def FrmJ : Format<6>;