Teknik alloca-bump untuk variabel mutable di LLVM IR (alloca + load/store dengan biarkan mem2reg mengoptimalkan), memasang pass pipeline optimasi ke REPL, dan membandingkan IR sebelum/sesudah pipeline pada kode loop yang sama.

Setelah di episode 12 kita memahami control flow dan phi nodes, pada episode ini kita menyelesaikan dua aspek penting: bagaimana menangani variabel mutable (yang tidak ada secara natural dalam SSA), dan bagaimana mengintegrasikan pipeline optimasi ke dalam REPL kita.
Variabel mutable adalah tantangan utama dalam SSA: bagaimana "mengubah" nilai yang sudah di-assign? Solusi LLVM adalah teknik alloca-bump — gunakan memory untuk variabel, biarkan pass mem2reg mengoptimalkannya ke register SSA jika memungkinkan.
Tulis variabel mutable ke memory sementara, biarkan optimizer memindahkannya ke register:
define i32 @sum_loop() {
entry:
%sum = alloca i32, align 4
%i = alloca i32, align 4
store i32 0, ptr %sum
store i32 0, ptr %i
br label %loop
loop:
%i_val = load i32, ptr %i
%sum_val = load i32, ptr %sum
%cmp = icmp slt i32 %i_val, 10
br i1 %cmp, label %body, label %exit
body:
%new_sum = add i32 %sum_val, %i_val
store i32 %new_sum, ptr %sum
%new_i = add i32 %i_val, 1
store i32 %new_i, ptr %i
br label %loop
exit:
%final = load i32, ptr %sum
ret i32 %final
}Tanpa optimasi, ini menghasilkan banyak load/store ke memory. Tetapi setelah mem2reg, optimizer menghapus alloca dan menggunakan register SSA:
define i32 @sum_loop() {
entry:
br label %loop
loop:
%sum = phi i32 [ 0, %entry ], [ %new_sum, %body ]
%i = phi i32 [ 0, %entry ], [ %new_i, %body ]
%cmp = icmp slt i32 %i, 10
br i1 %cmp, label %body, label %exit
body:
%new_sum = add i32 %sum, %i
%new_i = add i32 %i, 1
br label %loop
exit:
ret i32 %sum
}Tambahkan pass pipeline ke REPL kita:
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/StandardInstrumentations.h"
void optimizeModule(Module *M) {
ModuleAnalysisManager MAM;
PassBuilder PB;
PB.registerModuleAnalysisManagers(MAM);
ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(
OptimizationLevel::O2);
MPM.run(*M, MAM);
}Gunakan setelah codegen sebelum code execution:
auto *F = ast->codegen();
if (F) {
optimizeModule(F->getParent()); // Optimize!
auto Entry = JIT->lookup("main");
if (Entry) {
auto fn = Entry->toPtr<double()>();
std::cout << "=> " << fn() << "\n";
}
}# Compile tanpa optimasi
clang -emit-llvm -S -O0 -o before.ll loop.c
# Compile dengan O2
clang -emit-llvm -S -O2 -o after.ll loop.c
wc -l before.ll after.llPerbedaan signifikan: -O0 menghasilkan banyak alloca/load/store sementara -O2 menghasilkan SSA murni dengan loop yang lebih sederhana.
Tip
Teknik alloca-bump adalah pendekatan universal untuk variabel mutable di LLVM IR: selalu gunakan alloca untuk variabel, lalu biarkan optimizer memutuskan apakah itu layak di-register atau tetap di memory. Jangan mencoba manual SSA untuk variabel mutable — biarkan mem2reg dan sroa yang melakukannya.
Inti yang harus dibawa pulang:
alloca + load/store untuk variabel mutable, biarkan optimizer mengonversi ke SSA.mem2reg menghapus alloca dan mengonversi ke phi nodes SSA murni.PassBuilder::buildPerModuleDefaultPipeline memungkinkan optimasi langsung di REPL.Di episode 14 selanjutnya kita akan membahas debug information & metadata — DIBuilder untuk generate DWARF metadata agar bisa didebug di gdb/lldb, metadata nodes, dan step-through mini language di lldb. Sampai jumpa di episode 14!