[Util] Ensure scf.index_switch ops share the same selector before merging (#25037)

`MergeIndexSwitchPattern` merges adjacent `scf.index_switch` operations
when their case values match, but it fails to verify that both ops
switch on the same condition operand (selector). Consequently, the
merged operation retains only the first op's selector, causing the
second op's regions to execute based on the wrong value.

### Minimal reproducer

Run with `iree-opt --iree-util-apply-patterns
--allow-unregistered-dialect`:

```mlir
util.func @f(%case0: index, %case1: index) -> (i32, i32) {
  %result0 = scf.index_switch %case0 -> i32
  case 0 { %v = "some.op0.case0"() : () -> i32
           scf.yield %v : i32 }
  case 1 { %v = "some.op0.case1"() : () -> i32
           scf.yield %v : i32 }
  default { %v = "some.op0.default"() : () -> i32
            scf.yield %v : i32 }
  %result1 = scf.index_switch %case1 -> i32
  case 0 { %v = "some.op1.case0"() : () -> i32
           scf.yield %v : i32 }
  case 1 { %v = "some.op1.case1"() : () -> i32
           scf.yield %v : i32 }
  default { %v = "some.op1.default"() : () -> i32
            scf.yield %v : i32 }
  util.return %result0, %result1 : i32, i32
}
```

Prior to this fix, the pattern collapses both into a single
`scf.index_switch %case0 -> i32, i32`, silently discarding `%case1`.

### Changes

- Require matching selector operands in `MergeIndexSwitchPattern` before
merging.
- Add a negative test case to
`Dialect/Util/Transforms/test/patterns.mlir` to ensure distinct
selectors prevent merging.

Assisted-by: Claude Code

Signed-off-by: jonathansong <7958352+jonathansong@users.noreply.github.com>
Co-authored-by: jonathansong <7958352+jonathansong@users.noreply.github.com>
diff --git a/compiler/src/iree/compiler/Dialect/Util/Transforms/Patterns.cpp b/compiler/src/iree/compiler/Dialect/Util/Transforms/Patterns.cpp
index 94557c9..50541ca 100644
--- a/compiler/src/iree/compiler/Dialect/Util/Transforms/Patterns.cpp
+++ b/compiler/src/iree/compiler/Dialect/Util/Transforms/Patterns.cpp
@@ -487,6 +487,13 @@
       return failure();
     }
 
+    // The merged switch takes prevOp's selector: both must switch on the same
+    // value (e.g. the variant selections of two dispatches of executables with
+    // different variant conditions differ).
+    if (prevOp.getArg() != nextOp.getArg()) {
+      return rewriter.notifyMatchFailure(nextOp, "selectors differ");
+    }
+
     // Require that the cases line up exactly. There's probably some merging
     // we could do in other cases but it'd be best to leave other patterns to
     // hoist/CSE cases/etc instead.
diff --git a/compiler/src/iree/compiler/Dialect/Util/Transforms/test/patterns.mlir b/compiler/src/iree/compiler/Dialect/Util/Transforms/test/patterns.mlir
index becfa2a..26b2b37 100644
--- a/compiler/src/iree/compiler/Dialect/Util/Transforms/test/patterns.mlir
+++ b/compiler/src/iree/compiler/Dialect/Util/Transforms/test/patterns.mlir
@@ -297,3 +297,49 @@
   }
   util.return
 }
+
+// -----
+
+// Switches on different selector values must not be merged: the merged switch
+// would dispatch the second switch's cases on the first switch's selector.
+
+// CHECK-LABEL: @dontMergeIndexSwitchesWithDifferentSelectors
+// CHECK-SAME: (%[[CASE0:.+]]: index, %[[CASE1:.+]]: index)
+util.func @dontMergeIndexSwitchesWithDifferentSelectors(%case0: index, %case1: index) -> (i32, i32) {
+  // CHECK: %[[RESULT0:.+]] = scf.index_switch %[[CASE0]] -> i32
+  %result0 = scf.index_switch %case0 -> i32
+  // CHECK: "some.op0.case0"
+  case 0 {
+    %case0_0 = "some.op0.case0"() : () -> i32
+    scf.yield %case0_0 : i32
+  }
+  // CHECK: "some.op0.case1"
+  case 1 {
+    %case1_0 = "some.op0.case1"() : () -> i32
+    scf.yield %case1_0 : i32
+  }
+  // CHECK: "some.op0.default"
+  default {
+    %default_0 = "some.op0.default"() : () -> i32
+    scf.yield %default_0 : i32
+  }
+  // CHECK: %[[RESULT1:.+]] = scf.index_switch %[[CASE1]] -> i32
+  %result1 = scf.index_switch %case1 -> i32
+  // CHECK: "some.op1.case0"
+  case 0 {
+    %case0_1 = "some.op1.case0"() : () -> i32
+    scf.yield %case0_1 : i32
+  }
+  // CHECK: "some.op1.case1"
+  case 1 {
+    %case1_1 = "some.op1.case1"() : () -> i32
+    scf.yield %case1_1 : i32
+  }
+  // CHECK: "some.op1.default"
+  default {
+    %default_1 = "some.op1.default"() : () -> i32
+    scf.yield %default_1 : i32
+  }
+  // CHECK: util.return %[[RESULT0]], %[[RESULT1]]
+  util.return %result0, %result1 : i32, i32
+}