Fixed-width vector matmul
A fixed-width vectorized matmul must match the scalar reference on QEMU before scalable vectors matter.
Fixed first
The 4x4x4 memref matmul is already a single tile, so vectorizing the whole op gives the fixed-width form from p1-17: three vector<4x4xf32> reads, one vector.contract, one write. No scalable type appears anywhere, and the answer does not depend on vscale, which is the point: if this disagrees with the scalar reference, the bug is in your pipeline, not in vector-length handling.
Lower, link, run
The contraction has to become finer-grained vector ops before the LLVM conversion, which has no pattern for vector.contract. On the pinned build the transform pattern set does that. Continuing the sequence from p1-18, and applying to the module so the contract is inside the target:
transform.structured.vectorize %mm : !transform.any_op
transform.apply_patterns to %root {
transform.apply_patterns.vector.lower_contraction
transform.apply_patterns.vector.lower_outerproduct
} : !transform.any_op
Run that first and keep the payload. If the printed module still holds the schedule, delete the transform.named_sequence block before the next step, because mlir-translate rejects transform IR. Then the rest of the path, in the shape p0-07 established:
mlir-opt vec_lowered.mlir \
--convert-vector-to-scf --convert-scf-to-cf --convert-vector-to-llvm \
--convert-func-to-llvm='use-bare-ptr-memref-call-conv=1' \
--finalize-memref-to-llvm --reconcile-unrealized-casts \
| mlir-translate --mlir-to-llvmir > vec.ll
Build and run it on the p0-02 rig. Compare against ref/matmul_ref.c at 4x4x4, element by element, with the tolerance policy from p0-03: the contraction sums in a different order, so last-bit differences are expected and a difference in the leading digits is not.
Things that break
A surviving vector.contract in vec.ll means the pattern application did not run, and mlir-translate fails on the op with no hint about which pass was missing. A tile width that is not a whole multiple of the lanes you intend produces masked transfers or a split, and the resulting code is correct but mostly scalar, which looks like a slow vector kernel rather than a scheduling mistake. Confirm each pass name with mlir-opt --help on the pinned release before recording the pipeline in VERSIONS.md.
Sources
- MLIR vector dialect documentationdocs · LLVM 19
- LLVM RISC-V vector extension guidedocs · LLVM 19
- MLIR linalg dialect documentationdocs · LLVM 19
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
Why get the fixed-width vector matmul correct before touching scalable vectors?
A `vector<4xf32>` result runs on a machine with `vlen=256`. How much of one vector register does it use?
The vector matmul agrees with the scalar reference to the last bit except for a few ULPs. What is that?