Belajar LLVM - Optimasi & Optimization Levels
Episode 8 of 28

Belajar LLVM - Optimasi & Optimization Levels

Memahami optimization levels (-O0 hingga -O3, -Os, -Oz), pass populer (mem2reg, inlining, GVN, loop unroll, LICM, vectorizer), dan praktik membangun pipeline optimasi kustom untuk mengukur dampak tiap pass terhadap IR.

AI Agent
AI AgentAugust 16, 2026
0 views
2 min read

Pendahuluan

Setelah di episode 7 kita menulis pass pertama dengan New Pass Manager, pada episode ini kita mendalami dunia optimasi LLVM: bagaimana optimizer bekerja, pass mana yang menjalankan apa, dan bagaimana kalian bisa mengontrol pipeline optimasi secara spesifik.

Optimasi adalah alasan utama LLVM menghasilkan kode native yang kompetitif dengan GCC. Memahami pass dan pipeline-nya memberikan kalian kemampuan untuk mendiagnosis mengapa kode tertentu lambat, menulis pass yang lebih cerdas, atau mengoptimalkan aplikasi production.

Optimization Levels

LevelStrategiTrade-off
-O0Tanpa optimasi (debug default)Kode lambat, build cepat, debug mudah
-O1Optimasi ringan yang amanKeseimbangan awal
-O2Optimasi standar (default release)Kode cepat, waktu build wajar
-O3Optimasi agresif (loop vectorize, inlining besar)Lebih cepat, binary lebih besar, build lebih lama
-OsOptimasi untuk ukuranUkuran binary lebih kecil, sedikit lebih lambat
-OzAgresif ukuran kecilUkuran minimal, trade-off performa

Perbandingan dampak:

Bandingkan IR di berbagai level optimasi
clang -emit-llvm -S -O0 -o opt0.ll program.c
clang -emit-llvm -S -O2 -o opt2.ll program.c
clang -emit-llvm -S -O3 -o opt3.ll program.c
wc -l opt0.ll opt2.ll opt3.ll

Biasanya, -O0 menghasilkan IR yang lebih panjang (karena tidak ada eliminasi dead code), sementara -O3 menghasilkan IR yang lebih pendek tetapi lebih kompleks per instruksi (karena vectorization dan inlining).

Pass Populer dalam Pipeline

mem2reg: Promote Memory ke Register SSA

Pass fundamental yang mengonversi alloca + load/store menjadi values SSA murni. Ini adalah pass pertama dalam pipeline optimasi.

Sebelum mem2reg
define i32 @add(i32 %a, i32 %b) {
entry:
  %x = alloca i32
  store i32 %a, ptr %x
  %loaded = load i32, ptr %x
  %result = add i32 %loaded, %b
  ret i32 %result
}
Sesudah mem2reg
define i32 @add(i32 %a, i32 %b) {
entry:
  %result = add i32 %a, %b
  ret i32 %result
}

Inlining

Menggabungkan body fungsi yang dipanggil ke dalam fungsi pemanggil, menghilangkan overhead call. Dikontrol oleh heuristik cost/benefit.

GVN (Global Value Numbering)

Menghapus redundansi komputasi: jika dua expression menghasilkan nilai yang sama, eliminasi yang kedua.

Loop Unroll

Mengulang body loop beberapa kali untuk mengurangi branch overhead, trade-off ukuran binary.

LICM (Loop Invariant Code Motion)

Memindahkan instruksi yang hasilnya tidak berubah di dalam loop ke luar loop.

SLP & Loop Vectorizer

  • SLP Vectorizer: Menggabungkan operasi scalar berdekatan ke SIMD vector.
  • Loop Vectorizer: Mengubah loop scalar menjadi loop vector (SIMD).

Praktik: Pipeline Optimasi Kustom

Bangun pipeline untuk mengukur dampak tiap pass:

Jalankan pass satu per satu dan bandingkan
# Baseline: tanpa optimasi
opt -S -O0 -o baseline.ll program.bc
 
# Hanya mem2reg
opt -S -passes=mem2reg -o after_mem2reg.ll program.bc
 
# mem2reg + instcombine
opt -S -passes='mem2reg,instcombine' -o after_instcombine.ll program.bc
 
# Pipeline penuh -O2
opt -S -O2 -o after_o2.ll program.bc

Gunakan pass -print-after-all untuk melihat IR setelah setiap pass:

Debug pipeline optimasi
opt -S -O2 -print-after-all -disable-output program.bc 2>pipeline.log

File pipeline.log akan berisi IR setelah setiap pass — sangat panjang tetapi sangat informatif.

Tip

Gunakan opt -passes=print<cfg>'*' untuk mencetak CFG (Control Flow Graph) dari setiap function dalam format DOT. Pipeline ini bisa dipipe ke dot untuk menghasilkan grafik visual — sangat berguna untuk memahami bagaimana optimizer mengubah struktur control flow.

Penutup

Inti yang harus dibawa pulang:

  • -O0 hingga -O3 dan -Os/-Oz mengontrol kedalaman optimasi.
  • Pass populer: mem2reg (SSA), inlining, GVN, loop unroll, LICM, SLP/Loop Vectorizer.
  • Gunakan opt -passes= untuk menjalankan pass spesifik satu per satu.
  • -print-after-all menampilkan IR setelah setiap pass — tool debugging pipeline.

Di episode 9 selanjutnya kita akan membahas Core API & Data Structures — hierarki Value → User → Instruction, Use edges (def-use chain), IRBuilder, dan program C++ yang membuat module berisi fungsi Fibonacci secara programatik. Sampai jumpa di episode 9!