[clang] [CIR] Implement BranchOpInterface for switch.flat (PR #230340)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 8 18:56:28 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
@llvm/pr-subscribers-clang
Author: aokblast
<details>
<summary>Changes</summary>
Switch.flat should minic llvm.switch as they are both CFG formed. Implement the same mechanism as what we have in llvm.switch and add ArrayRef<Attribute> override when we actually know the operands.
Assisted-by: Claude # Tests
---
Full diff: https://github.com/llvm/llvm-project/pull/230340.diff
4 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+1)
- (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+20)
- (modified) clang/test/CIR/IR/switch-flat.cir (+4-4)
- (added) clang/test/CIR/Transforms/switch-flat-sccp.cir (+70)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index de805d4c642a27..259ab7fab9f0d8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1824,6 +1824,7 @@ def CIR_IsConstantOp : CIR_Op<"is_constant", [Pure]> {
//===----------------------------------------------------------------------===//
def CIR_SwitchFlatOp : CIR_Op<"switch.flat", [
+ DeclareOpInterfaceMethods<BranchOpInterface, ["getSuccessorForOperands"]>,
AttrSizedOperandSegments, Terminator
]> {
let summary = "A flattened version of cir.switch";
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index d5a587ff6d81ed..30c25e5b9f77ed 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2237,6 +2237,26 @@ void cir::SwitchFlatOp::build(OpBuilder &builder, OperationState &result,
defaultDestination, caseDestinations);
}
+SuccessorOperands cir::SwitchFlatOp::getSuccessorOperands(unsigned index) {
+ assert(index < getNumSuccessors() && "invalid successor index");
+ if (index == 0)
+ return SuccessorOperands(getDefaultOperandsMutable());
+ return SuccessorOperands(getCaseOperandsMutable()[index - 1]);
+}
+
+Block *
+cir::SwitchFlatOp::getSuccessorForOperands(ArrayRef<Attribute> operands) {
+ auto cond = dyn_cast_if_present<cir::IntAttr>(operands.front());
+ if (!cond)
+ return nullptr;
+ for (auto [value, dest] : llvm::zip(getCaseValues(), getCaseDestinations())) {
+ const APInt &caseValue = cast<cir::IntAttr>(value).getValue();
+ if (caseValue == cond.getValue())
+ return dest;
+ }
+ return getDefaultDestination();
+}
+
/// <cases> ::= `[` (case (`,` case )* )? `]`
/// <case> ::= integer `:` bb-id (`(` ssa-use-and-type-list `)`)?
static ParseResult parseSwitchFlatOpCases(
diff --git a/clang/test/CIR/IR/switch-flat.cir b/clang/test/CIR/IR/switch-flat.cir
index d39c3e7e81215d..61a9c91c4cea23 100644
--- a/clang/test/CIR/IR/switch-flat.cir
+++ b/clang/test/CIR/IR/switch-flat.cir
@@ -46,10 +46,10 @@ cir.func @switchWithOperands(%arg0: !s32i, %arg1: !s32i, %arg2: !s32i) {
0: ^bb1(%arg1, %arg2 : !s32i, !s32i),
1: ^bb2(%arg2, %arg1 : !s32i, !s32i)
]
-^bb1:
+^bb1(%0: !s32i, %1: !s32i):
cir.br ^bb3
-^bb2:
+^bb2(%2: !s32i, %3: !s32i):
cir.br ^bb3
^bb3:
@@ -60,9 +60,9 @@ cir.func @switchWithOperands(%arg0: !s32i, %arg1: !s32i, %arg2: !s32i) {
// CHECK-NEXT: 0: ^bb1(%arg1, %arg2 : !s32i, !s32i),
// CHECK-NEXT: 1: ^bb2(%arg2, %arg1 : !s32i, !s32i)
// CHECK-NEXT: ]
-// CHECK-NEXT: ^bb1:
+// CHECK-NEXT: ^bb1(%{{.*}}: !s32i, %{{.*}}: !s32i):
// CHECK-NEXT: cir.br ^bb3
-// CHECK-NEXT: ^bb2:
+// CHECK-NEXT: ^bb2(%{{.*}}: !s32i, %{{.*}}: !s32i):
// CHECK-NEXT: cir.br ^bb3
// CHECK-NEXT: ^bb3:
// CHECK-NEXT: cir.return
diff --git a/clang/test/CIR/Transforms/switch-flat-sccp.cir b/clang/test/CIR/Transforms/switch-flat-sccp.cir
new file mode 100644
index 00000000000000..bee9eefc1e15d2
--- /dev/null
+++ b/clang/test/CIR/Transforms/switch-flat-sccp.cir
@@ -0,0 +1,70 @@
+// RUN: cir-opt %s -sccp -o - | FileCheck %s
+
+// cir.switch.flat implements BranchOpInterface::getSuccessorForOperands, so
+// with a constant condition only the taken edge is live and SCCP can fold the
+// taken successor's block argument to the constant passed along that edge.
+
+!s32i = !cir.int<s, 32>
+
+cir.func @constant_switch() -> !s32i {
+ %cond = cir.const #cir.int<1> : !s32i
+ %c10 = cir.const #cir.int<10> : !s32i
+ %c20 = cir.const #cir.int<20> : !s32i
+ %c30 = cir.const #cir.int<30> : !s32i
+ cir.switch.flat %cond : !s32i, ^default(%c30 : !s32i) [
+ 0: ^case0(%c10 : !s32i),
+ 1: ^case1(%c20 : !s32i)
+ ]
+^case0(%a: !s32i):
+ cir.return %a : !s32i
+^case1(%b: !s32i):
+ cir.return %b : !s32i
+^default(%d: !s32i):
+ cir.return %d : !s32i
+}
+
+// CHECK-LABEL: cir.func @constant_switch
+// CHECK: %[[C20:.*]] = cir.const #cir.int<20> : !s32i
+// CHECK: ^bb1(%[[A:.*]]: !s32i):
+// CHECK-NEXT: cir.return %[[A]] : !s32i
+// CHECK: ^bb2(%{{.*}}: !s32i):
+// CHECK-NEXT: cir.return %[[C20]] : !s32i
+// CHECK: ^bb3(%[[D:.*]]: !s32i):
+// CHECK-NEXT: cir.return %[[D]] : !s32i
+
+// No case matches a constant condition: the default edge is the live one.
+cir.func @constant_switch_default() -> !s32i {
+ %cond = cir.const #cir.int<7> : !s32i
+ %c10 = cir.const #cir.int<10> : !s32i
+ %c30 = cir.const #cir.int<30> : !s32i
+ cir.switch.flat %cond : !s32i, ^default(%c30 : !s32i) [
+ 0: ^case0(%c10 : !s32i)
+ ]
+^case0(%a: !s32i):
+ cir.return %a : !s32i
+^default(%d: !s32i):
+ cir.return %d : !s32i
+}
+
+// CHECK-LABEL: cir.func @constant_switch_default
+// CHECK: %[[C30:.*]] = cir.const #cir.int<30> : !s32i
+// CHECK: ^bb1(%[[A:.*]]: !s32i):
+// CHECK-NEXT: cir.return %[[A]] : !s32i
+// CHECK: ^bb2(%{{.*}}: !s32i):
+// CHECK-NEXT: cir.return %[[C30]] : !s32i
+
+// A non-constant condition keeps every edge live: the block argument joins
+// two different constants and cannot fold.
+cir.func @dynamic_switch(%cond: !s32i) -> !s32i {
+ %c10 = cir.const #cir.int<10> : !s32i
+ %c20 = cir.const #cir.int<20> : !s32i
+ cir.switch.flat %cond : !s32i, ^join(%c20 : !s32i) [
+ 0: ^join(%c10 : !s32i)
+ ]
+^join(%v: !s32i):
+ cir.return %v : !s32i
+}
+
+// CHECK-LABEL: cir.func @dynamic_switch
+// CHECK: ^bb1(%[[V:.*]]: !s32i):
+// CHECK-NEXT: cir.return %[[V]] : !s32i
``````````
</details>
https://github.com/llvm/llvm-project/pull/230340
More information about the cfe-commits
mailing list