[Mlir-commits] [mlir] [mlir][cf] Fix cf.switch round-trip for case values wider than 64 bits (PR #221490)

Prajit Rahul llvmlistbot at llvm.org
Mon Sep 28 19:16:13 PDT 2026


https://github.com/Prajit-Rahul updated https://github.com/llvm/llvm-project/pull/221490

>From c41a3ab79826bbc31d0fce9657b6d2216b3ff469 Mon Sep 17 00:00:00 2001
From: Prajit-Rahul <prajitrahul05 at gmail.com>
Date: Sat, 5 Sep 2026 07:33:58 +0000
Subject: [PATCH 1/5] [mlir][cf] Fix cf.switch round-trip for case values wider
 than 64 bits

The custom printer used APInt::getLimitedValue(), which saturates values
above UINT64_MAX to UINT64_MAX. The custom parser read case values into
int64_t, preventing arbitrary-width values from being represented.

Fix the printer to print APInt values directly and the parser to parse
into APInt with sextOrTrunc to the declared flag width.

Fixes #220609
---
 .../Dialect/ControlFlow/IR/ControlFlowOps.cpp |   6 +-
 .../ControlFlow/switch-wide-integers.mlir     | 134 ++++++++++++++++++
 2 files changed, 137 insertions(+), 3 deletions(-)
 create mode 100644 mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir

diff --git a/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp b/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
index 435c37bc95aac..bfdbcf65cd17a 100644
--- a/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
+++ b/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
@@ -647,10 +647,10 @@ static ParseResult parseSwitchOpCases(
   SmallVector<APInt> values;
   unsigned bitWidth = flagType.getIntOrFloatBitWidth();
   while (succeeded(parser.parseOptionalComma())) {
-    int64_t value = 0;
+    APInt value;
     if (failed(parser.parseInteger(value)))
       return failure();
-    values.push_back(APInt(bitWidth, value, /*isSigned=*/true));
+    values.push_back(value.sextOrTrunc(bitWidth));
 
     Block *destination;
     SmallVector<OpAsmParser::UnresolvedOperand> operands;
@@ -693,7 +693,7 @@ static void printSwitchOpCases(
     p << ',';
     p.printNewline();
     p << "  ";
-    p << it.value().getLimitedValue();
+    p << it.value();
     p << ": ";
     p.printSuccessorAndUseList(caseDestinations[it.index()],
                                caseOperands[it.index()]);
diff --git a/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir b/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
new file mode 100644
index 0000000000000..2c6a136b16608
--- /dev/null
+++ b/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
@@ -0,0 +1,134 @@
+// RUN: mlir-opt %s | mlir-opt | FileCheck %s
+// RUN: mlir-opt %s --mlir-print-op-generic | FileCheck %s --check-prefix=GENERIC
+// RUN: mlir-opt %s | mlir-opt --mlir-print-op-generic | FileCheck %s --check-prefix=GENERIC
+// RUN: mlir-opt %s | mlir-opt --canonicalize | FileCheck %s --check-prefix=FOLD
+
+// Start with generic switches so the custom printer must preserve both values.
+// After reparsing, canonicalization must select the first and second cases,
+// respectively, rather than the default or the same case twice.
+// CHECK-LABEL: func.func @switch_i128_large_cases
+// CHECK:       18446744073709551616:
+// CHECK-NEXT:  18446744073709551617:
+// CHECK:       18446744073709551616:
+// CHECK-NEXT:  18446744073709551617:
+// GENERIC-LABEL: sym_name = "switch_i128_large_cases"
+// GENERIC: case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>
+// GENERIC: case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>
+// FOLD-LABEL: func.func @switch_i128_large_cases
+// FOLD-DAG:   %[[ONE:.*]] = arith.constant 1 : i32
+// FOLD-DAG:   %[[TWO:.*]] = arith.constant 2 : i32
+// FOLD-NEXT:  return %[[ONE]], %[[TWO]] : i32, i32
+// FOLD-NEXT:  }
+func.func @switch_i128_large_cases() -> (i32, i32) {
+  %first = arith.constant 18446744073709551616 : i128
+  %second = arith.constant 18446744073709551617 : i128
+  %zero = arith.constant 0 : i32
+  %one = arith.constant 1 : i32
+  %two = arith.constant 2 : i32
+  "cf.switch"(%first, %zero, %one, %two)[^bb1, ^bb1, ^bb1] <{case_operand_segments = array<i32: 1, 1>, case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>, operandSegmentSizes = array<i32: 1, 1, 2>}> : (i128, i32, i32, i32) -> ()
+^bb1(%first_result: i32):
+  "cf.switch"(%second, %zero, %one, %two)[^bb2, ^bb2, ^bb2] <{case_operand_segments = array<i32: 1, 1>, case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>, operandSegmentSizes = array<i32: 1, 1, 2>}> : (i128, i32, i32, i32) -> ()
+^bb2(%second_result: i32):
+  return %first_result, %second_result : i32, i32
+}
+
+// Negative literals must be sign-extended to the declared width, including
+// literals that already require more than 64 bits to parse.
+// CHECK-LABEL: func.func @switch_i128_negative
+// CHECK:       -1:
+// CHECK-NEXT:  -9223372036854775808:
+// CHECK-NEXT:  -9223372036854775809:
+// CHECK-NEXT:  -18446744073709551617:
+// CHECK-NEXT:  -170141183460469231731687303715884105728:
+// CHECK-NEXT:  170141183460469231731687303715884105727:
+// GENERIC-LABEL: sym_name = "switch_i128_negative"
+// GENERIC: case_values = dense<[-1, -9223372036854775808, -9223372036854775809, -18446744073709551617, -170141183460469231731687303715884105728, 170141183460469231731687303715884105727]> : vector<6xi128>
+func.func @switch_i128_negative(%flag: i128) {
+  cf.switch %flag : i128, [
+    default: ^bb1,
+    -1: ^bb1,
+    -9223372036854775808: ^bb1,
+    -9223372036854775809: ^bb1,
+    -18446744073709551617: ^bb1,
+    -170141183460469231731687303715884105728: ^bb1,
+    170141183460469231731687303715884105727: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func.func @switch_i1
+// CHECK:       default:
+// CHECK-NEXT:  0:
+// CHECK-NEXT:  -1:
+// GENERIC-LABEL: sym_name = "switch_i1"
+// GENERIC: case_values = dense<[false, true]> : vector<2xi1>
+func.func @switch_i1(%flag: i1) {
+  cf.switch %flag : i1, [
+    default: ^bb1,
+    0: ^bb1,
+    1: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// Preserve ordinary small values and truncation to the declared width.
+// CHECK-LABEL: func.func @switch_i8
+// CHECK:       42:
+// CHECK-NEXT:  -42:
+// CHECK-NEXT:  -128:
+// CHECK-NEXT:  -1:
+// CHECK-NEXT:  0:
+// CHECK-NEXT:  127:
+// GENERIC-LABEL: sym_name = "switch_i8"
+// GENERIC: case_values = dense<[42, -42, -128, -1, 0, 127]> : vector<6xi8>
+func.func @switch_i8(%flag: i8) {
+  cf.switch %flag : i8, [
+    default: ^bb1,
+    42: ^bb1,
+    -42: ^bb1,
+    128: ^bb1,
+    255: ^bb1,
+    256: ^bb1,
+    -129: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func.func @switch_i64_boundaries
+// CHECK:       9223372036854775807:
+// CHECK-NEXT:  -9223372036854775808:
+// CHECK-NEXT:  -1:
+// GENERIC-LABEL: sym_name = "switch_i64_boundaries"
+// GENERIC: case_values = dense<[9223372036854775807, -9223372036854775808, -1]> : vector<3xi64>
+func.func @switch_i64_boundaries(%flag: i64) {
+  cf.switch %flag : i64, [
+    default: ^bb1,
+    9223372036854775807: ^bb1,
+    -9223372036854775808: ^bb1,
+    18446744073709551615: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// The same positive literal must remain positive when the declared width has
+// room for its sign bit, and become negative when it sets that sign bit.
+// CHECK-LABEL: func.func @switch_i65_boundaries
+// CHECK:       18446744073709551615:
+// CHECK-NEXT:  -18446744073709551616:
+// CHECK-NEXT:  -1:
+// GENERIC-LABEL: sym_name = "switch_i65_boundaries"
+// GENERIC: case_values = dense<[18446744073709551615, -18446744073709551616, -1]> : vector<3xi65>
+func.func @switch_i65_boundaries(%flag: i65) {
+  cf.switch %flag : i65, [
+    default: ^bb1,
+    18446744073709551615: ^bb1,
+    18446744073709551616: ^bb1,
+    -1: ^bb1
+  ]
+^bb1:
+  return
+}

>From a75f9f0fb6eac9f267c587f37c22985d0ca3c6b7 Mon Sep 17 00:00:00 2001
From: Prajit-Rahul <prajitrahul05 at gmail.com>
Date: Fri, 25 Sep 2026 20:34:58 +0000
Subject: [PATCH 2/5] [mlir][cf] Diagnose switch case values outside the flag
 width

---
 .../Dialect/ControlFlow/IR/ControlFlowOps.cpp |  5 +
 mlir/test/Dialect/ControlFlow/invalid.mlir    | 96 +++++++++++++++++++
 .../ControlFlow/switch-wide-integers.mlir     | 33 ++++++-
 3 files changed, 131 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp b/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
index bfdbcf65cd17a..66238e5c5d095 100644
--- a/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
+++ b/mlir/lib/Dialect/ControlFlow/IR/ControlFlowOps.cpp
@@ -647,9 +647,14 @@ static ParseResult parseSwitchOpCases(
   SmallVector<APInt> values;
   unsigned bitWidth = flagType.getIntOrFloatBitWidth();
   while (succeeded(parser.parseOptionalComma())) {
+    SMLoc valueLoc = parser.getCurrentLocation();
     APInt value;
     if (failed(parser.parseInteger(value)))
       return failure();
+    if (value.isNegative() ? !value.isSignedIntN(bitWidth)
+                           : !value.isIntN(bitWidth))
+      return parser.emitError(valueLoc)
+             << "case value is out of range for " << flagType;
     values.push_back(value.sextOrTrunc(bitWidth));
 
     Block *destination;
diff --git a/mlir/test/Dialect/ControlFlow/invalid.mlir b/mlir/test/Dialect/ControlFlow/invalid.mlir
index 0a71c62ec31af..1622f9a1efab1 100644
--- a/mlir/test/Dialect/ControlFlow/invalid.mlir
+++ b/mlir/test/Dialect/ControlFlow/invalid.mlir
@@ -91,3 +91,99 @@ func.func @wrong_total_weight(%cond: i1) {
   ^bb2:
     return
 }
+
+// -----
+
+func.func @switch_i1_unsigned_overflow(%flag: i1) {
+  cf.switch %flag : i1, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i1'}}
+    2: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i1_signed_underflow(%flag: i1) {
+  cf.switch %flag : i1, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i1'}}
+    -2: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i8_unsigned_overflow(%flag: i8) {
+  cf.switch %flag : i8, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i8'}}
+    256: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i8_signed_underflow(%flag: i8) {
+  cf.switch %flag : i8, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i8'}}
+    -129: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i64_unsigned_overflow(%flag: i64) {
+  cf.switch %flag : i64, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i64'}}
+    18446744073709551616: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i64_signed_underflow(%flag: i64) {
+  cf.switch %flag : i64, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i64'}}
+    -9223372036854775809: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i128_unsigned_overflow(%flag: i128) {
+  cf.switch %flag : i128, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i128'}}
+    340282366920938463463374607431768211456: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_i128_signed_underflow(%flag: i128) {
+  cf.switch %flag : i128, [
+    default: ^bb1,
+    // expected-error at +1 {{case value is out of range for 'i128'}}
+    -170141183460469231731687303715884105729: ^bb1
+  ]
+^bb1:
+  return
+}
diff --git a/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir b/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
index 2c6a136b16608..1b121b193d6d2 100644
--- a/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
+++ b/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
@@ -73,7 +73,7 @@ func.func @switch_i1(%flag: i1) {
   return
 }
 
-// Preserve ordinary small values and truncation to the declared width.
+// Accept both signed and unsigned values representable at the declared width.
 // CHECK-LABEL: func.func @switch_i8
 // CHECK:       42:
 // CHECK-NEXT:  -42:
@@ -90,8 +90,8 @@ func.func @switch_i8(%flag: i8) {
     -42: ^bb1,
     128: ^bb1,
     255: ^bb1,
-    256: ^bb1,
-    -129: ^bb1
+    0: ^bb1,
+    127: ^bb1
   ]
 ^bb1:
   return
@@ -132,3 +132,30 @@ func.func @switch_i65_boundaries(%flag: i65) {
 ^bb1:
   return
 }
+
+// Signed narrow minima must also survive printing and reparsing.
+// CHECK-LABEL: func.func @switch_i8_signed_min
+// CHECK:       -128:
+// GENERIC-LABEL: sym_name = "switch_i8_signed_min"
+// GENERIC: case_values = dense<-128> : vector<1xi8>
+func.func @switch_i8_signed_min(%flag: i8) {
+  cf.switch %flag : i8, [
+    default: ^bb1,
+    -128: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func.func @switch_i128_unsigned_max
+// CHECK:       -1:
+// GENERIC-LABEL: sym_name = "switch_i128_unsigned_max"
+// GENERIC: case_values = dense<-1> : vector<1xi128>
+func.func @switch_i128_unsigned_max(%flag: i128) {
+  cf.switch %flag : i128, [
+    default: ^bb1,
+    340282366920938463463374607431768211455: ^bb1
+  ]
+^bb1:
+  return
+}

>From 43260405f97c81e449f462b6f3886d152822b98f Mon Sep 17 00:00:00 2001
From: Prajit-Rahul <prajitrahul05 at gmail.com>
Date: Fri, 25 Sep 2026 21:39:55 +0000
Subject: [PATCH 3/5] [mlir][cf] Keep switch regression tests with existing
 tests

---
 .../Dialect/ControlFlow/canonicalize.mlir     |  28 +++
 mlir/test/Dialect/ControlFlow/invalid.mlir    |  48 ------
 mlir/test/Dialect/ControlFlow/ops.mlir        |  56 ++++++
 .../ControlFlow/switch-wide-integers.mlir     | 161 ------------------
 4 files changed, 84 insertions(+), 209 deletions(-)
 delete mode 100644 mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir

diff --git a/mlir/test/Dialect/ControlFlow/canonicalize.mlir b/mlir/test/Dialect/ControlFlow/canonicalize.mlir
index c8cf5931a1297..4fe079eff0221 100644
--- a/mlir/test/Dialect/ControlFlow/canonicalize.mlir
+++ b/mlir/test/Dialect/ControlFlow/canonicalize.mlir
@@ -784,3 +784,31 @@ func.func @skip_unused_block_arg(%flag: i32, %a: i32, %b: i32) -> i32 {
   // CHECK-NEXT: return %[[A:.*]]
   return %arg1 : i32
 }
+
+// -----
+
+// The two i128 cases differ above bit 64. Select each after parsing.
+// CHECK-LABEL: func @switch_i128_large_cases
+// CHECK-DAG: %[[ONE:.*]] = arith.constant 1 : i32
+// CHECK-DAG: %[[TWO:.*]] = arith.constant 2 : i32
+// CHECK: return %[[ONE]], %[[TWO]] : i32, i32
+func.func @switch_i128_large_cases() -> (i32, i32) {
+  %first = arith.constant 18446744073709551616 : i128
+  %second = arith.constant 18446744073709551617 : i128
+  %zero = arith.constant 0 : i32
+  %one = arith.constant 1 : i32
+  %two = arith.constant 2 : i32
+  cf.switch %first : i128, [
+    default: ^bb1(%zero : i32),
+    18446744073709551616: ^bb1(%one : i32),
+    18446744073709551617: ^bb1(%two : i32)
+  ]
+^bb1(%first_result: i32):
+  cf.switch %second : i128, [
+    default: ^bb2(%zero : i32),
+    18446744073709551616: ^bb2(%one : i32),
+    18446744073709551617: ^bb2(%two : i32)
+  ]
+^bb2(%second_result: i32):
+  return %first_result, %second_result : i32, i32
+}
diff --git a/mlir/test/Dialect/ControlFlow/invalid.mlir b/mlir/test/Dialect/ControlFlow/invalid.mlir
index 1622f9a1efab1..e2eebb46d1223 100644
--- a/mlir/test/Dialect/ControlFlow/invalid.mlir
+++ b/mlir/test/Dialect/ControlFlow/invalid.mlir
@@ -94,30 +94,6 @@ func.func @wrong_total_weight(%cond: i1) {
 
 // -----
 
-func.func @switch_i1_unsigned_overflow(%flag: i1) {
-  cf.switch %flag : i1, [
-    default: ^bb1,
-    // expected-error at +1 {{case value is out of range for 'i1'}}
-    2: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// -----
-
-func.func @switch_i1_signed_underflow(%flag: i1) {
-  cf.switch %flag : i1, [
-    default: ^bb1,
-    // expected-error at +1 {{case value is out of range for 'i1'}}
-    -2: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// -----
-
 func.func @switch_i8_unsigned_overflow(%flag: i8) {
   cf.switch %flag : i8, [
     default: ^bb1,
@@ -142,30 +118,6 @@ func.func @switch_i8_signed_underflow(%flag: i8) {
 
 // -----
 
-func.func @switch_i64_unsigned_overflow(%flag: i64) {
-  cf.switch %flag : i64, [
-    default: ^bb1,
-    // expected-error at +1 {{case value is out of range for 'i64'}}
-    18446744073709551616: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// -----
-
-func.func @switch_i64_signed_underflow(%flag: i64) {
-  cf.switch %flag : i64, [
-    default: ^bb1,
-    // expected-error at +1 {{case value is out of range for 'i64'}}
-    -9223372036854775809: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// -----
-
 func.func @switch_i128_unsigned_overflow(%flag: i128) {
   cf.switch %flag : i128, [
     default: ^bb1,
diff --git a/mlir/test/Dialect/ControlFlow/ops.mlir b/mlir/test/Dialect/ControlFlow/ops.mlir
index 160534240e0fa..8c39bf86c44c7 100644
--- a/mlir/test/Dialect/ControlFlow/ops.mlir
+++ b/mlir/test/Dialect/ControlFlow/ops.mlir
@@ -61,3 +61,59 @@ func.func @cond_weights(%cond: i1) {
   ^bb2:
     return
 }
+
+// CHECK-LABEL: func @switch_i1_boundary(
+// CHECK: -1: ^
+func.func @switch_i1_boundary(%flag: i1) {
+  cf.switch %flag : i1, [default: ^bb1, 1: ^bb1]
+^bb1:
+  return
+}
+
+// Unsigned literals with the sign bit set print as signed case values.
+// CHECK-LABEL: func @switch_i8_boundaries(
+// CHECK: -128: ^
+// CHECK-NEXT: -1: ^
+func.func @switch_i8_boundaries(%flag: i8) {
+  cf.switch %flag : i8, [default: ^bb1, -128: ^bb1, 255: ^bb1]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func @switch_i64_boundaries(
+// CHECK: -9223372036854775808: ^
+// CHECK-NEXT: -1: ^
+func.func @switch_i64_boundaries(%flag: i64) {
+  cf.switch %flag : i64, [
+    default: ^bb1,
+    -9223372036854775808: ^bb1,
+    18446744073709551615: ^bb1
+  ]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func @switch_i65_boundary(
+// CHECK: -18446744073709551616: ^
+func.func @switch_i65_boundary(%flag: i65) {
+  cf.switch %flag : i65, [default: ^bb1, 18446744073709551616: ^bb1]
+^bb1:
+  return
+}
+
+// CHECK-LABEL: func @switch_i128_boundaries(
+// CHECK: 18446744073709551616: ^
+// CHECK-NEXT: 18446744073709551617: ^
+// CHECK-NEXT: -170141183460469231731687303715884105728: ^
+// CHECK-NEXT: 170141183460469231731687303715884105727: ^
+func.func @switch_i128_boundaries(%flag: i128) {
+  cf.switch %flag : i128, [
+    default: ^bb1,
+    18446744073709551616: ^bb1,
+    18446744073709551617: ^bb1,
+    -170141183460469231731687303715884105728: ^bb1,
+    170141183460469231731687303715884105727: ^bb1
+  ]
+^bb1:
+  return
+}
diff --git a/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir b/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
deleted file mode 100644
index 1b121b193d6d2..0000000000000
--- a/mlir/test/Dialect/ControlFlow/switch-wide-integers.mlir
+++ /dev/null
@@ -1,161 +0,0 @@
-// RUN: mlir-opt %s | mlir-opt | FileCheck %s
-// RUN: mlir-opt %s --mlir-print-op-generic | FileCheck %s --check-prefix=GENERIC
-// RUN: mlir-opt %s | mlir-opt --mlir-print-op-generic | FileCheck %s --check-prefix=GENERIC
-// RUN: mlir-opt %s | mlir-opt --canonicalize | FileCheck %s --check-prefix=FOLD
-
-// Start with generic switches so the custom printer must preserve both values.
-// After reparsing, canonicalization must select the first and second cases,
-// respectively, rather than the default or the same case twice.
-// CHECK-LABEL: func.func @switch_i128_large_cases
-// CHECK:       18446744073709551616:
-// CHECK-NEXT:  18446744073709551617:
-// CHECK:       18446744073709551616:
-// CHECK-NEXT:  18446744073709551617:
-// GENERIC-LABEL: sym_name = "switch_i128_large_cases"
-// GENERIC: case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>
-// GENERIC: case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>
-// FOLD-LABEL: func.func @switch_i128_large_cases
-// FOLD-DAG:   %[[ONE:.*]] = arith.constant 1 : i32
-// FOLD-DAG:   %[[TWO:.*]] = arith.constant 2 : i32
-// FOLD-NEXT:  return %[[ONE]], %[[TWO]] : i32, i32
-// FOLD-NEXT:  }
-func.func @switch_i128_large_cases() -> (i32, i32) {
-  %first = arith.constant 18446744073709551616 : i128
-  %second = arith.constant 18446744073709551617 : i128
-  %zero = arith.constant 0 : i32
-  %one = arith.constant 1 : i32
-  %two = arith.constant 2 : i32
-  "cf.switch"(%first, %zero, %one, %two)[^bb1, ^bb1, ^bb1] <{case_operand_segments = array<i32: 1, 1>, case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>, operandSegmentSizes = array<i32: 1, 1, 2>}> : (i128, i32, i32, i32) -> ()
-^bb1(%first_result: i32):
-  "cf.switch"(%second, %zero, %one, %two)[^bb2, ^bb2, ^bb2] <{case_operand_segments = array<i32: 1, 1>, case_values = dense<[18446744073709551616, 18446744073709551617]> : vector<2xi128>, operandSegmentSizes = array<i32: 1, 1, 2>}> : (i128, i32, i32, i32) -> ()
-^bb2(%second_result: i32):
-  return %first_result, %second_result : i32, i32
-}
-
-// Negative literals must be sign-extended to the declared width, including
-// literals that already require more than 64 bits to parse.
-// CHECK-LABEL: func.func @switch_i128_negative
-// CHECK:       -1:
-// CHECK-NEXT:  -9223372036854775808:
-// CHECK-NEXT:  -9223372036854775809:
-// CHECK-NEXT:  -18446744073709551617:
-// CHECK-NEXT:  -170141183460469231731687303715884105728:
-// CHECK-NEXT:  170141183460469231731687303715884105727:
-// GENERIC-LABEL: sym_name = "switch_i128_negative"
-// GENERIC: case_values = dense<[-1, -9223372036854775808, -9223372036854775809, -18446744073709551617, -170141183460469231731687303715884105728, 170141183460469231731687303715884105727]> : vector<6xi128>
-func.func @switch_i128_negative(%flag: i128) {
-  cf.switch %flag : i128, [
-    default: ^bb1,
-    -1: ^bb1,
-    -9223372036854775808: ^bb1,
-    -9223372036854775809: ^bb1,
-    -18446744073709551617: ^bb1,
-    -170141183460469231731687303715884105728: ^bb1,
-    170141183460469231731687303715884105727: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// CHECK-LABEL: func.func @switch_i1
-// CHECK:       default:
-// CHECK-NEXT:  0:
-// CHECK-NEXT:  -1:
-// GENERIC-LABEL: sym_name = "switch_i1"
-// GENERIC: case_values = dense<[false, true]> : vector<2xi1>
-func.func @switch_i1(%flag: i1) {
-  cf.switch %flag : i1, [
-    default: ^bb1,
-    0: ^bb1,
-    1: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// Accept both signed and unsigned values representable at the declared width.
-// CHECK-LABEL: func.func @switch_i8
-// CHECK:       42:
-// CHECK-NEXT:  -42:
-// CHECK-NEXT:  -128:
-// CHECK-NEXT:  -1:
-// CHECK-NEXT:  0:
-// CHECK-NEXT:  127:
-// GENERIC-LABEL: sym_name = "switch_i8"
-// GENERIC: case_values = dense<[42, -42, -128, -1, 0, 127]> : vector<6xi8>
-func.func @switch_i8(%flag: i8) {
-  cf.switch %flag : i8, [
-    default: ^bb1,
-    42: ^bb1,
-    -42: ^bb1,
-    128: ^bb1,
-    255: ^bb1,
-    0: ^bb1,
-    127: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// CHECK-LABEL: func.func @switch_i64_boundaries
-// CHECK:       9223372036854775807:
-// CHECK-NEXT:  -9223372036854775808:
-// CHECK-NEXT:  -1:
-// GENERIC-LABEL: sym_name = "switch_i64_boundaries"
-// GENERIC: case_values = dense<[9223372036854775807, -9223372036854775808, -1]> : vector<3xi64>
-func.func @switch_i64_boundaries(%flag: i64) {
-  cf.switch %flag : i64, [
-    default: ^bb1,
-    9223372036854775807: ^bb1,
-    -9223372036854775808: ^bb1,
-    18446744073709551615: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// The same positive literal must remain positive when the declared width has
-// room for its sign bit, and become negative when it sets that sign bit.
-// CHECK-LABEL: func.func @switch_i65_boundaries
-// CHECK:       18446744073709551615:
-// CHECK-NEXT:  -18446744073709551616:
-// CHECK-NEXT:  -1:
-// GENERIC-LABEL: sym_name = "switch_i65_boundaries"
-// GENERIC: case_values = dense<[18446744073709551615, -18446744073709551616, -1]> : vector<3xi65>
-func.func @switch_i65_boundaries(%flag: i65) {
-  cf.switch %flag : i65, [
-    default: ^bb1,
-    18446744073709551615: ^bb1,
-    18446744073709551616: ^bb1,
-    -1: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// Signed narrow minima must also survive printing and reparsing.
-// CHECK-LABEL: func.func @switch_i8_signed_min
-// CHECK:       -128:
-// GENERIC-LABEL: sym_name = "switch_i8_signed_min"
-// GENERIC: case_values = dense<-128> : vector<1xi8>
-func.func @switch_i8_signed_min(%flag: i8) {
-  cf.switch %flag : i8, [
-    default: ^bb1,
-    -128: ^bb1
-  ]
-^bb1:
-  return
-}
-
-// CHECK-LABEL: func.func @switch_i128_unsigned_max
-// CHECK:       -1:
-// GENERIC-LABEL: sym_name = "switch_i128_unsigned_max"
-// GENERIC: case_values = dense<-1> : vector<1xi128>
-func.func @switch_i128_unsigned_max(%flag: i128) {
-  cf.switch %flag : i128, [
-    default: ^bb1,
-    340282366920938463463374607431768211455: ^bb1
-  ]
-^bb1:
-  return
-}

>From f8d21b19668d0360eca722c02632f2aa17e1edba Mon Sep 17 00:00:00 2001
From: Prajit-Rahul <prajitrahul05 at gmail.com>
Date: Sun, 27 Sep 2026 02:21:29 +0000
Subject: [PATCH 4/5] [mlir][cf] Clarify wide switch case regression comment

---
 mlir/test/Dialect/ControlFlow/canonicalize.mlir | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/test/Dialect/ControlFlow/canonicalize.mlir b/mlir/test/Dialect/ControlFlow/canonicalize.mlir
index 4fe079eff0221..693801d1b4c00 100644
--- a/mlir/test/Dialect/ControlFlow/canonicalize.mlir
+++ b/mlir/test/Dialect/ControlFlow/canonicalize.mlir
@@ -787,7 +787,7 @@ func.func @skip_unused_block_arg(%flag: i32, %a: i32, %b: i32) -> i32 {
 
 // -----
 
-// The two i128 cases differ above bit 64. Select each after parsing.
+// These distinct i128 cases exceed 64 bits. Select each after parsing.
 // CHECK-LABEL: func @switch_i128_large_cases
 // CHECK-DAG: %[[ONE:.*]] = arith.constant 1 : i32
 // CHECK-DAG: %[[TWO:.*]] = arith.constant 2 : i32

>From 59aa4616bac3d70100e28a01c367910f259c029c Mon Sep 17 00:00:00 2001
From: Prajit-Rahul <prajitrahul05 at gmail.com>
Date: Tue, 29 Sep 2026 02:15:40 +0000
Subject: [PATCH 5/5] [mlir][cf] Address switch review comments

---
 .../Dialect/ControlFlow/IR/ControlFlowOps.td  |  2 +-
 mlir/test/Dialect/ControlFlow/invalid.mlir    | 18 +++++++++++++++++
 mlir/test/Dialect/ControlFlow/ops.mlir        | 20 +++++++++----------
 3 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/mlir/include/mlir/Dialect/ControlFlow/IR/ControlFlowOps.td b/mlir/include/mlir/Dialect/ControlFlow/IR/ControlFlowOps.td
index 0e4c4eb78b94b..e881e9220fbcf 100644
--- a/mlir/include/mlir/Dialect/ControlFlow/IR/ControlFlowOps.td
+++ b/mlir/include/mlir/Dialect/ControlFlow/IR/ControlFlowOps.td
@@ -264,7 +264,7 @@ def SwitchOp : CF_Op<"switch",
   }];
 
   let arguments = (ins
-    AnyInteger:$flag,
+    AnySignlessInteger:$flag,
     Variadic<AnyType>:$defaultOperands,
     VariadicOfVariadic<AnyType, "case_operand_segments">:$caseOperands,
     OptionalAttr<AnyIntElementsAttr>:$case_values,
diff --git a/mlir/test/Dialect/ControlFlow/invalid.mlir b/mlir/test/Dialect/ControlFlow/invalid.mlir
index e2eebb46d1223..ad9fa6381ba47 100644
--- a/mlir/test/Dialect/ControlFlow/invalid.mlir
+++ b/mlir/test/Dialect/ControlFlow/invalid.mlir
@@ -139,3 +139,21 @@ func.func @switch_i128_signed_underflow(%flag: i128) {
 ^bb1:
   return
 }
+
+// -----
+
+func.func @switch_signed_flag(%flag: si8) {
+  // expected-error at +1 {{'cf.switch' op operand #0 must be signless integer, but got 'si8'}}
+  cf.switch %flag : si8, [default: ^bb1]
+^bb1:
+  return
+}
+
+// -----
+
+func.func @switch_unsigned_flag(%flag: ui8) {
+  // expected-error at +1 {{'cf.switch' op operand #0 must be signless integer, but got 'ui8'}}
+  cf.switch %flag : ui8, [default: ^bb1]
+^bb1:
+  return
+}
diff --git a/mlir/test/Dialect/ControlFlow/ops.mlir b/mlir/test/Dialect/ControlFlow/ops.mlir
index 8c39bf86c44c7..f800a71e48ca8 100644
--- a/mlir/test/Dialect/ControlFlow/ops.mlir
+++ b/mlir/test/Dialect/ControlFlow/ops.mlir
@@ -63,7 +63,7 @@ func.func @cond_weights(%cond: i1) {
 }
 
 // CHECK-LABEL: func @switch_i1_boundary(
-// CHECK: -1: ^
+// CHECK: -1: ^bb1
 func.func @switch_i1_boundary(%flag: i1) {
   cf.switch %flag : i1, [default: ^bb1, 1: ^bb1]
 ^bb1:
@@ -72,8 +72,8 @@ func.func @switch_i1_boundary(%flag: i1) {
 
 // Unsigned literals with the sign bit set print as signed case values.
 // CHECK-LABEL: func @switch_i8_boundaries(
-// CHECK: -128: ^
-// CHECK-NEXT: -1: ^
+// CHECK: -128: ^bb1
+// CHECK-NEXT: -1: ^bb1
 func.func @switch_i8_boundaries(%flag: i8) {
   cf.switch %flag : i8, [default: ^bb1, -128: ^bb1, 255: ^bb1]
 ^bb1:
@@ -81,8 +81,8 @@ func.func @switch_i8_boundaries(%flag: i8) {
 }
 
 // CHECK-LABEL: func @switch_i64_boundaries(
-// CHECK: -9223372036854775808: ^
-// CHECK-NEXT: -1: ^
+// CHECK: -9223372036854775808: ^bb1
+// CHECK-NEXT: -1: ^bb1
 func.func @switch_i64_boundaries(%flag: i64) {
   cf.switch %flag : i64, [
     default: ^bb1,
@@ -94,7 +94,7 @@ func.func @switch_i64_boundaries(%flag: i64) {
 }
 
 // CHECK-LABEL: func @switch_i65_boundary(
-// CHECK: -18446744073709551616: ^
+// CHECK: -18446744073709551616: ^bb1
 func.func @switch_i65_boundary(%flag: i65) {
   cf.switch %flag : i65, [default: ^bb1, 18446744073709551616: ^bb1]
 ^bb1:
@@ -102,10 +102,10 @@ func.func @switch_i65_boundary(%flag: i65) {
 }
 
 // CHECK-LABEL: func @switch_i128_boundaries(
-// CHECK: 18446744073709551616: ^
-// CHECK-NEXT: 18446744073709551617: ^
-// CHECK-NEXT: -170141183460469231731687303715884105728: ^
-// CHECK-NEXT: 170141183460469231731687303715884105727: ^
+// CHECK: 18446744073709551616: ^bb1
+// CHECK-NEXT: 18446744073709551617: ^bb1
+// CHECK-NEXT: -170141183460469231731687303715884105728: ^bb1
+// CHECK-NEXT: 170141183460469231731687303715884105727: ^bb1
 func.func @switch_i128_boundaries(%flag: i128) {
   cf.switch %flag : i128, [
     default: ^bb1,



More information about the Mlir-commits mailing list