[clang] b6a6e31 - [CIR]/[OGCG] Fix handling of bool-backed-scoped-enums (#214084)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 07:18:58 PDT 2026
Author: Erich Keane
Date: 2026-08-05T14:18:52Z
New Revision: b6a6e310a2d5a7245845ae81e0a9f4b1c496e514
URL: https://github.com/llvm/llvm-project/commit/b6a6e310a2d5a7245845ae81e0a9f4b1c496e514
DIFF: https://github.com/llvm/llvm-project/commit/b6a6e310a2d5a7245845ae81e0a9f4b1c496e514.diff
LOG: [CIR]/[OGCG] Fix handling of bool-backed-scoped-enums (#214084)
This patch primarily fixes the case of a scoped enum with a boolean type
in CIR, which we assume is an 'int' type, whereas this one case, that is
not true. Rather than change the Dialect for what amounts to a very rare
case, we've instead opted to just coerce the bool type into a 1 bit int
type, so that all our passes will consider it the same as the rest of
the switches, and not have to special-case the 'bool' types.
AS A DRIVE-BY: I discovered that classic-codegen manages to assert on
llvm::isUIntN in the case where the storage of a range for
GNU-range-switch is less than 7 bits, so bit-int could possibly hit this
too with gnu-range. This patch would fix any case (as the test for the
'shortcut' is for <64).
Added:
Modified:
clang/lib/CIR/CodeGen/CIRGenStmt.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/test/CIR/CodeGen/switch.cpp
clang/test/CodeGen/enum-bool.cpp
clang/test/CodeGen/ext-int.c
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index d34769200dbfd..628daacb88950 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -870,6 +870,11 @@ mlir::LogicalResult CIRGenFunction::emitCaseStmt(const CaseStmt &s,
mlir::ArrayAttr value;
llvm::APSInt intVal = s.getLHS()->EvaluateKnownConstInt(getContext());
+ // Coerce a bool to an i1 for a switch, so we can just treat all its elements
+ // as an int later on.
+ if (isa<cir::BoolType>(condType))
+ condType = builder.getUIntNTy(1);
+
// If the case statement has an RHS value, it is representing a GNU
// case range statement, where LHS is the beginning of the range
// and RHS is the end of the range.
@@ -1279,6 +1284,13 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) {
mlir::Value condV = emitScalarExpr(s.getCond());
+ // Coerce bool values to an i1. There is no real sensible reason we need to
+ // represent a 'switch' of scoped-enum-with-bool-backing-type specially
+ // here. It is a rarely used thing, and would result in a lot of work to
+ // properly handle this everywhere.
+ if (isa<cir::BoolType>(condV.getType()))
+ condV = builder.createBoolToInt(condV, builder.getUIntNTy(1));
+
// TODO: PGO and likelihood (e.g. PGO.haveRegionCounts())
assert(!cir::MissingFeatures::pgoUse());
assert(!cir::MissingFeatures::emitCondLikelihoodViaExpectIntrinsic());
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index aeaf2e763fcf4..73f6c3c4aff1d 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -1794,7 +1794,8 @@ void CodeGenFunction::EmitCaseStmtRange(const CaseStmt &S,
Stmt::Likelihood LH = Stmt::getLikelihood(Attrs);
llvm::APInt Range = RHS - LHS;
// FIXME: parameters such as this should not be hardcoded.
- if (Range.ult(llvm::APInt(Range.getBitWidth(), 64))) {
+ if (Range.getBitWidth() < 7 ||
+ Range.ult(llvm::APInt(Range.getBitWidth(), 64))) {
// Range is small enough to add multiple switch instruction cases.
uint64_t Total = getProfileCount(&S);
unsigned NCases = Range.getZExtValue() + 1;
diff --git a/clang/test/CIR/CodeGen/switch.cpp b/clang/test/CIR/CodeGen/switch.cpp
index 9b608ba6985da..a1b617a14a9f5 100644
--- a/clang/test/CIR/CodeGen/switch.cpp
+++ b/clang/test/CIR/CodeGen/switch.cpp
@@ -1291,3 +1291,64 @@ void testSwitchNotCoverAllCase(M m) {
}
}
// CIR: cir.switch(%[[ARG:.*]] : !s32i) {
+
+enum class IsBoolClass : bool { F, T };
+
+void switch_enum_class(IsBoolClass b) {
+// CIR-LABEL: cir.func {{.*}}@_Z17switch_enum_class11IsBoolClass
+// CIR: %[[ARG:.*]] = cir.alloca "b" align(1) init : !cir.ptr<!cir.bool>
+// CIR: %[[ARG_LOAD:.*]] = cir.load align(1) %[[ARG]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: %[[CAST:.*]] = cir.cast bool_to_int %[[ARG_LOAD]] : !cir.bool -> !cir.int<u, 1>
+// CIR: cir.switch(%[[CAST]] : !cir.int<u, 1>) all_enum_cases_covered {
+// CIR: cir.case(equal, [#cir.int<1> : !cir.int<u, 1>]) {
+// CIR: cir.case(equal, [#cir.int<0> : !cir.int<u, 1>]) {
+
+// CIR: %[[ARG_LOAD:.*]] = cir.load align(1) %[[ARG]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: %[[CAST:.*]] = cir.cast bool_to_int %[[ARG_LOAD]] : !cir.bool -> !cir.int<u, 1>
+// CIR: cir.switch(%[[CAST]] : !cir.int<u, 1>) all_enum_cases_covered {
+// CIR: cir.case(range, [#cir.int<0> : !cir.int<u, 1>, #cir.int<1> : !cir.int<u, 1>]) {
+
+// LLVM-LABEL: define {{.*}}@_Z17switch_enum_class11IsBoolClass
+// LLVM: %[[ARG:.*]] = alloca i8
+// LLVM: %[[ARG_LOAD:.*]] = load i8, ptr %[[ARG]]
+// LLVM: %[[CAST:.*]] = trunc i8 %[[ARG_LOAD]] to i1
+// LLVM: switch i1 %[[CAST]], label %{{.*}} [
+// LLVM: i1 true, label %
+// LLVM: i1 false, label %
+// LLVM: ]
+//
+// LLVM: %[[ARG_LOAD:.*]] = load i8, ptr %[[ARG]]
+// LLVM: %[[CAST:.*]] = trunc i8 %[[ARG_LOAD]] to i1
+// LLVM: switch i1 %[[CAST]], label %{{.*}} [
+// LLVM: i1 false, label %
+// LLVM: i1 true, label %
+// LLVM: ]
+//
+//
+// OGCG-LABEL: define {{.*}}@_Z17switch_enum_class11IsBoolClass
+// OGCG: %[[ARG:.*]] = alloca i8
+// OGCG: %[[ARG_LOAD:.*]] = load i8, ptr %[[ARG]]
+// OGCG: %[[CAST:.*]] = icmp ne i8 %[[ARG_LOAD]], 0
+// OGCG: switch i1 %[[CAST]], label %{{.*}} [
+// OGCG: i1 true, label %
+// OGCG: i1 false, label %
+// OGCG: ]
+//
+// OGCG: %[[ARG_LOAD:.*]] = load i8, ptr %[[ARG]]
+// OGCG: %[[CAST:.*]] = icmp ne i8 %[[ARG_LOAD]], 0
+// OGCG: switch i1 %[[CAST]], label %{{.*}} [
+// OGCG: i1 false, label %
+// OGCG: i1 true, label %
+// OGCG: ]
+
+ switch(b) {
+ case IsBoolClass::T:
+ break;
+ case IsBoolClass::F:
+ break;
+ }
+ switch(b) {
+ case IsBoolClass::F ... IsBoolClass::T:
+ break;
+ }
+}
diff --git a/clang/test/CodeGen/enum-bool.cpp b/clang/test/CodeGen/enum-bool.cpp
index 4bf3b91361d28..6b971a6783c24 100644
--- a/clang/test/CodeGen/enum-bool.cpp
+++ b/clang/test/CodeGen/enum-bool.cpp
@@ -47,3 +47,21 @@ E b(int x) { return (E)x; }
} // namespace D
} // namespace dr2338
+
+namespace switchOnEnum {
+enum class E : bool { Zero, One };
+void func(E e) {
+ switch (e) {
+ case E::Zero...E::One:
+ break;
+ }
+// CHECK-LABEL: define {{.*}}@_ZN12switchOnEnum4funcENS_1EE
+// CHECK: %[[ARG:.*]] = alloca i8
+// CHECK: %[[ARG_LOAD:.*]] = load i8, ptr %[[ARG]]
+// CHECK: %[[CAST:.*]] = icmp ne i8 %[[ARG_LOAD]], 0
+// CHECK: switch i1 %[[CAST]], label %{{.*}} [
+// CHECK: i1 false, label %
+// CHECK: i1 true, label %
+// CHECK: ]
+}
+}
diff --git a/clang/test/CodeGen/ext-int.c b/clang/test/CodeGen/ext-int.c
index a12b11adbf00d..e2ea080c440d2 100644
--- a/clang/test/CodeGen/ext-int.c
+++ b/clang/test/CodeGen/ext-int.c
@@ -121,6 +121,23 @@ unsigned _BitInt(1) Size1PostDecUnsigned(unsigned _BitInt(1) A) {
return A;
}
+void SwitchSmallBitInt(unsigned _BitInt(3) B) {
+ // CHECK-LABEL: define{{.*}}@SwitchSmallBitInt
+ // CHECK: %[[PARAM_ADDR:.*]] = alloca i8
+ // CHECK: %[[PARAM_LOAD:.*]] = load i8, ptr %[[PARAM_ADDR]]
+ // CHECK: %[[PARAM_TRUNC:.*]] = trunc i8 %[[PARAM_LOAD]] to i3
+ // CHECK: switch i3 %[[PARAM_TRUNC]], label %{{.*}} [
+ // CHECK: i3 0, label %
+ // CHECK: i3 1, label %
+ // CHECK: i3 2, label %
+ // CHECK: ]
+
+ switch (B) {
+ case 0wb ... 2wb:
+ break;
+ }
+}
+
#if __BITINT_MAXWIDTH__ > 128
struct S1 {
_BitInt(17) A;
More information about the cfe-commits
mailing list