cp-11 · LLVM Codegen (the real C++ API)

Goal: emit a verified llvm::Module straight from our TAC IR using IRBuilder, then run the new-pass-manager O2 pipeline (mem2reg, instcombine, GVN, SimplifyCFG, …) over it. Execute with lli, or pipe through llc to produce native object code.

cp-10 wrote LLVM IR as text. cp-11 builds the same IR through the official C++ API, links against libLLVM*, and lets LLVM run its production-grade optimisation pipeline on the result.

What changed since cp-10

Concerncp-10cp-11
IR producerhand-written text emitterllvm::IRBuilder<>
Verifiernone — trusted lli to complainllvm::verifyModule after build
OptimisernonePassBuilder::buildPerModuleDefaultPipeline(O2)
Globalsstring-templated @x = global i64 0new llvm::GlobalVariable(...)
Format stringhand-emitted [6 x i8] literalIRBuilder::CreateGlobalString

Build & run

cmake -S src/cpp -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build build
./build/tests/test_llvm_codegen      # → 25/25 checks passed

# REPL-ish: pipe MiniLang in, get IR out
echo 'fn fib(n){ if(n<2){return n;} return fib(n-1)+fib(n-2);} print fib(20);' \
  | ./build/mlcc -O \
  | /opt/homebrew/opt/llvm/bin/lli
# 6765

-O runs both our cp-09 TAC passes and LLVM's O2 pipeline.

Layout

src/cpp/
├── CMakeLists.txt           # find_package(LLVM); link core/passes/analysis/...
├── src/
│   ├── llvm_codegen.hpp     # CodegenResult + build()/optimise()
│   ├── llvm_codegen.cpp     # IRBuilder emitter + new-PM pipeline
│   └── main.cpp             # `mlcc` CLI
└── tests/test_llvm_codegen.cpp

Reading order

  1. steps/01-llvm-cpp-api-tour.md
  2. steps/02-irbuilder.md
  3. steps/03-verifier.md
  4. steps/04-new-pass-manager.md
  5. steps/05-mem2reg-and-O2.md
  6. steps/06-globals-and-runtime.md
  7. steps/07-targets-and-llc.md