[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