FPGA開発日記

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

LLVM

LLVMのPassの作り方について学ぶ(ドキュメントを読む4)

https://llvm.org/docs/WritingAnLLVMPass.html LLVMについて、バックエンドの部分はある程度勉強したけど、そういえばPassの作り方をまじめに勉強したことが無かった。 LLVMと言えばPassだろう!ということでPassの作り方について勉強することにした。これ…

LLVMのPassの作り方について学ぶ(ドキュメントを読む3)

https://llvm.org/docs/WritingAnLLVMPass.html LLVMについて、バックエンドの部分はある程度勉強したけど、そういえばPassの作り方をまじめに勉強したことが無かった。 LLVMと言えばPassだろう!ということでPassの作り方について勉強することにした。これ…

LLVMのPassの作り方について学ぶ(ドキュメントを読む2)

https://llvm.org/docs/WritingAnLLVMPass.html LLVMについて、バックエンドの部分はある程度勉強したけど、そういえばPassの作り方をまじめに勉強したことが無かった。 LLVMと言えばPassだろう!ということでPassの作り方について勉強することにした。これ…

LLVMのPassの作り方について学ぶ(ドキュメントを読む1)

https://llvm.org/docs/WritingAnLLVMPass.html LLVMについて、バックエンドの部分はある程度勉強したけど、そういえばPassの作り方をまじめに勉強したことが無かった。 LLVMと言えばPassだろう!ということでPassの作り方について勉強することにした。これ…

LLVM13でのAPI変更点について確認

