[Stream] Deduplicate identical tensor import/export ops (#24655)

- This PR Adds canonicalization patterns to deduplicate identical
stream.tensor.import and stream.tensor.export operations

- Added test coverage for deduplication, non-deduplication, and
intermediate-operation scenarios.

---------

Signed-off-by: LekkalaSravya3 <lekkala.sravya@multicorewareinc.com>
diff --git a/compiler/src/iree/compiler/Dialect/Stream/IR/StreamOpFolders.cpp b/compiler/src/iree/compiler/Dialect/Stream/IR/StreamOpFolders.cpp
index 665531a..3dca743 100644
--- a/compiler/src/iree/compiler/Dialect/Stream/IR/StreamOpFolders.cpp
+++ b/compiler/src/iree/compiler/Dialect/Stream/IR/StreamOpFolders.cpp
@@ -21,6 +21,7 @@
 #include "mlir/IR/Matchers.h"
 #include "mlir/IR/OpDefinition.h"
 #include "mlir/IR/OpImplementation.h"
+#include "mlir/IR/OperationSupport.h"
 #include "mlir/IR/PatternMatch.h"
 #include "mlir/IR/TypeUtilities.h"
 #include "mlir/IR/Value.h"
@@ -1109,11 +1110,6 @@
   return {};
 }
 
-void TensorImportOp::getCanonicalizationPatterns(RewritePatternSet &results,
-                                                 MLIRContext *context) {
-  // TODO(benvanik): check operand and dedupe imports.
-}
-
 //===----------------------------------------------------------------------===//
 // stream.tensor.export
 //===----------------------------------------------------------------------===//
@@ -1132,9 +1128,49 @@
   return {};
 }
 
+//===----------------------------------------------------------------------===//
+// Generic deduplication pattern for stream.tensor.import and
+// stream.tensor.export
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+template <typename OpTy>
+struct DeduplicateTensorOp : OpRewritePattern<OpTy> {
+  using OpRewritePattern<OpTy>::OpRewritePattern;
+
+  LogicalResult matchAndRewrite(OpTy currentOp,
+                                PatternRewriter &rewriter) const override {
+    // Search for an equivalent operation.
+    for (Operation *previousOp = currentOp->getPrevNode();
+         previousOp != nullptr; previousOp = previousOp->getPrevNode()) {
+      auto previous = dyn_cast<OpTy>(previousOp);
+      if (!previous) {
+        continue;
+      }
+      if (!OperationEquivalence::isEquivalentTo(
+              previous.getOperation(), currentOp.getOperation(),
+              OperationEquivalence::IgnoreLocations)) {
+        continue;
+      }
+      rewriter.replaceOp(currentOp, previous.getResult());
+      return success();
+    }
+
+    return failure();
+  }
+};
+
+} // namespace
+
+void TensorImportOp::getCanonicalizationPatterns(RewritePatternSet &results,
+                                                 MLIRContext *context) {
+  results.insert<DeduplicateTensorOp<TensorImportOp>>(context);
+}
+
 void TensorExportOp::getCanonicalizationPatterns(RewritePatternSet &results,
                                                  MLIRContext *context) {
-  // TODO(benvanik): check operand and dedupe exports.
+  results.insert<DeduplicateTensorOp<TensorExportOp>>(context);
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/compiler/src/iree/compiler/Dialect/Stream/IR/test/tensor_folding.mlir b/compiler/src/iree/compiler/Dialect/Stream/IR/test/tensor_folding.mlir
index fd1d0d1..cb6088c 100644
--- a/compiler/src/iree/compiler/Dialect/Stream/IR/test/tensor_folding.mlir
+++ b/compiler/src/iree/compiler/Dialect/Stream/IR/test/tensor_folding.mlir
@@ -277,3 +277,85 @@
     -> tensor<?x?xf32, #encoding>{%arg2, %arg3} in !stream.resource<*>{%arg1}
   util.return %0 : !stream.resource<*>
 }
