FPGA開発日記

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

これからの時代、少量カスタムで大量種類のチップを設計する方法とは (UCBの論文を読む)

UCB(University of California, Berkeley)の論文を教えてもらい、読んでみることにした(実際には大量にGoogle翻訳した)。

この論文は"Generating the Next Wave of Custom Silicon"という論文である。 著者から分かる通り、RISC-VとChiselの思いっきり関係者である。

  • Generating the Next Wave of Custom Silicon
    • Borivoje Nikolić, Elad Alon, Krste Asanović, Electrical Engineering and Computer Sciences, University of California, Berkeley, CA, USA

https://ieeexplore.ieee.org/xpl/mostRecentIssue.jsp?punumber=8476980&filter=issueId%20EQ%20%228494227%22&pageNumber=1

この論文の要旨はこうだ。時代はDSA(Domain Specific Architecture)だ。そのためには、特定の用途に最適化されたチップを少量多品種で製造する必要がある。 その中で、IPの設計にかかる費用やコストを削減する必要がある。また素早く多品種のチップを製造するために、設計段階からそのメソドロジを変える必要があり、 そのために、

  • オープンソースのCPUアーキテクチャRISC-V」
  • 「柔軟な記述が可能なアーキテクチャジェネレータRocket-Chip」
  • 「柔軟なパラメタライズが可能なハードウェア構築言語Chisel」
  • 「プロセスをまたいでアナログ回路を生成することができるポータブルアナログ回路ジェネレータBAG2」

を開発した。

それぞれのコンポーネントについて説明する。

オープンな命令セットアーキテクチャで、CPUコアライセンス料やIPライセンス料を削減することができる

  • 柔軟なパラメタライズが可能なハードウェア構築言語Chisel

これまでに彼らは様々なハードウェア記述言語(Spiral, Genesis, Chisel)を開発してきたが、ChiselはScalaをベースにしDSL(Domain Specific Language)で、その特徴はハードウェア記述を抽象化できるということである。 また、ライブラリとしてハードウェアコンポーネントを提供できるということだ。ライブラリパッケージとしてDSPのライブラリを開発しており、それらを簡単に結合したり、ハードウェアを再利用することができる。

Chiselを活用して、アーキテクチャジェネレータRocket-Chipを開発した。Rocket-ChipはSoCとして活用することができ、共通のバスインタフェースとしてTileLink2を使用している。TileLink2で接続されたコンポーネントは、基本的にどのようなデザインでも再利用できる。

このRocket-Chip Generatorを使用して、例えばRoCCインタフェース(Rocket Custom Coprocessor)を使用した拡張を行うことができる。 以下の図はDSPサブシステムとシリアルリンクをRocket-Chipに追加したマルチコアプロセッサシステムである。このようなシステムを簡単に構築できる。

f:id:msyksphinz:20190601234113p:plain
Rocket-Chipを使って開発したカスタムDSP。本論文より抜粋。
  • プロセスをまたいでアナログ回路を生成することができるポータブルアナログ回路ジェネレータBAG2

BAG2はBerkeley Analog Generatorで、Pythonベースのプロセスをまたぐアナログ回路のジェネレータである。 このBAG2にはAPI、設計スクリプト、レイアウト生成エンジンなどを含んでおり、イコライザやSerDesなどを設計している。 これまでにSTマイクロの28FDSOI、GFの22FDX, TSMC 16FFCなどに対してBAG2によりアナログ回路を移植してきた。

f:id:msyksphinz:20190601233823p:plain
BAGのフレームワーク(本論文より抜粋)
f:id:msyksphinz:20190601233914p:plain
BAGで生成したSAR ADCレイアウト a) 28nmFDSOI, b) 22nm FDX, c) 16nm TSMC。本論文より抜粋。

これらの要素を組み合わせることで、プロセスを変えながら短いイタレーションでチップを数多くテープアウトしてきた。これが次の時代のチップ設計の手法だ、というのが本論文の趣旨である。


