Remove memory side effect from hal.interface.binding.subspan (#12494)

Enables CSE + DCE on duplicate/unused interface bindings as a followup
to #12471. This removes the test for interface bindings in
RemoveDeadMemAllocs as it is no longer marked as an alloc.

benchmarks: x86_64, cuda
diff --git a/compiler/src/iree/compiler/Codegen/Common/test/remove_dead_allocs.mlir b/compiler/src/iree/compiler/Codegen/Common/test/remove_dead_allocs.mlir
index 50040b1..146d3d0 100644
--- a/compiler/src/iree/compiler/Codegen/Common/test/remove_dead_allocs.mlir
+++ b/compiler/src/iree/compiler/Codegen/Common/test/remove_dead_allocs.mlir
@@ -16,13 +16,3 @@
 // CHECK-LABEL: func.func @alloc_keep
 //  CHECK-NEXT:   %[[ALLOC:.+]] = memref.alloc
 //  CHECK-NEXT:   return %[[ALLOC]]
-
-// -----
-
-func.func @cleanup_only_assume_alignment_uses() {
-  %0 = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) : memref<42xf32>
-  memref.assume_alignment %0, 64 : memref<42xf32>
-  return
-}
-// CHECK-LABEL: func.func @cleanup_only_assume_alignment_uses()
-//  CHECK-NEXT:   return
diff --git a/compiler/src/iree/compiler/Codegen/LLVMGPU/test/transform_dialect_promote_operands.mlir b/compiler/src/iree/compiler/Codegen/LLVMGPU/test/transform_dialect_promote_operands.mlir
index 2657759..f5372f9 100644
--- a/compiler/src/iree/compiler/Codegen/LLVMGPU/test/transform_dialect_promote_operands.mlir
+++ b/compiler/src/iree/compiler/Codegen/LLVMGPU/test/transform_dialect_promote_operands.mlir
@@ -6,7 +6,6 @@
       %c0 = arith.constant 0 : index
       %0 = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<readonly:tensor<250x500xf32>>
       %1 = hal.interface.binding.subspan set(0) binding(1) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<readonly:tensor<500x1020xf32>>
-      %2 = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<readwrite:tensor<250x1020xf32>>
       %3 = flow.dispatch.tensor.load %0, offsets = [0, 0], sizes = [250, 500], strides = [1, 1] : !flow.dispatch.tensor<readonly:tensor<250x500xf32>> -> tensor<250x500xf32>
       %4 = flow.dispatch.tensor.load %1, offsets = [0, 0], sizes = [500, 1020], strides = [1, 1] : !flow.dispatch.tensor<readonly:tensor<500x1020xf32>> -> tensor<500x1020xf32>
 
@@ -16,15 +15,14 @@
       // CHECK:      %[[CST:.+]] = arith.constant 0.000000e+00 : f32
       // CHECK:      %[[D0:.+]] = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) alignment(64)
       // CHECK:      %[[D1:.+]] = hal.interface.binding.subspan set(0) binding(1) type(storage_buffer) alignment(64)
-      // CHECK:      %[[D2:.+]] = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) alignment(64)
-      // CHECK:      %[[D3:.+]] = flow.dispatch.tensor.load %[[D0]], offsets = [0, 0], sizes = [250, 500]
-      // CHECK:      %[[D4:.+]] = flow.dispatch.tensor.load %[[D1]], offsets = [0, 0], sizes = [500, 1020]
-      // CHECK:      %[[D5:.+]] = tensor.empty() : tensor<250x1020xf32>
-      // CHECK-NEXT: %[[D6:.+]] = linalg.fill ins(%[[CST]] : f32) outs(%[[D5]] : tensor<250x1020xf32>) -> tensor<250x1020xf32>
-      // CHECK-NEXT: %[[D7:.+]] = bufferization.alloc_tensor() copy(%[[D3]]) {bufferization.escape = [false]} : tensor<250x500xf32>
-      // CHECK-NEXT: %[[D8:.+]] = bufferization.alloc_tensor() copy(%[[D4]]) {bufferization.escape = [false]} : tensor<500x1020xf32>
-      // CHECK-NEXT: %[[D9:.+]] = linalg.matmul ins(%[[D7]], %[[D8]] : tensor<250x500xf32>, tensor<500x1020xf32>)
-      // CHECK-SAME:                 outs(%[[D6]] : tensor<250x1020xf32>) -> tensor<250x1020xf32>
+      // CHECK:      %[[D2:.+]] = flow.dispatch.tensor.load %[[D0]], offsets = [0, 0], sizes = [250, 500]
+      // CHECK:      %[[D3:.+]] = flow.dispatch.tensor.load %[[D1]], offsets = [0, 0], sizes = [500, 1020]
+      // CHECK:      %[[D4:.+]] = tensor.empty() : tensor<250x1020xf32>
+      // CHECK-NEXT: %[[D5:.+]] = linalg.fill ins(%[[CST]] : f32) outs(%[[D4]] : tensor<250x1020xf32>) -> tensor<250x1020xf32>
+      // CHECK-NEXT: %[[D6:.+]] = bufferization.alloc_tensor() copy(%[[D2]]) {bufferization.escape = [false]} : tensor<250x500xf32>
+      // CHECK-NEXT: %[[D7:.+]] = bufferization.alloc_tensor() copy(%[[D3]]) {bufferization.escape = [false]} : tensor<500x1020xf32>
+      // CHECK-NEXT: %[[D8:.+]] = linalg.matmul ins(%[[D6]], %[[D7]] : tensor<250x500xf32>, tensor<500x1020xf32>)
+      // CHECK-SAME:                 outs(%[[D5]] : tensor<250x1020xf32>) -> tensor<250x1020xf32>
       %6 = linalg.matmul ins(%3, %4 : tensor<250x500xf32>, tensor<500x1020xf32>) outs(%5 : tensor<250x1020xf32>) -> tensor<250x1020xf32>
       return %6: tensor<250x1020xf32>
     }
