FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

LLVMのバックエンドを作るための第一歩 (53. RV64 / RV32両方のサポート)

f:id:msyksphinz:20190425001356p:plain

RISC-Vには32ビットモード(RV32)、64ビットモード(RV64)、128ビットモード(RV128)が定義されている。これまではRV32による32ビットのみをサポートしてきたが、RV64もサポートしたい。具体的に言えば、

  • RV64モードの時にレジスタ幅を64ビットとして取り扱う(スタックの取り扱いなどをそれに伴い変える)
  • RV64モード用の命令が使えるようになる。

などが行えるようになりたい。ここでは、RV64をサポートして、さまざまな追加命令と命令生成の調整を行う。

HwModeの追加

まず、2つのハードウェアタイプを追加する。RV64とRV32だ。ここでは、+64bit-64bitによりそれぞれのハードウェアタイプを識別することにする。

  • lib/Target/MYRISCVX/MYRISCVX.td
def RV64 : HwMode<"+64bit">;
def RV32 : HwMode<"-64bit">;

レジスタ定義の変更

次に、これまで32ビットで定義していたレジスタを拡張しなければならない。レジスタの定義はMYRISCVXRegister.tdで行っていたので、こちらを変更する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXRegisterInfo.td
def GPR : RegisterClass<"MYRISCVX", [i32], 32, (add
  // Reserved
  ZERO,
  // Return Values and Arguments
  A0, A1, A2, A3, A4, A5, A6, A7,
  // Not preserved across procedure calls
  T0, T1, T2, T3, T4, T5, T6,
  // Callee save
  FP, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11,
  // Reserved
  RA, SP, GP, TP
  )>

i32のみで定義していたので、HwModeに応じてレジスタの定義を変えるように変更する。

def XLenVT : ValueTypeByHwMode<[RV32, RV64, DefaultMode],
                               [i32,  i64,  i32]>;

def GPR : RegisterClass<"MYRISCVX", [XLenVT], 32, (add
  // Reserved
  ZERO,
  // Return Values and Arguments
  A0, A1, A2, A3, A4, A5, A6, A7,
  // Not preserved across procedure calls
  T0, T1, T2, T3, T4, T5, T6,
  // Callee save
  FP, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11,
  // Reserved
  RA, SP, GP, TP
  )> {
    let RegInfos = RegInfoByHwMode<
      [RV32,              RV64,              DefaultMode],
      [RegInfo<32,32,32>, RegInfo<64,64,64>, RegInfo<32,32,32>]>;
}

XLenVTという変数を導入した。XLenVTはHwModeに応じて型が変わる。これに応じて、レジスタのサイズを変える、という訳だ。

生成されたレジスタ情報MYRISCVXGenRegisterInfo.incには3種類のモードが登録されている。

  • build-myriscvx/lib/Target/MYRISCVX/MYRISCVXGenRegisterInfo.inc
static const TargetRegisterInfo::RegClassInfo RegClassInfos[] = {
  // Mode = 0 (Default)
  { 32, 32, 32, VTLists+4 },    // FPR_S
  { 32, 32, 32, VTLists+0 },    // GPR
  { 64, 64, 32, VTLists+6 },    // FPR_D
  // Mode = 1 (RV32)
  { 32, 32, 32, VTLists+4 },    // FPR_S
  { 32, 32, 32, VTLists+0 },    // GPR
  { 64, 64, 32, VTLists+6 },    // FPR_D
  // Mode = 2 (RV64)
  { 32, 32, 32, VTLists+4 },    // FPR_S
  { 64, 64, 64, VTLists+2 },    // GPR
  { 64, 64, 32, VTLists+6 },    // FPR_D
};

64ビットロードストア命令の追加

次に、64ビットのロードストア命令を追加する。RISC-Vでは、64ビットのロードストア命令としてLD/SDが定義されている。

funct7 rs2 rs1 funct3 rd opcode
LD imm[11:5] imm[4:0] rs1 011 rd 0000011
SD imm[11:5] rs2 rs1 011 imm[4:0] 0100011

これをMYRISCVXInstrInfo.tdに登録する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
defm LD     : LoadM32 <0b0000011, 0b011, "ld",  GPR, load_a>;
defm SD     : StoreM32<0b0100011, 0b011, "sd",  GPR, store_a>;

そして、生成パタンも登録する。

def : Pat<(i64 (load GPR:$rs1))                          , (LD GPR:$rs1, 0)           >;
def : Pat<(i64 (load addr_fi:$rs1))                      , (LD addr_fi:$rs1, 0)       >;
def : Pat<(i64 (load (add GPR:$rs1, simm12:$simm12)))    , (LD GPR:$rs1, $simm12)     >;
def : Pat<(i64 (load (add addr_fi:$rs1, simm12:$simm12))), (LD addr_fi:$rs1, $simm12) >;

def : Pat<(store GPR:$rs2, GPR:$rs1)                          , (SD GPR:$rs2, GPR:$rs1, 0)                         >;
def : Pat<(store GPR:$rs2, addr_fi:$rs1)                      , (SD GPR:$rs2, addr_fi:$rs1, 0)                     >;
def : Pat<(store GPR:$rs2, (add GPR:$rs1, simm12:$simm12))    , (SD GPR:$rs2, GPR:$rs1, simm12:$simm12)            >;
def : Pat<(store GPR:$rs2, (add addr_fi:$rs1, simm12:$simm12)), (SD GPR:$rs2, addr_fi:$rs1, simm12:$simm12)        >;