Mengonversi AST mini language ke LLVM IR menggunakan IRBuilder: visitor pattern codegen, konversi tipe dinamis, error handling codegen, dan menjalankan hasil kodegen via LLJIT (ORC) secara interaktif — REPL pertama yang menghasilkan 42 dari def f(x) x*2; f(21).

Setelah di episode 10 kita membangun lexer dan parser mini language — menghasilkan AST dari input REPL — pada episode ini kita menyelesaikan loop: codegen dari AST ke LLVM IR, lalu mengeksekusinya langsung via JIT.
Ini adalah momen magis pertama kalian: mengetik def f(x) x*2; f(21) di REPL dan mendapat hasil 42 — tanpa kompilasi ke file, tanpa proses build. Kode langsung dikonversi ke IR, dioptimasi, dan dieksekusi di memori.
Setiap node AST mengimplementasikan method codegen() yang menghasilkan LLVM Value:
Value *NumberExprAST::codegen() {
return ConstantFP::get(*TheContext, APFloat(val));
}
Value *VariableExprAST::codegen() {
Value *V = NamedValues[name];
if (!V) return LogErrorV("Unknown variable name");
return Builder->CreateLoad(Type::getDoubleTy(*TheContext), V, name.c_str());
}Value *BinaryExprAST::codegen() {
Value *L = LHS->codegen();
Value *R = RHS->codegen();
if (!L || !R) return nullptr;
switch (op) {
case '+': return Builder->CreateFAdd(L, R, "addtmp");
case '-': return Builder->CreateFSub(L, R, "subtmp");
case '*': return Builder->CreateFMul(L, R, "multmp");
case '<':
L = Builder->CreateFCmpULT(L, R, "cmptmp");
return Builder->CreateUIToFP(L, Type::getDoubleTy(*TheContext), "booltmp");
default: return LogErrorV("Invalid binary operator");
}
}Value *FunctionAST::codegen() {
Function *TheFunction = getFunction(proto->name);
if (!TheFunction) return nullptr;
BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", TheFunction);
Builder->SetInsertPoint(BB);
NamedValues.clear();
for (auto &Arg : TheFunction->args())
NamedValues[std::string(Arg.getName())] = &Arg;
Value *RetVal = body->codegen();
if (!RetVal) {
TheFunction->eraseFromParent();
return nullptr;
}
Builder->CreateRet(RetVal);
verifyFunction(*TheFunction);
return TheFunction;
}ORC JIT (On-Request Compilation) memungkinkan eksekusi kode LLVM langsung dari IR di runtime:
#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
static std::unique_ptr<orc::LLJIT> JIT;
void initJIT() {
auto JITBuilder = orc::LLJITBuilder();
JIT = JITBuilder.create().get();
}
Error addFunctionToJIT(Function *F) {
auto TSM = llvm::orc::ThreadSafeModule(
std::unique_ptr<Module>(F->getParent()->clone()),
std::make_unique<LLVMContext>());
JIT->addIRModule(std::move(TSM));
auto Entry = JIT->lookup("main");
if (!Entry) return Entry.takeError();
return Error::success();
}void mainLoop() {
initJIT();
while (true) {
std::cout << ">> ";
std::string line;
std::getline(std::cin, line);
if (line.empty()) continue;
Lexer lexer(line);
Parser parser(lexer);
auto ast = parser.parseTopLevel();
if (ast) {
auto *F = ast->codegen();
if (F) {
auto Entry = JIT->lookup("main");
if (Entry) {
auto fn = Entry->toPtr<double()>();
std::cout << "=> " << fn() << "\n";
}
}
}
}
}>> def f(x) x * 2
>> f(21)
=> 42
>> def fact(x) if x < 2 then 1 else x * fact(x-1)
>> fact(5)
=> 120Tip
LLJIT bisa dikonfigurasi untuk auto-optimasi setiap module yang ditambahkan. Gunakan JIT->getIRCompileLayer().setTransform(...) untuk menambahkan pass optimizer (misal O2) sebelum codegen — hasilnya lebih cepat tetapi JIT time sedikit lebih lama.
Error handling codegen penting untuk debugging:
std::unique_ptr<ExprAST> LogError(const char *Str) {
fprintf(stderr, "Error: %s\n", Str);
return nullptr;
}
Value *LogErrorV(const char *Str) {
LogError(Str);
return nullptr;
}
Function *LogErrorF(const char *Str) {
LogError(Str);
return nullptr;
}Selalu cek return value dari codegen() sebelum menggunakan hasilnya. Error yang tidak ditangkap akan menyebabkan segfault di LLVM — bukan error message yang informatif.
Inti yang harus dibawa pulang:
codegen() yang menghasilkan LLVM Value.CreateFAdd, CreateFMul, CreateCondBr.Di episode 12 selanjutnya kita akan membahas control flow, phi nodes, dan functions di IR — basic block & branching, phi nodes sebagai mekanisme merge nilai SSA, definisi/pemanggilan fungsi, recursion, dan implementasi if/else & loop pada mini language. Sampai jumpa di episode 12!