Compilation Pipeline

.ml implementation, .mli interface, .cmi compiled interface, .cmo/.cmx compiled object, .cma/.cmxa library archives, .cmt/.cmti typed trees (for tooling), .cmxs dynamic plugin.

41.1 Compilers

bytecode .cmo / .cma portable, fast compile, runs on ocamlrun native .cmx / .cmxa / .o wrapper resolving libraries

ocamlfind ocamlopt -package str -linkpkg used by build systems lexer generator (.mll) ocamlyacc / menhir parser generators (.mly)

41.2 File types

.ml implementation, .mli interface, .cmi compiled interface, .cmo/.cmx compiled object, .cma/.cmxa library archives, .cmt/.cmti typed trees (for tooling), .cmxs dynamic plugin.

41.3 Compiler pipeline

Inspect: ocamlopt -dparsetree -dtypedtree -dlambda -dcmm -S file.ml.

Source → Lexer → Parser → Parsetree (PPX operates here)
  → Typing → Typedtree → Lambda (untyped IR)
  → [bytecode] Instruct → bytecode
  → [native] Flambda (optional optimizer) / Closure → Cmm → Mach → Linear → assembly

41.4 Flambda

An optimizing middle-end (inlining, specialization, unboxing): opam switch create 5.3.0+flambda (or ocaml-option-flambda), compile with -O3. Jane Street's Flambda 2 / OxCaml branch adds modes (local_, unique), unboxed types, and stack allocation — an experimental superset worth watching.

41.5 Lexing & parsing with ocamllex + Menhir

(* lexer.mll *)
{ open Parser }
rule token = parse
  | [' ' '\t' '\n'] { token lexbuf }
  | ['0'-'9']+ as n { INT (int_of_string n) }
  | '+' { PLUS } | '*' { TIMES } | '(' { LPAREN } | ')' { RPAREN }
  | eof { EOF }
(* parser.mly *)
%token <int> INT
%token PLUS TIMES LPAREN RPAREN EOF
%left PLUS
%left TIMES
%start <int> main
%%
main: e = expr EOF { e }
expr:
  | i = INT { i }
  | LPAREN e = expr RPAREN { e }
  | a = expr PLUS b = expr { a + b }
  | a = expr TIMES b = expr { a * b }
(ocamllex lexer)
(menhir (modules parser))
let parse s = Parser.main Lexer.token (Lexing.from_string s)

41.6 Dynamic loading

Dynlink.loadfile "plugin.cmxs" loads plugins at runtime.

41.7 Cross-compilation & targets

Native backends: x86-64, ARM64, RISC-V, POWER, s390x. JS via js_of_ocaml, WebAssembly via wasm_of_ocaml, unikernels via MirageOS.

🧪 Part III Exercises

Write a GADT-typed stack machine where push/add instructions are checked to never underflow.

Implement a typed key-value heterogeneous map using Type.Id or an existential GADT.

Use polymorphic variants to compose error types from two independent "libraries".

Write a generator using effect handlers that yields all permutations of a list.

Implement async/await with effects on top of the cooperative scheduler from §35.6.

Parallel merge sort with Domainslib; benchmark against List.sort for 1M elements.

Write a PPX [%env "VAR"] that inlines an environment variable at compile time.

Bind a small C function (e.g., getpid) with both raw stubs and ctypes.

Implement a JSON parser using your own parser combinators (§33.7).

Build an Eio HTTP-less chat server: clients connect via TCP and broadcast lines.