この記事は個人的な学習メモです。
LLVMの中間言語の一つであるMachine IR(MIR)を公式ドキュメント等を参考に少しずつ理解したいと考えています。
Machine IR(MIR)とは
MIRは、LLVMのバックエンドの最適化で扱われるコード表現(中間言語)である。IR(intermediate representation)よりもマシンコードに近い表現で、命令やレジスタなどを含んだアーキテクチャ固有の表現となっている。
ヒューマンリーダブルなMIRは、主にLLVMバックエンドのコード生成パスをテストを記述するために利用される。例えば、llc
(LLVMのバックエンドでアセンブリを生成するコマンド)を使用して、以下のようにLLVMのコード生成パスをテストことができる。
- オプション
-run-pass=<pass>
で、指定したパスのMIRの変化を確認する - オプション
-stop-before=<pass>
で、指定したパスの直前のMIRを確認する - オプション
-stop-after=<pass>
で、指定したパスの直後のMIRを確認する
MIRの記述例
MIRには、alignment, tracksRegLiveness, frameInfo などのアトリビュート情報とregisters(レジスタ情報)、liveins(データが割り付けられたレジスタの情報)、body(命令列)などが含まれている。bodyには、ブロック開始時のliveinsや後続ブロックを示すsuccessorsが記載されている。
...<IRモジュール>...
---
name: f
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr64 }
- { id: 3, class: gr64_nosp }
...<中略>...
liveins:
- { reg: '$rdi', virtual-reg: '%14' }
- { reg: '$rsi', virtual-reg: '%15' }
- { reg: '$rdx', virtual-reg: '%16' }
- { reg: '$ecx', virtual-reg: '%17' }
frameInfo:
maxAlignment: 1
machineFunctionInfo: {}
body: |
bb.0.entry:
successors: %bb.1(0x50000000), %bb.11(0x30000000)
liveins: $rdi, $rsi, $rdx, $ecx
%17:gr32 = COPY $ecx
%16:gr64 = COPY $rdx
%15:gr64 = COPY $rsi
%14:gr64 = COPY $rdi
TEST32rr %17, %17, implicit-def $eflags
JCC_1 %bb.11, 14, implicit $eflags
JMP_1 %bb.1
...<中略>...
...
また、通常MIRにはIRモジュールも含まれている。上記のコードでは省略しているが、name: f
の前に記載されている。MIRにはグローバル変数や外部関数への参照、関数属性、メタデータ、デバッグ情報に相当するものがないため、IRを参照してそれらの情報を得る必要があるからである。MIRコードがIRに依存しない条件であれば、IRモジュールを記載する必要はない。
なお、上記のMIRはx86_64で以下のコマンドにより出力している。
$ cat << EOF > test.c
void f(float *a, float* b, float*c, int n) {
for(int i=0; i<n; i++) {
c[i] = a[i] + b[i];
}
}
EOF
$ clang -c -S -emit-llvm test.c
$ llc -simplify-mir -stop-after=dead-mi-elimination test.ll