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