diff --git a/compiler/src/iree/compiler/Codegen/SPIRV/test/emulate_i64.mlir b/compiler/src/iree/compiler/Codegen/SPIRV/test/emulate_i64.mlir
index 82253c8..a2b7338 100644
--- a/compiler/src/iree/compiler/Codegen/SPIRV/test/emulate_i64.mlir
+++ b/compiler/src/iree/compiler/Codegen/SPIRV/test/emulate_i64.mlir
@@ -37,7 +37,6 @@
 // Check that without the Int64 capability emulation produces expected i32 ops.
 //
 // CHECK-LABEL: func.func @buffer_types
-//       CHECK:   [[REF_I32:%.+]]   = hal.interface.binding.subspan set(0) binding(0) type(storage_buffer) : memref<8xi32, #spirv.storage_class<StorageBuffer>>
 //       CHECK:   [[REF_I64_0:%.+]] = hal.interface.binding.subspan set(0) binding(1) type(storage_buffer) : memref<8xvector<2xi32>, #spirv.storage_class<StorageBuffer>>
 //       CHECK:   [[REF_I64_1:%.+]] = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) : memref<8xvector<2xi32>, #spirv.storage_class<StorageBuffer>>
 //       CHECK:   [[VI64:%.+]]      = memref.load [[REF_I64_0]][{{%.+}}] : memref<8xvector<2xi32>, #spirv.storage_class<StorageBuffer>>
diff --git a/compiler/src/iree/compiler/Dialect/HAL/IR/HALOps.td b/compiler/src/iree/compiler/Dialect/HAL/IR/HALOps.td
index 45247ff..7e2aa7a 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/IR/HALOps.td
+++ b/compiler/src/iree/compiler/Dialect/HAL/IR/HALOps.td
@@ -2316,9 +2316,8 @@
   }];
 }
 
-def HAL_InterfaceBindingSubspanOp : HAL_Op<"interface.binding.subspan", [
+def HAL_InterfaceBindingSubspanOp : HAL_PureOp<"interface.binding.subspan", [
     AttrSizedOperandSegments,
-    MemoryEffects<[MemAlloc]>,
     Util_ShapeAwareOp,
   ]> {
   let summary = [{returns an alias to a subspan of interface binding data}];
@@ -2342,7 +2341,7 @@
     OptionalAttr<HAL_DescriptorFlagsAttr>:$descriptor_flags
   );
   let results = (outs
-    Res<AnyType, "", [MemAlloc]>:$result
+    AnyType:$result
   );
 
   let assemblyFormat = [{
diff --git a/compiler/src/iree/compiler/Dialect/HAL/IR/test/interface_ops.mlir b/compiler/src/iree/compiler/Dialect/HAL/IR/test/interface_ops.mlir
index 99a3e6b..9874be5 100644
--- a/compiler/src/iree/compiler/Dialect/HAL/IR/test/interface_ops.mlir
+++ b/compiler/src/iree/compiler/Dialect/HAL/IR/test/interface_ops.mlir
@@ -1,4 +1,5 @@
 // RUN: iree-opt --split-input-file --verify-diagnostics %s | FileCheck %s
+// RUN: iree-opt --split-input-file -cse --verify-diagnostics %s | FileCheck %s --check-prefix=CSE
 
 // CHECK-LABEL: @interface_workgroup_info
 func.func @interface_workgroup_info() {
@@ -29,6 +30,23 @@
 
 // -----
 
+// CSE-LABEL: @interface_subspan_cse
+func.func @interface_subspan_cse() {
+  %c0 = arith.constant 0 : index
+
+//   CSE: %[[BIND:.+]] = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) alignment(64) offset({{.+}}) : !flow.dispatch.tensor<readwrite:tensor<2x32x16384xf32>
+  %0 = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<readwrite:tensor<2x32x16384xf32>>
+  %1 = hal.interface.binding.subspan set(0) binding(2) type(storage_buffer) alignment(64) offset(%c0) : !flow.dispatch.tensor<readwrite:tensor<2x32x16384xf32>>
+
+//   CSE-NEXT: %[[LOAD:.+]] = flow.dispatch.tensor.load %[[BIND]]
+  %2 = flow.dispatch.tensor.load %1, offsets = [0, 0, 0], sizes = [2, 32, 16384], strides = [1, 1, 1] : !flow.dispatch.tensor<readwrite:tensor<2x32x16384xf32>> -> tensor<2x32x16384xf32>
+//   CSE-NEXT: flow.dispatch.tensor.store %[[LOAD]], %[[BIND]]
+  flow.dispatch.tensor.store %2, %0, offsets = [0, 0, 0], sizes = [2, 32, 16384], strides = [1, 1, 1] : tensor<2x32x16384xf32> -> !flow.dispatch.tensor<readwrite:tensor<2x32x16384xf32>>
+  return
+}
+
+// -----
+
 func.func @interface_io_subspan_wrong_dynamic_dim(%dim: index) {
   %c8 = arith.constant 8 : index