まあ言いたいことは分かる。ただオープンソースとパラメタライズできるハードウェア構築言語で解決できる問題って、半導体設計・製造の深い闇の中でもかなり上流工程だけに過ぎない。 実際に本気でチップを作るときにもっとも時間がかかるのはやはりバックエンド、レイアウトだということを痛感することも多い。 チップをどのようにレイアウトするのか、クロックはどのように張るか、どのようにモジュールを配置するかというところが重要になる。 これらの問題は、フロントエンドではおいそれと解決できない闇がある。

そこにも、メスが入ってほしいというのは事実だ。現状の業界構成を見ても、大きなEDAベンダは2社しかいない。 この2社が互いにけん制しあって、新規技術に力を注がない、ライセンス料は互いにけん制して高いまま、これでは進化するゆとりがない。

本音を言えばこの2社ははやいところ潰れてもらって、競争力を上げるためにより多くのベンダに参入してもらい、技術競争をした方が業界構造としてはまともに思える。

ChiselやRocket-Chipは、たしかにハードウェア設計という面で一つの波になるのかもしれない。 しかしそれをうまく活用できるかというのはまた謎だ。UCBの優秀な学生はChiselを使いこなすかもしれないが、Chiselの本来の力をすべての技術者が発揮できるかというのは別問題だ。Rocketの究極的なカスタマイズのメソドロジをすべての技術者が理解できるわけではない。 Chiselを実際に使ってみると、どうしてもいろんな問題にぶち当たるし、まだ開発環境がしっかりしているわけではない。問題は山積している。

半導体業界に表れたこの小さな波が消えてしまわないように、半導体業界全体を巻き込んでどうにかして大きな波にしてほしい。

私は決してRISC-VとChiselを手放しで褒めちぎるつもりは無い。 今後もこのような技術の調査を進めるし、いろんな技術を公平に見ていきたいものだ。

LLVMのバックエンドを作るための第一歩 (17. ISELDagToDAGの働きについて)

f:id:msyksphinz:20190425001356p:plain

DAG-to-DAGの目的は、ターゲットとは独立したノード情報をターゲット固有のノードに変換する処理を行うことだ。命令を選択するアルゴリズムはSelectionDAGに対して実行される。

DAGToDAGの中で実装しなければならない重要な関数の一つは、アドレスを計算するためのSelectAddrだ。 SelectAddrMYRISCVXInstrInfo.tdで以下のように定義されている。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
// MYRISCVX Address Mode! SDNode frameindex could possibily be a match
// since load and store instructions from stack used it.
def addr :
  ComplexPattern<iPTR, 2, "SelectAddr", [frameindex]>;

この"SelectAddr"を実装しなければなりません。ここでは、アドレスを計算するためにBaseOffsetを計算する。 しかし、とりあえずオフセットは使わず、Baseがそのままアドレス、Offsetは0となるように構築する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelDAGToDAG.cpp
//@SelectAddr {
/// ComplexPattern used on MYRISCVXInstrInfo
/// Used on MYRISCVX Load/Store instructions
bool MYRISCVXDAGToDAGISel::
SelectAddr(SDValue Addr, SDValue &Base, SDValue &Offset) {
  //@SelectAddr }
  EVT ValTy = Addr.getValueType();
  SDLoc DL(Addr);

  // if Address is FI, get the TargetFrameIndex.
  if (FrameIndexSDNode *FIN = dyn_cast<FrameIndexSDNode>(Addr)) {
    Base   = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy);
    Offset = CurDAG->getTargetConstant(0, DL, ValTy);
    return true;
  }

  Base   = Addr;
  Offset = CurDAG->getTargetConstant(0, DL, ValTy);
  return true;
}

ここまでの実装で一度コンパイルを実行してみる。ターゲットをMYRISCVXでアセンブリを実行してみる。

./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch3.cpp -emit-llvm -o ch3.bc
./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pic -filetype=asm ch3.bc -o -
        .text
        .section .mdebug.abilp32
        .previous
        .file   "ch3.cpp"
