Self-hosted service
esumii/min-caml avatar
esumii/min-caml

min-caml: a teaching compiler for a subset of OCaml, in about 2000 lines

moved from https://sourceforge.net/p/min-caml/code/

532 stars130 forksOCamlNOASSERTION

At a glance

What is it?
min-caml is an educational compiler for a minimal subset of OCaml, written in roughly 2000 lines of OCaml, that targets x86, SPARC and PowerPC and emits assembly you can read. It exists to be read and modified, and the documentation is a set of papers and slides rather than a manual.
Who is it for?
Adopt min-caml if you want to understand how a compiler works from source you can read end to end, since the whole project is about two thousand lines and the test suite checks its output against real OCaml. Do not adopt it as a toolchain, because it only compiles a subset and only targets three old architectures, and the x86_64 version is deliberately left as an exercise.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 67 days ago.
What is it written in?
Mainly OCaml, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A compiler meant to be read rather than used

min-caml is described in one line in the README as an educational compiler for a minimal subset of OCaml, written in about 2000 lines of OCaml. Everything else follows from that sentence.

The scale is the point. A production compiler for a language of OCaml's size is hundreds of thousands of lines, and the interesting parts are buried. min-caml is small enough that a reader can hold the entire pipeline in their head, which is what makes it usable as a teaching artefact. The repository layout is consistent with that: alpha.ml, beta.ml, typing.ml, closure.ml, inline.ml, constFold.ml, elim.ml, regAlloc.ml, emit.ml and asm.ml sit side by side in the root, with the .mli interfaces next to them, plus the parser and lexer as myParser.mly and myLexer.mll.

Those file names are the conventional compiler pipeline stages, and their presence tells you the shape of the thing before you read a line of it. Parsing produces a syntax tree, typing checks it, alpha and beta are the conversion stages, closure does the lambda lifting, inline and constFold are the optimisations, elim eliminates redundant code, regAlloc allocates registers, and emit produces the target code.

The other giveaway is the tutorial material sitting in the repository root: tutorial-mincaml.doc, tutorial-ml.doc, and three presentation files, fdpe05.ppt, gaiyou.ppt and saishuu.ppt. A project that ships slides about itself is a project that expects to be taught from.

The primary language is OCaml, the default branch is master, the last push was on 2026-08-17, and there are no GitHub releases. The licence is not one GitHub can classify, so the LICENSE file is the thing to read before you do anything with it.

The project has also moved. The README records that it moved from SourceForge to GitHub on October 9, 2013, which is when the URL in the repository description points, and the tree still carries a bytemark/ directory, an OCamlBytecodeMark-style benchmark, along with shootout/ and test/ directories.

Building it, and the architecture switch you have to pick first

The install steps in the README are six numbered lines and they contain one decision you have to make before anything compiles.

First you install OCaml if you have not. Then you download and expand the project.

bash
git clone https://github.com/esumii/min-caml.git

Then you change into the directory and execute the architecture script.

bash
cd min-caml/
./to_x86

The README gives the alternatives: ./to_x86 for x86, or ./to_sparc for SPARC, ./to_ppc for PowerPC. Then make.

bash
make

And then it suggests trying the ray tracer, which is the part that takes time.

bash
cd min-rt/ ; make

The README is upfront about the cost of that last step, noting that it takes time because of OCaml bytecode, for testing by comparison, and not MinCaml. That parenthetical is the whole point of the ray tracer: it is not there to be fast, it is there to produce output you can compare against OCaml's own compiler.

The architecture scripts are not configuration in the usual sense. They are checked into the repository as executable files named to_x86, to_sparc and to_ppc, and the README states for the PowerPC and SPARC addition that you must execute either ./to_ppc or ./to_sparc before make. The Makefile then picks up the matching target directory, which is why the repository has SPARC/, PowerPC/ and x86/ directories at the top level. The choice selects which backend's assembly and code generation the build uses.

The order matters and it is easy to get wrong. The scripts are not a post-install step you can run later, because make reads the switch that the script wrote.

The build itself is driven by a Makefile for GNU Make alongside an OCamlMakefile, and the Makefile shows the OCaml side compiled in both debug-code and native-code flavours, with a note explaining that in OCamlMakefile the .mli files get compiled for both targets, which is why both appear on the right-hand side of the default target.

