FForge
← Labsmlir-setup-01MLIR toolchain setup●○○○

Getting an MLIR toolchain, and knowing what you can skip

mlir-setup/01_toolchain.mdneeds: git, cmake, ninja, a C++ compiler, ~25 GB free disk
evidence to leave with
check_env.sh output with every WARN either fixed or consciously accepted, and four tools reporting the same version
<!-- steps: manual — these commands clone and build LLVM. tools/steps.py lists them and refuses to run them; type the ones you need yourself. -->

SETUP LAB #01 — Getting an MLIR toolchain, and knowing what you can skip

TRACK: setup | DIFFICULTY: 1 | NEEDS: git, cmake, ninja, a C++ compiler, ~25 GB free disk FILES: check_env.sh (the verifier), ../tools/env.sh (the shell setup), ../tools/env.local.sh.example

What this teaches

Unlike the rest of debug_lab, these labs do not run on a stock distro toolchain. They need one consistent LLVM/MLIR build — mlir-opt, mlir-translate, mlir-runner, opt, llc and clang all from the same commit. Mixing a distro llc with a newer mlir-translate works most of the time and fails confusingly the rest of the time, which is the worst possible behaviour for a lab whose whole point is reading exact output.

The good news: most tracks need only that one build. Triton, IREE and qemu are per-track extras, and the table in STEP 5 says exactly what you lose without each one. You can do tracks 01–06 and 09 with nothing but LLVM.

STEP 1 — Check whether you already have MLIR

Distros increasingly package the MLIR tools. If you get a version here, you may be able to skip the build entirely.

$ mlir-opt --version

If that prints a version, check that clang --version reports the same major version. If both agree, jump to STEP 4.

If it is not found, search your package manager before building — a package is minutes, a build is an hour:

$ apt-cache search mlir # Debian / Ubuntu $ dnf search mlir # Fedora $ pacman -Ss mlir # Arch

Two things to check on a packaged install: that it includes libmlir_runner_utils.so (several labs JIT-execute and need it), and that mlir-runner exists. It was called mlir-cpu-runner before LLVM 20; if you only have the old name, export MLIR_RUNNER=mlir-cpu-runner and substitute it where the labs say mlir-runner.

STEP 2 — Otherwise, build LLVM with MLIR

A shallow clone is enough unless you plan to bisect upstream.

$ git clone --depth 1 https://github.com/llvm/llvm-project.git ~/llvm-project $ cd ~/llvm-project

Configure. Every option here earns its place, and the comments say which lab needs it:

$ cmake -G Ninja -S llvm -B build \ -DLLVM_ENABLE_PROJECTS="mlir;clang" \ -DLLVM_TARGETS_TO_BUILD="X86;AArch64" \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ASSERTIONS=ON \ -DMLIR_ENABLE_BINDINGS_PYTHON=ON \ -DLLVM_CCACHE_BUILD=ON
  • mlir;clang — clang must come from the same build, because tracks 02, 04 and 05 hand mlir-translate output straight to clang.
  • X86;AArch64 — AArch64 is not optional: tracks 05 and 07 cross-compile to it. Add your host arch if it is neither.
  • LLVM_ENABLE_ASSERTIONS=ON — this is what gives you -debug-only and pass statistics. Without it, tracks 06 and 09 lose their main instrument. A release build without assertions still runs every other lab.
  • MLIR_ENABLE_BINDINGS_PYTHON=ON — only track 10 needs it. Drop it to save build time if you are skipping that track.

Then build. This is the long part:

$ ninja -C build

Budget roughly 20–30 GB of disk and 30–90 minutes on a modern multi-core machine; both vary enough with core count and linker that you should measure rather than trust the range. If you are short on RAM, add -DLLVM_PARALLEL_LINK_JOBS=1 to the cmake line — LLVM's link steps are the memory spike, and an OOM at 95 % is a miserable way to find that out.

To build only what the labs invoke, rather than all of LLVM:

$ ninja -C build mlir-opt mlir-translate mlir-runner opt llc clang \ llvm-mca FileCheck llvm-lit llvm-reduce mlir-reduce not

STEP 3 — Point the labs at your build

Your paths go in tools/env.local.sh, which is not tracked by git. That is deliberate: nobody should have to edit a tracked file to use these labs, and no one's home directory should end up in the repository.

$ cp tools/env.local.sh.example tools/env.local.sh $ $EDITOR tools/env.local.sh # set LLVM_BUILD, and LLVM_SRC if you cloned it

Then source the environment. Every lab assumes you have done this in the shell you are working in:

$ source tools/env.sh

That puts one toolchain on PATH and defines the helpers the labs use: mrun (JIT-run a lowered module), lower_std (the textbook tensor-to-LLVM pipeline), $LOWER_STD / $LOWER_SVE (those pipelines as strings), a64cc / a64run (cross-compile and run under qemu), and mca_v2.

STEP 4 — Verify, and read the warnings

$ 00_setup/check_env.sh

It prints one line per tool: OK, WARN or MISSING, and for anything missing it names the fix. Two results are worth understanding rather than just fixing:

  • WARN assertions build — your LLVM was built without assertions. Labs 06/01 and 09 use -debug-only and pass statistics, which do not exist in such a build. Everything else works.
  • WARN headers != build — your LLVM source tree is at a different commit than your build. Harmless until you write out-of-tree C++ (track 09), where it produces link errors that look like missing symbols rather than a version mismatch. Lab 09 STEP 0 covers this, and 09_write_a_pass/sync_headers.sh fixes it.

WARN governor is about measurement noise, not correctness — see STEP 5.

STEP 5 — The per-track extras, and what you lose without each

Nothing below is needed for tracks 01, 02, 03, 06 or 09.

extrahowtrackswithout it
qemu-aarch64 + sysroottools/a64-setup.sh at the repo root — no sudo, it installs under ~/.local02/04, 05, 07/02You can still compile to AArch64 and read the asm; you cannot run it, so correctness claims become "it compiled"
Tritonpython3 -m venv .venv && .venv/bin/pip install numpy triton07No TTIR to dump. The lab's pasted TTIR is still readable, but you are reading rather than doing
IREE (runtime).venv/bin/pip install iree-base-compiler iree-base-runtime08No reference numbers of your own
IREE (source)git clone https://github.com/iree-org/iree, then set IREE_SRC08, 11The "go read how they decided that" steps. Track 11 is only source reading, so it needs this
MLIR Python bindingsbuilt by MLIR_ENABLE_BINDINGS_PYTHON=ON above, plus a venv on the same Python version the bindings were built for10Track 10 does not run
perfyour distro's package; may need kernel.perf_event_paranoid lowered04/02, 07/02The profile steps. These labs are built around "profile before you touch the compiler", so this one costs more than it looks
CPU pinningtaskset, via tools/pin.sh04, 05Much noisier timings

On the last two: this lab's own numbers were taken on a laptop with 30–50 % run-to-run variation. That is why the labs insist on explaining a speedup with an artifact — instruction counts, spill counts, page faults — rather than a stopwatch. Treat a ratio under 2× as unproven until you have repeats or a noise-free counter, whatever your machine.

TRY THIS YOURSELF

  1. Run check_env.sh and write down every WARN. Decide for each whether you are going to fix it or work around it, before you start track 01 — that decision is easier now than halfway through a lab.
  2. Confirm your toolchain is internally consistent: for t in mlir-opt clang llc opt; do $t --version | head -1; done. All four should agree. If they do not, PATH is finding a distro tool.
  3. echo "$LOWER_STD" and read it. It is 8 lines and it is the pipeline behind most of tracks 01–04; you will meet every pass in it individually.
◇
Finish the lab: write the evidence note

A lab is done when you can say what it showed on your machine — not when you have read it.