https://github.com/aokblast created 
https://github.com/llvm/llvm-project/pull/230340

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

>From 5b0ebe6169c317b41ce5a762170710401388ec0e Mon Sep 17 00:00:00 2001
From: ShengYi Hung <[email protected]>
Date: Thu, 8 Oct 2026 20:49:26 -0500
Subject: [PATCH] [CIR] Implement BranchOpInterface for switch.flat

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
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  1 +
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 20 ++++++
 clang/test/CIR/IR/switch-flat.cir             |  8 +--
 .../test/CIR/Transforms/switch-flat-sccp.cir  | 70 +++++++++++++++++++
 4 files changed, 95 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/Transforms/switch-flat-sccp.cir

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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to