https://github.com/adams381 updated 
https://github.com/llvm/llvm-project/pull/197085

>From bac1566d9217647e5e8ba699e85138f427f62458 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Mon, 11 May 2026 20:00:44 -0700
Subject: [PATCH 1/3] [CIR] Lower bool bit-fields correctly
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

`cir.set_bitfield` and `cir.get_bitfield` are constrained to take and
produce `CIR_IntType` values, but a bit-field whose declared type is
`bool` (e.g. `bool flag : 1;`) is naturally `!cir.bool` at the CIRGen
layer.  CIRGen was passing `convertType(boolType)` (= `!cir.bool`) as
the op's result type, which trips MLIR's auto-generated TypedValue
cast in the op constructor:

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

This is the second-largest libcxx-with-CIR blocker behind PR
#197068 (119 of 1,494 fails in the May 11 post-fix baseline; mostly
in `<format>` parsing state, where bool bit-fields are common).

Mirror what classic CodeGen does at the bit-field boundary: widen
the bool source to the bit-field's storage integer type for the op
call (`bool_to_int`), and narrow the integer result back to bool
with `int_to_bool` so callers that consume the value of an
assignment expression — and the read half of compound assignments
or bit-field-to-bit-field copies — still see a `!cir.bool`.  The
op's result is `Pure`, so the narrowing cast is naturally DCE'd
when an assignment expression is used at statement level.

Non-bool bit-fields (signed/unsigned integers of any width,
`_BitInt(N)`, regular and scoped enums with integer underlying
type) are unaffected — they keep their previous code shape with
no extra casts.

New `clang/test/CIR/CodeGen/bool-bitfield.cpp` with CIR + LLVM +
OGCG checks covering:

