Phase 1, week 6retrospective

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

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?

0 / 3 correct

← back to the map