[clang] [CIR] Load enums with a boolean underlying type (PR #205880)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 26 13:07:58 PDT 2026
https://github.com/adams381 updated https://github.com/llvm/llvm-project/pull/205880
>From 6cf785c70e5d122e9391beee8e3be4c3f2a08df1 Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Thu, 25 Jun 2026 10:57:07 -0700
Subject: [PATCH 1/2] [CIR] Load enums with a boolean underlying type
emitLoadOfScalar reported NYI for any scalar whose type has a boolean
representation but isn't the builtin bool. The types that reach this point
are an enum with a fixed bool underlying type and _BitInt(1); both convert to
the same literal CIR type as bool / _BitInt(N) (!cir.bool and !cir.int<1>),
and convertTypeForMem does not widen them. CIR
keeps these in their literal type through CIRGen and defers the in-memory
integer widening to LowerToLLVM, so the loaded value is already correct and
no register/memory conversion is needed at this point. The errorNYI also left
the function's entry block without a terminator, which then failed CIR module
verification.
Drop the over-conservative errorNYI and return the loaded value, matching the
existing emitToMemory deferral. This lets enum-with-bool-underlying loads
(and _BitInt(1) loads) compile; it does not change the in-memory
representation, which LowerToLLVM still owns.
---
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 7 ++--
clang/test/CIR/CodeGen/enum-bool.cpp | 50 ++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 2 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/enum-bool.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index fa14b45cbb015..9b9811f7c4cb1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -769,9 +769,12 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile,
mlir::Value loadOp =
builder.createLoad(getLoc(loc), addr, isVolatile, isNontemporal);
- if (!ty->isBooleanType() && ty->hasBooleanRepresentation())
- cgm.errorNYI("emitLoadOfScalar: boolean type with boolean representation");
+ // Types with a boolean representation that are not the builtin bool (an enum
+ // whose underlying type is bool, or a _BitInt(1)) need no register/memory
+ // conversion here: like bool and _BitInt(N), CIR keeps them in their literal
+ // type until LowerToLLVM widens them to the in-memory integer type (see
+ // emitToMemory).
return loadOp;
}
diff --git a/clang/test/CIR/CodeGen/enum-bool.cpp b/clang/test/CIR/CodeGen/enum-bool.cpp
new file mode 100644
index 0000000000000..3dfb4966627a6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/enum-bool.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+enum BoolEnum : bool { False, True };
+
+BoolEnum loadEnum(BoolEnum *p) { return *p; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z8loadEnumP8BoolEnum(%arg0: !cir.ptr<!cir.bool>
+// CIR-SAME: -> (!cir.bool
+// CIR: %[[P:.*]] = cir.alloca {{.*}} : !cir.ptr<!cir.ptr<!cir.bool>>
+// CIR: %[[PV:.*]] = cir.load deref {{.*}}%[[P]] : !cir.ptr<!cir.ptr<!cir.bool>>, !cir.ptr<!cir.bool>
+// CIR: %[[V:.*]] = cir.load {{.*}}%[[PV]] : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: cir.return
+
+// LLVM-LABEL: define dso_local noundef i1 @_Z8loadEnumP8BoolEnum(ptr noundef %0)
+// LLVM: %[[PTR:.*]] = load ptr, ptr
+// LLVM: %[[MEM:.*]] = load i8, ptr %[[PTR]], align 1
+// LLVM: %[[BOOL:.*]] = trunc i8 %[[MEM]] to i1
+
+// OGCG-LABEL: define dso_local noundef zeroext i1 @_Z8loadEnumP8BoolEnum(ptr noundef %p)
+// OGCG: %[[MEM:.*]] = load i8, ptr {{.*}}, align 1
+// OGCG: %[[BOOL:.*]] = icmp ne i8 %[[MEM]], 0
+// OGCG: ret i1 %[[BOOL]]
+
+void storeEnum(BoolEnum *p, BoolEnum v) { *p = v; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z9storeEnumP8BoolEnumS_(%arg0: !cir.ptr<!cir.bool>
+// CIR-SAME: %arg1: !cir.bool
+// CIR: %[[V:.*]] = cir.load {{.*}} : !cir.ptr<!cir.bool>, !cir.bool
+// CIR: %[[P:.*]] = cir.load deref {{.*}} : !cir.ptr<!cir.ptr<!cir.bool>>, !cir.ptr<!cir.bool>
+// CIR: cir.store {{.*}}%[[V]], %[[P]] : !cir.bool, !cir.ptr<!cir.bool>
+
+// LLVM-LABEL: define dso_local void @_Z9storeEnumP8BoolEnumS_(ptr noundef %0, i1 noundef %1)
+// LLVM: %[[Z:.*]] = zext i1 %1 to i8
+// LLVM: store i8 %[[Z]], ptr
+// LLVM: %[[LD:.*]] = load i8, ptr
+// LLVM: %[[TR:.*]] = trunc i8 %[[LD]] to i1
+// LLVM: %[[ZB:.*]] = zext i1 %[[TR]] to i8
+// LLVM: store i8 %[[ZB]], ptr
+
+// OGCG-LABEL: define dso_local void @_Z9storeEnumP8BoolEnumS_(ptr noundef %p, i1 noundef zeroext %v)
+// OGCG: %[[SV:.*]] = zext i1 %v to i8
+// OGCG: store i8 %[[SV]], ptr %v.addr
+// OGCG: %[[LV:.*]] = icmp ne i8 {{.*}}, 0
+// OGCG: %[[SV1:.*]] = zext i1 %[[LV]] to i8
+// OGCG: store i8 %[[SV1]], ptr
>From f35bc039da1800d11e523137d72fbbf07f6f06dd Mon Sep 17 00:00:00 2001
From: Adam Smith <adams at nvidia.com>
Date: Fri, 26 Jun 2026 13:02:46 -0700
Subject: [PATCH 2/2] [CIR] Combine LLVM/OGCG checks in enum-bool.cpp
Combine the separate LLVM and OGCG FileCheck prefixes in enum-bool.cpp
into a single LLVM prefix over the lines that match in both emits (the
i8 memory loads/stores and the i1 signature/return), and replace the
hard-coded value names with wildcards.
The CIR-lowered and classic emits still differ in ways unrelated to this
test: CIR drops the zeroext on the i1 return/parameter and narrows i8 to
i1 with trunc where classic uses icmp ne. Those are pre-existing
bool-lowering differences, not specific to an enum with a bool underlying
type, so the incidental lines are left unchecked here rather than pinned
as a split.
---
clang/test/CIR/CodeGen/enum-bool.cpp | 33 ++++++++--------------------
1 file changed, 9 insertions(+), 24 deletions(-)
diff --git a/clang/test/CIR/CodeGen/enum-bool.cpp b/clang/test/CIR/CodeGen/enum-bool.cpp
index 3dfb4966627a6..c8a7a0c1ebb79 100644
--- a/clang/test/CIR/CodeGen/enum-bool.cpp
+++ b/clang/test/CIR/CodeGen/enum-bool.cpp
@@ -3,7 +3,7 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
enum BoolEnum : bool { False, True };
@@ -16,15 +16,9 @@ BoolEnum loadEnum(BoolEnum *p) { return *p; }
// CIR: %[[V:.*]] = cir.load {{.*}}%[[PV]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: cir.return
-// LLVM-LABEL: define dso_local noundef i1 @_Z8loadEnumP8BoolEnum(ptr noundef %0)
-// LLVM: %[[PTR:.*]] = load ptr, ptr
-// LLVM: %[[MEM:.*]] = load i8, ptr %[[PTR]], align 1
-// LLVM: %[[BOOL:.*]] = trunc i8 %[[MEM]] to i1
-
-// OGCG-LABEL: define dso_local noundef zeroext i1 @_Z8loadEnumP8BoolEnum(ptr noundef %p)
-// OGCG: %[[MEM:.*]] = load i8, ptr {{.*}}, align 1
-// OGCG: %[[BOOL:.*]] = icmp ne i8 %[[MEM]], 0
-// OGCG: ret i1 %[[BOOL]]
+// LLVM-LABEL: define dso_local noundef {{(zeroext )?}}i1 @_Z8loadEnumP8BoolEnum(ptr noundef %{{.*}})
+// LLVM: load i8, ptr %{{.*}}, align 1
+// LLVM: ret i1 %{{.*}}
void storeEnum(BoolEnum *p, BoolEnum v) { *p = v; }
@@ -34,17 +28,8 @@ void storeEnum(BoolEnum *p, BoolEnum v) { *p = v; }
// CIR: %[[P:.*]] = cir.load deref {{.*}} : !cir.ptr<!cir.ptr<!cir.bool>>, !cir.ptr<!cir.bool>
// CIR: cir.store {{.*}}%[[V]], %[[P]] : !cir.bool, !cir.ptr<!cir.bool>
-// LLVM-LABEL: define dso_local void @_Z9storeEnumP8BoolEnumS_(ptr noundef %0, i1 noundef %1)
-// LLVM: %[[Z:.*]] = zext i1 %1 to i8
-// LLVM: store i8 %[[Z]], ptr
-// LLVM: %[[LD:.*]] = load i8, ptr
-// LLVM: %[[TR:.*]] = trunc i8 %[[LD]] to i1
-// LLVM: %[[ZB:.*]] = zext i1 %[[TR]] to i8
-// LLVM: store i8 %[[ZB]], ptr
-
-// OGCG-LABEL: define dso_local void @_Z9storeEnumP8BoolEnumS_(ptr noundef %p, i1 noundef zeroext %v)
-// OGCG: %[[SV:.*]] = zext i1 %v to i8
-// OGCG: store i8 %[[SV]], ptr %v.addr
-// OGCG: %[[LV:.*]] = icmp ne i8 {{.*}}, 0
-// OGCG: %[[SV1:.*]] = zext i1 %[[LV]] to i8
-// OGCG: store i8 %[[SV1]], ptr
+// LLVM-LABEL: define dso_local void @_Z9storeEnumP8BoolEnumS_(ptr noundef %{{.*}}, i1 noundef {{(zeroext )?}}%{{.*}})
+// LLVM: zext i1 %{{.*}} to i8
+// LLVM: store i8 %{{.*}}, ptr %{{.*}}, align 1
+// LLVM: load i8, ptr %{{.*}}, align 1
+// LLVM: store i8 %{{.*}}, ptr %{{.*}}, align 1
More information about the cfe-commits
mailing list