FPGA開発日記

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

LLVMのバックエンドを作るための第一歩 (7. ターゲットとサブターゲット)

f:id:msyksphinz:20190425001356p:plain
LLVM Compiler Infrastructure

MYRISCVXTargetMachine.{h,cpp}

その名の通りターゲットマシンを定義するファイルだ。MYRISCVXTargetMachineLLVMTargetMachineを継承したクラスで、ターゲットマシンのすべての情報を集約する。 このクラスには、サブターゲットの情報も含まれている。

f:id:msyksphinz:20190515233753p:plain
MYRISCVXTargetMachineクラス
  • MYRISCVXTargetMachine.h
namespace llvm {

class MYRISCVXTargetMachine : public LLVMTargetMachine {
  std::unique_ptr<TargetLoweringObjectFile> TLOF;
  // Selected ABI
  MYRISCVXABIInfo ABI;
  MYRISCVXSubtarget DefaultSubtarget;
...

また、MYRISCVXTargetMachineを継承したクラスとして、リトルエンディアンの実装であるMYRISCVXelTargetMachineが定義される。 MIPSなどのバイエンディアンアーキテクチャでは、リトルエンディアンとビックエンディアンの両方が定義されるが、RISC-Vではリトルエンディアンしか存在しないので1種類しか不要なのだが、ここでは32ビット版と64ビット版のターゲットマシンを作りたいので、MYRISCVX32TargetMachineMYRISCVX64TargetMachineを定義する。

  • MYRISCVXTargetMachine.h
/// MYRISCVX32TargetMachine - MYRISCVX32 little endian target machine.
///
class MYRISCVX32TargetMachine : public MYRISCVXTargetMachine {
  virtual void anchor();
 public:
  MYRISCVX32TargetMachine(const Target &T, const Triple &TT, StringRef CPU,
                          StringRef FS, const TargetOptions &Options,
                          Optional<Reloc::Model> RM,
                          Optional<CodeModel::Model> CM,
                          CodeGenOpt::Level OL, bool JIT);
};

/// MYRISCVX64TargetMachine - MYRISCVX64 little endian target machine.
///
class MYRISCVX64TargetMachine : public MYRISCVXTargetMachine {
  virtual void anchor();
 public:
  MYRISCVX64TargetMachine(const Target &T, const Triple &TT, StringRef CPU,
                          StringRef FS, const TargetOptions &Options,
                          Optional<Reloc::Model> RM,
                          Optional<CodeModel::Model> CM,
                          CodeGenOpt::Level OL, bool JIT);
};

TargetMachineには何を記述すべきなのか

MYRISCVXTargetMachine.cppには、ターゲットアーキテクチャの様々なことを記述する必要がある。

  • コンストラク MYRISCVXTargetMachine (...) : 親クラスLLVMTargetMachineを呼び出して基本的な情報の登録を行う。基本的な情報とは、ビッグエンディアン・リトルエンディアン、ABI、リロケーションモデルなどだ。 MYRISCVX32TargetMachineMYRISCVX64TargetMachineは両方ともリトルエンディアンなので、とりあえず現時点ではコンストラクタに渡す引数は同じだ。
  MYRISCVX32TargetMachine::MYRISCVX32TargetMachine(const Target &T, const Triple &TT,
                                                   StringRef CPU, StringRef FS,
                                                   const TargetOptions &Options,
                                                   Optional<Reloc::Model> RM,
                                                   Optional<CodeModel::Model> CM,
                                                   CodeGenOpt::Level OL, bool JIT)
      : MYRISCVXTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {}
  
  void MYRISCVX64TargetMachine::anchor() { }
  
  MYRISCVX64TargetMachine::MYRISCVX64TargetMachine(const Target &T, const Triple &TT,
                                                   StringRef CPU, StringRef FS,
                                                   const TargetOptions &Options,
                                                   Optional<Reloc::Model> RM,
                                                   Optional<CodeModel::Model> CM,
                                                   CodeGenOpt::Level OL, bool JIT)
      : MYRISCVXTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {}
  
  // MYRISCVX32TargetMachine / MYRISCVX64TargetMachineが呼び出す先のコンストラクタ
  MYRISCVXTargetMachine::MYRISCVXTargetMachine(const Target &T, const Triple &TT,
                                               StringRef CPU, StringRef FS,
                                               const TargetOptions &Options,
                                               Optional<Reloc::Model> RM,
                                               Optional<CodeModel::Model> CM,
                                               CodeGenOpt::Level OL, bool JIT,
                                               bool isLittle)
      : LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options, isLittle), TT,
                          CPU, FS, Options, getEffectiveRelocModel(JIT, RM),
                          getEffectiveCodeModel(CM, CodeModel::Small), OL),
        isLittle(isLittle), TLOF(make_unique<MYRISCVXTargetObjectFile>()),
        ABI(MYRISCVXABIInfo::computeTargetABI()),
        DefaultSubtarget(TT, CPU, FS, isLittle, *this) {
  }