^C
zsh: interrupt  ./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pi

まだ命令の生成途中でフリーズしてしまう。 これはLLVM IRのstoreノードをしょるすることができないためだ。 これにはスタックの処理を追加する必要があるが、とりあえずここではまだ対処しない。

./bin/llvm-dis ch3.bc -o -
; Function Attrs: noinline norecurse nounwind optnone
define dso_local i32 @main() #0 {
entry:
  %retval = alloca i32, align 4
  store i32 0, i32* %retval, align 4
  ret i32 0
}

とりあえずclangに-O2オプションを付けることでstoreの生成を除去する。

./bin/clang -target mips-unknown-linux-gnu -c ../lbdex/input/ch3.cpp -emit-llvm -O2 -o ch3.o2.bc
./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=+64bit -relocation-model=pic -filetype=asm ch3.o2.bc -o -
...
main:
        .frame  $x8,0,$x1
        .mask   0x00000000,0
        .set    noreorder
        .set    nomacro
# %bb.0:                                # %entry
        addi    x10, zero, 0
        ret     x1
        .set    macro
        .set    reorder
        .end    main
...

無事に命令を生成することができた。

LLVMのバックエンドを作るための第一歩 (16. ISELLowering内でのReturn文の取り扱い)

MYRISCVXISelLoweringLLVM IRからSelectionDAG(データフローグラフ)への変換プロセスになる。 バックエンドのかなり初期の部分で適用される。

ここで必要な実装はMYRISCVXISelLowering.cppを実装する必要がある。 ここではLowerReturnLowerFormalArgumentsを実装した。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
//@LowerFormalArguments {
/// LowerFormalArguments - transform physical registers into virtual registers
/// and generate load operations for arguments places on the stack.
SDValue
MYRISCVXTargetLowering::LowerFormalArguments(SDValue Chain,
                                             CallingConv::ID CallConv,
                                             bool IsVarArg,
                                             const SmallVectorImpl<ISD::InputArg> &Ins,
                                             const SDLoc &DL, SelectionDAG &DAG,
                                             SmallVectorImpl<SDValue> &InVals)
const {

  return Chain;
}
// @LowerFormalArguments }

//===----------------------------------------------------------------------===//
//@              Return Value Calling Convention Implementation
//===----------------------------------------------------------------------===//

SDValue
MYRISCVXTargetLowering::LowerReturn(SDValue Chain,
                                    CallingConv::ID CallConv, bool IsVarArg,
                                    const SmallVectorImpl<ISD::OutputArg> &Outs,
                                    const SmallVectorImpl<SDValue> &OutVals,
                                    const SDLoc &DL, SelectionDAG &DAG) const {
  return DAG.getNode(MYRISCVXISD::Ret, DL, MVT::Other,
                     Chain, DAG.getRegister(MYRISCVX::RA, MVT::i32));
}

ただし、このままではLowerReturnは正しく動作しない。また、MIPSなどと同様にMYRISCVXにもCalling Conventionが存在し、引数を渡す際にも特定のレジスタを使用する必要がある。 MYRISCVXの場合はRISC-Vと同様に整数の引数は汎用レジスタA0-A7に格納して渡す必要がある。 このCalling Conventionを実現するのがMYRISCVXCallingConv.tdに記述している以下のCCIfTypeおよびCCAssignToRegである。

以下の記述では、関数の戻り値が渡される場合に、CCAssignToRegで示されるレジスタのどれかに引数が格納されるルールが追加されている。 そしてCalling ConventionであるRetCC_MYRISCVXを定義する。

f:id:msyksphinz:20190601165037p:plain
関数戻り時の戻り値を特定のレジスタ経由で行う仕組み
  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXCallingConv.td
def RetCC_MYRISCVXEABI : CallingConv<[
  // i32 are returned in registers A0, A1
  CCIfType<[i32], CCAssignToReg<[A0, A1]>>
]>;

def RetCC_MYRISCVX : CallingConv<[
  CCDelegateTo<RetCC_MYRISCVXEABI>
]>;