- store to a `bool` bit-field (statement form, narrowing cast
  DCE'd),
- store with the assignment-expression value used (narrowing
  preserved),
- load from a `bool` bit-field,
- compound `|=` to a `bool` bit-field (exercises both halves of
  the fix in a single read-modify-write expression),
- bit-field-to-bit-field copy (`p->flag = p->other` between two
  `bool` bit-fields),
- mix of bool and int bit-fields in the same storage word
  (regression check that ordinary int bit-fields keep their shape
  with no `bool_to_int` / `int_to_bool` introduced).

AAPCS volatile bit-fields are not directly exercised by this PR
(the host build target is x86_64); the volatile/AAPCS path is
preserved structurally, with the bool↔int wrap applied on top.

ninja check-clang-cir-codegen and ninja check-clang-cir remain
green.

Co-authored-by: Cursor <[email protected]>
---
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp     |  42 ++++++-
 clang/test/CIR/CodeGen/bool-bitfield.cpp | 144 +++++++++++++++++++++++
 2 files changed, 180 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/bool-bitfield.cpp

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

>From 9c77ccc3e1afa4668ef70b196a17644efe8e1bf9 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Tue, 12 May 2026 15:36:23 -0700
Subject: [PATCH 2/3] [CIR] Address review on #197085: widen set/get_bitfield
 to accept bool

@andykaylor pointed out that the right fix is to widen the
SetBitfieldOp / GetBitfieldOp result-type constraint from
CIR_IntType to CIR_AnyIntOrBoolType so a bool bit-field can be
modelled directly, rather than working around the constraint in
CIRGen by widening/narrowing through the storage's integer type.
@erichkeane had flagged the same shape ("set_bitfield should be
able to accept ANY type that can be used in bitfields and handle
this right").

Revert the CIRGenExpr.cpp workaround to its original two-call
form and widen both ops' result type in CIROps.td.
LowerToLLVM's existing `mlir::cast<mlir::IntegerType>(resTy)` and
`createIntCast` already handle the i1 / i8 case correctly (the
narrowing emits `trunc i8 to i1`, matching OGCG).

Test expectations rewritten to pin the new shape (bool-typed
set/get_bitfield, no surrounding bool_to_int / int_to_bool casts
in CIR), and the multi-line motivation preamble at the top of
the test file dropped per the rule documented after the
#197089 / #197094 reviewer feedback.

Int bit-field expectations are unchanged (`load_int_bitfield` /
`store_int_bitfield` regression checks preserved).
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td |  4 +-
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp         | 42 +++------------
 clang/test/CIR/CodeGen/bool-bitfield.cpp     | 57 +++-----------------
 3 files changed, 16 insertions(+), 87 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td 
b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 9d9aaec1b275a..b7c7469699fac 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3284,7 +3284,7 @@ def CIR_SetBitfieldOp : CIR_Op<"set_bitfield"> {
     UnitAttr:$is_volatile
   );
 
-  let results = (outs CIR_IntType:$result);
+  let results = (outs CIR_AnyIntOrBoolType:$result);
 
   let assemblyFormat = [{
     (`align` `(` $alignment^ `)`)?
@@ -3371,7 +3371,7 @@ def CIR_GetBitfieldOp : CIR_Op<"get_bitfield"> {
     UnitAttr:$is_volatile
     );
 
-  let results = (outs CIR_IntType:$result);
+  let results = (outs CIR_AnyIntOrBoolType:$result);
 
   let assemblyFormat = [{
     (`align` `(` $alignment^ `)`)?
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index e3df2e344358d..34a7e4d655610 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -528,27 +528,9 @@ mlir::Value 
CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
 
   assert(currSrcLoc && "must pass in source location");
 
-  // 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;
+  return builder.createSetBitfield(*currSrcLoc, resLTy, ptr,
+                                   ptr.getElementType(), src.getValue(), info,
+                                   dst.isVolatileQualified(), useVoaltile);
 }
 
 RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) 
{
@@ -561,21 +543,9 @@ RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, 
SourceLocation loc) {
   bool useVoaltile = lv.isVolatileQualified() && info.volatileOffset != 0 &&
                      isAAPCS(cgm.getTarget());
 
-  // 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);
-
+  mlir::Value field =
+      builder.createGetBitfield(getLoc(loc), resLTy, ptr, ptr.getElementType(),
+                                info, lv.isVolatile(), useVoaltile);
   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
index 84a068663369d..6837c10e8d794 100644
--- a/clang/test/CIR/CodeGen/bool-bitfield.cpp
+++ b/clang/test/CIR/CodeGen/bool-bitfield.cpp
@@ -5,31 +5,18 @@
 // 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
+// CIR:         cir.set_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>, 
%[[TRUE]] : !cir.bool) -> !cir.bool
 
 // LLVM-LABEL: define {{.*}} void @_Z19store_bool_bitfieldP1B
 // LLVM:         load ptr, ptr %{{.+}}
@@ -45,82 +32,54 @@ void store_bool_bitfield(B *b) {
 // 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
+// CIR:         cir.set_bitfield{{.*}}, %[[V]] : !cir.bool) -> !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
+// CIR:         cir.get_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>) -> 
!cir.bool
 
 // LLVM-LABEL: define {{.*}}i1 @_Z18load_bool_bitfieldP1B
 // LLVM:         load i8, ptr %{{.+}}
 // LLVM:         and i8 %{{.+}}, 1
-// LLVM:         icmp ne i8 %{{.+}}, 0
+// LLVM:         trunc i8 %{{.+}} to i1
 
 // 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).
+// CIR:         cir.get_bitfield{{.*}}-> !cir.bool
+// CIR:         cir.set_bitfield{{.*}} : !cir.bool) -> !cir.bool
 
 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).
+// CIR:         %[[OTHER:.+]] = cir.get_bitfield{{.*}}-> !cir.bool
+// CIR:         cir.set_bitfield{{.*}}, %[[OTHER]] : !cir.bool) -> !cir.bool
 
 struct M {
   bool b : 1;

>From 277c387d8019bf83c429a125077c931d2c4043d5 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 14 May 2026 08:44:34 -0700
Subject: [PATCH 3/3] [CIR] Address review on #197085: combine LLVM/OGCG, fill
 coverage, named captures

Three review asks on the May 12 redo (commit 9c77ccc3) of #197085,
all test-side and all on clang/test/CIR/CodeGen/bool-bitfield.cpp.

@andykaylor's first ask, "LLVM and OGCG checks can be combined?":
the CIR-emit-llvm and classic-emit-llvm outputs share the same
instruction shape modulo SSA-name and explicit-GEP differences that
`%{{.+}}` plus sequential `LLVM:` matches absorb.  Same simplification
we applied on #197068.  Both backend RUN lines now share a single
`LLVM` prefix.  His second ask, "Missing LLVM/OGCG checks from here
on", covered the compound-or, copy, load-int, and store-int functions
that had CIR coverage only; LLVM check blocks added for all four.

@erichkeane's ask, "A ton of 'unnamed' wildcards...": swept the CIR
checks for `cir.set_bitfield{{.*}}` / `#{{.+}}` / `%{{.+}}` and
replaced them with explicit qualifiers (`align(1)`), explicit
bitfield-info names (`#bfi_flag`, `#bfi_other`, `#bfi_n`), and named
captures (`%[[B_PTR]]`, `%[[FLAG_PTR]]`, etc.) that show the data
flow from get_member through the bitfield op.  LLVM checks pick up
named captures for the load->and->or->store RMW chains where the
value flow is structurally identical across the two backends; the
few places where backend differences (CIR's extra GEP, CIR's
value-from-byte via trunc vs OGCG's via icmp ne, CIR's extra
`and i8 ..., 1` mask before the RMW) make named captures unreliable,
the CHECK keeps a `%{{.+}}` wildcard rather than misleading the
reader.
---
 clang/test/CIR/CodeGen/bool-bitfield.cpp | 102 +++++++++++++++--------
 1 file changed, 65 insertions(+), 37 deletions(-)

diff --git a/clang/test/CIR/CodeGen/bool-bitfield.cpp 
b/clang/test/CIR/CodeGen/bool-bitfield.cpp
index 6837c10e8d794..44d90e71cc9c4 100644
--- a/clang/test/CIR/CodeGen/bool-bitfield.cpp
+++ b/clang/test/CIR/CodeGen/bool-bitfield.cpp
@@ -3,7 +3,7 @@
 // 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
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM
 
 struct B {
   bool flag : 1;
@@ -16,70 +16,84 @@ void store_bool_bitfield(B *b) {
 
 // CIR-LABEL: cir.func{{.*}} @_Z19store_bool_bitfieldP1B
 // CIR:         %[[TRUE:.+]] = cir.const #true
-// CIR:         cir.set_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>, 
%[[TRUE]] : !cir.bool) -> !cir.bool
+// CIR:         %[[B_PTR:.+]] = cir.load align(8) %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+// CIR:         %[[FLAG_PTR:.+]] = cir.get_member %[[B_PTR]][0] {name = 
"flag"} : !cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         cir.set_bitfield align(1) (#bfi_flag, %[[FLAG_PTR]] : 
!cir.ptr<!u8i>, %[[TRUE]] : !cir.bool) -> !cir.bool
 
 // 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 %{{.+}}
+// LLVM:         %[[OLD:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[CLEARED:.+]] = and i8 %[[OLD]], -2
+// LLVM:         %[[NEW:.+]] = or {{.*}}i8 %[[CLEARED]], 1
+// LLVM:         store i8 %[[NEW]], ptr %{{.+}}
 
 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:         cir.set_bitfield{{.*}}, %[[V]] : !cir.bool) -> !cir.bool
+// CIR:         %[[V:.+]] = cir.load align(1) %{{.+}} : !cir.ptr<!cir.bool>, 
!cir.bool
+// CIR:         %[[B_PTR:.+]] = cir.load align(8) %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+// CIR:         %[[FLAG_PTR:.+]] = cir.get_member %[[B_PTR]][0] {name = 
"flag"} : !cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         cir.set_bitfield align(1) (#bfi_flag, %[[FLAG_PTR]] : 
!cir.ptr<!u8i>, %[[V]] : !cir.bool) -> !cir.bool
 
 // LLVM-LABEL: define {{.*}}i1 @_Z24store_bool_bitfield_usedP1Bb
-// LLVM:         zext i1 %{{.+}} to i8
-// LLVM:         store i8 %{{.+}}, ptr %{{.+}}
-
-// OGCG-LABEL: define {{.*}}i1 @_Z24store_bool_bitfield_usedP1Bb
-// OGCG:         zext i1 %{{.+}} to i8
-// OGCG:         store i8 %{{.+}}, ptr %{{.+}}
+// LLVM:         %{{.+}} = zext i1 %{{.+}} to i8
+// LLVM:         %[[CLEARED:.+]] = and i8 %{{.+}}, -2
+// LLVM:         %[[NEW:.+]] = or i8 %[[CLEARED]], %{{.+}}
+// LLVM:         store i8 %[[NEW]], ptr %{{.+}}
 
 bool load_bool_bitfield(B *b) {
   return b->flag;
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z18load_bool_bitfieldP1B
-// CIR:         cir.get_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>) -> 
!cir.bool
+// CIR:         %[[B_PTR:.+]] = cir.load align(8) %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B>
+// CIR:         %[[FLAG_PTR:.+]] = cir.get_member %[[B_PTR]][0] {name = 
"flag"} : !cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         %[[FLAG:.+]] = cir.get_bitfield align(1) (#bfi_flag, 
%[[FLAG_PTR]] : !cir.ptr<!u8i>) -> !cir.bool
 
 // LLVM-LABEL: define {{.*}}i1 @_Z18load_bool_bitfieldP1B
-// LLVM:         load i8, ptr %{{.+}}
-// LLVM:         and i8 %{{.+}}, 1
-// LLVM:         trunc i8 %{{.+}} to i1
-
-// OGCG-LABEL: define {{.*}}i1 @_Z18load_bool_bitfieldP1B
-// OGCG:         load i8, ptr %{{.+}}
-// OGCG:         and i8 %{{.+}}, 1
-// OGCG:         trunc i8 %{{.+}} to i1
+// LLVM:         %[[OLD:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[MASKED:.+]] = and i8 %[[OLD]], 1
+// LLVM:         %{{.+}} = trunc i8 %[[MASKED]] to i1
 
 void compound_or_bool_bitfield(B *b, bool v) {
   b->flag |= v;
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z25compound_or_bool_bitfieldP1Bb
-// CIR:         cir.get_bitfield{{.*}}-> !cir.bool
-// CIR:         cir.set_bitfield{{.*}} : !cir.bool) -> !cir.bool
+// CIR:         %[[V:.+]] = cir.load align(1) %{{.+}} : !cir.ptr<!cir.bool>, 
!cir.bool
+// CIR:         %[[V_I32:.+]] = cir.cast bool_to_int %[[V]] : !cir.bool -> 
!s32i
+// CIR:         %[[FLAG_PTR:.+]] = cir.get_member %{{.+}}[0] {name = "flag"} : 
!cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         %[[OLD:.+]] = cir.get_bitfield align(1) (#bfi_flag, 
%[[FLAG_PTR]] : !cir.ptr<!u8i>) -> !cir.bool
+// CIR:         %[[OLD_I32:.+]] = cir.cast bool_to_int %[[OLD]] : !cir.bool -> 
!s32i
+// CIR:         %[[OR:.+]] = cir.or %[[OLD_I32]], %[[V_I32]] : !s32i
+// CIR:         %[[NEW:.+]] = cir.cast int_to_bool %[[OR]] : !s32i -> !cir.bool
+// CIR:         cir.set_bitfield align(1) (#bfi_flag, %[[FLAG_PTR]] : 
!cir.ptr<!u8i>, %[[NEW]] : !cir.bool) -> !cir.bool
+
+// LLVM-LABEL: define {{.*}} void @_Z25compound_or_bool_bitfieldP1Bb
+// LLVM:         %[[CLEARED:.+]] = and i8 %{{.+}}, -2
+// LLVM:         %[[NEW:.+]] = or i8 %[[CLEARED]], %{{.+}}
+// LLVM:         store i8 %[[NEW]], ptr %{{.+}}
 
 void copy_bool_bitfield(B *b) {
   b->flag = b->other;
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z18copy_bool_bitfieldP1B
-// CIR:         %[[OTHER:.+]] = cir.get_bitfield{{.*}}-> !cir.bool
-// CIR:         cir.set_bitfield{{.*}}, %[[OTHER]] : !cir.bool) -> !cir.bool
+// CIR:         %[[OTHER_PTR:.+]] = cir.get_member %{{.+}}[0] {name = "other"} 
: !cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         %[[OTHER:.+]] = cir.get_bitfield align(1) (#bfi_other, 
%[[OTHER_PTR]] : !cir.ptr<!u8i>) -> !cir.bool
+// CIR:         %[[FLAG_PTR:.+]] = cir.get_member %{{.+}}[0] {name = "flag"} : 
!cir.ptr<!rec_B> -> !cir.ptr<!u8i>
+// CIR:         cir.set_bitfield align(1) (#bfi_flag, %[[FLAG_PTR]] : 
!cir.ptr<!u8i>, %[[OTHER]] : !cir.bool) -> !cir.bool
+
+// LLVM-LABEL: define {{.*}} void @_Z18copy_bool_bitfieldP1B
+// LLVM:         %[[OTHER_BYTE:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[OTHER_SHIFTED:.+]] = lshr i8 %[[OTHER_BYTE]], 1
+// LLVM:         %[[OTHER_BIT:.+]] = and i8 %[[OTHER_SHIFTED]], 1
+// LLVM:         %{{.+}} = trunc i8 %[[OTHER_BIT]] to i1
+// LLVM:         %[[FLAG_BYTE:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[FLAG_CLEARED:.+]] = and i8 %[[FLAG_BYTE]], -2
+// LLVM:         %[[NEW:.+]] = or i8 %[[FLAG_CLEARED]], %{{.+}}
+// LLVM:         store i8 %[[NEW]], ptr %{{.+}}
 
 struct M {
   bool b : 1;
@@ -91,13 +105,27 @@ int load_int_bitfield(M *m) {
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z17load_int_bitfieldP1M
-// CIR:         cir.get_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>) -> 
!s32i
+// CIR:         %[[N_PTR:.+]] = cir.get_member %{{.+}}[0] {name = "n"} : 
!cir.ptr<!rec_M> -> !cir.ptr<!u8i>
+// CIR:         %[[N:.+]] = cir.get_bitfield align(4) (#bfi_n, %[[N_PTR]] : 
!cir.ptr<!u8i>) -> !s32i
 // CIR-NOT:     cir.cast int_to_bool
 
+// LLVM-LABEL: define {{.*}}i32 @_Z17load_int_bitfieldP1M
+// LLVM:         %[[BYTE:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[SHIFTED:.+]] = ashr i8 %[[BYTE]], 1
+// LLVM:         %{{.+}} = sext i8 %[[SHIFTED]] to i32
+
 void store_int_bitfield(M *m) {
   m->n = 5;
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z18store_int_bitfieldP1M
+// CIR:         %[[FIVE:.+]] = cir.const #cir.int<5> : !s32i
+// CIR:         %[[N_PTR:.+]] = cir.get_member %{{.+}}[0] {name = "n"} : 
!cir.ptr<!rec_M> -> !cir.ptr<!u8i>
 // CIR-NOT:     cir.cast bool_to_int
-// CIR:         cir.set_bitfield{{.*}}(#{{.+}}, %{{.+}} : !cir.ptr<!u8i>, 
%{{.+}} : !s32i) -> !s32i
+// CIR:         cir.set_bitfield align(4) (#bfi_n, %[[N_PTR]] : 
!cir.ptr<!u8i>, %[[FIVE]] : !s32i) -> !s32i
+
+// LLVM-LABEL: define {{.*}} void @_Z18store_int_bitfieldP1M
+// LLVM:         %[[OLD:.+]] = load i8, ptr %{{.+}}
+// LLVM:         %[[CLEARED:.+]] = and i8 %[[OLD]], 1
+// LLVM:         %[[NEW:.+]] = or i8 %[[CLEARED]], 10
+// LLVM:         store i8 %[[NEW]], ptr %{{.+}}

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

Reply via email to