[Mlir-commits] [mlir] 62b1dce - [mlir][tensor][bufferization] Preserve memory space for tensor.concat (#213528)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Aug 23 01:37:09 PDT 2026
Author: Hyunjun Shin
Date: 2026-08-23T08:37:04Z
New Revision: 62b1dce2e660c48407289bc921871590e68e83a9
URL: https://github.com/llvm/llvm-project/commit/62b1dce2e660c48407289bc921871590e68e83a9
DIFF: https://github.com/llvm/llvm-project/commit/62b1dce2e660c48407289bc921871590e68e83a9.diff
LOG: [mlir][tensor][bufferization] Preserve memory space for tensor.concat (#213528)
## Summary
- Use the buffer type selected for the destination `alloc_tensor` when
bufferizing `tensor.concat`.
- Pass that type to `ToBufferOp` instead of reconstructing a
default-memory-space `MemRefType`.
- Use the same type for the destination subviews, preserving its memory
space and layout.
- Add an encoding-based regression test for a non-default destination
memory space.
## Motivation
While investigating `tensor.concat` bufferization with a non-default
memory space, I reproduced a mismatch between the destination allocation
type and the type requested by `ToBufferOp`.
The destination was allocated as `memref<16xf32, 2>`, but
`ConcatOpInterface` reconstructed a default-space `memref<16xf32>`.
Since `memref.cast` cannot change memory spaces, bufferization inserted
an additional default-space allocation and a `memref.copy`. This extra
allocation and copy defeat the intended memory-space placement.
Before this change, `ConcatOpInterface` explicitly rejected non-default
memory spaces and reconstructed the destination type from only the
shape, element type, and layout. That was sufficient for default-space
cases, but it did not account for other type-conversion hooks that can
select a non-default buffer type.
This change obtains the buffer type selected for the destination
`alloc_tensor` with `getBufferType(*tensorAlloc, options, state)` and
passes that type directly to `ToBufferOp`, preserving the selected
memory space and layout.
## Background
`ConcatOpInterface` allocates its destination through
`allocateTensorForShapedValue`. The resulting `alloc_tensor` may
bufferize to a memref in a non-default memory space. The implementation
then manually reconstructed the destination `MemRefType` without a
memory-space attribute.
This could make `ToBufferOp` observe a non-default-space source buffer
and a default-space requested result type. Since those types cannot be
reconciled with `memref.cast`, bufferization could introduce an
additional default-space allocation and a full-result copy. The
input-to-subview copies that implement the concat itself are still
required.
The existing `defaultMemorySpaceFn` guard is not sufficient because the
actual buffer type may be selected through another type-conversion hook.
Querying `getBufferType(*tensorAlloc, options, state)` obtains the
buffer type selected for the allocation and preserves the selected
memory space and other type information such as layout. Passing this
type to `ToBufferOp` and using it for the destination subviews keeps the
allocation, conversion, and copies consistent.
Assisted-by: Codex
Added:
Modified:
mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
mlir/test/Dialect/Tensor/one-shot-bufferize-encodings.mlir
Removed:
################################################################################
diff --git a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
index 525d57341cee5..930f0663e2ab8 100644
--- a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
@@ -1119,18 +1119,12 @@ struct ConcatOpInterface
if (failed(tensorAlloc))
return failure();
auto tensorType = cast<RankedTensorType>(tensorAlloc->getType());
-
- // TODO: Implement memory space for this op.
- if (options.defaultMemorySpaceFn(cast<TensorLikeType>(tensorType)) !=
- Attribute())
- return op->emitError("memory space not implemented yet");
-
- MemRefLayoutAttrInterface layout;
- MemRefType memrefType =
- MemRefType::get(concatOp.getResultType().getShape(),
- concatOp.getResultType().getElementType(), layout);
+ FailureOr<BufferLikeType> memrefType =
+ bufferization::getBufferType(*tensorAlloc, options, state);
+ if (failed(memrefType))
+ return failure();
Value dstBuffer = bufferization::ToBufferOp::create(
- rewriter, op->getLoc(), memrefType, *tensorAlloc);
+ rewriter, op->getLoc(), *memrefType, *tensorAlloc);
// Extract the dimension for the concat op
uint64_t concatDim = concatOp.getDim();
@@ -1166,7 +1160,7 @@ struct ConcatOpInterface
sizes[concatDim] = concatDimSize;
// Create a subview of the destination buffer.
- auto dstMemrefType = cast<MemRefType>(memrefType);
+ auto dstMemrefType = cast<MemRefType>(*memrefType);
MemRefType subviewMemRefType =
memref::SubViewOp::inferRankReducedResultType(
operandTensorType.getShape(), dstMemrefType, offsets, sizes,
diff --git a/mlir/test/Dialect/Tensor/one-shot-bufferize-encodings.mlir b/mlir/test/Dialect/Tensor/one-shot-bufferize-encodings.mlir
index 7398fdf614e1a..5dc301d7f2310 100644
--- a/mlir/test/Dialect/Tensor/one-shot-bufferize-encodings.mlir
+++ b/mlir/test/Dialect/Tensor/one-shot-bufferize-encodings.mlir
@@ -18,3 +18,24 @@ func.func @from_elements(%fill: f32, %f: f32, %idx: index) -> tensor<3xf32, 1> {
// CHECK: memref.store %[[arg1]], %[[alloc]][%[[arg2]]] : memref<3xf32, 1>
// CHECK: %[[v0:.+]] = bufferization.to_tensor %[[alloc]] : memref<3xf32, 1> to tensor<3xf32, 1 : i64>
// CHECK: return %[[v0]] : tensor<3xf32, 1 : i64>
+
+// -----
+
+func.func @concat_memory_space(%arg0: tensor<8xf32, 1>,
+ %arg1: tensor<8xf32, 1>) -> tensor<16xf32, 1> {
+ %0 = tensor.concat dim(0) %arg0, %arg1
+ : (tensor<8xf32, 1>, tensor<8xf32, 1>) -> tensor<16xf32, 1>
+ return %0 : tensor<16xf32, 1>
+}
+
+// CHECK-LABEL: @concat_memory_space
+// CHECK-SAME: (%[[ARG0:.+]]: tensor<8xf32, 1 : i64>, %[[ARG1:.+]]: tensor<8xf32, 1 : i64>)
+// CHECK-DAG: %[[BUFFER0:.+]] = bufferization.to_buffer %[[ARG0]]
+// CHECK-DAG: %[[BUFFER1:.+]] = bufferization.to_buffer %[[ARG1]]
+// CHECK: %[[ALLOC:.+]] = memref.alloc() {{.*}} : memref<16xf32, 1>
+// CHECK: %[[SUBVIEW0:.+]] = memref.subview %[[ALLOC]][0] [8] [1]
+// CHECK: memref.copy %[[BUFFER0]], %[[SUBVIEW0]]
+// CHECK: %[[SUBVIEW1:.+]] = memref.subview %[[ALLOC]][8] [8] [1]
+// CHECK: memref.copy %[[BUFFER1]], %[[SUBVIEW1]]
+// CHECK: %[[RESULT:.+]] = bufferization.to_tensor %[[ALLOC]]
+// CHECK: return %[[RESULT]] : tensor<16xf32, 1 : i64>
More information about the Mlir-commits
mailing list