FPGA開発日記

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

LLVM

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/…

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…