FPGA開発日記

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

LLVM

LLVMのバックエンドを作るための第一歩 (35. 末尾関数呼び出し最適化を実装する1)

関数呼び出しには様々な最適化の形があるが、その中の一つである末尾関数呼び出しでの最適化を実行してみる。末尾関数呼び出しとは、ある関数f1()が関数f2()の最後の処理として呼び出されるケースを言う。 int f1(a, b) { a + b; } int f2(c, d, e, f) { /* …

LLVMのバックエンドを作るための第一歩 (34. 関数コールと戻り値に関するDAGを生成する)

3. 関数呼び出しのDAG生成 次に、ジャンプ先となる関数のアドレスを計算する。PICモードでコンパイルしている場合はGOT経由でのアドレスを計算、そうでない場合はっ直接アドレスの計算を行う。これをGlobalAddressSDNode, ExternalSymbolSDNodeのタイプに対…

LLVMのバックエンドを作るための第一歩 (33. 関数コールに関するLLVM IRをDAGに変換する)

関数コールに関するLLVM IRをDAGに変換するためには、MYRISCVXTaregtLowering::LowerCallを実装する。LowerCallの役割を理解するために、LowerCallのコメントを読んでみる。 llvm-myriscvx/include/llvm/CodeGen/TargetLowering.h /// This hook must be imp…

LLVMのバックエンドを作るための第一歩 (32. 関数を呼び出す側のDAGの作成:ノードの定義)

引数のある関数を変換するにあたり、関数本体側の引数を取り込む処理は完成したのだが、次は関数を呼び出す側だ。関数コールにあたり、引数をABIのルールに則ってレジスタもしくはスタックに配置する必要がある。 関数呼び出し側の処理を行うためには、MYRIS…

LLVMのバックエンドを作るための第一歩 (31. i32よりも小さな値を引数に渡すとどのようなDAGになるのか)

i32型の変数を引数として渡した関数の場合は、レジスタもスタックもサイズはi32で考えているのでこの場合は問題ないのだが、ではi32よりも小さな型の引数を渡す場合にはどのような処理になっているのだろうか。 この場合は呼び出し規約のルールで型の拡張を…

LLVMのバックエンドを作るための第一歩 (29. 関数コールのサポート: Calling Conventionの定義)

今までのバックエンドの実装では、関数の取り扱いについていろいろとさぼっている部分があった。今回は、関数の定義と関数コールをきちんとサポートしようと思う。このためには、 スタックフレームの定義 引数の処理 などを実装していく。 現状では、引数の…

LLVMのバックエンドを作るための第一歩 (30. 関数コールのサポート: LowerFormalArgumentsの実装)

関数の呼び出し規約が決まったので、関数呼び出しを含むLLVM IRを処理するフェーズに入る。 まず、LLVM IRをDAGに変換する。 LLVM IRをDAGに変換するのはMYRISCVXTaregtLowerigの仕事だ。 その中で引数の解析とDAG変換を行うための関数はLowerFormalArgument…

LLVMのバックエンドを作るための第一歩 (28. 分岐の際に発生する不要なジャンプを削除するPassの追加)

前回の制御構文を追加した際に、不要なジャンプ命令が発生しているのが気になる。例えば、LLVM IRをダンプし時に以下のようなIRが出力されたはずだ。この時、br label %if.endはすぐ後ろのif.endにジャンプするため、このジャンプ文は不要なはずだ。ここでは…

LLVMのバックエンドを作るための第一歩 (28. SELECTノードをMYRISCVXISD::SELECT_CCに変換する)

SELECTノードは以下の実装を用いてMYRISCVXISD::SELECT_CCに変換する。SELECTノードはカスタム実装に置き換え、(MYRISCVXISDではないデフォルトの)SELECT_CCはCombine時に生成しないようにする。 llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXISelLowering.cp…

LLVMのバックエンドを作るための第一歩 (27. 3項演算子のためのSELECTノードの処理)

3項演算子をコンパイルしてみると、以下のようなLLVM IRが生成されることが分かる。 int test_movx_1() { volatile int a = 1; int c = 0; c = !a ? 1:3; return c; } ./bin/clang --target=mips-unknown-elf ../lbdex/input/ch8_2_select.cpp -c -emit-llvm…

LLVMのバックエンドを作るための第一歩 (26. 制御構文の追加)

制御フロー生成 いよいよLLVMバックエンドの醍醐味、制御構文の生成に入る。if文やfor文などの制御構文を生成できるようにしたい。 まずは、以下のような簡単なCプログラムをClangに入力し、LLVM IRを生成してみる。どのような制御IRが必要になるのかを確認…

LLVMのバックエンドを作るための第一歩 (25. int32型以外のサポート)

現在は、int型(32ビット整数)のみLLVM IRをサポートしているが、それ以外の型をサポートしたいと思う。32ビット整数、16ビット整数、8ビット整数、そしてBool型をサポートする。 これらの値をメモリアクセスするために、AlignedLoadを拡張して以下のノードを…

LLVMのバックエンドを作るための第一歩 (24. ポインタのサポート)

LLVM IRでポインタのサポートをする。ポインタをサポートするためには、該当するシンボルに対するアドレスを計算するというIRを追加する必要がある。これを、EffectiveAddressというクラスで追加する。 llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo…

LLVMのバックエンドを作るための第一歩 (23. グローバル変数の取り扱い)

今回は、グローバル変数を取り扱うための手法について調査する。 現状では、グローバル変数の入っているプログラムをコンパイルすると以下のようにエラーが発生する。 int gStart = 3; int gI = 100; int test_global() { int c = 0; c = gI; return c; } ./…

LLVMのバックエンドを作るための第一歩 (22. RISC-Vに存在しないパタンをどのように命令生成するか)

例えば、以下のようなプログラムをコンパイルする。 このプログラムは命令を生成する段階でローテートを示すIRを生成する。 int test_rotate_left() { unsigned int a = 8; int result = ((a << 30) | (a >> 2)); return result; } ./bin/clang -target mips…

LLVMのバックエンドを作るための第一歩 (21. 演算命令の追加)

定数が生成できるようになったので、次は演算命令を追加する。 こちらも同様にMYRISCVXInstrInfo.tdに命令パタンを追加していく。 llvm-myriscvx/lib/Target/MYRISCVX/MYRISCVXInstrInfo.td def ADDI : ArithLogicI<0b0010011, 0b000, "addi", add, simm12, …

LLVMのバックエンドを作るための第一歩 (20. 定数を生成させるパタンの追加)

簡単な関数をコンパイルしてアセンブリ命令が出力できるようになったので、様々な命令をサポートしていきましょう。MYRISCVXに命令を追加して、生成できるコードの量を増やしていく。 基本的な算術演算から進めるのが良いと思う。 MYRISCVXに命令を追加する…

LLVMのバックエンドを作るための第一歩 (19. 関数のスタックフレームの作り方)

MYRISCVXRegisterInfo::eliminateFrameIndex これも必須の関数だ。関数のスタックフレーム内での参照について、計算できていなかったオフセットを計算するための関数だ。 このeliminateFrameIndexが呼ばれるまでは、スタックポインタをベースに参照されるデ…

LLVMのバックエンドを作るための第一歩 (18. emitPrologue, emitEpilogue)

最終的に関数をコンパイルして命令を出力するためには、関数のプロローグ・エピローグを生成する必要がある。 関数のプロローグは関数に入った時の最初に行う処理、関数のエピローグは関数から出るときに最後に行う処理である。 基本的には Callee Saved Reg…

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

DAG-to-DAGの目的は、ターゲットとは独立したノード情報をターゲット固有のノードに変換する処理を行うことだ。命令を選択するアルゴリズムはSelectionDAGに対して実行される。 DAGToDAGの中で実装しなければならない重要な関数の一つは、アドレスを計算する…

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

MYRISCVXISelLoweringはLLVM IRからSelectionDAG(データフローグラフ)への変換プロセスになる。 バックエンドのかなり初期の部分で適用される。 ここで必要な実装はMYRISCVXISelLowering.cppを実装する必要がある。 ここではLowerReturnとLowerFormalArgumen…

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

MYRISCVXISelLoweringはLLVM IRからSelectionDAG(データフローグラフ)への変換プロセスだ。バックエンドのかなり初期の部分で適用される。 ここで必要な実装はMYRISCVXISelLowering.cppを実装する必要がある。ここではLowerReturnとLowerFormalArgumentsを実…

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

printInstruction()などの命令プリントメソッドとMYRISCVXTargetMachineとの関連付けは、MCTargetDesc/MYRISCVXMCTargetDesc.cppで行われている。 LLVMInitializeMYRISCVXTargetMC()により登録されるクラス群 llvm-myriscvx/lib/Target/MYRISCVX/MCTargetDes…

LLVMのバックエンドを作るための第一歩 (13. 命令のプリントに関するメソッド群)

命令のプリント、つまりアセンブリ命令をファイルに出力する処理は、基本的にMYRISCVXInstrInfo.tdに記述した命令の定義に基づいて生成される。 LLVMではPrintInstruction()というメソッドがその役割を担います。 このメソッドはMYRISCVXInstPrinterクラスに…

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

llcでLLVM IRからアセンブリ言語を生成する場合、バックエンドのターゲットとして大きく分けて以下の3つを指定する。 ./bin/llc -march=myriscvx32 -mcpu=simple32 -mattr=-64bit コマンドラインから設定されるターゲットの情報と内部変数の関係 それぞれが…

LLVMのバックエンドを作るための第一歩 (11. LLVMが独自ターゲットマシンを認識する仕組み)

LLVM Compiler Infrastructure MYRISCVXTargetMachineはターゲットマシンを定義するファイルである。 また、MYRISCVXTargetMachineを継承したクラスとして複数のターゲットマシンを作ることができる。 MIPSなどのバイエンディアンのアーキテクチャでは、リト…

LLVMのバックエンドを作るための第一歩 (10. サブターゲットを追加するためのSubtargetFeature)

LLVM Compiler Infrastructure 前回に引き続きLLVMのバックエンドを作るために必要なファイルを読み解いていく。 MYRISCVXSubTarget.{h,cpp} サブターゲットはMYRISCVXの中でもアーキテクチャのバリエーションを付けるために使用されるもので、その名の通り…

LLVMのバックエンドを作るための第一歩 (9. MCTargetDescとABIInfo)

LLVM Compiler Infrastructure 前回に引き続きLLVMのバックエンドを作るために必要なファイルを読み解いていく。 MCTargetDesc/MYRISCVXMCTargetDesc.{h,cpp} MYRISCVXMCTargeTDescでは明確なクラスを定義するわけではない。 その代わりに、これまでTarget D…

LLVMのバックエンドを作るための第一歩 (8. Calling Conventionとレジスタ定義)

LLVM Compiler Infrastructure MYRISCVXCallingConv.td MYRISCVXのCalling Convention、つまり呼び出し規約について設定するTarget Descriptionファイルだ。MYRISCXVの呼び出し規約は、RISC-Vのものをそのまま使用しようと思う。 ここでは、関数呼び出しの時…

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

LLVM Compiler Infrastructure MYRISCVXTargetMachine.{h,cpp} その名の通りターゲットマシンを定義するファイルだ。MYRISCVXTargetMachineはLLVMTargetMachineを継承したクラスで、ターゲットマシンのすべての情報を集約する。 このクラスには、サブターゲ…