このRetCC_MYRISCVXはInstruction Selection時のAnalyzeReturnで使用される。 AnalyzeReturnLowerReturnから呼び出されるのだが、RetCC_MYRISCVXの実装を見てみると、i32以外の型を使用する(今のところは未サポート)、もしくは該当のレジスタに割り付けが失敗するとTrueを返し、Abortするような仕組みにしている。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
template<typename Ty>
void MYRISCVXTargetLowering::MYRISCVXCC::
analyzeReturn(const SmallVectorImpl<Ty> &RetVals, bool IsSoftFloat,
              const SDNode *CallNode, const Type *RetTy) const {
  CCAssignFn *Fn;

  Fn = RetCC_MYRISCVX;

...
    if (Fn(I, VT, RegVT, CCValAssign::Full, Flags, this->CCInfo)) {
      LLVM_DEBUG(dbgs() << "Call result #" << I << " has unhandled type "
                 << EVT(VT).getEVTString() << '\n');
      llvm_unreachable(nullptr);
    }
...

次にReturn命令の扱い方だが、これもMYRISCVXRetノードを使用して、ある条件下の下で別のノードを生成するようなルールを追加する。 MYRISCVXInstrInfo.tdに以下を追加した。 Return文(関数の戻り)の際にこの命令が生成される。ただし実装から分かるようにこの命令は疑似命令で、内部はMYRISCVXRetとなる。

f:id:msyksphinz:20190601165006p:plain
Retノードから関数戻り時に使用するRetRAを生成する仕組み
  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td
let isReturn=1, isTerminator=1, hasDelaySlot=0, isBarrier=1, hasCtrlDep=1 in
  def RetRA : MYRISCVXPseudo<(outs), (ins), "", [(MYRISCVXRet)]>;
  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrFormats.td
//===-------------------------------===//
// MYRISCVX Pseudo Instructions Format
//===-------------------------------===//
class MYRISCVXPseudo<dag outs, dag ins, string asmstr, list<dag> pattern>:
      MYRISCVXInst<outs, ins, asmstr, pattern, IIPseudo, Pseudo> {
  let isCodeGenOnly = 1;
  let isPseudo = 1;
}

そして、RetRAMYRISCVXSEInstrInfo.cppで展開の方法が定義される。 つまり、expandPostRAPseudoによってRetRAが指定されると、expandRetRAによって当該疑似命令を引数RAレジスタMYRISCVX::Ret命令に変換する。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXSEInstrInfo.cpp
//@expandPostRAPseudo
/// Expand Pseudo instructions into real backend instructions
bool MYRISCVXSEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
//@expandPostRAPseudo-body
  MachineBasicBlock &MBB = *MI.getParent();

  switch (MI.getDesc().getOpcode()) {
  default:
    return false;
  case MYRISCVX::RetRA:
    expandRetRA(MBB, MI);
    break;
  }

  MBB.erase(MI);
  return true;
}


void MYRISCVXSEInstrInfo::expandRetRA(MachineBasicBlock &MBB,
                                      MachineBasicBlock::iterator I) const {
  BuildMI(MBB, I, I->getDebugLoc(), get(MYRISCVX::RET)).addReg(MYRISCVX::RA);
}

LLVMのバックエンドを作るための第一歩 (15. ISelLoweringによりInstruction Selection)

MYRISCVXISelLoweringLLVM IRからSelectionDAG(データフローグラフ)への変換プロセスだ。バックエンドのかなり初期の部分で適用される。

ここで必要な実装はMYRISCVXISelLowering.cppを実装する必要がある。ここではLowerReturnLowerFormalArgumentsを実装した。

  • llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cpp
//@LowerFormalArguments {
/// LowerFormalArguments - transform physical registers into virtual registers
/// and generate load operations for arguments places on the stack.
SDValue
MYRISCVXTargetLowering::LowerFormalArguments(SDValue Chain,
                                             CallingConv::ID CallConv,
                                             bool IsVarArg,
                                             const SmallVectorImpl<ISD::InputArg> &Ins,
                                             const SDLoc &DL, SelectionDAG &DAG,
                                             SmallVectorImpl<SDValue> &InVals)
