Vector to LLVM lowering
Lowering vector ops to LLVM IR requires converting vector types, transfers, and contracts into backend-friendly forms.
Three rewrites, one pipeline
Nothing in LLVM IR is a vector.transfer_read. Lowering the vector dialect means rewriting its types, its memory operations, and its contractions into the small set of things LLVM has: 1-D vectors, load and store, and intrinsics.
Types first. vector<8xf32> becomes <8 x float>. Rank above one becomes an array of 1-D vectors, so vector<4x8xf32> becomes [4 x <8 x float>]. That nesting is a documented choice, not a law, and it constrains you: indexing an array needs a constant index, so a dynamic loop over the outer dimension has to be unrolled before translation.
Transfers second. A vector.transfer_read carries an indexing map saying which loop dimension each lane reads. LLVM has no such idea, so the map must be folded into address arithmetic before conversion. A map that is not a minor identity permutation becomes a gather or a loop of scalar loads, which is how a vectorized kernel arrives with no vector loads in it.
Contractions third. vector.contract has no LLVM counterpart. It must become vector.fma plus a reduction, or be unrolled away, before the LLVM conversion runs.
What backend-friendly means
LLVM hands the RISC-V backend 1-D vectors, scalar pointers, and explicit masks. That is enough for it to pick SEW, LMUL and an AVL: a <8 x float> fixes the lane count, a <vscale x 2 x float> leaves it to vsetvli, and an llvm.vp.load says how many lanes are live. Anything MLIR knew that has no LLVM form must be gone by the end of the conversion, because the backend cannot recover it.
mlir-opt vec.mlir \
--convert-vector-to-scf \
--convert-scf-to-cf \
--convert-vector-to-llvm \
--convert-func-to-llvm \
--finalize-memref-to-llvm \
--reconcile-unrealized-casts \
| mlir-translate --mlir-to-llvmir > vec.ll
Pass names and their options move between releases. Confirm each name with mlir-opt --help on the build you pinned, and read that release’s documentation rather than a tutorial written two years earlier.
Things that break
The pipeline reports success and vec.ll still contains vector. operations. Grep for vector\. before translating: anything left is an op the conversion cannot express, and translation stops on it. The quieter version translates cleanly and is full of scalar loads, which you catch only by reading the disassembly for the vle32.v that should be there.
Sources
- MLIR vector dialectdocs · LLVM 23
- MLIR pass management and textual pipelinesdocs · LLVM 23
- LLVM Language Reference: vector typesdocs
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 can a `vector.contract` not survive into LLVM IR?
What does `vector<4x8xf32>` become in LLVM IR?
A `vector.transfer_read` whose map is not a minor identity permutation lowers to what?