FPGA開発日記

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

LLVMのバックエンドを作るための第一歩 (12. llcのターゲット・CPU・アトリビュートの関係)

llcLLVM IRからアセンブリ言語を生成する場合、バックエンドのターゲットとして大きく分けて以下の3つを指定する。

./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=-64bit
f:id:msyksphinz:20190526124548p:plain
コマンドラインから設定されるターゲットの情報と内部変数の関係

それぞれが意味を持っている。

  • -march まず、-marchによってターゲットアーキテクチャを指定する。これはTripleで登録したmyriscvx32myriscvx64を指定しなければならない。

  • -mattr

-mattrでは、そのアーキテクチャの機能を記述する。例えば、RISC-Vだと拡張命令セットとしてM,A,F,Dなどの拡張命令セットをサポートしている。 どの命令セットを拡張するか、このattrで指定することができる。これはLLVMの中でFeatureとして設定される。 MYRISCVXでは、Featureとして以下を追加した。

  • lib/Target/MYRISCVX/MYRISCVX.td
def FeatureMExt : SubtargetFeature<"m", "is_enable_M", "true",
    "'M' (Integer Multiplication and Division) support">;

def FeatureAExt : SubtargetFeature<"a", "is_enable_A", "true",
    "'A' (Atomic) support">;

def FeatureFExt : SubtargetFeature<"f", "is_enable_F", "true",
    "'F' (Singple Precision Floating Point) support">;

def FeatureDExt : SubtargetFeature<"d", "is_enable_D", "true",
    "'D' (Double Precision Floating Point) support">;

def FeatureCExt : SubtargetFeature<"c", "is_enable_C", "true",
    "'C' (Compressed Instruction) support">;

def FeatureRV64 : SubtargetFeature<"64bit", "HasRV64", "true", "RV64 support">;

サブターゲットの記述には要素が4つ並んでいる。

  • Featureの名前
  • Featureの属性(実装時に追加される変数の名前)
  • Featureの値(属性に設定される値)
  • Featureの説明。

この記述を加えることによって、tdファイルから生成されるMYRISCVXGenSubtargetInfo.incにはSubtargetFeatureに基づく変数制御が追加される。

  • build-myriscvx/lib/Target/MYRISCVX/MYRISCVXGenSubtargetInfo.inc
void llvm::MYRISCVXSubtarget::ParseSubtargetFeatures(StringRef CPU, StringRef FS) {
  LLVM_DEBUG(dbgs() << "\nFeatures:" << FS);
  LLVM_DEBUG(dbgs() << "\nCPU:" << CPU << "\n\n");
  InitMCProcessorInfo(CPU, FS);
  const FeatureBitset& Bits = getFeatureBits();
  if (Bits[MYRISCVX::FeatureAExt]) is_enable_A = true;
  if (Bits[MYRISCVX::FeatureCExt]) is_enable_C = true;
  if (Bits[MYRISCVX::FeatureDExt]) is_enable_D = true;
  if (Bits[MYRISCVX::FeatureFExt]) is_enable_F = true;
  if (Bits[MYRISCVX::FeatureMExt]) is_enable_M = true;
  if (Bits[MYRISCVX::FeatureRV64]) HasRV64 = true;
}

したがって、MYRISCVXSubtargetクラスには、これらのメソッドを追加する必要がある。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSubtarget.h
class MYRISCVXSubtarget : public MYRISCVXGenSubtargetInfo {
  virtual void anchor();

 public:

 protected:
  bool is_enable_M = false;
  bool is_enable_A = false;
  bool is_enable_F = false;
  bool is_enable_D = false;
  bool is_enable_C = false;

  bool HasRV64 = false;
  • -mcpu ターゲットアーキテクチャの中で、さらに特定のCPUを指定するために記述する。 特定のCPUを定義することによって、さまざまなFeatureをまとめて1つのCPU情報として記述することができる。 ここでは、以下の2つを定義した。

  • simple32 : 32ビットのシンプルなMYRISCVX実装。M/A/F/Dの拡張命令は実装していない。

  • rocket64 : 64ビットのRISC-V実装。M/A/F/Dの拡張命令を実装している。

という訳でMYRISCVX.tdに以下の2つのProcessorModelを追加する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVX.td
def : ProcessorModel<"simple32", NoSchedModel, []>;
def : ProcessorModel<"rocket64", NoSchedModel, [FeatureRV64,
                                                FeatureMExt,
                                                FeatureAExt, FeatureFExt, FeatureDExt, FeatureCExt]>;

これにより、MYRISCVXGenSubtargetInfoクラスに以下の情報が追加される。

  • build-myriscvx/lib/Target/MYRISCVX/MYRISCVXGenSubtargetInfo.inc
// Sorted (by key) array of values for CPU subtype.
extern const llvm::SubtargetFeatureKV MYRISCVXSubTypeKV[] = {
  { "rocket64", "Select the rocket64 processor", { MYRISCVX::FeatureRV64, MYRISCVX::FeatureMExt, MYRISCVX::FeatureAExt, MYRISCVX::FeatureFExt, MYRISCVX::FeatureDExt, MYRISCVX::FeatureCExt }, { } },
  { "simple32", "Select the simple32 processor", { }, { } },
};

-mcpuオプションを指定すると、Featureの設定されたCPUが生成される、という仕組みである。

  • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
/// Select the MYRISCVX Architecture Feature for the given triple and cpu name.
/// The function will be called at command 'llvm-objdump -d' for MYRISCVX elf input.
static StringRef selectMYRISCVXArchFeature(const Triple &TT, StringRef CPU) {
  if (TT.getArch() == Triple::ArchType::myriscvx64) {
    return "+64bit";
  } else if (TT.getArch() == Triple::ArchType::myriscvx32) {
    return "-64bit";
  }
  return "";
}

static MCSubtargetInfo *createMYRISCVXMCSubtargetInfo(const Triple &TT,
                                                      StringRef CPU, StringRef FS) {
  std::string ArchFS = selectMYRISCVXArchFeature(TT, CPU);

  if (!FS.empty()) {
    if (!ArchFS.empty())
      ArchFS = ArchFS + "," + FS.str();
    else
      ArchFS = FS;
  }
  // createMYRISCVXMCSubtargetInfoImpl defined in MYRISCVXGenSubtargetInfo.inc
  return createMYRISCVXMCSubtargetInfoImpl(TT, CPU, ArchFS);
}
f:id:msyksphinz:20190526124652p:plain
コマンドラインから設定されるターゲットの情報と内部クラスの関係