FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages

RISC-V 64-bit LLVM Backendを試す (3. TargetMachineとTargetInfoを追加する)

LLVMについて理解するために、RISC-Vをネタにしていろいろ勉強してみたい。 Release 7.0では一応RISC-Vの32-bitと64-bit版がサポートされているようなので、ビルドに追加して様子を見てみる。

f:id:msyksphinz:20190104013152p:plain

次はRISCV_msyksphinzのターゲットアーキテクチャについて定義していく。 Cpu0をベースにして、少しずつコードを追加していこう。

computeDataLayout(const Triple &TT, StringRef CPU, const TargetOptions &Options, bool isLittle)

アーキテクチャのデータレイアウトを定義する関数。データレイアウトについてはさっぱり知らなかったので、資料を参考にしながら読み解いていく。

LLVM Language Reference Manual — LLVM 8 documentation

static std::string computeDataLayout(const Triple &TT, StringRef CPU,
                                     const TargetOptions &Options,
                                     bool isLittle) {

データレイアウトがリトルエンディアンならば"e", ビッグエンディアンならば "E"とする。 RISC-Vにはリトルエンディアンしか存在しないので"e"で良いと思う。

  if (isLittle)
    Ret += "e";
  else
    Ret += "E";

C++などでも使用されるマングリング?を指定する。ここで"m"となっているので、MIPS形式のマングリングが適用されるのだと思う。

  Ret += "-m:m";

ポインタと変数のアラインメントについて。Cpu0では、以下のように定義されていた。 ポインタについては32ビット長で、32bitアライメントを前提とする。その次の行は、8bit、16bitはそれぞれそのビット長でアライメントしてよいが、実際には32bitでアライメントしてほしいことを意味している?

  // Pointers are 32 bit on some ABIs.
  Ret += "-p:32:32";

  // 8 and 16 bit integers only need to have natural alignment, but try to
  // align them to 32 bits. 64 bit integers have natural alignment.
  Ret += "-i8:8:32-i16:16:32-i64:64";
  // 32 bit registers are always available and the stack is at least 64 bit
  // aligned.
  Ret += "-n32-S64";

RISCVの実装を参考にすると、以下のようになっているので、

  if (TT.isArch64Bit()) {
    return "e-m:e-p:64:64-i64:64-i128:128-n64-S128";
  } else {
    assert(TT.isArch32Bit() && "only RV32 and RV64 are currently supported");
    return "e-m:e-p:32:32-i64:64-n32-S128";
  }
  • RV64のときは64bit長でポインタをアライメント、変数は64bitでアライメントすべき。128bitの変数もアライメントを定義する。
  • RV32のときは32bit長でポインタをアライメント、64bitと128bitの変数もアライメントを定義する。

RISCV_msyksphinzアーキテクチャインスタンス

そして、このComptueDataLayoutを使用してアーキテクチャを定義する関数は2つ用意されている。

RISCV_msyksphinzebTargetMachine::RISCV_msyksphinzebTargetMachine(const Target &T, const Triple &TT,
                                         StringRef CPU, StringRef FS,
                                         const TargetOptions &Options,
                                         Optional<Reloc::Model> RM,
                                         CodeModel::Model CM,
                                         CodeGenOpt::Level OL)
    : RISCV_msyksphinzTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, false) {}

void RISCV_msyksphinzelTargetMachine::anchor() { }

RISCV_msyksphinzelTargetMachine::RISCV_msyksphinzelTargetMachine(const Target &T, const Triple &TT,
                                         StringRef CPU, StringRef FS,
                                         const TargetOptions &Options,
                                         Optional<Reloc::Model> RM,
                                         CodeModel::Model CM,
                                         CodeGenOpt::Level OL)
    : RISCV_msyksphinzTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, true) {}

ここで、ビッグエンディアンは必要ないので削除してしまおう。RISCV_msyksphinzebTargetMachineの方だ。ebとelが微妙に異なる。

そして、LLVMのターゲットを作成するのはLLVMInitializeRISCV_msyksphinzTarget()である。これもビッグエンディアン用が存在したのだが削除した。

extern "C" void LLVMInitializeRISCV_msyksphinzTarget() {
  // Register the target.
  //- Little endian Target Machine
  RegisterTargetMachine<RISCV_msyksphinzelTargetMachine> Y(TheRISCV_msyksphinzelTarget);
}