[clang] cb4b6ad - [CIR] Add the ability to detect if SwitchOp covers all the cases (#171246)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 10 22:53:55 PST 2025
Author: Jasmine Tang
Date: 2025-12-10T22:53:50-08:00
New Revision: cb4b6ad8171e9f3dd991b0d7fa1771bcafc3c0a6
URL: https://github.com/llvm/llvm-project/commit/cb4b6ad8171e9f3dd991b0d7fa1771bcafc3c0a6
DIFF: https://github.com/llvm/llvm-project/commit/cb4b6ad8171e9f3dd991b0d7fa1771bcafc3c0a6.diff
LOG: [CIR] Add the ability to detect if SwitchOp covers all the cases (#171246)
Added:
Modified:
clang/include/clang/CIR/Dialect/IR/CIROps.td
clang/lib/CIR/CodeGen/CIRGenStmt.cpp
clang/lib/CIR/Dialect/IR/CIRDialect.cpp
clang/test/CIR/CodeGen/atomic.c
clang/test/CIR/CodeGen/switch.cpp
clang/test/CIR/CodeGen/switch_flat_op.cpp
clang/test/CIR/IR/switch.cir
clang/test/CIR/Transforms/switch-fold.cir
Removed:
################################################################################
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c4ad6a0c0732c..74e0860762ec6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1080,6 +1080,12 @@ def CIR_SwitchOp : CIR_Op<"switch", [
conditionally executing multiple regions of code. The operand to an switch
is an integral condition value.
+ Besides taking an integer condition and CIR regions, it also accepts an
+ `all_enum_cases_covered` attribute indicating whether all enum cases are
+ handled by the operation. Note that the presence of a default CaseOp does
+ not imply `all_enum_cases_covered`. The original AST switch must explicitly list
+ every enum case.
+
The set of `cir.case` operations and their enclosing `cir.switch`
represent the semantics of a C/C++ switch statement. Users can use
`collectCases(llvm::SmallVector<CaseOp> &cases)` to collect the `cir.case`
@@ -1206,7 +1212,10 @@ def CIR_SwitchOp : CIR_Op<"switch", [
```
}];
- let arguments = (ins CIR_IntType:$condition);
+ let arguments = (ins
+ CIR_IntType:$condition,
+ UnitAttr:$allEnumCasesCovered
+ );
let regions = (region AnyRegion:$body);
@@ -1217,9 +1226,9 @@ def CIR_SwitchOp : CIR_Op<"switch", [
];
let assemblyFormat = [{
- custom<SwitchOp>(
- $body, $condition, type($condition)
- )
+ `(` $condition `:` qualified(type($condition)) `)`
+ (`allEnumCasesCovered` $allEnumCasesCovered^)?
+ $body
attr-dict
}];
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index f13e7cb32c71e..b7bd405bf4df4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -1105,6 +1105,8 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {
terminateBody(builder, caseOp.getCaseRegion(), caseOp.getLoc());
terminateBody(builder, swop.getBody(), swop.getLoc());
+ swop.setAllEnumCasesCovered(s.isAllEnumCasesCovered());
+
return res;
}
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 8077dc6597047..d888fdcf081e7 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -1359,44 +1359,6 @@ void cir::CaseOp::build(OpBuilder &builder, OperationState &result,
// SwitchOp
//===----------------------------------------------------------------------===//
-static ParseResult parseSwitchOp(OpAsmParser &parser, mlir::Region ®ions,
- mlir::OpAsmParser::UnresolvedOperand &cond,
- mlir::Type &condType) {
- cir::IntType intCondType;
-
- if (parser.parseLParen())
- return mlir::failure();
-
- if (parser.parseOperand(cond))
- return mlir::failure();
- if (parser.parseColon())
- return mlir::failure();
- if (parser.parseCustomTypeWithFallback(intCondType))
- return mlir::failure();
- condType = intCondType;
-
- if (parser.parseRParen())
- return mlir::failure();
- if (parser.parseRegion(regions, /*arguments=*/{}, /*argTypes=*/{}))
- return failure();
-
- return mlir::success();
-}
-
-static void printSwitchOp(OpAsmPrinter &p, cir::SwitchOp op,
- mlir::Region &bodyRegion, mlir::Value condition,
- mlir::Type condType) {
- p << "(";
- p << condition;
- p << " : ";
- p.printStrippedAttrOrType(condType);
- p << ")";
-
- p << ' ';
- p.printRegion(bodyRegion, /*printEntryBlockArgs=*/false,
- /*printBlockTerminators=*/true);
-}
-
void cir::SwitchOp::getSuccessorRegions(
mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ion) {
if (!point.isParent()) {
diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
index 64e0961fe20d9..5fbbee0e88a15 100644
--- a/clang/test/CIR/CodeGen/atomic.c
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -1143,7 +1143,7 @@ int atomic_load_dynamic_order(int *ptr, int order) {
// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
- // CIR-NEXT: cir.switch (%[[ORDER]] : !s32i) {
+ // CIR-NEXT: cir.switch(%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[RES:.+]] = cir.load align(4) syncscope(system) atomic(relaxed) %[[PTR]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) %[[RES]], %[[RES_SLOT:.+]] : !s32i, !cir.ptr<!s32i>
@@ -1219,7 +1219,7 @@ void atomic_store_dynamic_order(int *ptr, int order) {
// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
- // CIR: cir.switch (%[[ORDER]] : !s32i) {
+ // CIR: cir.switch(%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[VALUE:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: cir.store align(4) atomic(relaxed) %[[VALUE]], %[[PTR]] : !s32i, !cir.ptr<!s32i>
@@ -1288,7 +1288,7 @@ int atomic_load_and_store_dynamic_order(int *ptr, int order) {
// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
// CIR-NEXT: %[[ORDER:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
- // CIR: cir.switch (%[[ORDER]] : !s32i) {
+ // CIR: cir.switch(%[[ORDER]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: %[[LIT:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!s32i>, !s32i
// CIR-NEXT: %[[RES:.+]] = cir.atomic.xchg relaxed %[[PTR]], %[[LIT]] : (!cir.ptr<!s32i>, !s32i) -> !s32i
diff --git a/clang/test/CIR/CodeGen/switch.cpp b/clang/test/CIR/CodeGen/switch.cpp
index 3824be0d08c2f..b7bd2da5e39b8 100644
--- a/clang/test/CIR/CodeGen/switch.cpp
+++ b/clang/test/CIR/CodeGen/switch.cpp
@@ -20,7 +20,7 @@ void sw1(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw1i
-// CIR: cir.switch (%[[COND:.*]] : !s32i) {
+// CIR: cir.switch(%[[COND:.*]] : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<0> : !s32i]) {
// CIR: cir.break
// CIR: cir.case(equal, [#cir.int<1> : !s32i]) {
@@ -101,7 +101,7 @@ void sw2(int a) {
// CIR: cir.scope {
// CIR-NEXT: %[[YOLO:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["yolo", init]
// CIR-NEXT: %[[FOMO:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["fomo", init]
-// CIR: cir.switch (%[[COND:.*]] : !s32i) {
+// CIR: cir.switch(%[[COND:.*]] : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<3> : !s32i]) {
// CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CIR-NEXT: cir.store{{.*}} %[[ZERO]], %[[FOMO]] : !s32i, !cir.ptr<!s32i>
@@ -154,7 +154,7 @@ void sw3(int a) {
// CIR: cir.func{{.*}} @_Z3sw3i
// CIR: cir.scope {
// CIR-NEXT: %[[COND:.*]] = cir.load{{.*}} %[[A:.*]] : !cir.ptr<!s32i>, !s32i
-// CIR-NEXT: cir.switch (%[[COND]] : !s32i) {
+// CIR-NEXT: cir.switch(%[[COND]] : !s32i) {
// CIR-NEXT: cir.case(default, []) {
// CIR-NEXT: cir.break
// CIR-NEXT: }
@@ -196,7 +196,7 @@ int sw4(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw4i
-// CIR: cir.switch (%[[COND:.*]] : !s32i) {
+// CIR: cir.switch(%[[COND:.*]] : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<42> : !s32i]) {
// CIR-NEXT: cir.scope {
// CIR-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
@@ -264,7 +264,7 @@ void sw5(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw5i
-// CIR: cir.switch (%[[A:.*]] : !s32i) {
+// CIR: cir.switch(%[[A:.*]] : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<1> : !s32i]) {
// CIR-NEXT: cir.yield
// CIR-NEXT: }
@@ -314,7 +314,7 @@ void sw6(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw6i
-// CIR: cir.switch (%[[A:.*]] : !s32i) {
+// CIR: cir.switch(%[[A:.*]] : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<0> : !s32i]) {
// CIR-NEXT: cir.yield
// CIR-NEXT: }
@@ -406,7 +406,7 @@ void sw7(int a) {
// CIR: cir.func{{.*}} @_Z3sw7i
// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x"]
-// CIR: cir.switch (%[[A:.*]] : !s32i)
+// CIR: cir.switch(%[[A:.*]] : !s32i)
// CIR-NEXT: cir.case(equal, [#cir.int<0> : !s32i]) {
// CIR-NEXT: cir.yield
// CIR-NEXT: }
@@ -499,7 +499,7 @@ void sw8(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw8i
-// CIR: cir.switch (%[[A:.*]] : !s32i)
+// CIR: cir.switch(%[[A:.*]] : !s32i)
// CIR-NEXT: cir.case(equal, [#cir.int<3> : !s32i]) {
// CIR-NEXT: cir.break
// CIR-NEXT: }
@@ -557,7 +557,7 @@ void sw9(int a) {
}
// CIR: cir.func{{.*}} @_Z3sw9i
-// CIR: cir.switch (%[[A:.*]] : !s32i)
+// CIR: cir.switch(%[[A:.*]] : !s32i)
// CIR-NEXT: cir.case(equal, [#cir.int<3> : !s32i]) {
// CIR-NEXT: cir.break
// CIR-NEXT: }
@@ -616,7 +616,7 @@ void sw10(int a) {
}
// CIR: cir.func{{.*}} @_Z4sw10i
-// CIR: cir.switch (%[[A:.*]] : !s32i)
+// CIR: cir.switch(%[[A:.*]] : !s32i)
// CIR-NEXT: cir.case(equal, [#cir.int<3> : !s32i]) {
// CIR-NEXT: cir.break
// CIR-NEXT: }
@@ -688,7 +688,7 @@ void sw11(int a) {
}
// CIR: cir.func{{.*}} @_Z4sw11i
-// CIR: cir.switch (%[[A:.*]] : !s32i)
+// CIR: cir.switch(%[[A:.*]] : !s32i)
// CIR-NEXT: cir.case(equal, [#cir.int<3> : !s32i]) {
// CIR-NEXT: cir.break
// CIR-NEXT: }
@@ -1063,7 +1063,7 @@ int nested_switch(int a) {
return 0;
}
-// CIR: cir.switch (%[[COND:.*]] : !s32i) {
+// CIR: cir.switch(%[[COND:.*]] : !s32i) {
// CIR: cir.case(equal, [#cir.int<0> : !s32i]) {
// CIR: cir.yield
// CIR: }
@@ -1198,7 +1198,7 @@ int sw_return_multi_cases(int x) {
}
// CIR-LABEL: cir.func{{.*}} @_Z21sw_return_multi_casesi
-// CIR: cir.switch (%{{.*}} : !s32i) {
+// CIR: cir.switch(%{{.*}} : !s32i) {
// CIR-NEXT: cir.case(equal, [#cir.int<0> : !s32i]) {
// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
// CIR: cir.store{{.*}} %[[ZERO]], %{{.*}} : !s32i, !cir.ptr<!s32i>
@@ -1270,3 +1270,25 @@ int sw_return_multi_cases(int x) {
// OGCG: [[RETURN]]:
// OGCG: %[[RETVAL_LOAD:.*]] = load i32, ptr %[[RETVAL]], align 4
// OGCG: ret i32 %[[RETVAL_LOAD]]
+
+enum M {
+ Six,
+ Seven
+};
+
+void testSwitchCoverAllCase(M m) {
+ switch (m) {
+ case Six:case Seven:
+ break;
+ }
+}
+// CIR: cir.switch(%[[ARG:.*]] : !s32i) allEnumCasesCovered {
+
+void testSwitchNotCoverAllCase(M m) {
+ switch (m) {
+ case Six:
+ default:
+ break;
+ }
+}
+// CIR: cir.switch(%[[ARG:.*]] : !s32i) {
diff --git a/clang/test/CIR/CodeGen/switch_flat_op.cpp b/clang/test/CIR/CodeGen/switch_flat_op.cpp
index a3ea7e7a15547..ba0a82da52c70 100644
--- a/clang/test/CIR/CodeGen/switch_flat_op.cpp
+++ b/clang/test/CIR/CodeGen/switch_flat_op.cpp
@@ -21,7 +21,7 @@ void swf(int a) {
// BEFORE: cir.func{{.*}} @_Z3swfi
// BEFORE: %[[VAR_B:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["b", init] {alignment = 4 : i64}
// BEFORE: %[[CONST_3:.*]] = cir.const #cir.int<3> : !s32i
-// BEFORE: cir.switch (%[[COND:.*]] : !s32i) {
+// BEFORE: cir.switch(%[[COND:.*]] : !s32i) {
// BEFORE: cir.case(equal, [#cir.int<3> : !s32i]) {
// BEFORE: %[[LOAD_B_EQ:.*]] = cir.load{{.*}} %[[VAR_B]] : !cir.ptr<!s32i>, !s32i
// BEFORE: %[[CONST_2:.*]] = cir.const #cir.int<2> : !s32i
diff --git a/clang/test/CIR/IR/switch.cir b/clang/test/CIR/IR/switch.cir
index 87d45bf1f5219..89614480e43cd 100644
--- a/clang/test/CIR/IR/switch.cir
+++ b/clang/test/CIR/IR/switch.cir
@@ -21,7 +21,7 @@ cir.func @s0() {
cir.return
}
-// CHECK: cir.switch (%0 : !s32i) {
+// CHECK: cir.switch(%0 : !s32i) {
// CHECK-NEXT: cir.case(default, []) {
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
@@ -36,3 +36,33 @@ cir.func @s0() {
// CHECK-NEXT: }
// CHECK-NEXT: cir.yield
// CHECK-NEXT: }
+
+
+// Pretends that this is lowered from a C file and was tagged with allEnumCasesCovered = true
+cir.func @s1(%1 : !s32i) {
+ cir.switch (%1 : !s32i) allEnumCasesCovered {
+ cir.case (default, []) {
+ cir.return
+ }
+ cir.case (equal, [#cir.int<1> : !s32i]) {
+ cir.yield
+ }
+ cir.case (equal, [#cir.int<2> : !s32i]) {
+ cir.yield
+ }
+ cir.yield
+ } { }
+ cir.return
+}
+// CHECK: cir.switch(%[[ARG:.*]] : !s32i) allEnumCasesCovered {
+// CHECK-NEXT: cir.case(default, []) {
+// CHECK-NEXT: cir.return
+// CHECK-NEXT: }
+// CHECK-NEXT: cir.case(equal, [#cir.int<1> : !s32i]) {
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: }
+// CHECK-NEXT: cir.case(equal, [#cir.int<2> : !s32i]) {
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: }
+// CHECK-NEXT: cir.yield
+// CHECK-NEXT: }
diff --git a/clang/test/CIR/Transforms/switch-fold.cir b/clang/test/CIR/Transforms/switch-fold.cir
index 62a94f4fde2c3..c348a05128671 100644
--- a/clang/test/CIR/Transforms/switch-fold.cir
+++ b/clang/test/CIR/Transforms/switch-fold.cir
@@ -27,7 +27,7 @@ module {
cir.return
}
//CHECK: cir.func @foldCascade
- //CHECK: cir.switch (%[[COND:.*]] : !s32i) {
+ //CHECK: cir.switch(%[[COND:.*]] : !s32i) {
//CHECK-NEXT: cir.case(anyof, [#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]) {
//CHECK-NEXT: %[[TWO:.*]] = cir.const #cir.int<2> : !s32i
//CHECK-NEXT: cir.store %[[TWO]], %[[ARG0:.*]] : !s32i, !cir.ptr<!s32i>
@@ -66,7 +66,7 @@ module {
cir.return
}
//CHECK: @foldCascade2
- //CHECK: cir.switch (%[[COND2:.*]] : !s32i) {
+ //CHECK: cir.switch(%[[COND2:.*]] : !s32i) {
//CHECK: cir.case(anyof, [#cir.int<0> : !s32i, #cir.int<2> : !s32i, #cir.int<4> : !s32i]) {
//CHECK: cir.break
//cehck: }
@@ -106,7 +106,7 @@ module {
cir.return
}
//CHECK: cir.func @foldCascade3
- //CHECK: cir.switch (%[[COND3:.*]] : !s32i) {
+ //CHECK: cir.switch(%[[COND3:.*]] : !s32i) {
//CHECK: cir.case(anyof, [#cir.int<0> : !s32i, #cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i]) {
//CHECK: cir.break
//CHECK: }
@@ -142,7 +142,7 @@ module {
cir.return
}
//CHECK: cir.func @foldCascadeWithDefault
- //CHECK: cir.switch (%[[COND:.*]] : !s32i) {
+ //CHECK: cir.switch(%[[COND:.*]] : !s32i) {
//CHECK: cir.case(equal, [#cir.int<3> : !s32i]) {
//CHECK: cir.break
//CHECK: }
@@ -187,7 +187,7 @@ module {
cir.return
}
//CHECK: cir.func @foldAllCascade
- //CHECK: cir.switch (%[[COND:.*]] : !s32i) {
+ //CHECK: cir.switch(%[[COND:.*]] : !s32i) {
//CHECK: cir.case(anyof, [#cir.int<0> : !s32i, #cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<4> : !s32i, #cir.int<5> : !s32i]) {
//CHECK: cir.yield
//CHECK: }
More information about the cfe-commits
mailing list