cp-10 — LLVM IR Fundamentals

Status: ✅ Built (30/30 checks). Emitted IR runs under lli.

This lab takes the TAC IR from cp-08/09 and emits textual LLVM IR. The emitter is hand-rolled — no LLVM library dependency — so the lab builds anywhere a C++17 compiler is available. cp-11 replaces this with the real C++ IRBuilder API and integrates find_package(LLVM).

Restriction

Programs in cp-10 must be numeric-only. All MiniLang values lower to i64; booleans are 0 / 1 in i64. Strings (and function values) raise an error from the emitter — the runtime for them lands in cp-14.

Layout

FilePurpose
src/llvm_emit.{hpp,cpp}TAC IR → textual LLVM IR.
src/main.cppmlllvm driver. -O enables our cp-09 passes first.
tests/test_llvm_emit.cpp30 assertions covering module shape, arithmetic, control flow, function definition, globals, and the strings-rejected error path.

Build, test, smoke-run

cd src/cpp
cmake -S . -B build && cmake --build build
ctest --test-dir build --output-on-failure

# Use the system LLVM toolchain to actually execute the emitted module:
echo 'let i=0; while(i<3){print i; i=i+1;}' | ./build/mlllvm -O > /tmp/m.ll
/opt/homebrew/opt/llvm/bin/lli /tmp/m.ll      # prints 0 1 2

What the IR looks like

; ModuleID = 'minilang'
target triple = "arm64-apple-macosx"
@.fmt = private constant [6 x i8] c"%lld\0A\00"
declare i32 @printf(i8*, ...)

define i32 @main() {
  %i.addr = alloca i64
  store i64 0, i64* %i.addr
  br label %L0
L0:
  %v0 = load i64, i64* %i.addr
  %v1 = icmp slt i64 %v0, 3
  ...
}

Why hand-roll the emitter?

  • Forces engagement with the textual format — every LLVM IR document starts there, and you should be able to read it.
  • Keeps cp-10 dependency-free and portable.
  • Demonstrates the lowering decisions without LLVM's IRBuilder doing them silently — alloca for locals, load/store everywhere, explicit icmp + zext for booleans, explicit printf call.
  • Sets the conceptual baseline so cp-11 can focus on what the C++ API gives you for free (block management, name uniquing, type-erasure of constants, type verification, optimisation passes).

Step docs

  1. LLVM IR overview
  2. Types, values, and constants
  3. The module/function/block structure
  4. Memory model: alloca / load / store
  5. Lowering arithmetic and comparisons
  6. Lowering control flow
  7. The runtime ABI: printf and main