const {

  return Chain;
}
// @LowerFormalArguments }

//===----------------------------------------------------------------------===//
//@              Return Value Calling Convention Implementation
//===----------------------------------------------------------------------===//

SDValue
MYRISCVXTargetLowering::LowerReturn(SDValue Chain,
                                    CallingConv::ID CallConv, bool IsVarArg,
                                    const SmallVectorImpl<ISD::OutputArg> &Outs,
                                    const SmallVectorImpl<SDValue> &OutVals,
                                    const SDLoc &DL, SelectionDAG &DAG) const {
  return DAG.getNode(MYRISCVXISD::Ret, DL, MVT::Other,
                     Chain, DAG.getRegister(MYRISCVX::RA, MVT::i32));
}
f:id:msyksphinz:20190531021416p:plain

RISC-VボードSipeed Maix GOを試行する(1. 組み立て)

Sipeed Maix GOはRISC-Vコアの搭載された小型ボードだ。

www.seeedstudio.com

このボードのスペックについては、上記のページをそのまま引用すると、

In hardware, MAIX have powerful KPU K210 inside, it offers many excited features:

  • 1st competitive RISC-V chip, also 1st competitive AI chip, newly release in Sep. 2018
    • 28nm process, dual-core RISC-V 64bit IMAFDC, on-chip huge 8MB high-speed SRAM (not for XMR :D), 400MHz frequency (able to 800MHz)
    • KPU (Neural Network Processor) inside, 64 KPU which is 576bit width, support convolution kernels, any form of activation function.
      • It offers 0.25TOPS@0.3W,400MHz, when overclock to 800MHz, it offers 0.5TOPS. It means you can do object recognition 60fps@VGA
    • APU (Audio Processor) inside, support 8mics, up to 192KHz sample rate, hardcore FFT unit inside, easy to make a Mic Array (MAIX offer it too)
    • Flexible FPIOA (Field Programmable IO Array), you can map 255 functions to all 48 GPIOs on the chip
    • DVP camera and MCU LCD interface, you can connect an DVP camera, run your algorithm, and display on LCD
    • Many other accelerators and peripherals: AES Accelerator, SHA256 Accelerator, FFT Accelerator (not APU's one), OTP, UART, WDT, IIC, SPI, I2S, TIMER, RTC, PWM, etc.
  • MAix's Module
    • Inherit the advantage of K210's small footprint,
    • Sipeed MAIX-I module, or called M1, integrate K210, 3-channel DC-DC power, 8MB/16MB/128MB Flash (M1w module add wifi chip esp8285 on it) into Square Inch Module. All usable IO breaks out as 1.27mm(50mil) pins, and pin's voltage is selectable from 3.3V and 1.8V.

という訳で、コア自体は400MHzで動作するRISC-Vコアを積んでいる。

このボードは結構昔に買ってみたものの、組み立て方や使い方のマニュアルがほとんど無くて困っていたのだが、どうも調べると以下のYoutubeチャンネルが組み立て方について詳しかったので参考にして組み立てた。

www.youtube.com

写真を撮りながら組み立てた。Youtuberみたいだ。

  • 内容物一式。
f:id:msyksphinz:20190529224755p:plain
内容物一式。
f:id:msyksphinz:20190529224906p:plain
シールを剥がす。表面のシールが剥げるなんて知らなかった。
f:id:msyksphinz:20190529225036p:plain
四方の支柱を組み立てていく。
f:id:msyksphinz:20190529225148p:plain
四方の支柱を組み立てていく。
f:id:msyksphinz:20190529225305p:plain
無線モジュールを接続する。アンテナを接続する。
f:id:msyksphinz:20190529225353p:plain
バッテリーを接続する。最初はUSBケーブル経由で充電する必要がある。
f:id:msyksphinz:20190529225524p:plain
LCDを接続する。ここでLCDがいつの間にか割れていることに気が付いた。どの段階で割れていたのかはよく分からない。
f:id:msyksphinz:20190529230242p:plain
完成。
f:id:msyksphinz:20190529225920p:plain
電源を入れた状態。LCDが点灯した。

SiFive Tech Symposium in TokyoのAgenda公開

f:id:msyksphinz:20190529003601p:plain

いつの間にか公開されていた、SiFive Tech Symposium in Tokyoのアジェンダを見ることができるようになった。

sifivetechsymposium.com

情報が錯綜していてどうしたらいいのか分からなかったが、これが正式なアジェンダ、なんだと思う。 結構他の都市のイベントでもギリギリでアジェンダが公開されていたので、東京だけが異常なわけではないらしい。

これまでのRISC-V Tokyoのイベントと違って、企業の営業の匂いがプンプンするので、RISC-Vそのものに興味がある人は参加しなくてもいいのかも。

レジストレーションのフォームも当該ページにできているのだが、別口のレジストレーションの連絡も来た気がするので、どうしたらいいんだ。。。

LLVMのバックエンドを作るための第一歩 (14. LLVMInitializeCPUTargetMC()により登録されるクラス群)

printInstruction()などの命令プリントメソッドとMYRISCVXTargetMachineとの関連付けは、MCTargetDesc/MYRISCVXMCTargetDesc.cppで行われている。

f:id:msyksphinz:20190528001406p:plain
LLVMInitializeMYRISCVXTargetMC()により登録されるクラス群
  • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
// MYRISCVXInstPrinterメソッドを返す。
static MCInstPrinter *createMYRISCVXMCInstPrinter(const Triple &T,
                                                  unsigned SyntaxVariant,
                                                  const MCAsmInfo &MAI,
                                                  const MCInstrInfo &MII,
                                                  const MCRegisterInfo &MRI) {
  return new MYRISCVXInstPrinter(MAI, MII, MRI);
}

extern "C" void LLVMInitializeMYRISCVXTargetMC() {
  for (Target *T : {&getTheMYRISCVX32Target(), &getTheMYRISCVX64Target()}) {
    // Register the MC asm info.
    RegisterMCAsmInfoFn X(*T, createMYRISCVXMCAsmInfo);

    // Register the MC instruction info.
    TargetRegistry::RegisterMCInstrInfo(*T, createMYRISCVXMCInstrInfo);

    // Register the MC register info.
    TargetRegistry::RegisterMCRegInfo(*T, createMYRISCVXMCRegisterInfo);

    // Register the MC subtarget info.
    TargetRegistry::RegisterMCSubtargetInfo(*T,
                                            createMYRISCVXMCSubtargetInfo);
    // Register the MC instruction analyzer.
    TargetRegistry::RegisterMCInstrAnalysis(*T, createMYRISCVXMCInstrAnalysis);

    // createMYRISCVXMCInstPrinterクラスを通じて、MYRISCVXInstPrintクラスを登録する。
    TargetRegistry::RegisterMCInstPrinter(*T,
                                          createMYRISCVXMCInstPrinter);
  }

}

このLLVMInitializeMYRISCVXTargetMCでは、各TargetMachineに必要な処理を登録している。

MYRISCVXは2つのTargetMachineMYRISCVX32TargetMachineMYRISCVX64TargetMachineの2つがあるので、それぞれに命令を生成するためのメソッドを登録している。

  • RegisterMCAsmInfo : アセンブリ言語生成のためのルーチンを登録する。ここで登録するルーチンはMYRISCVXMCAsmInfoクラスをベースにして生成される。 MYRISCVXAsmInfo()では、アセンブリ出力時のファイルの先頭に出す基本的な情報を記載している。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCAsmInfo.cpp
  MYRISCVXMCAsmInfo::MYRISCVXMCAsmInfo(const Triple &TheTriple) {
    AlignmentIsInBytes          = false;
    Data16bitsDirective         = "\t.2byte\t";
    Data32bitsDirective         = "\t.4byte\t";
    Data64bitsDirective         = "\t.8byte\t";
    PrivateGlobalPrefix         = "$";
    // PrivateLabelPrefix: display $BB for the labels of basic block
    PrivateLabelPrefix          = "$";
    CommentString               = "#";
    ZeroDirective               = "\t.space\t";
    GPRel32Directive            = "\t.gpword\t";
    GPRel64Directive            = "\t.gpdword\t";
    WeakRefDirective            = "\t.weak\t";
    UseAssignmentForEHBegin = true;
  
    SupportsDebugInformation = true;
    ExceptionsType = ExceptionHandling::DwarfCFI;
    DwarfRegNumForCFI = true;
  }
  • RegisterMCInstrInfo : MYRISCVXに属する命令に関する情報を持つクラスを登録する。ここで登録するクラスはMCInstrInfoクラスだ。MCInstrInfoクラスを生成し、MYRISCVXGenInstrInfo.incで生成したMYRISCVX命令群の基本情報を登録してTargetMachineに登録する。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
  static MCInstrInfo *createMYRISCVXMCInstrInfo() {
    MCInstrInfo *X = new MCInstrInfo();
    InitMYRISCVXMCInstrInfo(X); // defined in MYRISCVXGenInstrInfo.inc
    return X;
  }
  • RegisterMCRegInfo : MYRISCVXに属するレジスタの情報を持つクラスを登録する。ここで登録するクラスはMCRegisterInfoクラスだ。MCRegisterInfoクラスを生成し、MYRISCVXGenRegisterInfo.incd絵生成したMYRISCVXのレジスタ群の基本情報を追加してTargetMachineに登録する。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
  static MCRegisterInfo *createMYRISCVXMCRegisterInfo(const Triple &TT) {
    MCRegisterInfo *X = new MCRegisterInfo();
    InitMYRISCVXMCRegisterInfo(X, MYRISCVX::RA); // defined in MYRISCVXGenRegisterInfo.inc
    return X;
  }
  • RegisterMCSubtargetInfo : MYRISCVXに属するサブターゲットの情報を登録する。サブターゲットはMYRISCVXSubtarget.hで登録したように、MYRISCVXの様々なアーキテクチャとCPUオプションを凝縮している。これらのサブターゲットの情報をTargetMachineに登録する。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
  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;
    }
    return createMYRISCVXMCSubtargetInfoImpl(TT, CPU, ArchFS);
    // createMYRISCVXMCSubtargetInfoImpl defined in MYRISCVXGenSubtargetInfo.inc
  }
  • RegisterMCInstrAnalysis : MYRISCVXに関する命令解析の情報を登録する。この時点では特に命令解析に関して特別なことをする必要はないので、MCInstrAnalysisクラスをそのまま継承してMYRISCVXInstrAnalysisクラスを作成して登録している。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
  class MYRISCVXMCInstrAnalysis : public MCInstrAnalysis {
   public:
    MYRISCVXMCInstrAnalysis(const MCInstrInfo *Info) : MCInstrAnalysis(Info) {}
  };
  }
  
  static MCInstrAnalysis *createMYRISCVXMCInstrAnalysis(const MCInstrInfo *Info) {
    return new MYRISCVXMCInstrAnalysis(Info);
  }
  • RegisterMCInstPrinter : 命令を出力するためのルーチンだ。MYRISCVXInstPrinterクラスを生成してそのまま返する。TargetMachineにコンパイル後の命令をファイルに出力するためのルーチンを登録する。

    • llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDesc/MYRISCVXMCTargetDesc.cpp
  static MCInstPrinter *createMYRISCVXMCInstPrinter(const Triple &T,
                                                    unsigned SyntaxVariant,
                                                    const MCAsmInfo &MAI,
                                                    const MCInstrInfo &MII,
                                                    const MCRegisterInfo &MRI) {
    return new MYRISCVXInstPrinter(MAI, MII, MRI);
  }