https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/214084
>From a20e8b196fd7a1f49d06796c4a140161fd7c9bc3 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 4 Aug 2026 15:02:06 -0700 Subject: [PATCH 1/3] [CIR]/[OGCG] Fix handling of bool-backed-scoped-enums 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). --- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 12 ++++++ clang/lib/CodeGen/CGStmt.cpp | 3 +- clang/test/CIR/CodeGen/switch.cpp | 61 ++++++++++++++++++++++++++++ clang/test/CodeGen/enum-bool.cpp | 18 ++++++++ 4 files changed, 93 insertions(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index d34769200dbfd..d6dd7d72bdd1b 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 27e74d966eca1..ef1f3984556f4 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -1789,7 +1789,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: ] +} +} >From 8b7a06eb500d1ffecd38358a42d7c4e197e8d635 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Tue, 4 Aug 2026 15:12:50 -0700 Subject: [PATCH 2/3] format --- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index d6dd7d72bdd1b..76e6627b5e750 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -1289,7 +1289,7 @@ mlir::LogicalResult CIRGenFunction::emitSwitchStmt(const clang::SwitchStmt &s) { // 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)); + condV = builder.createBoolToInt(condV, builder.getUIntNTy(1)); // TODO: PGO and likelihood (e.g. PGO.haveRegionCounts()) assert(!cir::MissingFeatures::pgoUse()); >From 18f45a3c2941b2346bcb77db8517bbee54ab2c56 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 5 Aug 2026 06:37:09 -0700 Subject: [PATCH 3/3] Add test Aaron requested, fix comment aaron requested. --- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 2 +- clang/test/CodeGen/ext-int.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 76e6627b5e750..628daacb88950 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -870,7 +870,7 @@ 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 + // 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); 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