LLVMTargetMachineクラスのインスタンス化にあたり必要な情報は、以下の関数で算出している。

  • computeDataLayout(TT, CPU, Options, isLittle) xxx節で説明した。
  • getEffectiveRelocModel(JIT, RM) : MYRISCVXの有効なリロケーションモデルを返す。リロケーションモデルには以下が定義されている。

    • include/llvm/Support/CodeGen.h cpp // Relocation model types. namespace Reloc { enum Model { Static, PIC_, DynamicNoPIC, ROPI, RWPI, ROPI_RWPI }; }
  • getEffectiveCodeModel(CM, CodeModel::Small) : MYRISCVXのコードモデルを設定する。コードモデルというのは命令生成に置いてメモリアドレスの計算の方法を示し、現在のPCに対してコードとデータの相対的な関係に対する制限を付け加えることができる。ここでは、Default値としてCodeModel::Smallが設定されている。

MYRISCVXSubTarget.{h,cpp}

f:id:msyksphinz:20190515233849p:plain
MYRISCVXSubtarget

サブターゲットはMYRISCVXの中でもアーキテクチャのバリエーションを付けるために使用されるもので、その名の通りサブのターゲットだ。 llcにおいてアーキテクチャ-march=myriscvx32で指定するが、サブターゲットは-mcpu=で指定する。例えば、同じMIPSアーキテクチャの中でも、ISAのバージョン違いや実装の違いによってさまざまなバリエーションがある。

  • lib/Target/Mips/Mips.td
  def FeatureNoABICalls  : SubtargetFeature<"noabicalls", "NoABICalls", "true",
                                  "Disable SVR4-style position-independent code">;
  def FeaturePTR64Bit    : SubtargetFeature<"ptr64", "IsPTR64bit", "true",
                                  "Pointers are 64-bit wide">;
  def FeatureGP64Bit     : SubtargetFeature<"gp64", "IsGP64bit", "true",
                                  "General Purpose Registers are 64-bit wide">;
  def FeatureFP64Bit     : SubtargetFeature<"fp64", "IsFP64bit", "true",
                                  "Support 64-bit FP registers">;
  ...
./bin/llc -march=mips -mcpu=help
...

Available features for this target:

  mips1                    - Mips I ISA Support [highly experimental].
  mips16                   - Mips16 mode.
  mips2                    - Mips II ISA Support [highly experimental].
  mips3                    - MIPS III ISA Support [highly experimental].
  mips32                   - Mips32 ISA Support.
...
  noabicalls               - Disable SVR4-style position-independent code.
...

このサブターゲットを定義するためのクラスMYRISCVXSubTargetMYRISCVX.tdから生成された自動生成クラスMYRISCVXGenSubtargetInfoから生成される。

RISC-Vの場合、アーキテクチャに大きなバリエーションがある訳ではないのだが、ISAのどのモジュールが使用できるのか、例えば乗除算命令は使用できるのか、Compressed命令は使用できるのか、浮動小数点命令は使用できるのかなどのバリエーションが作れそうだ。

  • lib/Target/MYRISCVX/MYRISCVXSubtarget.h
    class MYRISCVXSubtarget : public MYRISCVXGenSubtargetInfo {
  ...
       protected:
      bool is_enable_M = false;
      bool is_enable_A = false;
      bool is_enable_F = false;
      bool is_enable_D = false;
  ...
  • lib/Target/MYRISCVX/MYRISCVXSubtarget.cpp
  MYRISCVXSubtarget &
  MYRISCVXSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS,
                                                     const TargetMachine &TM) {
    if (TargetTriple.getArch() == Triple::myriscvx32) {
      if (CPU.empty() || CPU == "generic") {
        CPU = "cpu-rv32";
      }
    } else if (TargetTriple.getArch() == Triple::myriscvx64) {
      if (CPU.empty() || CPU == "generic") {
        CPU = "cpu-rv64";
      }
    } else {
      errs() << "!!!Error, TargetTriple.getArch() = " << TargetTriple.getArch()
             <<  "CPU = " << CPU << "\n";
      exit(0);
    }
  
    // Parse features string.
    ParseSubtargetFeatures(CPU, FS);
    // Initialize scheduling itinerary for the specified CPU.
    InstrItins = getInstrItineraryForCPU(CPU);
  
    return *this;
  }