The test suite, which compares MinCaml against real OCaml

The most instructive part of the repository is the Makefile's test machinery, because it shows how correctness is established for a compiler this small.

There is a list of test programs, and the do_test target turns each into a comparison file.

make
TESTS = print sum-tail gcd sum fib ack even-odd \
 adder adder2 funcomp cls-rec cls-bug cls-bug2 cls-reg-bug \
 shuffle spill spill2 spill3 join-stack join-stack2 join-stack3 \
 join-reg join-reg2 non-tail-if non-tail-if2 \
 inprod inprod-rec inprod-loop matmul matmul-flat \
 manyargs

do_test: $(TESTS:%=test/%.cmp)

The names are worth reading, because they say what the compiler is being asked to get right. There are the ordinary arithmetic programs like gcd, sum, fib and ack, and then the interesting ones: spill, spill2 and spill3 exercise register spilling, which is the part of a register allocator that is hardest to verify by eye. join-stack, join-stack2 and join-stack3 and the join-reg pair cover stack and register joining, the transformation where two control paths merge and the compiler has to reconcile their live values. cls-rec, cls-bug, cls-bug2 and cls-reg-bug are named after bugs, which tells you they are regression tests for defects that were found and fixed rather than features. non-tail-if and non-tail-if2 cover a case in the closure conversion where the tail-call optimisation does not apply.

The comparison itself is the mechanism, and it is elegant. Each test is compiled twice.

make
test/%.s: $(RESULT) test/%.ml
	./$(RESULT) test/$*
test/%: test/%.s libmincaml.S stub.c
	$(CC) $(CFLAGS) -m32 $^ -lm -o $@
test/%.res: test/%
	$< > $@
test/%.ans: test/%.ml
	ocaml $< > $@
test/%.cmp: test/%.res test/%.ans
	diff $^ > $@

MinCaml compiles the program to assembly, the assembly is linked with the runtime library and a stub and executed, and the output is captured. The same source is then run through the real OCaml compiler and its output is captured. The two are diffed. The test passes when the compiler's output matches OCaml's output byte for byte.

That is a genuinely strong oracle for a teaching compiler. It does not test that the code is good, only that the observable behaviour matches the reference implementation, which is the right thing to check for a compiler of a subset of a real language.

The -m32 flag in the C compilation step is the reason the build needs a 32-bit toolchain, and the runtime library and stub are compiled from the runtime sources rather than linked against anything installed.

The Makefile's comments are in Japanese, and the test/ directory in the tree holds the test programs themselves, so the comparison cases are readable.

The three questions the README answers, and what they reveal

The README has three FAQ entries, and between them they describe the project's scope precisely.

FAQ 1 asks whether there is an x86_64 version. The answer is that there is, but it is left as an exercise for students and not included in this distribution.

That is a design decision stated as a limitation, and it is the clearest signal of the project's purpose. Supporting x86_64 means porting the backend, and the author has decided that doing that work is a better exercise than handing it over. A reader who wants to run the compiler on a modern machine is expected to do the port.

FAQ 2 asks whether there is a version that emits C code. The answer is: see above. The same reasoning applies, and the cross-reference makes the intent obvious.

FAQ 3 asks what to do if your OS does not support x86_32, i386. The answer is to use Docker.

bash
docker run --platform=linux/386 -it --name min-caml debian:bookworm

That is a practical answer to a real constraint, and it is worth noting that the platform flag is what makes it work: the image is a standard Debian, run under emulation in 32-bit mode.

So the three answers together describe the project's boundaries. It compiles a subset of OCaml, it targets 32-bit x86, SPARC and PowerPC, and it deliberately stops short of the modern architectures and of the C backend.

There is one more piece of history in the README that speaks to the register allocator. The update from September 17, 2008 records that the register allocator now uses a simpler algorithm, and that it omits the backtracking, ToSpill and NoSpill, in previous versions.

So the allocator was deliberately simplified at some point, which is exactly the kind of change you would make if the goal were teachability rather than output quality. The spilling test cases in the test list exist because the simpler algorithm still has to get spilling right, and a spill bug is the kind of defect a differential test against OCaml catches immediately.

