FPGA開発日記

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

RISC-V 64-bit LLVM Backendを試す (4. SubtargetとProcessor Model)

LLVMにはすでにRISC-Vのバックエンドサポートが追加されている。しかし、勉強のために独自のRISC-V実装をLLVMに追加している。 まだ現時点ではビルドにすら成功していないが、徐々に修正しながら必要なものを追加してビルドしている。

まず、ターゲットアーキテクチャについては、さすがにRISCV_msyksphinzは長すぎて実装している自分が嫌になってきたので、名前を変更した。 MYRISCVXという名前に変更して再スタートだ。

ビルド試行をしていると、サブターゲットがないと怒られてしまった。サブターゲットとはなんだか良く分からなかったのだが、どうやらメインとなるターゲット以外にそのターゲットに準ずる流派や、細かい実装の違い、ISAの機能の分離などが可能らしい。

情報が無くて苦労しているのだが、例えば、RISC-Vの本物の実装には以下のようなサブターゲットが使用されていた。

この例では、RISC-VのISAのモジュールの部分であるM(乗除算命令のサポート)と、A(アトミック命令)のサポートをサブターゲットとして定義している(もちろんそれ以外にも、FやDなどのモジュールもサブターゲットとして定義されている。このサブターゲットを最低1つでも定義しておかないと、LLVMのターゲット生成時にエラー(というか、実際にはクラスの中身が空)で怒られてしまう。

  • lib/Target/RISCV/RISCV.td
//===----------------------------------------------------------------------===//
// RISC-V subtarget features and instruction predicates.
//===----------------------------------------------------------------------===//
def FeatureStdExtM
    : SubtargetFeature<"m", "HasStdExtM", "true",
                       "'M' (Integer Multiplication and Division)">;
def FeatureStdExtA
    : SubtargetFeature<"a", "HasStdExtA", "true",
                       "'A' (Atomic Instructions)">;

これ以外に、プロセッサのモデルについても定義することができる。このプロセッサモデルについても最低1個は定義しておく必要がある。

  • lib/Target/RISCV/RISCV.td
//===----------------------------------------------------------------------===//
// RISC-V processors supported.
//===----------------------------------------------------------------------===//
def : ProcessorModel<"generic-rv32", NoSchedModel, []>;
def : ProcessorModel<"generic-rv64", NoSchedModel, [Feature64Bit]>;

さらにCpu0の実装をパクッて、以下のようなMYRISCVXのサブターゲットを定義する。 つまり、ここではFeatureMYRISCVX64というサブターゲット(64bitのRISC-V ISAをサポートするという意味(自分で勝手に付ける))を追加し、さらに、MYRISCVXのプロセッサモデルとしてmyriscv64_implを定義した。 このプロセッサはFeatureMYRISCVX64のISAサブターゲットをサポートしており、またCPUのパイプラインモデルは含まないという意味だ。

  • lib/Target/MYRISCV/MYRISCV.td
def FeatureMYRISCVX64 : SubtargetFeature<"x64", "HasArchX64", "true", "MYRISCVX64 ISA Support">;
def : ProcessorModel<"myriscv64_impl", NoSchedModel, [FeatureMYRISCVX64]>;

これでLLVMのビルドを行うと、${ビルドルートディレクトリ}/lib/Target/MYRISCVX/MYRISCVXGenSubtargetInfo.incにこの情報が追加された。

  • ${ビルドルートディレクトリ}/lib/Target/MYRISCVX/MYRISCVXGenSubtargetInfo.inc
namespace llvm {
// Sorted (by key) array of values for CPU features.
extern const llvm::SubtargetFeatureKV MYRISCVXFeatureKV[] = {
  { "x64", "MYRISCVX64 ISA Support", { MYRISCVX::FeatureMYRISCVX64 }, { } },
};

// Sorted (by key) array of values for CPU subtype.
extern const llvm::SubtargetFeatureKV MYRISCVXSubTypeKV[] = {
  { "myriscv64_impl", "Select the myriscv64_impl processor", { MYRISCVX::FeatureMYRISCVX64 }, { } },
};

この辺のクラスの関係は、Cpu0の資料を見ながら進める。

https://jonathan2251.github.io/lbd/_images/1.png
https://jonathan2251.github.io/lbd/_images/2.png

サブターゲットの識別は、見よう見まねで以下のように実装している。

  • MYRISCVSubtarget.cpp
MYRISCVXSubtarget &
MYRISCVXSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS,
                                               const TargetMachine &TM) {
  if (TargetTriple.getArch() == Triple::myriscvx) {
    if (CPU.empty() || CPU == "generic") {
      CPU = "myriscvx";
    }
    else if (CPU == "help") {
      CPU = "";
      return *this;
    }
    else if (CPU != "myriscvx") {
      CPU = "myriscvx";
    }
  }
  else {
    errs() << "!!!Error, TargetTriple.getArch() = " << TargetTriple.getArch()
           <<  "CPU = " << CPU << "\n";
    exit(0);
  }
  • 追加したファイル群

MYRISCVXのビルドを通すために、さらに以下のファイルを追加した。

  • MYRISCVXFrameLowering.cpp
  • MYRISCVXFrameLowering.h
  • MYRISCVXMachineFunction.cpp
  • MYRISCVXMachineFunction.h
  • MYRISCVXRegisterInfo.cpp
  • MYRISCVXRegisterInfo.h

MYRISCVXRegisterInfo.cppをいじっていた。保存レジスタを設定する。 本当はABIでCalling Conventionを定義していたらそれを設定すればよいのだが、まだ設定していないのでまだ何も実装していない。

getReservedRegs()では、RISC-Vのzero, gp, tp, sp, ra, s0(つまりfp)を設定してみた。

// pure virtual method
//@getReservedRegs {
BitVector MYRISCVXRegisterInfo::
getReservedRegs(const MachineFunction &MF) const {
//@getReservedRegs body {
  static const uint16_t ReservedCPURegs[] = {
    MYRISCVX::ZERO, MYRISCVX::GP, MYRISCVX::TP, MYRISCVX::SP, MYRISCVX::RA, MYRISCVX::S0
  };
  BitVector Reserved(getNumRegs());

  for (unsigned I = 0; I < array_lengthof(ReservedCPURegs); ++I)
    Reserved.set(ReservedCPURegs[I]);

  return Reserved;
}