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の資料を見ながら進める。
サブターゲットの識別は、見よう見まねで以下のように実装している。
- 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; }