Skip to content

Commit 464dfeb

Browse files
[mlir][tensor][bufferize] tensor.empty bufferizes to an allocation (#68080)
Make `tensor.empty` bufferizable, so that the `-empty-tensor-to-alloc-tensor` pass becomes optional. This makes the bufferization easier to use. `tensor.empty` used to be non-bufferizable, so that there two separate ops, one that can be optimized away (`tensor.empty`) and one that is guaranteed to bufferize to an allocation (`bufferization.alloc_tensor`). With the recent improvements of "empty tensor elimination" this is no longer needed and `bufferization.alloc_tensor` can be phased out.
1 parent 3dc7039 commit 464dfeb

File tree

2 files changed

+17
-13
lines changed

2 files changed

+17
-13
lines changed

mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,7 @@ struct DimOpInterface
253253
}
254254
};
255255

256-
/// Bufferization of tensor.empty. This op does not bufferize, but we need an
257-
/// interface implementation, so that the result of this op is considered
258-
/// "writable" (default impl. of `isWritable`). Results of ops that do not
259-
/// implement `BufferizableOpInterface` are not writable.
256+
/// Bufferization of "tensor.empty". Replace with "bufferization.alloc_tensor".
260257
struct EmptyOpInterface
261258
: public BufferizableOpInterface::ExternalModel<EmptyOpInterface,
262259
tensor::EmptyOp> {
@@ -268,17 +265,21 @@ struct EmptyOpInterface
268265

269266
LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
270267
const BufferizationOptions &options) const {
268+
auto emptyOp = cast<tensor::EmptyOp>(op);
269+
270+
// Optimization: Fold away the op if it has no uses.
271271
if (op->getUses().empty()) {
272272
rewriter.eraseOp(op);
273273
return success();
274274
}
275275

276-
// tensor.empty ops are used to indicate the shape of a tensor. They have
277-
// no defined contents and cannot be bufferized. However, they can be
278-
// converted to bufferization.alloc_tensor ops, which then bufferize to an
279-
// allocation (--empty-tensor-to-alloc-tensor).
280-
return op->emitOpError("cannot be bufferized, but can be converted to "
281-
"bufferization.alloc_tensor");
276+
// Allocate a tensor. This emits a "bufferization.alloc_tensor" op.
277+
FailureOr<Value> allocTensor = allocateTensorForShapedValue(
278+
rewriter, op->getLoc(), emptyOp.getResult(), options, /*copy=*/false);
279+
if (failed(allocTensor))
280+
return failure();
281+
rewriter.replaceOp(op, *allocTensor);
282+
return success();
282283
}
283284
};
284285

mlir/test/Dialect/Tensor/bufferize.mlir

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: mlir-opt %s -tensor-bufferize -cse -split-input-file -verify-diagnostics | FileCheck %s
1+
// RUN: mlir-opt %s -tensor-bufferize -cse -split-input-file | FileCheck %s
22

33
// CHECK-LABEL: func @dim(
44
// CHECK-SAME: %[[TENSOR:.*]]: tensor<*xf32>,
@@ -62,9 +62,12 @@ func.func @tensor.cast_to_unranked(%arg0: tensor<2xf32>) -> tensor<*xf32> {
6262
}
6363

6464
// -----
65+
66+
// CHECK-LABEL: func @tensor.empty(
67+
// CHECK: %[[ALLOC:.*]] = memref.alloc() {{.*}} : memref<5xf32>
68+
// CHECK: %[[RET:.*]] = bufferization.to_tensor %[[ALLOC]] : memref<5xf32>
69+
// CHECK: return %[[RET]] : tensor<5xf32>
6570
func.func @tensor.empty() -> tensor<5xf32> {
66-
// expected-error@+2 {{failed to bufferize op}}
67-
// expected-error@+1 {{cannot be bufferized, but can be converted to bufferization.alloc_tensor}}
6871
%0 = tensor.empty() : tensor<5xf32>
6972
return %0 : tensor<5xf32>
7073
}

0 commit comments

Comments
 (0)