[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
Fri Sep 25 13:54:28 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/2] [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/2] [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
+}
More information about the Mlir-commits
mailing list