[clang] [CIR] Allow boolean operands in cir.cmp (PR #206846)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 30 14:56:43 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: adams381
<details>
<summary>Changes</summary>
GROMACS uses scoped enums with a boolean underlying type as type-safe
flags (`enum class EmulateGpuNonbonded : bool`), and compares them with
`==` / `!=` / `<`. A scoped enum is not integer-promoted before the
comparison, so the operand reaches `cir.cmp` as a `!cir.bool` (CIR already
represents a boolean-underlying enum with `!cir.bool`). The `cir.cmp`
operand constraint `CIR_ComparableType` does not list bool, so module
verification fails before the CIR-to-CIR passes with:
```
'cir.cmp' op operand #<!-- -->0 must be comparable type, but got '!cir.bool'
```
Add bool to `CIR_ComparableType` and give `CmpOp` lowering a bool branch
that emits an unsigned `icmp` (`ult` for `<`), matching classic CodeGen
(compared as `i1`). The constraint now admits any `cir.cmp` on `!cir.bool`;
in practice that is the scoped `enum class : bool` path above. Plain `bool`
comparisons are unchanged -- they still carry the AST integral promotion
and compare as `i32` -- and unscoped `enum : bool` still promotes too.
Six translation units in a minimal GROMACS build hit this
(`taskassignment/decidegpuusage`, `mdrun/runner`, the three `domdec`
topology files, and a gtest-based test); they compile under `-fclangir`
with the fix, and `enum-bool.cpp` pins the CIR/LLVM parity for scoped
`enum class : bool`.
---
Full diff: https://github.com/llvm/llvm-project/pull/206846.diff
4 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+3-2)
- (modified) clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td (+2-2)
- (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+10)
- (modified) clang/test/CIR/CodeGen/enum-bool.cpp (+28)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 0a54a7edf85fd..4e8fdf76a6966 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -2342,8 +2342,9 @@ def CIR_CmpOp : CIR_Op<"cmp", [Pure, SameTypeOperands]> {
let summary = "Compare two values and produce a boolean result";
let description = [{
The `cir.cmp` operation compares two operands of the same type and produces
- a `!cir.bool` result. It supports integral, floating-point, and pointer
- types.
+ a `!cir.bool` result. It supports integral, boolean, floating-point, and
+ pointer types. Booleans (including enums with a boolean underlying type)
+ are compared as unsigned integers.
The following comparison predicates are available:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
index a8643ec21af80..cf73b89294040 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypeConstraints.td
@@ -390,8 +390,8 @@ def CIR_AnyScalarType : AnyTypeOf<CIR_ScalarTypes, "cir scalar type"> {
def CIR_AnyMethodType : CIR_TypeBase<"::cir::MethodType", "method type">;
def CIR_ComparableType
- : AnyTypeOf<[CIR_AnyIntType, CIR_AnyFloatType, CIR_AnyPtrType,
- CIR_AnyComplexType, CIR_AnyVPtrType,
+ : AnyTypeOf<[CIR_AnyIntType, CIR_AnyBoolType, CIR_AnyFloatType,
+ CIR_AnyPtrType, CIR_AnyComplexType, CIR_AnyVPtrType,
CIR_AnyDataMemberType, CIR_AnyMethodType],
"comparable type"> {
let cppFunctionName = "isComparableType";
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index e0fc9e58ed4b7..d7409544bd31a 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2949,6 +2949,16 @@ mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite(
return mlir::success();
}
+ if (mlir::isa<cir::BoolType>(type)) {
+ // Booleans (and enums with a boolean underlying type) compare as
+ // unsigned integers.
+ mlir::LLVM::ICmpPredicate kind =
+ convertCmpKindToICmpPredicate(cmpOp.getKind(), /*isSigned=*/false);
+ rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
+ cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
+ return mlir::success();
+ }
+
if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(type)) {
mlir::LLVM::ICmpPredicate kind =
convertCmpKindToICmpPredicate(cmpOp.getKind(),
diff --git a/clang/test/CIR/CodeGen/enum-bool.cpp b/clang/test/CIR/CodeGen/enum-bool.cpp
index c8a7a0c1ebb79..3050d446e9a16 100644
--- a/clang/test/CIR/CodeGen/enum-bool.cpp
+++ b/clang/test/CIR/CodeGen/enum-bool.cpp
@@ -33,3 +33,31 @@ void storeEnum(BoolEnum *p, BoolEnum v) { *p = v; }
// LLVM: store i8 %{{.*}}, ptr %{{.*}}, align 1
// LLVM: load i8, ptr %{{.*}}, align 1
// LLVM: store i8 %{{.*}}, ptr %{{.*}}, align 1
+
+// A scoped enum with a boolean underlying type is compared directly (no
+// integral promotion), so cir.cmp must accept !cir.bool operands.
+enum class ScopedBoolEnum : bool { No, Yes };
+
+bool eqEnum(ScopedBoolEnum a, ScopedBoolEnum b) { return a == b; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z6eqEnum14ScopedBoolEnumS_
+// CIR: cir.cmp eq %{{.*}}, %{{.*}} : !cir.bool
+
+// LLVM-LABEL: define dso_local noundef {{(zeroext )?}}i1 @_Z6eqEnum14ScopedBoolEnumS_
+// LLVM: icmp eq i1 %{{.*}}, %{{.*}}
+
+bool neEnum(ScopedBoolEnum a, ScopedBoolEnum b) { return a != b; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z6neEnum14ScopedBoolEnumS_
+// CIR: cir.cmp ne %{{.*}}, %{{.*}} : !cir.bool
+
+// LLVM-LABEL: define dso_local noundef {{(zeroext )?}}i1 @_Z6neEnum14ScopedBoolEnumS_
+// LLVM: icmp ne i1 %{{.*}}, %{{.*}}
+
+bool ltEnum(ScopedBoolEnum a, ScopedBoolEnum b) { return a < b; }
+
+// CIR-LABEL: cir.func{{.*}} @_Z6ltEnum14ScopedBoolEnumS_
+// CIR: cir.cmp lt %{{.*}}, %{{.*}} : !cir.bool
+
+// LLVM-LABEL: define dso_local noundef {{(zeroext )?}}i1 @_Z6ltEnum14ScopedBoolEnumS_
+// LLVM: icmp ult i1 %{{.*}}, %{{.*}}
``````````
</details>
https://github.com/llvm/llvm-project/pull/206846
More information about the cfe-commits
mailing list