Where min-caml is the wrong tool

There are three limits, and the first one is that it is not a compiler you would use for work.

The first is that it compiles a minimal subset of OCaml. The README says so, and the test list shows the boundary: there are integers, arithmetic, conditionals, recursion, tuples and lists, but no objects, no modules beyond what the subset covers, no lazy values, no effects beyond what the runtime supports. Code that uses the rest of OCaml will not compile, and the way you find out is a type error from the subset's own typing module rather than a clear unsupported-feature message.

The second is the architecture and word size. The supported targets are 32-bit x86 with SSE2, SPARC and PowerPC. The x86_64 port is an exercise, the C backend is an exercise, and the Docker workaround is a 32-bit emulation. On a modern arm64 or x86_64 laptop, getting this running means either the emulator or doing the port yourself.

The third is the licence and the maintenance shape. The licence is not one GitHub can classify, so the LICENSE file is the only authority, and the project has no GitHub releases at all. The last push was on 2026-08-17, so it is not abandoned, but a compiler with no releases is a repository you clone rather than a package you depend on.

There is a fourth point that is not a limitation so much as a boundary of the medium. Everything about this project is in the repository: the source, the papers, the slides and the tutorial documents, at esumii.github.io/min-caml/. The Japanese and English web pages and the two academic papers, jpaper.pdf and paper.pdf, are the primary documentation, and they are in two languages. That is a genuine advantage over a man page, because a compiler's design is better explained in a paper with diagrams than in a README, but it does mean the material is not searchable in the way API documentation is.

The honest alternatives are different in kind. To learn compilation, a modern book with a working implementation in a language you already read is faster than reverse-engineering a two-thousand-line OCaml project, and there are several of those. To read a compiler written in OCaml specifically, the OCaml compiler itself is a larger and more realistic system, and reading it alongside min-caml shows what production concerns look like.

What min-caml offers that those do not is the combination of being small enough to finish and tested well enough to trust. The differential test suite against real OCaml is the part that makes it a usable teaching artefact rather than an interesting toy, and the deliberate gaps, the 32-bit targets and the missing backends, are what a student is meant to fill in.

Editorial conclusion

Adopt min-caml if you want to understand how a compiler works from source you can read end to end, since the whole project is about two thousand lines and the test suite checks its output against real OCaml. Do not adopt it as a toolchain, because it only compiles a subset and only targets three old architectures, and the x86_64 version is deliberately left as an exercise. Before you start, note that the build needs a 32-bit capable toolchain and that the last push was on 2026-08-17 with no tagged releases.

Frequently asked questions

What is min-caml and what is it for?

It is an educational compiler for a minimal subset of OCaml, written in about 2000 lines of OCaml. It is meant to be read and modified, which is why the x86_64 port and the C backend are left as exercises for students.

How do I build min-caml?

Install OCaml, clone the repository, then run the architecture script before make: ./to_x86 for x86, ./to_sparc for SPARC or ./to_ppc for PowerPC. The scripts must run before make because the build reads the switch they write. Afterwards you can try the ray tracer with cd min-rt/ ; make, which takes time because it is OCaml bytecode rather than MinCaml.

How does the min-caml test suite check correctness?

Each test program is compiled by MinCaml to assembly, linked with the runtime and a stub, and executed, while the same source is also run through the real OCaml compiler. The two outputs are captured and diffed, so a test passes only when the compiler's behaviour matches OCaml's exactly. The test list includes spill, join-stack and join-reg cases for the harder parts of register allocation.

Can I run min-caml on a 64-bit machine?

There is an x86_64 version but it is left as an exercise for students and is not included in the distribution. The README's practical answer for operating systems that do not support x86_32 is to use Docker with the linux/386 platform, for example docker run --platform=linux/386 -it --name min-caml debian:bookworm.

Where is the min-caml documentation?

The README points at a Japanese web page, an English web page, and academic papers in both languages, all hosted at esumii.github.io/min-caml/, and the repository also contains tutorial-mincaml.doc, tutorial-ml.doc and three presentation files. The project moved from SourceForge to GitHub on October 9, 2013.

Official sources

  1. esumii/min-caml on GitHub
  2. Issues
  3. README