Compilers & Code Generation

LLVM IR

/ EL-el-vee-em eye-arr /

Many compilers used to be one giant program that turned a single language into a single chip's code, and every new language or chip meant rewriting half of it. LLVM is a project that pulled out a reusable middle: a well-specified intermediate representation plus a large library of optimizations and back ends. LLVM IR is that shared representation — the common language that Clang (for C and C++), the Rust compiler, Swift, and many others all lower into.

LLVM IR is a low-level, typed, SSA-based representation. It looks a bit like a clean assembly language for an abstract machine with unlimited virtual registers (written with a % prefix, like %1, %sum). Every value has an explicit type (i32 for a 32-bit integer, i8* for a pointer to a byte, and so on). The code is organized into functions, each made of basic blocks (straight-line runs of instructions) connected by branches, and it uses phi nodes at merges because it is in SSA form. It exists in three equivalent shapes: human-readable text (.ll files), a compact binary called bitcode (.bc), and an in-memory data structure. The optimizer and back ends all operate on this one IR.

It matters because LLVM IR is the practical place where you can SEE what the optimizer is doing: dump it, read it, and watch constants fold and dead code vanish between -O0 and -O2. It also explains why so many languages share tooling — they all target one IR. A caveat: LLVM IR is not perfectly portable across LLVM versions or targets. It carries target assumptions (pointer size, data layout), and the same .ll can behave differently per target; treat it as an internal artifact, not a stable interchange format.

define i32 @add(i32 %a, i32 %b) { %sum = add i32 %a, %b ret i32 %sum } $ clang -O0 -S -emit-llvm add.c -o add.ll # write the textual LLVM IR

A whole add function in LLVM IR: typed values, virtual registers with %, SSA, one explicit return.

LLVM IR is not a stable, portable interchange format: it embeds target-specific assumptions (data layout, pointer width) and changes between LLVM versions, so the same .ll is not guaranteed to mean the same thing everywhere.

Also called
LLVM intermediate representationLLVM bitcode (the binary form)LLVM 位元碼