Tensors versus memrefs
A tensor is an immutable SSA value; a memref value is also immutable, but the memory it designates is mutable, with layout and space.
Tensor is a value
tensor<4x4xf32> is an immutable SSA value. tensor.insert returns a new tensor, tensor.extract reads an element, and the original still denotes its old contents. The type carries shape and element type, and optionally an encoding attribute — a hint about a future layout, not a layout. There is no address, stride, or memory space, and two tensor values never alias. That is what makes linalg on tensors trivially parallel: every operand is a value with no writer.
tensor.empty allocates nothing. It promises that every element will be written before it is read, and bufferization is where that promise is checked.
Memref is a buffer with a layout
memref<4x4xf32, strided<[4, 1], offset: 0>, 1> carries strides, an offset, and a memory space. Its value is still SSA, but the memory is mutable: memref.store changes what a later memref.load sees, and memref.subview produces an alias rather than a copy. Layout is part of the type, so a function accepting memref<4x4xf32, strided<[1, 4]>> rejects an identity-layout buffer; memref.cast and memref.reinterpret_cast move between layouts.
Bufferization crosses the line
mlir-opt matmul-tensor.mlir \
--one-shot-bufferize='bufferize-function-boundaries' \
--canonicalize \
--convert-linalg-to-loops
-one-shot-bufferize analyses tensor ops implementing BufferizableOpInterface and rewrites them to memref form, inserting a copy only where in-place reuse would break a read-after-write dependency. bufferize-function-boundaries turns tensor arguments and results into memrefs. Without it, tensors reach the boundary and later lowering stops. Confirm the pass name and option spelling on your pinned release with mlir-opt --help.
Things that break
The loud failure is a leftover tensor type after bufferization: mlir-translate rejects the module, which is a good error to get. The quiet one is an extra memref.alloc where you expected in-place reuse. The analysis proved a conflict, usually from a value with two uses, and the fix is a copy you place deliberately or a restructured chain, not a flag that suppresses the check.
Sources
- tensor Dialectdocs · LLVM 19
- memref Dialect: layout and memory spacedocs · LLVM 19
- Bufferizationdocs · 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
Which of these is carried by a memref type but not by a tensor type?
`%t2 = tensor.insert %v, %t1[%i] : ...` — did it mutate `%t1`?
After bufferization, why does a linalg op on memrefs have no results?