LLVMバックエンドの開発で、これまで使用していたrelease/12.xブランチからrelease/13.xに移行したときのAPIの変更点について確認した。 今回はAPIの変更点が少ない。 namespace llvm { // 以下のコードを追加 class formatted_raw_ostream; MCTargetDesc/MY…

CIRCTの仕組みとMLIRを読み解く (MLIRを使ってCIRCTに新しいユニットを作成してみる)

MLIRの勉強のために、CIRCTにSystemVerilogの新しいツールを追加してみよう。やり方が分からないので試行錯誤でやってみる。FIRRTLのディレクトリを参考にしながら以下のようにファイルを追加してみる。 Dialect/SV/Import CMakeLists.txt SVAnnotations.cpp…

LLVM 8.0ベースのプロジェクトを LLVM 10.0ベースに移行するための作業まとめ

llvm.org LLVM 10.0がリリースされた。メジャーバージョンアップの頻度が3か月に1回程度となっており追いかけるのも大変だ。 また、メジャーバージョンがアップデートされる度にAPIの仕様が大きく変更されており、プロジェクトを移行させるのも並大抵ではな…

LLVMのバックエンドを作るための第一歩 (53. RV64 / RV32両方のサポート格闘中2)

RV64の64ビットモードでLLVMがアセンブラを生成できるように格闘している。とりあえずサンプルプログラムとして以下を用意した。 xlen64_func.cpp int64_t func() { int64_t a0 = 10; int64_t a1 = 20; int64_t ans = a0 + a1; return ans; } いろいろ奮闘し…

LLVMのバックエンドを作るための第一歩 (53. RV64 / RV32両方のサポート格闘中)

RV64の64ビットモードでLLVMがアセンブラを生成できるように格闘している。とりあえずサンプルプログラムとして以下を用意した。 xlen64_func.cpp #include <stdint.h> int64_t func() { return 0x100; } 要点としては、0x100の戻り値をint64_tのデータ型で返せるか、</stdint.h>…

LLVMのバックエンドを作るための第一歩 (54. Compressed命令を実装する検討)

RISC-Vの命令セットは基本的に32ビット長だが、Compresed命令という16ビット長の短縮命令が定義されている。これはArmのThumb命令のようなもので、命令フェッチのサイズを減らし、性能を向上させることを目的としている。 今回はこのCompressed命令をLLVMに…

LLVMのバックエンドを作るための第一歩 (53. RV64 / RV32両方のサポート)

RISC-Vには32ビットモード(RV32)、64ビットモード(RV64)、128ビットモード(RV128)が定義されている。これまではRV32による32ビットのみをサポートしてきたが、RV64もサポートしたい。具体的に言えば、 RV64モードの時にレジスタ幅を64ビットとして取り扱う(…

LLVMのバックエンドを作るための第一歩 (52. 浮動小数点のその他の命令)

比較命令以外にも、浮動小数点の様々な演算をサポートする。例えば、以下のようなものが挙げられる。 SQRT 浮動小数点符号反転 浮動小数点絶対値 符号付整数から浮動小数点への変換、符号なし整数から浮動小数点への変換 浮動小数点から符号付整数への変換、…

LLVMのバックエンドを作るための第一歩 (51. 浮動小数点比較命令の追加)

RISC-Vには、浮動小数の比較命令命令として以下が定義されている。 funct7 rs2 rs1 funct3 rd opcode FMIN.D 0010101 rs2 rs1 000 rd 1010011 FMAX.D 0010101 rs2 rs1 001 rd 1010011 FEQ.D 1010001 rs2 rs1 010 rd 1010011 FLT.D 1010001 rs2 rs1 001 rd 10…

LLVMのバックエンドを作るための第一歩 (50. 浮動小数点レジスタ向けのCalling Conventionの追加)

浮動小数点演算命令を使った算術演算は生成できるようになったが、まだ関数の引数として浮動小数点型を使用することができない。 これは、MYRISCVXのCalling Conventionに、浮動小数点を追加していないからだ。 RISC-VのCalling Conventionでは、浮動小数点…

LLVMのバックエンドを作るための第一歩 (49. 浮動小数点算術演算命令の追加)

浮動小数点のロードストア命令が生成できるようになったので、次は簡単な算術演算ができるようにする。とりあえずは、加減乗除、そして積和演算ができるようになりたい。 私たちはすでに命令定義のクラスとテンプレートを持っているので、そこに当てはめるだ…

LLVMのバックエンドを作るための第一歩 (48. 浮動小数点メモリアクセスの追加)

では、まずは簡単な浮動小数点命令の実装から入る。 演算命令を定義するところから始めてもよいが、そのまえに必ずロードストア命令が必要になるので、ロードストア命令から入る。 RISC-Vでは、単精度浮動小数点用のFLW / FSW、倍精度浮動小数点用のFLD / FS…

LLVMのバックエンドを作るための第一歩 (47. 浮動小数点レジスタの定義)

前章まででMYRISCVXの整数命令に関してLLVMバックエンドへの実装を進めてきた。 これだけでも随分と完成度が上がってきたが、まだ足りていないものがある。その一つとして、浮動小数点命令のサポートだ。 RISC-Vでは現状では2種類の浮動小数点命令が定義され…

LLVMのバックエンドを作るための第一歩 (46. LR/SCでアトミック操作を実現することを考える)

LR/SCを用いたAtomicコードの生成 以下のようなコードを考える。 cpp_atomic.cpp #include <stdint.h> #include <atomic> std::atomic<int32_t> x32; std::atomic<int16_t> x16; std::atomic<int8_t > x8; int32_t test_32() { x32.fetch_add(2); return x32.load(); } int16_t test_16() { x16.fetch_add(</int8_t></int16_t></int32_t></atomic></stdint.h>…

LLVMのバックエンドを作るための第一歩 (45. Atomic命令を生成する)

C++ではスレッド機能がサポートされており、ClangでもLLVM IRがサポートされている。例えば、以下のようなコードでIRを出力してみる。 cpp_atomic.cpp #include <stdint.h> #include <atomic> int test_32() { std::atomic<int> x(3); int before = x.fetch_add(2); return x.load()</int></atomic></stdint.h>…

LLVMのバックエンドを作るための第一歩 (44. 拡張Inline Assemblyのサポート)

次にコンパイルしたいのは、以下のようなCコードだ。 inline_assembly2.cpp int global_data = 200; void test () { int add_res; int b = 200, c = 300; __asm__ __volatile__("add %0,%1,%2" :"=r"(add_res) :"r"(b), "r"(c) ); int lw_res; int *lw_p = (…

LLVMのバックエンドを作るための第一歩 (43. インラインアセンブリをサポートする)

llcにアセンブラをサポートする。具体的には、インラインアセンブリなどのC言語の内部にアセンブラを埋め込む処理をサポートする。これまで、さんざんアセンブリ言語のサポートを追加してきたじゃないか、と思うかもしれないが、インラインアセンブリでのア…

LLVMのバックエンドを作るための第一歩 (42. LLVM IRをサポートするためのIntrinsicsを実装する)

LLVM IRをサポートするためのIntrinsicsのサポート LLVM IRには、いくつかの特殊な構文が存在し、これらをサポートすることでより多くのコードを生成できるようになる。 これらについては、clang側からIntrinsic関数を呼び出すことでLLVM IRを生成し、llcに…

LLVMのバックエンドを作るための第一歩 (41. objdumpのコードを読み解く)

現状、llvm-objdumpを実行すると以下のようになってしまう。これをサポートする。 % ./bin/llvm-objdump -d if_ctrl.o if_ctrl.o: file format ELF32-myriscvx if_ctrl.o': can't find target: : error: unable to get target for 'unknown--', see --versio…

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

現状、llvm-objdumpを実行すると以下のようになってしまう。これをサポートする。 % ./bin/llvm-objdump -d results/if_ctrl-O0_-march=mips_-relocation-model=static/if_ctrl.bc ./bin/llvm-objdump: error: 'results/if_ctrl-O0_-march=mips_-relocation-…

LLVMのバックエンドを作るための第一歩 (39. 可変長引数で生成されたアセンブリを見てみる)

前回の続き。サンプルプログラムをコンパイルして、どのようなアセンブリ言語が出力されるのか観察する。 vararg.cpp #include <stdarg.h> int sum_i(int amount, ...) { int i = 0; int val = 0; int sum = 0; va_list vl; va_start(vl, amount); for (i = 0; i < amo</stdarg.h>…

LLVMのバックエンドを作るための第一歩 (38. 可変長引数のサポート)

C言語の可変長引数では、例えば以下のような記述が可能となる。 vararg.cpp #include <stdarg.h> int sum_i(int amount, ...) { int i = 0; int val = 0; int sum = 0; va_list vl; va_start(vl, amount); for (i = 0; i < amount; i++) { val = va_arg(vl, int); sum </stdarg.h>…

LLVMのバックエンドを作るための第一歩 (38. 可変長引数のサポート)

C言語の可変長引数では、例えば以下のような記述が可能となる。 vararg.cpp #include <stdarg.h> int sum_i(int amount, ...) { int i = 0; int val = 0; int sum = 0; va_list vl; va_start(vl, amount); for (i = 0; i < amount; i++) { val = va_arg(vl, int); sum </stdarg.h>…

LLVMのバックエンドを作るための第一歩 (37. 構造体を値渡しするためのByval属性の引数 2)

ByVal属性の値を関数の引数として受け渡す処理の続き。 それでは、次に呼び出し側はどのようになっているのだろうか。呼び出し側でも、ByVal属性のついた引数を一つ一つコピーして関数呼び出され側に渡す必要がある。関数呼び出し側は、MYRISCVXTargetLoweri…

LLVMのバックエンドを作るための第一歩 (37. 構造体を値渡しするためのByval属性の引数 1)

今までの関数処理の中で、引数は基本的にポインタか値渡し、そして値も何らかの型の値を1つずつ渡していくという形式だった。しかし、C言語では構造体などの複数の要素をまとめた型を渡すことができる。また、C言語では構造体の値をそのまま値渡しで引数とし…

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

最後に、アセンブリの生成です。TAILCALLとTAILCALL_Rは疑似命令なのでそのままではアセンブリ命令を出力することができない。これを置き換える処理は、tdファイルからすでに生成されている。MYRISCVXGenMCPseudoLowering.incを見てみみる。 build-myriscvx/…