+
+// -----
+
+// CHECK-LABEL: @DeduplicateTensorImportSameOperand
+util.func private @DeduplicateTensorImportSameOperand(%arg0: !util.buffer, %arg1: index) -> (!stream.resource<external>, !stream.resource<external>) {
+  %c20 = arith.constant 20 : index
+  // CHECK: %[[IMPORT:.+]] = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  // CHECK-NOT: stream.tensor.import
+  // CHECK: util.return %[[IMPORT]], %[[IMPORT]]
+  %0 = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  %1 = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  util.return %0, %1 : !stream.resource<external>, !stream.resource<external>
+}
+
+// -----
+
+// CHECK-LABEL: @NofoldTensorImportDifferentEncoding
+// Different result encoding — must keep both imports.
+util.func private @NofoldTensorImportDifferentEncoding(%arg0: !util.buffer, %arg1: index) -> (!stream.resource<external>, !stream.resource<external>) {
+  %c20 = arith.constant 20 : index
+  // CHECK: stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1}
+  // CHECK: stream.tensor.import %arg0 : !util.buffer -> tensor<1x?x5xf32>{%arg1}
+  %0 = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  %1 = stream.tensor.import %arg0 : !util.buffer -> tensor<1x?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  util.return %0, %1 : !stream.resource<external>, !stream.resource<external>
+}
+
+// -----
+
+// CHECK-LABEL: @DeduplicateTensorImportConsume
+util.func private @DeduplicateTensorImportConsume(%arg0: !util.buffer, %arg1: index) -> (!stream.resource<external>, !stream.resource<external>) {
+  %c20 = arith.constant 20 : index
+  // CHECK: %[[IMPORT:.+]] = stream.tensor.import consume %arg0
+  // CHECK-NOT: stream.tensor.import
+  // CHECK: util.return %[[IMPORT]], %[[IMPORT]]
+  %0 = stream.tensor.import consume %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  %1 = stream.tensor.import consume %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  util.return %0, %1 : !stream.resource<external>, !stream.resource<external>
+}
+
+// -----
+
+// CHECK-LABEL: @DeduplicateTensorExportSameOperand
+util.func private @DeduplicateTensorExportSameOperand(%arg0: !stream.resource<external>, %arg1: index) -> (!util.buffer, !util.buffer) {
+  %c20 = arith.constant 20 : index
+  // CHECK: %[[EXPORT:.+]] = stream.tensor.export %arg0 : tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20} -> !util.buffer
+  // CHECK-NOT: stream.tensor.export
+  // CHECK: util.return %[[EXPORT]], %[[EXPORT]]
+  %0 = stream.tensor.export %arg0 : tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20} -> !util.buffer
+  %1 = stream.tensor.export %arg0 : tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20} -> !util.buffer
+  util.return %0, %1 : !util.buffer, !util.buffer
+}
+
+// -----
+
+// CHECK-LABEL: @NofoldTensorExportDifferentSize
+// Different source size value — must keep both exports.
+util.func private @NofoldTensorExportDifferentSize(%arg0: !stream.resource<external>, %arg1: index, %arg2: index) -> (!util.buffer, !util.buffer) {
+  // CHECK-COUNT-2: stream.tensor.export
+  %0 = stream.tensor.export %arg0 : tensor<?x5xf32>{%arg1} in !stream.resource<external>{%arg1} -> !util.buffer
+  %1 = stream.tensor.export %arg0 : tensor<?x5xf32>{%arg1} in !stream.resource<external>{%arg2} -> !util.buffer
+  util.return %0, %1 : !util.buffer, !util.buffer
+}
+
+// -----
+// CHECK-LABEL: @DeduplicateTensorImportWithIntermediateOp
+// Intermediate operations should not prevent deduplication.
+util.func private @DeduplicateTensorImportWithIntermediateOp(%arg0: !util.buffer, %arg1: index)-> (!stream.resource<external>, !stream.resource<external>, index) {
+
+  %c20 = arith.constant 20 : index
+  %c0 = arith.constant 10 : index
+
+  // CHECK: %[[IMPORT:.+]] = stream.tensor.import %arg0
+  // CHECK: %[[SUM:.+]] = arith.addi
+  // CHECK-NOT: stream.tensor.import
+  // CHECK: util.return %[[IMPORT]], %[[IMPORT]], %[[SUM]]
+
+  %0 = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  %1 = arith.addi %c0, %arg1 : index
+  %3 = stream.tensor.import %arg0 : !util.buffer -> tensor<?x5xf32>{%arg1} in !stream.resource<external>{%c20}
+  util.return %0, %3, %1 : !stream.resource<external>, !stream.resource<external>, index
+}