Phase 2, week 13retrospective

Dynamic-shape IR

Dynamic dimensions appear as `?` in types and as runtime SSA operands, so shapes become values in the IR.


A question mark is not a value

tensor<4x4xf32> fixes both dimensions in the type. tensor<?x?xf32> says the rank is two and the extents are unknown until the program runs. That is all the ? says. It is a compile-time marker, not a runtime check, and nothing in the type records where the extent will come from.

The extent becomes a value when an op asks for it:

%m = tensor.dim %A, %c0 : tensor<?x?xf32>
%n = tensor.dim %B, %c1 : tensor<?x?xf32>
%c = tensor.empty(%m, %n) : tensor<?x?xf32>

tensor.dim returns an index, and that SSA value is now an operand to everything that needs a size: tensor.empty, tensor.extract_slice, memref.alloc, and the ABI lowering of the function itself. Shapes have moved from the type into the dataflow graph. That is the whole change, and it is why a dynamic pipeline has more operands and fewer constants, not a different kind of IR.

What the verifier stops checking

With static shapes, linalg.matmul can reject mismatched dimensions at parse time. With ?, it cannot: the verifier accepts any dynamic extents, and the multiplication is correct only if the runtime values agree. MLIR will not insert that check for you. If the K read from A and the K read from B disagree, the program reads out of bounds or computes nonsense, and no pass diagnoses it.

tensor.cast moves between the two worlds. Static to dynamic is always safe. Dynamic to static is an assertion that the extent equals the number in the type, and the cast does not verify it at runtime.

Check your pinned release for both: which ops accept dynamic operands, and whether bufferization keeps the extents as memref<?x?xf32>. That surface has changed between releases.

Things that break

A ? treated as a runtime-checked dimension is the first failure: the type promises nothing. The second is a tensor.dim taken from one operand and reused for another operand whose extent differs, which is silent. The third is an index value cast to i32 early and overflowing on a large tensor; keep sizes in index until the ABI boundary.

Sources

A version records the documentation the claim was checked against, not a house pin. Confirm flags with --help on the release you installed.

Check yourself

What does the `?` in `tensor<?x4xf32>` guarantee at runtime?

Which operation turns an unknown extent into an SSA value?

What does `tensor.cast` from `tensor<?x?xf32>` to `tensor<4x4xf32>` do at runtime?

0 / 3 correct

← back to the map