llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: adams381

<details>
<summary>Changes</summary>

`bool` bit-fields like `bool flag : 1;` trip an assertion in CIR codegen 
because `cir.set_bitfield` and `cir.get_bitfield` are constrained to take and 
produce `CIR_IntType` values, but CIRGen was passing `convertType(boolType)` (= 
`!cir.bool`) as the op's result type. Both load and store paths fail with:

    Assertion `isa&lt;To&gt;(Val) &amp;&amp; "cast&lt;Ty&gt;() argument of 
incompatible type!"' failed
        [To = mlir::detail::TypedValue&lt;cir::IntType&gt;, From = 
mlir::OpResult]

This is the second-largest libcxx-with-CIR blocker behind #<!-- -->197068 — 
~119 of 1,494 fails in our May 11 `std/` baseline, mostly in `&lt;format&gt;` 
parsing state where bool bit-fields are common.

The fix mirrors classic CodeGen at the bit-field boundary: widen the bool 
source to the storage integer type for the op call, and narrow the integer 
result back to bool with `int_to_bool` so callers that consume the 
assignment-expression value — and the read half of compound assignments or 
bit-field-to-bit-field copies — still see a `!cir.bool`. Non-bool bit-fields 
are unaffected.

New test at `clang/test/CIR/CodeGen/bool-bitfield.cpp` covers store, load, 
store-with-result-used, compound `|=`, bit-field-to-bit-field copy, and a 
regression check for an int bit-field sharing storage with a bool one. CIR + 
LLVM + OGCG checks throughout.


---
Full diff: https://github.com/llvm/llvm-project/pull/197085.diff


2 Files Affected:

- (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+36-6) 
- (added) clang/test/CIR/CodeGen/bool-bitfield.cpp (+144) 


``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 34a7e4d655610..e3df2e344358d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -528,9 +528,27 @@ mlir::Value 
CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
 
   assert(currSrcLoc && "must pass in source location");
 
-  return builder.createSetBitfield(*currSrcLoc, resLTy, ptr,
-                                   ptr.getElementType(), src.getValue(), info,
-                                   dst.isVolatileQualified(), useVoaltile);
+  // cir.set_bitfield consumes and produces integer values only.  When the
+  // user-visible field type is bool (e.g. `bool flag : 1;`), widen the
+  // source to the storage's integer type for the op call and narrow the
+  // op's integer result back to bool for callers that consume the value
+  // of the assignment expression.
+  bool isBool = mlir::isa<cir::BoolType>(resLTy);
+  mlir::Value srcVal = src.getValue();
+  mlir::Type opResLTy = resLTy;
+  if (isBool) {
+    opResLTy = ptr.getElementType();
+    srcVal = builder.createBoolToInt(srcVal, opResLTy);
+  }
+
+  mlir::Value stored = builder.createSetBitfield(
+      *currSrcLoc, opResLTy, ptr, ptr.getElementType(), srcVal, info,
+      dst.isVolatileQualified(), useVoaltile);
+
+  if (isBool)
+    stored = builder.createCast(*currSrcLoc, cir::CastKind::int_to_bool, 
stored,
+                                resLTy);
+  return stored;
 }
 
 RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) 
{
@@ -543,9 +561,21 @@ RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, 
SourceLocation loc) {
   bool useVoaltile = lv.isVolatileQualified() && info.volatileOffset != 0 &&
                      isAAPCS(cgm.getTarget());
 
-  mlir::Value field =
-      builder.createGetBitfield(getLoc(loc), resLTy, ptr, ptr.getElementType(),
-                                info, lv.isVolatile(), useVoaltile);
+  // cir.get_bitfield always produces an integer.  When the user-visible
+  // field type is bool (e.g. `bool flag : 1;`), perform the load in the
+  // storage's integer type and narrow the result back to bool with
+  // int_to_bool (i != 0).
+  bool isBool = mlir::isa<cir::BoolType>(resLTy);
+  mlir::Type opResLTy = isBool ? ptr.getElementType() : resLTy;
+
+  mlir::Value field = builder.createGetBitfield(getLoc(loc), opResLTy, ptr,
+                                                ptr.getElementType(), info,
+                                                lv.isVolatile(), useVoaltile);
+
+  if (isBool)
+    field = builder.createCast(getLoc(loc), cir::CastKind::int_to_bool, field,
+                               resLTy);
+
   assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck() && "NYI");
   return RValue::get(field);
 }
diff --git a/clang/test/CIR/CodeGen/bool-bitfield.cpp 
b/clang/test/CIR/CodeGen/bool-bitfield.cpp
new file mode 100644
index 0000000000000..84a068663369d
--- /dev/null
+++ b/clang/test/CIR/CodeGen/bool-bitfield.cpp
@@ -0,0 +1,144 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+// `cir.set_bitfield` and `cir.get_bitfield` produce integer values only,
+// but a bit-field whose declared type is `bool` (e.g. `bool flag : 1;`,
+// common in libcxx's `std::format` parsing state) is naturally typed as
+// `!cir.bool` at the CIRGen layer.  CIRGen widens `bool` to the storage's
+// integer type for the op call and narrows back to `bool` with
+// `int_to_bool` for callers that consume the result.
+
+struct B {
+  bool flag : 1;
+  bool other : 1;
+};
+
+// Store: object expression is unused, so the int_to_bool on the stored
+// value gets DCE'd by the `Pure` trait on `cir.cast`; we just want to
+// see the bool source widened with `bool_to_int` and a `!u8i`-typed
+// `cir.set_bitfield`.
+
+void store_bool_bitfield(B *b) {
+  b->flag = true;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z19store_bool_bitfieldP1B
+// CIR:         %[[TRUE:.+]] = cir.const #true
+// CIR:         %[[WIDEN:.+]] = cir.cast bool_to_int %[[TRUE]] : !cir.bool -> 
!u8i
+// CIR:         cir.set_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>, 
%[[WIDEN]] : !u8i) -> !u8i
+
+// LLVM-LABEL: define {{.*}} void @_Z19store_bool_bitfieldP1B
+// LLVM:         load ptr, ptr %{{.+}}
+// LLVM:         load i8, ptr %{{.+}}
+// LLVM:         %{{.+}} = and i8 %{{.+}}, -2
+// LLVM:         %{{.+}} = or {{.*}}i8 %{{.+}}, 1
+// LLVM:         store i8 %{{.+}}, ptr %{{.+}}
+
+// OGCG-LABEL: define {{.*}} void @_Z19store_bool_bitfieldP1B
+// OGCG:         load ptr, ptr %{{.+}}
+// OGCG:         load i8, ptr %{{.+}}
+// OGCG:         %{{.+}} = and i8 %{{.+}}, -2
+// OGCG:         %{{.+}} = or {{.*}}i8 %{{.+}}, 1
+// OGCG:         store i8 %{{.+}}, ptr %{{.+}}
+
+// Assignment-expression value used: the store result must be narrowed
+// back to bool with int_to_bool so the caller sees a `!cir.bool`.
+
+bool store_bool_bitfield_used(B *b, bool v) {
+  return b->flag = v;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z24store_bool_bitfield_usedP1Bb
+// CIR:         %[[V:.+]] = cir.load{{.*}} %{{.+}} : !cir.ptr<!cir.bool>, 
!cir.bool
+// CIR:         %[[VINT:.+]] = cir.cast bool_to_int %[[V]] : !cir.bool -> !u8i
+// CIR:         %[[STORED:.+]] = cir.set_bitfield {{.*}} %[[VINT]] : !u8i) -> 
!u8i
+// CIR:         %[[BACK:.+]] = cir.cast int_to_bool %[[STORED]] : !u8i -> 
!cir.bool
+
+// LLVM-LABEL: define {{.*}}i1 @_Z24store_bool_bitfield_usedP1Bb
+// LLVM:         zext i1 %{{.+}} to i8
+// LLVM:         store i8 %{{.+}}, ptr %{{.+}}
+// LLVM:         icmp ne i8 %{{.+}}, 0
+
+// OGCG-LABEL: define {{.*}}i1 @_Z24store_bool_bitfield_usedP1Bb
+// OGCG:         zext i1 %{{.+}} to i8
+// OGCG:         store i8 %{{.+}}, ptr %{{.+}}
+// OGCG:         icmp ne i8 %{{.+}}, 0
+
+// Load: read a bool bitfield.  The op produces an integer, which CIRGen
+// then narrows to `!cir.bool` with `int_to_bool`.
+
+bool load_bool_bitfield(B *b) {
+  return b->flag;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z18load_bool_bitfieldP1B
+// CIR:         %[[INT:.+]] = cir.get_bitfield{{.*}}(#{{.+}}, %{{.+}} : 
!cir.ptr<!u8i>) -> !u8i
+// CIR:         %[[BOOL:.+]] = cir.cast int_to_bool %[[INT]] : !u8i -> 
!cir.bool
+
+// LLVM-LABEL: define {{.*}}i1 @_Z18load_bool_bitfieldP1B
+// LLVM:         load i8, ptr %{{.+}}
+// LLVM:         and i8 %{{.+}}, 1
+// LLVM:         icmp ne i8 %{{.+}}, 0
+
+// OGCG-LABEL: define {{.*}}i1 @_Z18load_bool_bitfieldP1B
+// OGCG:         load i8, ptr %{{.+}}
+// OGCG:         and i8 %{{.+}}, 1
+// OGCG:         trunc i8 %{{.+}} to i1
+
+// Compound assignment to a `bool` bit-field (e.g. `state.flags |= v;`,
+// common in libcxx `<format>` parsing state).  The read-modify-write
+// path goes through both `emitLoadOfBitfieldLValue` and
+// `emitStoreThroughBitfieldLValue` and so exercises both halves of
+// the fix.
+
+void compound_or_bool_bitfield(B *b, bool v) {
+  b->flag |= v;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z25compound_or_bool_bitfieldP1Bb
+// CIR:         cir.get_bitfield{{.*}}-> !u8i
+// CIR:         cir.cast int_to_bool %{{.+}} : !u8i -> !cir.bool
+// CIR:         %[[WIDEN:.+]] = cir.cast bool_to_int %{{.+}} : !cir.bool -> 
!u8i
+// CIR:         cir.set_bitfield{{.*}}, %[[WIDEN]] : !u8i) -> !u8i
+
+// Bit-field-to-bit-field copy (e.g. `state.a = state.b;` between two
+// `bool` bit-fields in the same record).
+
+void copy_bool_bitfield(B *b) {
+  b->flag = b->other;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z18copy_bool_bitfieldP1B
+// CIR:         cir.get_bitfield{{.*}}-> !u8i
+// CIR:         cir.cast int_to_bool %{{.+}} : !u8i -> !cir.bool
+// CIR:         %[[WIDEN:.+]] = cir.cast bool_to_int %{{.+}} : !cir.bool -> 
!u8i
+// CIR:         cir.set_bitfield{{.*}}, %[[WIDEN]] : !u8i) -> !u8i
+
+// Mix of bool and int bitfields in the same storage word — regression
+// check that ordinary int-typed bitfields keep their previous code
+// shape (no extra bool_to_int / int_to_bool).
+
+struct M {
+  bool b : 1;
+  int  n : 7;
+};
+
+int load_int_bitfield(M *m) {
+  return m->n;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z17load_int_bitfieldP1M
+// CIR:         cir.get_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>) -> 
!s32i
+// CIR-NOT:     cir.cast int_to_bool
+
+void store_int_bitfield(M *m) {
+  m->n = 5;
+}
+
+// CIR-LABEL: cir.func{{.*}} @_Z18store_int_bitfieldP1M
+// CIR-NOT:     cir.cast bool_to_int
+// CIR:         cir.set_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>, 
%{{.+}} : !s32i) -> !s32i

``````````

</details>


https://github.com/llvm/llvm-project/pull/197085
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to