FForge
← Labsmlir-basics-03MLIR anatomy●○○○

Reading MLIR errors (and testing for them)

mlir-basics/03_errors.mlirneeds: mlir-opt3 steps
0/3 steps
what this lab teaches

Four broken functions, one per kind of error you'll hit daily. MLIR errors are GOOD errors: every one has file:line:col, the op, and a "see current operation" dump in generic form. Learn the four shapes and you'll know in one glance which layer is complaining: A. PARSER "use of value '%b' expects different type" -> you typed it wrong B. SSA "operand #0 does not dominate this use" -> a value is used where it may not exist C. OP VERIFIER "'linalg.matmul' op inferred ... to be 8, but found 16" -> op-specific invariants D. REGION/INTERFACE "region branch point has 0 operands..." -> structure (yields, terminators) When a PASS produces invalid IR, you get the same kinds of messages, but pointing at IR the pass created. That's a pass bug (or a pattern bug), not your input's fault — lab 06_debugging/02 shows how to catch it. A line holding `//` + five dashes splits the file into independent chunks (--split-input-file), so one bad function doesn't hide the next one. That's how every MLIR upstream test file is organised. GOTCHA: the marker matches anywhere in a line, so a decorative dashed comment splits your file too (this header uses ~~~ for exactly that reason). ALSO: the `expected-error` annotations below are only CHECKED under --verify-diagnostics (STEP 3); without it, recent mlir-opt also prints "expected ... was not produced" noise, hence the grep -v.

evidence to leave with
All four error shapes reproduced, each with the one-line reason and an expected-error test that catches it
walk it, one step at a time
run this
$ mlir-opt 03_errors.mlir --split-input-file -o /dev/null 2>&1 | grep error: | grep -v expected
what it printed on the machine that wrote this lab
03_errors.mlir:79:23: error: use of value '%b' expects different type than prior uses: 'f32' vs 'f64'
03_errors.mlir:93:3: error: operand #0 does not dominate this use
03_errors.mlir:100:8: error: 'linalg.matmul' op inferred input/output operand #1 has shape's dimension #0 to be 8, but found 16
03_errors.mlir:111:8: error: 'scf.for' op along control flow edge from Operation scf.yield to Region #0: region branch point has 0 operands, but region successor needs 1 inputs
run this
$ mlir-opt 03_errors.mlir --split-input-file -o /dev/null 2>&1 | sed -n '/linalg.matmul. op/,/linalg.yield/p'
what it printed on the machine that wrote this lab
03_errors.mlir:100:8: error: 'linalg.matmul' op inferred input/output operand #1 has shape's dimension #0 to be 8, but found 16
  %r = linalg.matmul ins(%a, %b : tensor<4x8xf32>, tensor<16x4xf32>) outs(%c : tensor<4x4xf32>) -> tensor<4x4xf32>
       ^
03_errors.mlir:100:8: note: see current operation: 
%0 = "linalg.matmul"(%arg0, %arg1, %arg2) <{indexing_maps = [affine_map<(d0, d1, d2) -> (d0, d2)>, affine_map<(d0, d1, d2) -> (d2, d1)>, affine_map<(d0, d1, d2) -> (d0, d1)>], operandSegmentSizes = array<i32: 2, 1>}> ({
^bb0(%arg3: f32, %arg4: f32, %arg5: f32):
  %1 = "arith.mulf"(%arg3, %arg4) <{fastmath = #arith.fastmath<none>}> : (f32, f32) -> f32
  %2 = "arith.addf"(%arg5, %1) <{fastmath = #arith.fastmath<none>}> : (f32, f32) -> f32
  "linalg.yield"(%2) : (f32) -> ()
run this
$ mlir-opt 03_errors.mlir --split-input-file --verify-diagnostics -o /dev/null; echo "exit=$?"
what it printed on the machine that wrote this lab
  exit=0
  Every `// expected-error @+1 {{...}}` comment says "the op one line below
  must produce this error". With --verify-diagnostics the run SUCCEEDS iff
  exactly the expected diagnostics appear. Delete one annotation and it
  fails with "unexpected error". That's how you write a regression test for
  "this pass must reject that input" (lab 06_debugging/05 does the positive
  case with FileCheck).

THE SYMPTOM → LAYER CHEAT SHEET:
  "expected operation name in quotes"       you used an op from a dialect mlir-opt doesn't
                                            know, or broke syntax (custom tools like
                                            triton-shared-opt register extra dialects)
  "custom op 'x.y' is unknown"              dialect not registered / not loaded -> wrong tool
  "does not dominate this use"              SSA/CFG problem; often a pass hoisted/sank badly
  "'<op>' op ..."                            that op's verifier; read the op's docs (mlir.llvm.org/docs/Dialects)
  "failed to legalize operation"            a CONVERSION pass left something behind (lab 02_lowering/03)
  "unrealized_conversion_cast" remaining    two conversions didn't meet in the middle (lab 02_lowering/03)
take it further

1. Fix case B by moving %x into the entry block. Why is that now legal? 2. Fix case D by adding `scf.yield %s : index`. (Look at the generic dump: the parser had silently inserted an EMPTY yield — that's the bug.) 3. Break the `expected-error` text by one character and watch STEP 3 fail. ============================================================ == case A: type mismatch (verifier)

read the source · mlir-basics/03_errors.mlir
func.func @type_mismatch(%a: f32, %b: f64) -> f32 { // expected-note @-1 {{prior use here}} // expected-error @+1 {{use of value '%b' expects different type than prior uses: 'f32' vs 'f64'}} %s = arith.addf %a, %b : f32 return %s : f32 } // ----- // == case B: SSA dominance (use before def) func.func @dominance(%c: i1) -> i32 { cf.cond_br %c, ^bb1, ^bb2 ^bb1: // expected-note @+1 {{operand defined here}} %x = arith.constant 1 : i32 cf.br ^bb2 ^bb2: // expected-error @+1 {{operand #0 does not dominate this use}} return %x : i32 } // ----- // == case C: op-specific verifier (linalg) func.func @bad_matmul(%a: tensor<4x8xf32>, %b: tensor<16x4xf32>, %c: tensor<4x4xf32>) -> tensor<4x4xf32> { // expected-error @+1 {{inferred input/output operand #1 has shape's dimension #0 to be 8, but found 16}} %r = linalg.matmul ins(%a, %b : tensor<4x8xf32>, tensor<16x4xf32>) outs(%c : tensor<4x4xf32>) -> tensor<4x4xf32> return %r : tensor<4x4xf32> } // ----- // == case D: region terminator func.func @no_yield(%n: index) { %c0 = arith.constant 0 : index %c1 = arith.constant 1 : index // expected-note @+2 {{region branch point}} // expected-error @+1 {{region branch point has 0 operands, but region successor needs 1 inputs}} %r = scf.for %i = %c0 to %n step %c1 iter_args(%acc = %c0) -> (index) { %s = arith.addi %acc, %i : index } return }
◇
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.