1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Crystal / LLVM 初心者向け 逆引きチートシート

Last updated at Posted at 2025-12-14

はじめに

目的

この記事は、Crystalのコンパイラの遅さを歯がゆく思っているkojix2がCrystalとLLVMまわりを自分でもっと観察したいと考えて、初心者向けのチートシートとして作成しました。

作成方法と検証

この記事は生成AIを繰り返し用いて作成しています。kojix2 の書いた下書きを元に、ChatGPT, Claude という2つのAIに記事を何回か通して作成しました。(まさにこの記事のテーマであるPASSによる最適化みたいですね~)

さらに記事に記載されているコマンドが実際に動作するかについてAgentを用いてUbuntu環境で一通り検証を行いました。

環境セットアップ

バージョン確認

この記事では llvm-20 に基づいて書かれています。

llc --version
crystal --version

Ubuntu/APT でのコマンド名

LLVM 20 をインストールした場合:

llvm-as-20
llvm-dis-20
llc-20
opt-20

短縮する方法:

# aliasを使う
alias llc='llc-20'
alias opt='opt-20'

# または update-alternatives を使う
sudo update-alternatives --install /usr/bin/llc llc /usr/bin/llc-20 100

macOS/Homebrew での注意点

Homebrewでインストールした場合、PATHが通っていない可能性があります:

export PATH="$(brew --prefix llvm)/bin:$PATH"

ファイルフォーマット

フォーマット 拡張子 説明
Crystal source .cr Crystalのソースコード
LLVM IR (text) .ll 人間が読めるLLVM中間表現
LLVM bitcode .bc バイナリ形式のLLVM IR
Assembly .s ターゲット依存のアセンブリ
Object file .o オブジェクトファイル
Executable (なし) 実行ファイル

やりたいこと別コマンド

コンパイル・変換

Crystal → Assembly

crystal build --emit asm --prelude empty test.cr
crystal build --release --emit asm test.cr  # 最適化あり

Crystal → LLVM IR

crystal build --emit llvm-ir --prelude empty test.cr
crystal build --debug --emit llvm-ir test.cr  # デバッグ情報付き

LLVM IR (.ll) → Assembly (.s)

llc --relocation-model=pic test.ll
llc -asm-verbose test.ll  # コメント多め

LLVM IR (.ll) → Bitcode (.bc)

llvm-as test.ll

Bitcode (.bc) → LLVM IR (.ll)

llvm-dis test.bc
llvm-dis -o test.ll test.bc  # 出力先指定

Assembly (.s) → Executable

clang test.s
clang test.s -o test  # 出力ファイル名指定
clang test.s -levent -lgc -lm  # ライブラリ指定

Bitcode を直接実行

lli test.bc

注意: Crystalのランタイム依存関係(GCなど)がないため、実行時エラーが発生する可能性があります。


最適化

最適化レベルを指定してビルド

crystal build --release test.cr # -O3 --single-module
crystal build --no-debug test.cr

LLVM IR に最適化をかける

opt -S -O0 test.ll > O0.ll
opt -S -O1 test.ll > O1.ll
opt -S -O2 test.ll > O2.ll
opt -S -O3 test.ll > O3.ll

特定のパスだけ適用

opt -S -p instcombine test.ll > ic.ll
opt -S -p inline test.ll > inlined.ll
opt -S -p 'function(mem2reg)' test.ll > readable.ll  # メモリ→レジスタ化

特定の最適化を無効化

opt -S -O3 -disable-loop-unrolling test.ll > no_unroll.ll

最適化パイプラインの表示

opt -O3 -print-pipeline-passes test.ll -disable-output

最適化前後の差分確認

llvm-diff O0.ll O3.ll

ターゲット指定でコード生成

llc -march=x86-64 -mcpu=skylake test.ll
llc -mtriple=aarch64-apple-darwin test.ll

解析・デバッグ

コンパイル時間を測定

time crystal build test.cr
crystal build --stats test.cr  # 統計

LLVM 最適化パスの時間を測定

opt -time-passes -O3 test.ll -disable-output
opt -disable-output -time-passes -stats -O3 test.ll

コード生成(llc)の時間を測定

llc -time-passes test.ll

パス適用の詳細ログ

