Skip to content

[mlir][tensor][bufferize] tensor.empty bufferizes to an allocation #68080

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

matthias-springer
Copy link
Member

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.

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.
@llvmbot
Copy link
Member

llvmbot commented Oct 3, 2023

@llvm/pr-subscribers-mlir-tensor

@llvm/pr-subscribers-mlir

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/68080.diff

2 Files Affected:

  • (modified) mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp (+11-10)
  • (modified) mlir/test/Dialect/Tensor/bufferize.mlir (+6-3)
diff --git a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
index b08283f0070784c..5f34fd45679b7fd 100644
--- a/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.cpp
@@ -253,10 +253,7 @@ struct DimOpInterface
   }
 };
 
-/// Bufferization of tensor.empty. This op does not bufferize, but we need an
-/// interface implementation, so that the result of this op is considered
-/// "writable" (default impl. of `isWritable`). Results of ops that do not
-/// implement `BufferizableOpInterface` are not writable.
+/// Bufferization of tensor.empty. Replace with "bufferization.alloc_tensor".
 struct EmptyOpInterface
     : public BufferizableOpInterface::ExternalModel<EmptyOpInterface,
                                                     tensor::EmptyOp> {
@@ -268,17 +265,21 @@ struct EmptyOpInterface
 
   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
                           const BufferizationOptions &options) const {
+    auto emptyOp = cast<tensor::EmptyOp>(op);
+
+    // Optimization: Fold away the op if it has no uses.
     if (op->getUses().empty()) {
       rewriter.eraseOp(op);
       return success();
     }
 
-    // tensor.empty ops are used to indicate the shape of a tensor. They have
-    // no defined contents and cannot be bufferized. However, they can be
-    // converted to bufferization.alloc_tensor ops, which then bufferize to an
-    // allocation (--empty-tensor-to-alloc-tensor).
-    return op->emitOpError("cannot be bufferized, but can be converted to "
-                           "bufferization.alloc_tensor");
+    // Allocate a tensor. This emits a "bufferization.alloc_tensor" op.
+    FailureOr<Value> allocTensor = allocateTensorForShapedValue(
+        rewriter, op->getLoc(), emptyOp.getResult(), options, /*copy=*/false);
+    if (failed(allocTensor))
+      return failure();
+    rewriter.replaceOp(op, *allocTensor);
+    return success();
   }
 };
 
diff --git a/mlir/test/Dialect/Tensor/bufferize.mlir b/mlir/test/Dialect/Tensor/bufferize.mlir
index c7b16315bfed1b4..a8b3c6af9ae893b 100644
--- a/mlir/test/Dialect/Tensor/bufferize.mlir
+++ b/mlir/test/Dialect/Tensor/bufferize.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -tensor-bufferize -cse -split-input-file -verify-diagnostics | FileCheck %s
+// RUN: mlir-opt %s -tensor-bufferize -cse -split-input-file | FileCheck %s
 
 // CHECK-LABEL:   func @dim(
 // CHECK-SAME:              %[[TENSOR:.*]]: tensor<*xf32>,
@@ -62,9 +62,12 @@ func.func @tensor.cast_to_unranked(%arg0: tensor<2xf32>) -> tensor<*xf32> {
 }
 
 // -----
+
+// CHECK-LABEL:   func @tensor.empty(
+// CHECK:           %[[ALLOC:.*]] = memref.alloc() {{.*}} : memref<5xf32>
+// CHECK:           %[[RET:.*]] = bufferization.to_tensor %[[ALLOC]] : memref<5xf32>
+// CHECK:           return %[[RET]] : tensor<5xf32>
 func.func @tensor.empty() -> tensor<5xf32> {
-  // expected-error@+2 {{failed to bufferize op}}
-  // expected-error@+1 {{cannot be bufferized, but can be converted to bufferization.alloc_tensor}}
   %0 = tensor.empty() : tensor<5xf32>
   return %0 : tensor<5xf32>
 }

@matthias-springer matthias-springer merged commit 464dfeb into llvm:main Oct 3, 2023
matthias-springer added a commit to matthias-springer/llvm-project that referenced this pull request Oct 4, 2023
`BufferizableOpInterface::bufferizesToAllocation` is queried when forming equivalence sets during bufferization. It is not really needed for ops like `tensor.empty` which do not have tensor operands, but it should be added for consistency.

This change should have been part of llvm#68080. No test is added because the return value of this function is irrelevant for ops without tensor operands. (However, this function acts as a form documentation, describing the bufferization semantics of the op.)
matthias-springer added a commit that referenced this pull request Oct 5, 2023
…8201)

`BufferizableOpInterface::bufferizesToAllocation` is queried when
forming equivalence sets during bufferization. It is not really needed
for ops like `tensor.empty` which do not have tensor operands, but it
should be added for consistency.

This change should have been part of #68080. No test is added because
the return value of this function is irrelevant for ops without tensor
operands. (However, this function acts as a form documentation,
describing the bufferization semantics of the op.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants