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;


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

Reply via email to