Phase 2, week 13retrospective

Dynamic tiling

Runtime bounds need affine.min or masked loads at the edge; a static interior tile requires peeling or masking, not assumption.


The edge is a minimum

Tiling a dynamic M by 64 means the last iteration owns fewer than 64 rows. The bound is an SSA value:

%tile = affine.min affine_map<(d0)[s0] -> (64, s0 - d0)>(%iv)[%M]
%sA = tensor.extract_slice %A[%iv, 0] [%tile, %K] [1, 1]
    : tensor<?x?xf32> to tensor<?x?xf32>

affine.min takes the smaller of the static tile and the remaining extent. It is an affine expression of the induction variable and a symbol, which is why it folds and analyzes; arith.minui on the same values is correct but opaque to affine passes. The slice type is dynamic because %tile is.

The static number still has a job. The tile size is the loop step and the constant in the map; only the final iteration needs the minimum. That distinction matters: if the tile length is dynamic in every iteration, the vectorizer has to mask every access and cannot unroll against a constant. Peel the full tiles into a loop with a static 64, then run an epilogue for the remainder. The interior keeps static slices, static vectors and static unrolling; the edge pays for the generality.

Masked loads are the other spelling. Keep the static tile, compute a mask from the remaining extent, and let the load suppress the out-of-range lanes. The tile shape stays static; the mask carries the dynamic information.

Things that break

affine.min on a value that is not an affine expression fails verification, and arith.minui hides the bound from every affine pass. A loop that computes %tile once outside the loop freezes the wrong bound. And an epilogue that runs when the remainder is zero writes a zero-length slice at best, or reads past the buffer if the address arithmetic assumed a full tile. Confirm the exact affine.min syntax and the mask op names on your pinned release.

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

With tile size 64 and `%M = 100`, what does `affine.min (64, %M - %iv)` return at `%iv = 64`?

Why peel the full tiles into their own loop?

At `%M = 128` with tile size 64, what does the epilogue loop do?

0 / 3 correct

← back to the map