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