opt -O3 -debug-pass=Structure test.ll -disable-output 2>&1 | head -100

特定の最適化のデバッグログ

# 要: -DLLVM_ENABLE_ASSERTIONS=ON でビルドされたLLVM
opt -S -O3 -debug-only=instcombine test.ll 2>&1 | less

Bitcode の統計情報

llvm-bcanalyzer test.bc
llvm-bcanalyzer -dump test.bc | less

IR の検証

opt -p verify test.ll -disable-output

-p-passes= の短縮形です。

関数の抽出

llvm-extract -func=fibonacci test.ll -o foo.ll

コードサイズの確認

wc -l test.ll O3.ll  # 行数比較
llvm-size test.o  # バイナリサイズ
llvm-size test.o O3.o  # 最適化前後の比較

シンボル情報の確認

llvm-nm test.o
llvm-readelf -s test.o
llvm-objdump -t test.o

ディスアセンブル

llvm-objdump -d a.out | less
llvm-objdump -d -C a.out  # C++デマングル付き

インライン化の確認

opt -S -p inline -print-after=inline test.ll -disable-output

可視化

Control Flow Graph (CFG) の生成

opt -p dot-cfg test.ll
dot -Tpng <generated.dot> -o foo.png

注: CFG ファイルのパスに特殊文字が含まれる場合、生成に失敗することがあります。

Call Graph の生成

opt -p dot-callgraph test.ll
dot -Tpng test.ll.callgraph.dot -o callgraph.png

プロファイリング

PGO (Profile-Guided Optimization)

# 1. インストルメント付きビルド
crystal build --emit llvm-ir test.cr
clang -fprofile-instr-generate test.ll -o test

# 2. 実行してプロファイル収集
./test
# default.profraw が生成される

# 3. プロファイルデータのマージ
llvm-profdata merge -output=default.profdata default.profraw

# 4. プロファイルを使って最適化
clang -fprofile-instr-use=default.profdata test.ll -o test_opt

注: Crystalランタイムの依存関係により、リンク時に失敗する可能性があります。


高度なテクニック

並列リンク (LLDリンカー使用)

crystal build --link-flags="-fuse-ld=lld -Wl,--threads=8" test.cr

プリリュードなしビルド (コンパイル時間短縮)

crystal build --prelude=empty test.cr

トラブルシューティング

「なぜこの最適化がされない?」

  1. 最適化前後のIRを比較: llvm-diff before.ll after.ll
  2. パスのログを確認: opt -O3 -debug-pass=Structure test.ll -disable-output 2>&1 | head -100
  3. 特定パスのデバッグ: opt -debug-only=instcombine test.ll (要assertions)

「どのパスが遅い?」

opt -time-passes -O3 test.ll -o /dev/null 2>&1 | sort -k2 -rn | head -20

「コードサイズが大きすぎる」

# サイズ確認
llvm-size a.out
llvm-objdump -d a.out | wc -l

# サイズ最適化ビルド
crystal build --release test.cr
opt -Oz test.ll -o test_opt.ll

「デバッグシンボルを確認したい」

llvm-dwarfdump a.out
llvm-symbolizer --obj=a.out

よく使うコマンド組み合わせ

パターン1: Crystalコードの最適化効果を確認

# 最適化なし
crystal build --emit llvm-ir test.cr
opt -S -O0 test.ll > O0.ll

# 最適化あり
opt -S -O3 test.ll > O3.ll

# 差分確認
llvm-diff O0.ll O3.ll
wc -l O0.ll O3.ll

パターン2: 特定関数の最適化を深掘り

# 1. 関数を抽出
llvm-extract -func=fibonacci test.ll -o func.ll

# 2. 最適化前後を比較
opt -S -O0 func.ll > func_O0.ll
opt -S -O3 func.ll > func_O3.ll
llvm-diff func_O0.ll func_O3.ll

# 3. Assembly確認
llc func_O3.ll
cat func_O3.s

パターン3: コンパイル時間のボトルネック調査

# Crystal側
crystal build --stats --verbose test.cr

# LLVM最適化側
opt -time-passes -O3 test.ll -disable-output 2>&1 | tee opt_time.log

# コード生成側
llc -time-passes test.ll 2>&1 | tee llc_time.log

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?