[flang-commits] [flang] [flang] Keep switch successor operand counts in sync (PR #234126)
via flang-commits
flang-commits at lists.llvm.org
Wed Oct 21 02:18:11 PDT 2026
https://github.com/leovancekang created https://github.com/llvm/llvm-project/pull/234126
FIR switch operations fail to update target_operand_offsets when successor operands are added or removed through MutableOperandRange.
Declare the attribute in ODS so it can be updated by setInherentAttr, which MutableOperandRange has used since ad9dc60252ed (#218907).
Add regression tests for operand insertion and removal.
>From 796e572e74b7246c81f4770c7c38d9fbea26e2fa Mon Sep 17 00:00:00 2001
From: leovancekang <leovancekang at outlook.com>
Date: Wed, 21 Oct 2026 14:34:27 +0800
Subject: [PATCH] [flang] Keep switch successor operand counts in sync
FIR switch operations fail to update target_operand_offsets when
successor operands are added or removed through MutableOperandRange.
Declare the attribute in ODS so it can be updated by setInherentAttr,
which MutableOperandRange has used since ad9dc60252ed (#218907).
Add regression tests for operand insertion and removal.
---
.../include/flang/Optimizer/Dialect/FIROps.td | 3 +-
flang/test/Fir/select-successor-operands.fir | 76 +++++++++++++++++++
2 files changed, 78 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Fir/select-successor-operands.fir
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index fc00daac1695b1..789a510316b758 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -649,7 +649,8 @@ class fir_SwitchTerminatorOp<string mnemonic, list<Trait> traits = []> :
let arguments = (ins
AnyType:$selector,
Variadic<AnyType>:$compareArgs,
- Variadic<AnyType>:$targetArgs
+ Variadic<AnyType>:$targetArgs,
+ DenseI32ArrayAttr:$target_operand_offsets
);
let results = (outs);
diff --git a/flang/test/Fir/select-successor-operands.fir b/flang/test/Fir/select-successor-operands.fir
new file mode 100644
index 00000000000000..aad1211671a11f
--- /dev/null
+++ b/flang/test/Fir/select-successor-operands.fir
@@ -0,0 +1,76 @@
+// RUN: fir-opt %s --canonicalize="region-simplify=aggressive" | FileCheck %s
+// RUN: fir-opt %s --canonicalize="region-simplify=aggressive" --mlir-print-op-generic | fir-opt | FileCheck %s
+
+// Removing an unused block argument must update the operand count of each
+// incoming edge, including multiple edges from the same operation.
+// CHECK-LABEL: func.func @select_remove_unused(
+// CHECK-SAME: %[[SEL:.*]]: i32, %[[A:.*]]: i32, %[[B:.*]]: i32)
+// CHECK: fir.select %[[SEL]] : i32 [1, ^[[DEST:bb[0-9]+]](%[[A]] : i32), unit, ^[[DEST]](%[[B]] : i32)]
+// CHECK: ^[[DEST]](%[[VALUE:.*]]: i32):
+// CHECK-NEXT: return %[[VALUE]] : i32
+func.func @select_remove_unused(%selector: i32, %a: i32, %b: i32) -> i32 {
+ fir.select %selector : i32 [1, ^dest(%a, %b : i32, i32),
+ unit, ^dest(%b, %a : i32, i32)]
+^dest(%value: i32, %unused: i32):
+ return %value : i32
+}
+
+// CHECK-LABEL: func.func @select_rank_remove_unused(
+// CHECK-SAME: %[[SEL:.*]]: i32, %[[A:.*]]: i32, %[[B:.*]]: i32)
+// CHECK: fir.select_rank %[[SEL]] : i32 [1, ^[[DEST:bb[0-9]+]](%[[A]] : i32), unit, ^[[DEST]](%[[B]] : i32)]
+// CHECK: ^[[DEST]](%[[VALUE:.*]]: i32):
+// CHECK-NEXT: return %[[VALUE]] : i32
+func.func @select_rank_remove_unused(%selector: i32, %a: i32, %b: i32) -> i32 {
+ fir.select_rank %selector : i32 [1, ^dest(%a, %b : i32, i32),
+ unit, ^dest(%b, %a : i32, i32)]
+^dest(%value: i32, %unused: i32):
+ return %value : i32
+}
+
+// CHECK-LABEL: func.func @select_type_remove_unused(
+// CHECK-SAME: %[[SEL:.*]]: !fir.class<none>, %[[A:.*]]: i32, %[[B:.*]]: i32)
+// CHECK: fir.select_type %[[SEL]] : !fir.class<none> [#fir.type_is<i32>, ^[[DEST:bb[0-9]+]](%[[A]] : i32), unit, ^[[DEST]](%[[B]] : i32)]
+// CHECK: ^[[DEST]](%[[VALUE:.*]]: i32):
+// CHECK-NEXT: return %[[VALUE]] : i32
+func.func @select_type_remove_unused(%selector: !fir.class<none>, %a: i32, %b: i32) -> i32 {
+ fir.select_type %selector : !fir.class<none> [#fir.type_is<i32>, ^dest(%a, %b : i32, i32),
+ unit, ^dest(%b, %a : i32, i32)]
+^dest(%value: i32, %unused: i32):
+ return %value : i32
+}
+
+// Merging the two store blocks adds a block argument and an operand to each
+// incoming edge. The second block has two incoming edges from select_case;
+// both must be updated, while the default edge must remain empty.
+// CHECK-LABEL: func.func @select_case_merge_blocks(
+// CHECK-SAME: %[[SEL:.*]]: i32, %[[REF:.*]]: !fir.ref<f64>)
+// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : i32
+// CHECK-DAG: %[[C2:.*]] = arith.constant 2 : i32
+// CHECK-DAG: %[[C3:.*]] = arith.constant 3 : i32
+// CHECK-DAG: %[[ONE:.*]] = arith.constant 1.000000e+00 : f64
+// CHECK-DAG: %[[TWO:.*]] = arith.constant 2.000000e+00 : f64
+// CHECK: fir.select_case %[[SEL]] : i32 [#fir.point, %[[C1]], ^[[STORE:bb[0-9]+]](%[[ONE]] : f64), #fir.point, %[[C2]], ^[[STORE]](%[[TWO]] : f64), #fir.point, %[[C3]], ^[[STORE]](%[[TWO]] : f64), unit, ^[[EXIT:bb[0-9]+]]]
+// CHECK: ^[[STORE]](%[[VALUE:.*]]: f64):
+// CHECK-NEXT: fir.store %[[VALUE]] to %[[REF]] : !fir.ref<f64>
+// CHECK-NEXT: cf.br ^[[EXIT]]
+// CHECK: ^[[EXIT]]:
+// CHECK-NEXT: return
+func.func @select_case_merge_blocks(%selector: i32, %value: !fir.ref<f64>) {
+ %c1 = arith.constant 1 : i32
+ %c2 = arith.constant 2 : i32
+ %c3 = arith.constant 3 : i32
+ %one = arith.constant 1.0 : f64
+ %two = arith.constant 2.0 : f64
+ fir.select_case %selector : i32 [#fir.point, %c1, ^first,
+ #fir.point, %c2, ^second,
+ #fir.point, %c3, ^second,
+ unit, ^exit]
+^first:
+ fir.store %one to %value : !fir.ref<f64>
+ cf.br ^exit
+^second:
+ fir.store %two to %value : !fir.ref<f64>
+ cf.br ^exit
+^exit:
+ return
+}
More information about the flang-commits
mailing list