Author: aokblast
Date: 2026-10-09T11:55:16+08:00
New Revision: 33840cf328ca48334cb9359fd2c61539f11ee901

URL: 
https://github.com/llvm/llvm-project/commit/33840cf328ca48334cb9359fd2c61539f11ee901
DIFF: 
https://github.com/llvm/llvm-project/commit/33840cf328ca48334cb9359fd2c61539f11ee901.diff

LOG: [CIR] Implement BranchOpInterface for switch.flat (#230340)

cir.switch.flat is the CFG-form terminator produced by CIR flattening,
but unlike cir.br and cir.brcond, it doesn't implmenet BranchOpIterface.
As a result, generic MLIR control-flow infrastructure could not tell
which operands are forwarded to which successor's block arguments,. For
example, SCCP pass. When the switch condition is a know constant, SCCP
can know that which sucessor block is actually taken. Before, SCCP
didn't understand switch.flat. It treated every sucessor as reachable,
which forbid it to fold anything.

Implmenet the interface the same way llvm.switch do now.

Assisted-by: Claude # Tests

Added: 
    clang/test/CIR/Transforms/switch-flat-sccp.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp
    clang/test/CIR/IR/switch-flat.cir

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index bf6d36bb89323..1f264ce184c4a 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 408d43bf9d886..8a62ae541b4b6 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2333,6 +2333,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 d39c3e7e81215..61a9c91c4cea2 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 0000000000000..bee9eefc1e15d
--- /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 
diff erent 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