Memahami hierarki data LLVM: Value → User → Instruction, Use edges (def-use chain), Module/Function/BasicBlock traversal, serta IRBuilder untuk membangun instruksi secara programatik — ditambah praktik program C++ yang membuat module berisi fungsi Fibonacci.

Setelah di episode 8 kita memahami optimasi LLVM dan pipeline pass, pada episode ini kita beralih dari CLI ke C++ API. Memahami data structures dan API LLVM di level code adalah langkah selanjutnya untuk menulis pass yang kompleks, membangun front-end, atau menggunakan libTooling.
LLVM API mungkin terasa overwhelming di awal karena jumlah kelasnya yang besar. Tetapi ada beberapa kelas inti yang menjadi fondasi semuanya: Value, User, Instruction, Module, Function, BasicBlock, dan IRBuilder.
Semua elemen dalam LLVM IR mewarisi dari kelas Value:
add i32 %a, %b adalah User yang menggunakan %a dan %b.LLVM mempertahankan def-use chain — untuk setiap Value, kalian bisa menemukan semua User-nya:
for (User *U : myValue->users()) {
if (Instruction *Inst = dyn_cast<Instruction>(U)) {
errs() << "Used by: " << *Inst << "\n";
}
}Ini adalah salah satu fitur paling powerful dari LLVM API — kalian bisa menelusuri dari satu nilai ke seluruh kode yang menggunakannya tanpa scanning seluruh module.
IRBuilder adalah helper class yang menyederhanakan pembuatan instruksi LLVM IR:
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
using namespace llvm;
int main() {
LLVMContext Context;
Module *M = new Module("fibonacci", Context);
IRBuilder<> Builder(Context);
// Define function: i32 @fib(i32)
FunctionType *FuncTy = FunctionType::get(
Builder.getInt32Ty(), {Builder.getInt32Ty()}, false);
Function *F = Function::Create(
FuncTy, Function::ExternalLinkage, "fib", M);
// Create basic blocks
BasicBlock *EntryBB = BasicBlock::Create(Context, "entry", F);
BasicBlock *RecurseBB = BasicBlock::Create(Context, "recurse", F);
BasicBlock *BaseBB = BasicBlock::Create(Context, "base", F);
// Entry: if (n <= 1) goto base else goto recurse
Argument *N = &*F->arg_begin();
Builder.SetInsertPoint(EntryBB);
Value *Cmp = Builder.CreateICmpSLE(N, Builder.getInt32(1));
Builder.CreateCondBr(Cmp, BaseBB, RecurseBB);
// Base: return 1
Builder.SetInsertPoint(BaseBB);
Builder.CreateRet(Builder.getInt32(1));
// Recurse: return n * fib(n-1)
Builder.SetInsertPoint(RecurseBB);
Value *NMinus1 = Builder.CreateSub(N, Builder.getInt32(1));
Value *TailCall = Builder.CreateCall(F, {NMinus1});
Value *Result = Builder.CreateMul(N, TailCall);
Builder.CreateRet(Result);
verifyModule(*M);
M->print(errs(), nullptr);
return 0;
}Build dan jalankan:
clang++ -std=c++17 $(llvm-config --cxxflags) \
$(llvm-config --ldflags) -o fibonacci fibonacci.cpp $(llvm-config --libs core)
./fibonacciOutputnya adalah LLVM IR textual dari fungsi Fibonacci yang dibangun sepenuhnya secara programatik.
for (Function &F : *M) {
if (F.isDeclaration()) continue;
errs() << "Function: " << F.getName() << "\n";
for (BasicBlock &BB : F) {
errs() << " Block: " << BB.getName() << "\n";
for (Instruction &I : BB) {
errs() << " " << I << "\n";
}
}
}Note
LLVMContext adalah container untuk state LLVM yang tidak berbagi dengan context lain. Setiap module harus punya context sendiri — ini mencepatkan threading dan mengisolasi modul satu sama lain. Dalam production, satu context per thread adalah praktik umum.
Inti yang harus dibawa pulang:
users().Di episode 10 selanjutnya kita akan memasuki Fase 3: Membangun Language Front-End — mini language dengan lexer & parser ala Kaleidoscope, tokenisasi, recursive descent parsing, dan pembentukan AST dari input REPL. Sampai jumpa di episode 10!