TRACK: debugging | DIFFICULTY: 2 | NEEDS: llvm-lit, FileCheck, not
FILES: tests/lit.cfg.py, tests/*.mlir
Every MLIR project (upstream, IREE, triton-shared, any vendor backend) tests
passes the same way: a .mlir file whose // RUN: lines run
the tool, and whose // CHECK: lines are patterns FileCheck matches against the
output, in order. llvm-lit finds and runs the files. When you fix something,
the PR carries a test like this. A test you haven't seen fail proves nothing,
so this lab's tests each come with a way to make them fail.
| test | pins | FileCheck features used |
|---|---|---|
fusion_no_temp_buffer.mlir | lab 04/02: fused kernel writes straight to its output | CHECK-LABEL, CHECK-NOT, CHECK-SAME, CHECK-NEXT, %{{.*}} regex, [[OUT:.*]] capture |
sve_no_alloca_in_loop.mlir | lab 05/02 failure 3: no stack alloca in the k loop | two RUN lines: the fix passes, and the broken pipeline is detected (--check-prefix=BROKEN) |
sve_backend_crash.mlir | lab 05/02 failure 2 (via lab 06/02's reduction) | not --crash llc: asserts the bad order still crashes |
$ llvm-lit -v tests 2>&1 | grep -E '^(PASS|FAIL)|Passed|Failed' PASS: mlir_lab :: fusion_no_temp_buffer.mlir (1 of 3) PASS: mlir_lab :: sve_backend_crash.mlir (2 of 3) PASS: mlir_lab :: sve_no_alloca_in_loop.mlir (3 of 3) Passed: 3 (100.00%)
(lit.cfg.py puts build-full/bin on PATH and defines %lab as the lab
root, so tests can refer to lab inputs.)
eliminate-empty-tensors)$ mlir-opt ../04_optimization/fusion.mlir --pass-pipeline='builtin.module(linalg-fuse-elementwise-ops,one-shot-bufferize{bufferize-function-boundaries function-boundary-type-conversion=identity-layout-map},canonicalize)' | FileCheck tests/fusion_no_temp_buffer.mlir 2>&1 | head -5 tests/fusion_no_temp_buffer.mlir:7:15: error: CHECK-NOT: excluded string found in input // CHECK-NOT: memref.alloc ^ <stdin>:5:11: note: found here %alloc = memref.alloc() alignment = 64 : memref<16777216xf32>
FileCheck quotes the check line that failed and the input line that broke it. That's the message a CI bot will post on a PR that reintroduces the 64 MiB temp buffer.
CHECK-LABEL per function. It splits the output into independent chunks,
so a failure in one function can't make checks match in another.CHECK-NOT: memref.alloc survives
unrelated changes; pasting 40 lines of expected IR breaks on every upstream
bump. (mlir/utils/generate-test-checks.py generates full checks. Use it,
then delete what isn't the point of the test.)outs(%[[OUT:.*]] : ...) then later %[[OUT]]. SSA numbering changes; names from captures don't.sve_no_alloca_in_loop.mlir's
BROKEN prefix proves the pattern would actually find an alloca if there
were one. A CHECK-NOT with a typo in the pattern passes forever.--verify-diagnostics and expected-error
(lab 01_basics/03), not FileCheck.sve_no_alloca_in_loop.mlir fail by removing {full-unroll=true}
from its first RUN line. Read the FileCheck message.convert-arith-to-llvm, fastmath<reassoc> must become llvm.fadd ... {fastmathFlags = #llvm.fastmath<reassoc>}.
(Run the pass first and copy the exact spelling.)IREE’s Codegen/LLVMCPU/test/
and identify their RUN tool and CHECK style.A lab is done when you can say what it showed on your machine — not when you have read it.
It decides which problems come first, which drills open, which labs you see and how the Learn surface is organised. Change it any time from the switch in the top bar, or press Escape to look around first.