A teaching C compiler built with flex, bison, and LLVM 20. It uses bottom-up LALR parsing (not recursive descent) so the grammar stays small enough to study and modify.
lcc compiles a .c translation unit to a .o object file (required -o). Optionally emit assembly with -S. Link with clang or gcc to run the program.
| Small enough to read | Bottom-up LALR grammar (flex/bison) instead of a hand-written recursive-descent parser — the whole front-end is small enough to study and modify. |
| A real toolchain | Emits genuine LLVM 20 IR and native object files through IRBuilder and TargetMachine — not a toy backend. |
| Cleanly layered | Front-end, middle-end (IrOptimizer), and back-end (TargetBackend) are separated, so you can study or change one layer at a time. |
| Guided curriculum | An 18-milestone learning plan (M0–M18) takes you from a first build to custom LLVM passes and codegen — see docs/LearningPlan.md. |
| Inspectable | Dump AST graphs (Graphviz), pre/post-optimization IR, assembly, and IR / machine-instruction stats straight from the CLI. |
.c source
│ [front-end] flex (Lexer.l) + bison (Parser.y)
▼
AST (namespace AST, rooted at g_root)
│ [front-end] CodeGenerator::genIrCode() — single pass; types resolved on demand
▼
LLVM IR (llvm::Module, built with IRBuilder)
│ [middle-end] IrOptimizer::run() — LLVM New PM: -O0..-O3, custom passes
▼
Optimized IR (+ DWARF when -g)
│ [back-end] TargetBackend — llvm::TargetMachine
▼
.o object file (-o) ──optional──► .s assembly (-S)

The front-end AST for tests/0.hello_world.c, dumped with lcc -v out.dot and rendered by Graphviz (debug/0.hello_world.png).
There is no separate semantic-analysis pass: C type information is resolved on demand during genCode(), using opaque pointers (pointee types live on the AST VarType, not on llvm::Type*). Link the resulting .o with clang or gcc. Full walkthrough: docs/Pipeline.md and docs/LearningPlan.md.
git clone git@github.com:yanghuafang/lcc.git
cd lcc/scripts
# macOS: ./install-deps-macos.sh
# Ubuntu 24.04 / 26.04: ./install-deps-ubuntu.sh
./build-lcc.sh
./compile-tests.sh 0.hello_world.c
./link-tests.sh 0.hello_world.c
./run-tests.sh 0.hello_world.cExpected output: 0.hello_world.c PASS
Full regression suite:
./compile-tests.sh && ./link-tests.sh && ./run-tests.shBuild artifacts go to ../../lcc-build/ (sibling of the repo). Sample AST graphs and LLVM IR live under lcc/debug/ — middle-end snapshots use *.debug.pre.ll / *.post.ll (or .release.*); final IR after object emission uses *.debug.ll / *.release.ll (see compile modes).
The Quick start above uses the test harness; under the hood it is a plain compile → link → run flow that works on any C file in the supported subset. Since there is no preprocessor, declare the libc functions you use.
// sum.c
int printf(char*, ...);
int main() {
int total = 0;
for (int i = 1; i <= 10; i++) {
total += i;
}
printf("sum(1..10) = %d\n", total);
return 0;
}Save it as /tmp/sum.c, then from lcc/scripts (after ./build-lcc.sh):
# 1. Compile C -> object with lcc (-i and -o are both required)
../../lcc-build/lcc -i /tmp/sum.c -o /tmp/sum.o
# 2. Link the object with the system toolchain
clang /tmp/sum.o -o /tmp/sum
# 3. Run it
/tmp/sum
# -> sum(1..10) = 55lcc emits position-independent objects, so the same link step works on macOS and Linux without -no-pie. Add -O2 to optimize, -S /tmp/sum.s to also emit assembly, -g for a debuggable build, or -v /tmp/sum.dot to dump the AST graph. Full flag reference: docs/Usage.md.
Types (builtin, struct, union, enum), pointers, 1D/2D arrays and brace init, typedef, file/block static, functions, sizeof, casts, full expression/statement grammar (control flow, break/continue), and -g DWARF for debuggable builds.
Not included: preprocessor (#include, #define), 3D arrays, extern variables, block-scope typedef.
Details: docs/Language.md
lcc/
├── src/ # Compiler sources
│ ├── Lexer.l # flex lexer
│ ├── Parser.y # bison LALR grammar
│ ├── AbstractSyntaxTree.* # AST nodes + genCode() IR emission
│ ├── CodeGenerator.* # LLVM context/module, scoped symbol tables
│ ├── IrOptimizer.* # middle-end (LLVM New Pass Manager)
│ ├── TargetBackend.* # back-end (.o / .s emission)
│ ├── DebugInfoBuilder.* # DWARF debug info (-g)
│ ├── Visualizer.* # Graphviz AST graphs (-v)
│ └── passes/ # Custom LLVM passes (IR stats, fold-add-zero, machine stats)
├── tests/ # 40+ small C programs, each self-checks and prints "<name> PASS"
├── benchmarks/ # Larger workloads for bench.sh (M15)
├── scripts/ # build-lcc.sh, compile/link/run-tests.sh, smoke checks, bench
├── docs/ # Guides (start with LearningPlan.md)
├── debug/ # Committed AST / IR / asm goldens for the test suite
├── CMakeLists.txt # flex/bison codegen + LLVM configuration
└── LICENSE # MIT
Full index (grouped by learning path, how-to, and reference): docs/README.md.
| Guide | Topics |
|---|---|
| docs/LearningPlan.md | Start here — full learning path (M0–M18) |
| docs/MiddleBackendRoadmap.md | Middle/back-end implementation detail |
| docs/Install.md | Dependencies, build lcc, CMake options |
| docs/Usage.md | CLI flags, link, debug compiled programs |
| docs/Testing.md | Scripts, unit tests, compile modes |
| docs/Benchmark.md | Benchmark harness, workloads, recording opt results (M15) |
| docs/Development.md | Debug lcc in VS Code / LLDB |
| docs/Language.md | Full feature list and limitations |
| docs/Roadmap.md | Front-end language features (complete) |
| docs/Pipeline.md | Pipeline overview, LLVM tool reference, opt/asm/vectorization study (M9, M12, M14) |
| docs/Conflicts.md | Bison parser conflicts |
LLVM 20, flex, bison, argparse, graphviz, CMake 3.22+, C++17, and a system linker (clang or gcc). Supported platforms: macOS (Homebrew) and Ubuntu 24.04 / 26.04 LTS. See docs/Install.md.
Front-end language work (arrays through -g debug info) is complete; see docs/Roadmap.md. Middle/back-end milestones M0–M18 are complete, including the optional ones (M7, M8, M13, M15, M16, M17); see docs/LearningPlan.md.
Exploratory ideas for later — real diagnostics, more C language features, and deeper optimization/back-end passes — are recorded as unscheduled Future directions (no milestones attached) in docs/Roadmap.md and docs/MiddleBackendRoadmap.md.
Contributions are welcome — bug fixes, new test programs, documentation, and milestone work. Please read CONTRIBUTING.md for the build, test, and coding-style workflow, and run the full compile → link → run suite before opening a pull request.
lcc is released under the MIT License — see LICENSE.