Author: Adam Smith
Date: 2026-08-19T21:58:18Z
New Revision: 935bfc708590c60147a79c7df145bb6e68b1d388

URL: 
https://github.com/llvm/llvm-project/commit/935bfc708590c60147a79c7df145bb6e68b1d388
DIFF: 
https://github.com/llvm/llvm-project/commit/935bfc708590c60147a79c7df145bb6e68b1d388.diff

LOG: [CIR] Mark bit-field access units as their own member kinds (#216864)

A bit-field access unit is only as wide as the compiler needs it to be,
which can be narrower than the type the bit-fields were declared with.
Nothing in the record type says so. The unit just gets `data`, or
`empty` when none of its bit-fields are named, and an ordinary field
gets those same marks.

That makes `struct { long long x : 32; }` and `struct { unsigned x; }`
look identical here. Classic CodeGen tells them apart and coerces the
first to `i64`, the second to `i32`. It reads the declared type, where
the bit-field still leaves user data past bit 32.

The fix is to give a unit its own marks, `bitfield` and
`empty_bitfield`. Nothing reads them yet, and no record's emptiness
answer changes. The next PR records a zero-width bit-field, which the
marks miss too, and pad-aware classification can then use both.

Assisted-by: Cursor / claude-opus-5

Added: 
    

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRTypes.h
    clang/include/clang/CIR/Dialect/IR/CIRTypes.td
    clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
    clang/lib/CIR/Dialect/IR/CIRTypes.cpp
    clang/test/CIR/CodeGen/bitfield-union.c
    clang/test/CIR/CodeGen/bitfields.c
    clang/test/CIR/CodeGen/bitfields.cpp
    clang/test/CIR/CodeGen/bitfields_be.c
    clang/test/CIR/CodeGen/dumb-record.cpp
    clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
    clang/test/CIR/CodeGen/mms-bitfields.c
    clang/test/CIR/CodeGen/no-unique-address.cpp
    clang/test/CIR/CodeGen/record-member-kinds.c
    clang/test/CIR/CodeGen/record-member-kinds.cpp
    clang/test/CIR/CodeGen/record-zero-init-padding.c
    clang/test/CIR/IR/bitfield_info.cir
    clang/test/CIR/IR/invalid-record-member-kinds.cir
    clang/test/CIR/IR/struct.cir
    clang/unittests/CIR/RecordMemberKindTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index 9b5b3957f6453..cc1cdfe2cbe52 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -38,6 +38,22 @@ struct UnionTypeStorage;
 
 bool isValidFundamentalIntWidth(unsigned width);
 
+/// Whether a member of this kind holds data for argument passing.
+inline bool holdsDataForABI(RecordMemberKind kind) {
+  return kind == RecordMemberKind::Data || kind == RecordMemberKind::BitField;
+}
+
+/// Whether a member of this kind is a bit-field access unit holding data.  The
+/// compiler chooses an access unit's width, so the member can be narrower than
+/// the declared type of the bit-fields it holds.  A true answer does not mean
+/// the member holds a bit-field: a union's base subobject takes this mark when
+/// any variant is an access unit, whatever its own storage type came from.  A
+/// unit holding only unnamed bit-fields is `empty` instead, and is told apart
+/// from the rest of `empty` by occupying bytes.
+inline bool isBitFieldAccessUnit(RecordMemberKind kind) {
+  return kind == RecordMemberKind::BitField;
+}
+
 /// Returns true if the type is a CIR sized type.
 ///
 /// Types are sized if they implement SizedTypeInterface and

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 2d8d7abe9b404..acbd6ad071d63 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -624,29 +624,45 @@ def CIR_VoidType : CIR_Type<"Void", "void"> {
 
//===----------------------------------------------------------------------===//
 // RecordMemberKind
 //
-// What a record member holds, for members that do not hold source data.
+// What a record member holds, and whether its extent is a declared one.
 
//===----------------------------------------------------------------------===//
 
 def CIR_RecordMemberKind : CIR_I32EnumAttr<
     "RecordMemberKind", "what a record member holds", [
   I32EnumAttrCase<"Data", 0, "data">,
   I32EnumAttrCase<"Pad", 1, "pad">,
-  I32EnumAttrCase<"Empty", 2, "empty">
+  I32EnumAttrCase<"Empty", 2, "empty">,
+  I32EnumAttrCase<"BitField", 3, "bitfield">
 ]> {
   let description = [{
-    Distinguishes a record member that holds source data from one that does
-    not.  `pad` is storage the compiler inserted to place a later member at its
+    `pad` is storage the compiler inserted to place a later member at its
     required offset, and is reusable tail padding when it trails the record.
-    `empty` is storage the source declared that carries no data for argument
-    passing: an unnamed bit-field unit, or a field of a record that is empty 
for
-    the ABI.  Everything else, including a vtable pointer, a base subobject, 
and
-    a bit-field unit with a named occupant, is `data`.
-
-    A record is empty for the ABI when no member is `data`, which is vacuously
-    true for a record with no members.  The distinction between `pad` and
-    `empty` is load-bearing beyond that: only `pad` is reusable, so a record
-    whose trailing member is an unnamed bit-field unit keeps that unit in its
-    data size.
+
+    `data` and `empty` both span something whose extent the source fixed: a
+    field, a vtable pointer, or a base subobject.  They 
diff er only on whether
+    it holds data for argument passing.
+
+    `bitfield` is a bit-field access unit holding at least one named
+    bit-field.  A unit can be narrower than the declared type of the bit-fields
+    it holds, so its extent does not answer what the source declared.  Without
+    it, a unit holding a 32-bit bit-field of `long long` would be
+    indistinguishable from an `unsigned` field, and clang's x86-64 lowering
+    coerces two records that 
diff er only in that to `i64` and `i32`.
+
+    An access unit holding only unnamed bit-fields is `empty`, since it carries
+    no data for argument passing.  A consumer that has to recognize one can:
+    among `empty` members only an access unit occupies bytes, everything else
+    the source declared as empty for the ABI occupies none.
+
+    A union's base subobject is the one exception to all of this: a single
+    member stands in for every variant, and it takes a unit mark as soon as any
+    variant is one, whatever its own storage type came from.
+
+    A record is empty for the ABI when no member is `data` or `bitfield`, which
+    is vacuously true for a record with no members.  The distinction between
+    `pad` and the rest is load-bearing beyond that: only `pad` is reusable, so 
a
+    record whose trailing member is an unnamed bit-field unit keeps that unit 
in
+    its data size.
   }];
 
   let genSpecializedAttr = 0;
@@ -700,6 +716,7 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
         !rec_packed   = !cir.struct<"p1" packed {data !u8i, data !u8i}>
         !rec_pad      = !cir.struct<"p3" {data !u8i, pad !cir.array<!u8i x 3>}>
         !rec_empty    = !cir.struct<"e" {empty !u8i}>
+        !rec_bits     = !cir.struct<"b" {bitfield !u8i, empty !u8i}>
         !recursive    = !cir.struct<"Node" {data 
!cir.ptr<!cir.struct<"Node">>}>
     ```
   }];
@@ -805,8 +822,8 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
     bool isSized() const { return isComplete(); }
 
     /// Returns the data size for this struct type.  Tail padding is the
-    /// trailing run of pad members, so interior padding and a trailing empty
-    /// member stay inside the data size.
+    /// trailing run of pad members, so interior padding and a trailing member
+    /// of any other kind stay inside the data size.
     unsigned computeStructDataSize(const mlir::DataLayout &dataLayout) const;
 
   private:
@@ -855,6 +872,7 @@ def CIR_UnionType : CIR_Type<"Union", "union", [
         !u_anonymous  = !cir.union<{data !s32i, data !u8i}>
         !u_padded     = !cir.union<"U" {data !s32i, data !u8i}, padding = 
{!u8i}>
         !u_empty      = !cir.union<"U" {empty !u8i}>
+        !u_bits       = !cir.union<"U" {bitfield !u8i, empty !u8i}>
     ```
   }];
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 1ce70091d9f4b..dc06fa90f9d14 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -148,10 +148,23 @@ struct CIRRecordLowering final {
     return cirGenTypes.isZeroInitializable(rd);
   }
 
+  /// The mark for a member, given whether it holds data for argument passing
+  /// and whether it is a bit-field access unit.  A run of bit-fields is
+  /// allocated as a single storage type, and that storage is the access unit,
+  /// so its width is ours to choose and can be narrower than the declared type
+  /// of the bit-fields it holds.
+  static cir::RecordMemberKind makeMemberKind(bool holdsData,
+                                              bool isBitFieldAccessUnit) {
+    if (!holdsData)
+      return cir::RecordMemberKind::Empty;
+    return isBitFieldAccessUnit ? cir::RecordMemberKind::BitField
+                                : cir::RecordMemberKind::Data;
+  }
+
   /// The mark for a field.
   cir::RecordMemberKind getFieldMemberKind(const FieldDecl *fd) {
-    return isEmptyFieldForABI(astContext, fd) ? cir::RecordMemberKind::Empty
-                                              : cir::RecordMemberKind::Data;
+    return makeMemberKind(/*holdsData=*/!isEmptyFieldForABI(astContext, fd),
+                          /*isBitFieldAccessUnit=*/fd->isBitField());
   }
 
   /// The mark for a base subobject.  A base contributes no ABI data when it is
@@ -422,7 +435,7 @@ 
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
       assert(members[storageIdx].offset == bitsToCharUnits(startBitOffset) &&
              "storageIdx must name the current run's storage");
       if (!field->isUnnamedBitField())
-        members[storageIdx].memberKind = cir::RecordMemberKind::Data;
+        members[storageIdx].memberKind = cir::RecordMemberKind::BitField;
       // Bitfields get the offset of their storage but come afterward and 
remain
       // there after a stable sort.
       members.push_back(MemberInfo(bitsToCharUnits(startBitOffset),
@@ -607,7 +620,7 @@ 
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
             makeStorageInfo(beginOffset, type, cir::RecordMemberKind::Empty));
         for (; begin != bestEnd; ++begin) {
           if (!begin->isUnnamedBitField())
-            members[storageIdx].memberKind = cir::RecordMemberKind::Data;
+            members[storageIdx].memberKind = cir::RecordMemberKind::BitField;
           if (!begin->isZeroLengthBitField())
             members.push_back(MemberInfo(beginOffset,
                                          MemberInfo::InfoKind::Field, nullptr,
@@ -948,9 +961,7 @@ void CIRRecordLowering::lowerUnion(bool nonVirtualBaseType) 
{
     }
 
     fieldIdxMap[field->getCanonicalDecl()] = 0;
-    addField(fieldType, isEmptyFieldForABI(astContext, field)
-                            ? cir::RecordMemberKind::Empty
-                            : cir::RecordMemberKind::Data);
+    addField(fieldType, getFieldMemberKind(field));
   }
 
   // Compute zero-initializable status.
@@ -994,12 +1005,15 @@ void CIRRecordLowering::lowerUnion(bool 
nonVirtualBaseType) {
   // the storage type and any trailing padding as ordinary fields rather than
   // routing padding through the union's single tail-padding slot.
   if (nonVirtualBaseType) {
-    // One member stands in for every variant, so it holds data unless no
-    // variant does.  Computed before clearFields() drops the variant marks.
-    const cir::RecordMemberKind storageKind =
-        llvm::is_contained(getFieldKinds(), cir::RecordMemberKind::Data)
-            ? cir::RecordMemberKind::Data
-            : cir::RecordMemberKind::Empty;
+    // A unit mark here says the stand-in's extent is not a declared extent, 
not
+    // that its storage came from a bit-field: UnionBitAndWide in
+    // clang/test/CIR/CodeGen/no-unique-address.cpp takes its double as storage
+    // and still marks bitfield.  Computed before clearFields() drops the
+    // variant marks.
+    const cir::RecordMemberKind storageKind = makeMemberKind(
+        /*holdsData=*/llvm::any_of(getFieldKinds(), cir::holdsDataForABI),
+        /*isBitFieldAccessUnit=*/llvm::any_of(getFieldKinds(),
+                                              cir::isBitFieldAccessUnit));
     clearFields();
     addField(storageType, storageKind);
     CharUnits padding = layoutSize - getSize(storageType);

diff  --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 4a26f970b5bc5..5ee0e493ef918 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -172,7 +172,8 @@ 
verifyRecordMemberKinds(function_ref<mlir::InFlightDiagnostic()> emitError,
 
 /// The keywords that spell a member kind.  A union's tail-padding slot probes
 /// for one of these to reject it, since that slot is not a member.
-static const llvm::StringRef memberKindMarks[] = {"data", "pad", "empty"};
+static const llvm::StringRef memberKindMarks[] = {"data", "pad", "empty",
+                                                  "bitfield"};
 
 static std::optional<RecordMemberKind>
 parseMemberKind(mlir::AsmParser &parser) {
@@ -685,9 +686,7 @@ bool RecordType::isEmptyForABI() const {
   // holding no data.
   if (isIncomplete())
     return false;
-  return llvm::none_of(getMemberKinds(), [](RecordMemberKind kind) {
-    return kind == RecordMemberKind::Data;
-  });
+  return llvm::none_of(getMemberKinds(), holdsDataForABI);
 }
 
 
//===----------------------------------------------------------------------===//
@@ -868,9 +867,9 @@ unsigned
 StructType::computeStructDataSize(const mlir::DataLayout &dataLayout) const {
   assert(isComplete() && "Cannot get layout of incomplete records");
 
-  // Tail padding is the trailing run of pad members.  An empty member stays
-  // inside the data size: it is storage the source declared, which a derived
-  // class may not reuse.
+  // Tail padding is the trailing run of pad members.  A member of any other
+  // kind stays inside the data size, only pad being reusable by a derived
+  // class.
   llvm::ArrayRef<mlir::Type> members = getMembers();
   llvm::ArrayRef<RecordMemberKind> kinds = getMemberKinds();
   assert(kinds.size() == members.size() &&

diff  --git a/clang/test/CIR/CodeGen/bitfield-union.c 
b/clang/test/CIR/CodeGen/bitfield-union.c
index 2dcc81295af90..70b3a669041c0 100644
--- a/clang/test/CIR/CodeGen/bitfield-union.c
+++ b/clang/test/CIR/CodeGen/bitfield-union.c
@@ -11,7 +11,7 @@ typedef union {
   int z : 8;
 } demo;
 
-// CIR:  !rec_demo = !cir.union<"demo" {data !s32i, data !u8i, data !u8i}>
+// CIR:  !rec_demo = !cir.union<"demo" {data !s32i, bitfield !u8i, bitfield 
!u8i}>
 // LLVM: %union.demo = type { i32 }
 // OGCG: %union.demo = type { i32 }
 
@@ -22,7 +22,7 @@ typedef union {
   int z : 2;
 } zero_bit;
 
-// CIR:  !rec_zero_bit = !cir.union<"zero_bit" {data !s32i, data !u8i, data 
!u8i}>
+// CIR:  !rec_zero_bit = !cir.union<"zero_bit" {data !s32i, bitfield !u8i, 
bitfield !u8i}>
 // LLVM: %union.zero_bit = type { i32 }
 // OGCG: %union.zero_bit = type { i32 }
 

diff  --git a/clang/test/CIR/CodeGen/bitfields.c 
b/clang/test/CIR/CodeGen/bitfields.c
index 2ed9c6147212b..d4bd980a057d2 100644
--- a/clang/test/CIR/CodeGen/bitfields.c
+++ b/clang/test/CIR/CodeGen/bitfields.c
@@ -12,7 +12,7 @@ typedef struct {
   unsigned still_more_bits : 7;
 } A;
 
-// CIR-DAG:  !rec_A = !cir.struct<"A" packed {data !s8i, data !s8i, data !s8i, 
data !u16i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG:  !rec_A = !cir.struct<"A" packed {data !s8i, data !s8i, data !s8i, 
bitfield !u16i, pad !cir.array<!u8i x 3>}>
 // CIR-DAG:  #bfi_more_bits = #cir.bitfield_info<name = "more_bits", 
storage_type = !u16i, size = 4, offset = 3, is_signed = false>
 // LLVM-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }>
 // OGCG-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }>
@@ -23,7 +23,7 @@ typedef struct {
   int c;
 } D;
 
-// CIR-DAG:  !rec_D = !cir.struct<"D" {data !u16i, data !s32i}>
+// CIR-DAG:  !rec_D = !cir.struct<"D" {bitfield !u16i, data !s32i}>
 // LLVM-DAG: %struct.D = type { i16, i32 }
 // OGCG-DAG: %struct.D = type { i16, i32 }
 
@@ -36,7 +36,7 @@ typedef struct {
   unsigned f; // type other than int above, not a bitfield
 } S;
 // CIR-DAG:  #bfi_c = #cir.bitfield_info<name = "c", storage_type = !u64i, 
size = 17, offset = 32, is_signed = true>
-// CIR-DAG:  !rec_S = !cir.struct<"S" {data !u64i, data !u16i, data !u32i}>
+// CIR-DAG:  !rec_S = !cir.struct<"S" {bitfield !u64i, bitfield !u16i, data 
!u32i}>
 // LLVM-DAG: %struct.S = type { i64, i16, i32 }
 // OGCG-DAG: %struct.S = type { i64, i16, i32 }
 
@@ -45,7 +45,7 @@ typedef struct {
   unsigned b;
 } T;
 
-// CIR-DAG:  !rec_T = !cir.struct<"T" {data !u8i, data !u32i}>
+// CIR-DAG:  !rec_T = !cir.struct<"T" {bitfield !u8i, data !u32i}>
 // LLVM-DAG: %struct.T = type { i8, i32 }
 // OGCG-DAG: %struct.T = type { i8, i32 }
 
@@ -67,7 +67,7 @@ typedef struct {
     int l: 14;
 } U;
 
-// CIR-DAG:  !rec_U = !cir.struct<"U" packed {data !s8i, data !s8i, data !s8i, 
data !u8i, data !u64i}>
+// CIR-DAG:  !rec_U = !cir.struct<"U" packed {data !s8i, data !s8i, data !s8i, 
bitfield !u8i, bitfield !u64i}>
 // LLVM-DAG: %struct.U = type <{ i8, i8, i8, i8, i64 }>
 // OGCG-DAG: %struct.U = type <{ i8, i8, i8, i8, i64 }>
 
@@ -77,7 +77,7 @@ typedef struct{
     int c: 30;
 } Clip;
 
-// CIR-DAG: !rec_Clip = !cir.struct<"Clip" {data !cir.array<!u8i x 3>, data 
!s8i, data !u32i}>
+// CIR-DAG: !rec_Clip = !cir.struct<"Clip" {bitfield !cir.array<!u8i x 3>, 
data !s8i, bitfield !u32i}>
 // LLVM-DAG: %struct.Clip = type { [3 x i8], i8, i32 }
 // OGCG-DAG: %struct.Clip = type { [3 x i8], i8, i32 }
 

diff  --git a/clang/test/CIR/CodeGen/bitfields.cpp 
b/clang/test/CIR/CodeGen/bitfields.cpp
index 7dce9bf29cd4a..8aec3cf99a66a 100644
--- a/clang/test/CIR/CodeGen/bitfields.cpp
+++ b/clang/test/CIR/CodeGen/bitfields.cpp
@@ -15,7 +15,7 @@ typedef struct {
   int e : 15;
   unsigned f; // type other than int above, not a bitfield
 } S;
-// CIR-DAG:  !rec_S = !cir.struct<"S" {data !u64i, data !u16i, data !u32i}>
+// CIR-DAG:  !rec_S = !cir.struct<"S" {bitfield !u64i, bitfield !u16i, data 
!u32i}>
 // CIR-DAG:  #bfi_c = #cir.bitfield_info<name = "c", storage_type = !u64i, 
size = 17, offset = 32, is_signed = true>
 // LLVM-DAG: %struct.S = type { i64, i16, i32 }
 // OGCG-DAG: %struct.S = type { i64, i16, i32 }
@@ -25,7 +25,7 @@ typedef struct {
   unsigned b;
 } T;
 
-// CIR-DAG:  !rec_T = !cir.struct<"T" {data !u8i, data !u32i}>
+// CIR-DAG:  !rec_T = !cir.struct<"T" {bitfield !u8i, data !u32i}>
 // LLVM-DAG: %struct.T = type { i8, i32 }
 // OGCG-DAG: %struct.T = type { i8, i32 }
 

diff  --git a/clang/test/CIR/CodeGen/bitfields_be.c 
b/clang/test/CIR/CodeGen/bitfields_be.c
index 3a40a65a26192..0ed234a362068 100644
--- a/clang/test/CIR/CodeGen/bitfields_be.c
+++ b/clang/test/CIR/CodeGen/bitfields_be.c
@@ -11,7 +11,7 @@ typedef struct {
     int c : 17;
 } S;
 
-// CIR:  !rec_S = !cir.struct<"S" {data !u32i}>
+// CIR:  !rec_S = !cir.struct<"S" {bitfield !u32i}>
 // LLVM: %struct.S = type { i32 }
 // OGCG: %struct.S = type { i32 }
 void def() {

diff  --git a/clang/test/CIR/CodeGen/dumb-record.cpp 
b/clang/test/CIR/CodeGen/dumb-record.cpp
index 10870124bd019..0d8c898415108 100644
--- a/clang/test/CIR/CodeGen/dumb-record.cpp
+++ b/clang/test/CIR/CodeGen/dumb-record.cpp
@@ -30,8 +30,8 @@ struct BitfieldsInOrder {
 } bitfield_order;
 
 // CHECK: Layout: <CIRecordLayout
-// CHECK:  CIR Type:!cir.struct<"BitfieldsInOrder" {data !cir.int<s, 8>, data 
!cir.int<u, 8>, data !cir.int<u, 32>}>
-// CHECK:  NonVirtualBaseCIRType:!cir.struct<"BitfieldsInOrder" {data 
!cir.int<s, 8>, data !cir.int<u, 8>, data !cir.int<u, 32>}>
+// CHECK:  CIR Type:!cir.struct<"BitfieldsInOrder" {data !cir.int<s, 8>, 
bitfield !cir.int<u, 8>, bitfield !cir.int<u, 32>}>
+// CHECK:  NonVirtualBaseCIRType:!cir.struct<"BitfieldsInOrder" {data 
!cir.int<s, 8>, bitfield !cir.int<u, 8>, bitfield !cir.int<u, 32>}>
 // CHECK:  IsZeroInitializable:1
 // CHECK:  BitFields:[
 // CHECK-NEXT:   <CIRBitFieldInfo name:bit offset:0 size:8 isSigned:0 
storageSize:8 storageOffset:1 volatileOffset:0 volatileStorageSize:0 
volatileStorageOffset:0>

diff  --git a/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp 
b/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
index db97c97342ec8..7b9db8fd5358d 100644
--- a/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
+++ b/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
@@ -13,7 +13,7 @@ struct S1 {
   unsigned f5:8;
 };
 
-// CIR-DAG: !rec_S1 = !cir.struct<"S1" {data !u8i, data !u8i, data !u16i}>
+// CIR-DAG: !rec_S1 = !cir.struct<"S1" {bitfield !u8i, bitfield !u8i, bitfield 
!u16i}>
 // LLVM-DAG: %struct.S1 = type { i8, i8, i16 }
 // OGCG-DAG: %struct.S1 = type { i8, i8, i16 }
 
@@ -23,7 +23,7 @@ struct S2 {
   unsigned long f3:6;
 };
 
-// CIR-DAG: !rec_S2 = !cir.struct<"S2" {data !u16i, data !u16i, data !u8i, pad 
!cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_S2 = !cir.struct<"S2" {bitfield !u16i, bitfield !u16i, 
bitfield !u8i, pad !cir.array<!u8i x 3>}>
 // LLVM-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
 // OGCG-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
 
@@ -33,7 +33,7 @@ struct S3 {
   unsigned long f3:32;
 };
 
-// CIR-DAG: !rec_S3 = !cir.struct<"S3" {data !u32i, data !u32i}>
+// CIR-DAG: !rec_S3 = !cir.struct<"S3" {bitfield !u32i, bitfield !u32i}>
 // LLVM-DAG: %struct.S3 = type { i32, i32 }
 // OGCG-DAG: %struct.S3 = type { i32, i32 }
 

diff  --git a/clang/test/CIR/CodeGen/mms-bitfields.c 
b/clang/test/CIR/CodeGen/mms-bitfields.c
index 861164f4beeba..f9832f55e5405 100644
--- a/clang/test/CIR/CodeGen/mms-bitfields.c
+++ b/clang/test/CIR/CodeGen/mms-bitfields.c
@@ -10,7 +10,7 @@ struct s1 {
   long long f64 : 30;
 } s1;
 
-// CIR-DAG: !rec_s1 = !cir.struct<"s1" {data !s32i, data !s64i}>
+// CIR-DAG: !rec_s1 = !cir.struct<"s1" {bitfield !s32i, bitfield !s64i}>
 // LLVM-DAG: %struct.s1 = type { i32, i64 }
 // OGCG-DAG: %struct.s1 = type { i32, i64 }
 
@@ -20,7 +20,7 @@ struct s2 {
     int c : 30;
 } Clip;
 
-// CIR-DAG: !rec_s2 = !cir.struct<"s2" {data !s32i, data !s8i, data !s32i}>
+// CIR-DAG: !rec_s2 = !cir.struct<"s2" {bitfield !s32i, data !s8i, bitfield 
!s32i}>
 // LLVM-DAG: %struct.s2 = type { i32, i8, i32 }
 // OGCG-DAG: %struct.s2 = type { i32, i8, i32 }
 
@@ -30,7 +30,7 @@ struct s3 {
     int c : 14;
 } zero_bit;
 
-// CIR-DAG:  !rec_s3 = !cir.struct<"s3" {data !s32i, data !s32i}>
+// CIR-DAG:  !rec_s3 = !cir.struct<"s3" {bitfield !s32i, bitfield !s32i}>
 // LLVM-DAG: %struct.s3 = type { i32, i32 }
 // OGCG-DAG: %struct.s3 = type { i32, i32 }
 
@@ -45,7 +45,7 @@ struct Inner {
 
 #pragma pack (pop)
 
-// CIR-DAG: !rec_Inner = !cir.struct<"Inner" {data !u32i, data !u32i}>
+// CIR-DAG: !rec_Inner = !cir.struct<"Inner" {bitfield !u32i, bitfield !u32i}>
 // LLVM-DAG: %struct.Inner = type { i32, i32 }
 // OGCG-DAG: %struct.Inner = type { i32, i32 }
 
@@ -67,7 +67,7 @@ union HEADER {
 
 #pragma pack(pop)
 
-// CIR-DAG: !rec_A = !cir.struct<"A" {data !s32i, data !s32i, data !s32i}>
+// CIR-DAG: !rec_A = !cir.struct<"A" {bitfield !s32i, bitfield !s32i, bitfield 
!s32i}>
 // CIR-DAG: !rec_HEADER = !cir.union<"HEADER" {data !rec_A}>
 // LLVM-DAG: %struct.A = type { i32, i32, i32 }
 // LLVM-DAG: %union.HEADER = type { %struct.A }

diff  --git a/clang/test/CIR/CodeGen/no-unique-address.cpp 
b/clang/test/CIR/CodeGen/no-unique-address.cpp
index 54514d5f18b20..b2d1f845fdc48 100644
--- a/clang/test/CIR/CodeGen/no-unique-address.cpp
+++ b/clang/test/CIR/CodeGen/no-unique-address.cpp
@@ -62,6 +62,12 @@ struct Outer {
 // LLVM-DAG: @ozd = {{(dso_local )?}}global %struct.OuterZeroData 
zeroinitializer, align 4
 // LLVM-DAG: %struct.OuterAllEmpty = type { i8 }
 // LLVM-DAG: @oae = {{(dso_local )?}}global %struct.OuterAllEmpty 
zeroinitializer, align 1
+// LLVM-DAG: %struct.OuterUnionBitPad = type { %struct.UnionBitAndWide.base, 
i8, [6 x i8] }
+// LLVM-DAG: %struct.UnionBitAndWide.base = type <{ double, i8 }>
+// LLVM-DAG: %struct.OuterAllEmptyBits = type { 
%struct.UnionAllEmptyBits.base, i8, [4 x i8] }
+// LLVM-DAG: %struct.UnionAllEmptyBits.base = type { [3 x i8] }
+// LLVM-DAG: @oubp = {{(dso_local )?}}global %struct.OuterUnionBitPad 
zeroinitializer, align 8
+// LLVM-DAG: @oaeb = {{(dso_local )?}}global %struct.OuterAllEmptyBits 
zeroinitializer, align 8
 // OGCG-DAG: %struct.OuterUnion = type { %union.UnionForNUA, i32 }
 // OGCG-DAG: %union.UnionForNUA = type { i64 }
 // OGCG-DAG: %struct.OuterFinal = type { %struct.FinalForNUA, i8 }
@@ -82,6 +88,12 @@ struct Outer {
 // OGCG-DAG: @ozd = {{(dso_local )?}}global %struct.OuterZeroData 
zeroinitializer, align 4
 // OGCG-DAG: %struct.OuterAllEmpty = type { i8 }
 // OGCG-DAG: @oae = {{(dso_local )?}}global %struct.OuterAllEmpty 
zeroinitializer, align 1
+// OGCG-DAG: %struct.OuterUnionBitPad = type { %union.UnionBitAndWide.base, 
i8, [6 x i8] }
+// OGCG-DAG: %union.UnionBitAndWide.base = type <{ double, i8 }>
+// OGCG-DAG: %struct.OuterAllEmptyBits = type { %union.UnionAllEmptyBits.base, 
i8, [4 x i8] }
+// OGCG-DAG: %union.UnionAllEmptyBits.base = type { [3 x i8] }
+// OGCG-DAG: @oubp = {{(dso_local )?}}global %struct.OuterUnionBitPad 
zeroinitializer, align 8
+// OGCG-DAG: @oaeb = {{(dso_local )?}}global %struct.OuterAllEmptyBits 
zeroinitializer, align 8
 
 // LLVM-LABEL: define {{.*}} void @_ZN5OuterC2ERK6Middlec(
 // LLVM:         %[[GEP:.*]] = getelementptr inbounds nuw %struct.Outer, ptr 
%{{.+}}, i32 0, i32 0
@@ -184,6 +196,76 @@ struct OuterUnionPadAfterStorage {
 
 OuterUnionPadAfterStorage oupas;
 
+// The stand-in member takes a bit-field unit mark as soon as any variant is a
+// unit, even where the storage type comes from a variant that is not one.
+struct WideTail {
+  WideTail();
+
+private:
+  double d;
+  char c;
+};
+
+union UnionBitAndWide {
+  UnionBitAndWide();
+  [[no_unique_address]] WideTail w;
+  unsigned b : 3;
+  double d;
+};
+
+struct OuterUnionBitPad {
+  [[no_unique_address]] UnionBitAndWide u;
+  bool tail;
+};
+
+OuterUnionBitPad oubp;
+
+// No variant holds data and one is a unit, so the stand-in is an empty unit.
+struct alignas(8) EmptyBitsTail {
+  EmptyBitsTail();
+
+private:
+  int : 24;
+};
+
+union UnionAllEmptyBits {
+  UnionAllEmptyBits();
+  [[no_unique_address]] EmptyBitsTail e;
+  unsigned : 3;
+};
+
+struct OuterAllEmptyBits {
+  [[no_unique_address]] UnionAllEmptyBits u;
+  bool tail;
+};
+
+OuterAllEmptyBits oaeb;
+
+// The only variant holding data is a unit, so the stand-in holds data because
+// of a unit mark rather than a data mark.
+union OnlyBitData {
+  OnlyBitData();
+  [[no_unique_address]] EmptyBitsTail e;
+  unsigned b : 3;
+};
+
+struct OuterOnlyBitData {
+  [[no_unique_address]] OnlyBitData u;
+  bool tail;
+};
+
+OuterOnlyBitData oobd;
+
+// CIR-NUA-DAG: !rec_OnlyBitData2Ebase = !cir.struct<"OnlyBitData.base" 
{bitfield !cir.array<!u8i x 3>}>
+// CIR-NUA-DAG: !rec_OuterOnlyBitData = !cir.struct<"OuterOnlyBitData" {data 
!rec_OnlyBitData2Ebase, data !cir.bool, pad !cir.array<!u8i x 4>}>
+// CIR-NUA-DAG: cir.global external @oobd = #cir.zero : !rec_OuterOnlyBitData
+// CIR-NUA-DAG: !rec_UnionBitAndWide2Ebase = 
!cir.struct<"UnionBitAndWide.base" packed {bitfield !cir.double, pad !u8i}>
+// CIR-NUA-DAG: !rec_OuterUnionBitPad = !cir.struct<"OuterUnionBitPad" {data 
!rec_UnionBitAndWide2Ebase, data !cir.bool, pad !cir.array<!u8i x 6>}>
+// CIR-NUA-DAG: !rec_UnionAllEmptyBits2Ebase = 
!cir.struct<"UnionAllEmptyBits.base" {empty !cir.array<!u8i x 3>}>
+// CIR-NUA-DAG: !rec_OuterAllEmptyBits = !cir.struct<"OuterAllEmptyBits" 
{empty !rec_UnionAllEmptyBits2Ebase, data !cir.bool, pad !cir.array<!u8i x 4>}>
+// CIR-NUA-DAG: cir.global external @oubp = #cir.zero : !rec_OuterUnionBitPad
+// CIR-NUA-DAG: cir.global external @oaeb = #cir.zero : !rec_OuterAllEmptyBits
+
 // CIR-NUA-DAG: !rec_FinalForNUA = !cir.struct<"FinalForNUA" {data !s32i, data 
!s8i}>
 // CIR-NUA-DAG: !rec_UnionForNUA = !cir.union<"UnionForNUA" {data !s32i, data 
!s64i}>
 // CIR-NUA-DAG: !rec_OuterFinal = !cir.struct<"OuterFinal" {data 
!rec_FinalForNUA, data !s8i}>

diff  --git a/clang/test/CIR/CodeGen/record-member-kinds.c 
b/clang/test/CIR/CodeGen/record-member-kinds.c
index 1e46153862a49..831b68f23c4f9 100644
--- a/clang/test/CIR/CodeGen/record-member-kinds.c
+++ b/clang/test/CIR/CodeGen/record-member-kinds.c
@@ -40,37 +40,42 @@ struct UnnamedBitOnly { int : 8; };
 struct UnnamedBitThenField { int : 8; int f; };
 // CIR-DAG: !rec_UnnamedBitThenField = !cir.struct<"UnnamedBitThenField" 
{empty !u8i, data !s32i}>
 
+// The trailing unit is narrower than its bit-field's declared type and is not
+// pad, so it stays in the data size.
+struct NamedFieldThenUnnamedBit { char c; int : 24; };
+// CIR-DAG: !rec_NamedFieldThenUnnamedBit = 
!cir.struct<"NamedFieldThenUnnamedBit" {data !s8i, empty !cir.array<!u8i x 3>}>
+
 // The discrete ms_struct path allocates a unit per formal type.  A unit whose
 // only occupant is unnamed holds no data.
 struct MsOnlyUnnamed { int : 3; } __attribute__((ms_struct));
 // CIR-DAG: !rec_MsOnlyUnnamed = !cir.struct<"MsOnlyUnnamed" {empty !s32i}>
 
 struct MsNamedThenUnnamed { int a : 3; int : 3; } __attribute__((ms_struct));
-// CIR-DAG: !rec_MsNamedThenUnnamed = !cir.struct<"MsNamedThenUnnamed" {data 
!s32i}>
+// CIR-DAG: !rec_MsNamedThenUnnamed = !cir.struct<"MsNamedThenUnnamed" 
{bitfield !s32i}>
 
 struct MsUnnamedThenNamed { int : 3; int b : 3; } __attribute__((ms_struct));
-// CIR-DAG: !rec_MsUnnamedThenNamed = !cir.struct<"MsUnnamedThenNamed" {data 
!s32i}>
+// CIR-DAG: !rec_MsUnnamedThenNamed = !cir.struct<"MsUnnamedThenNamed" 
{bitfield !s32i}>
 
 // A 
diff ering formal type starts a new unit, so this record carries one unit of
 // each kind.
 struct MsMixed { int a : 3; char : 3; } __attribute__((ms_struct));
-// CIR-DAG: !rec_MsMixed = !cir.struct<"MsMixed" {data !s32i, empty !s8i}>
+// CIR-DAG: !rec_MsMixed = !cir.struct<"MsMixed" {bitfield !s32i, empty !s8i}>
 
 struct MsEmptyFirst { char : 3; int a : 3; } __attribute__((ms_struct));
-// CIR-DAG: !rec_MsEmptyFirst = !cir.struct<"MsEmptyFirst" {empty !s8i, data 
!s32i}>
+// CIR-DAG: !rec_MsEmptyFirst = !cir.struct<"MsEmptyFirst" {empty !s8i, 
bitfield !s32i}>
 
 struct MsEmptyMiddle {
   int a : 3; char : 3; short b : 3;
 } __attribute__((ms_struct));
-// CIR-DAG: !rec_MsEmptyMiddle = !cir.struct<"MsEmptyMiddle" {data !s32i, 
empty !s8i, data !s16i}>
+// CIR-DAG: !rec_MsEmptyMiddle = !cir.struct<"MsEmptyMiddle" {bitfield !s32i, 
empty !s8i, bitfield !s16i}>
 
 // A zero-width bit-field ends the run here too, so the unit after it is a
 // fresh one that has to be marked on its own.
 struct MsZeroWidthSplit { int a : 3; int : 0; int : 3; } 
__attribute__((ms_struct));
-// CIR-DAG: !rec_MsZeroWidthSplit = !cir.struct<"MsZeroWidthSplit" {data 
!s32i, empty !s32i}>
+// CIR-DAG: !rec_MsZeroWidthSplit = !cir.struct<"MsZeroWidthSplit" {bitfield 
!s32i, empty !s32i}>
 
 struct MsZeroWidthSplit2 { int : 3; int : 0; int b : 3; } 
__attribute__((ms_struct));
-// CIR-DAG: !rec_MsZeroWidthSplit2 = !cir.struct<"MsZeroWidthSplit2" {empty 
!s32i, data !s32i}>
+// CIR-DAG: !rec_MsZeroWidthSplit2 = !cir.struct<"MsZeroWidthSplit2" {empty 
!s32i, bitfield !s32i}>
 
 union UnnamedBitUnion { int : 8; };
 // CIR-DAG: !rec_UnnamedBitUnion = !cir.union<"UnnamedBitUnion" {empty !u8i}>
@@ -78,6 +83,20 @@ union UnnamedBitUnion { int : 8; };
 union ContainsEmptyUnion { struct E e; };
 // CIR-DAG: !rec_ContainsEmptyUnion = !cir.union<"ContainsEmptyUnion" {empty 
!rec_E}>
 
+// The two pairs that follow are byte-identical apart from their marks, in a
+// struct and in a union alike.
+struct BitWideUnit { long long x : 32; } __attribute__((aligned(16)));
+// CIR-DAG: !rec_BitWideUnit = !cir.struct<"BitWideUnit" {bitfield !u32i, pad 
!cir.array<!u8i x 12>}>
+
+struct UIntOverAligned { unsigned x; } __attribute__((aligned(16)));
+// CIR-DAG: !rec_UIntOverAligned = !cir.struct<"UIntOverAligned" {data !u32i, 
pad !cir.array<!u8i x 12>}>
+
+union BitUnit { unsigned a : 1; unsigned b : 1; };
+// CIR-DAG: !rec_BitUnit = !cir.union<"BitUnit" {bitfield !u8i, bitfield 
!u8i}, padding = {!cir.array<!u8i x 3>}>
+
+union UCharOverAligned { unsigned char c, d; } __attribute__((aligned(4)));
+// CIR-DAG: !rec_UCharOverAligned = !cir.union<"UCharOverAligned" {data !u8i, 
data !u8i}, padding = {!cir.array<!u8i x 3>}>
+
 struct AlignedTail { char c; int i __attribute__((aligned(8))); };
 // CIR-DAG: !rec_AlignedTail = !cir.struct<"AlignedTail" {data !s8i, pad 
!cir.array<!u8i x 7>, data !s32i, pad !cir.array<!u8i x 4>}>
 // LLVM-DAG: %struct.AlignedTail = type { i8, [7 x i8], i32, [4 x i8] }
@@ -92,7 +111,10 @@ void useTypes(struct ContainsEmpty *a, struct 
ContainsEmptyAndInt *b,
               struct MsMixed *l, struct MsEmptyFirst *m,
               struct MsEmptyMiddle *n, struct MsZeroWidthSplit *o,
               struct MsZeroWidthSplit2 *p, union UnnamedBitUnion *q,
-              union ContainsEmptyUnion *r) {}
+              union ContainsEmptyUnion *r,
+              struct NamedFieldThenUnnamedBit *s, struct BitWideUnit *t,
+              struct UIntOverAligned *u, union BitUnit *v,
+              union UCharOverAligned *w) {}
 
 struct AlignedTail gAlignedTail;
 

diff  --git a/clang/test/CIR/CodeGen/record-member-kinds.cpp 
b/clang/test/CIR/CodeGen/record-member-kinds.cpp
index 98be5496e7040..379f756912985 100644
--- a/clang/test/CIR/CodeGen/record-member-kinds.cpp
+++ b/clang/test/CIR/CodeGen/record-member-kinds.cpp
@@ -69,29 +69,29 @@ struct OnlyUnnamedBit { int : 24; };
 // A unit with a named occupant holds data, whichever order the occupants come
 // in, and however the storage is spelled.
 struct NamedClipped { int i; int j : 24; };
-// CIR-DAG: !rec_NamedClipped = !cir.struct<"NamedClipped" {data !s32i, data 
!u32i}>
+// CIR-DAG: !rec_NamedClipped = !cir.struct<"NamedClipped" {data !s32i, 
bitfield !u32i}>
 
 struct NamedFirst { int a : 8; int : 16; };
-// CIR-DAG: !rec_NamedFirst = !cir.struct<"NamedFirst" {data !u32i}>
+// CIR-DAG: !rec_NamedFirst = !cir.struct<"NamedFirst" {bitfield !u32i}>
 
 struct UnnamedFirst { int : 16; int a : 8; };
-// CIR-DAG: !rec_UnnamedFirst = !cir.struct<"UnnamedFirst" {data !u32i}>
+// CIR-DAG: !rec_UnnamedFirst = !cir.struct<"UnnamedFirst" {bitfield !u32i}>
 
 // A zero-length bit-field separates one span into two units, and a record can
 // carry a data unit and an empty unit at once, in either order.
 struct SpanMixed { int a : 3; int : 0; int : 3; };
-// CIR-DAG: !rec_SpanMixed = !cir.struct<"SpanMixed" {data !u8i, pad 
!cir.array<!u8i x 3>, empty !u8i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_SpanMixed = !cir.struct<"SpanMixed" {bitfield !u8i, pad 
!cir.array<!u8i x 3>, empty !u8i, pad !cir.array<!u8i x 3>}>
 
 struct SpanEmptyFirst { int : 3; int : 0; int b : 3; };
-// CIR-DAG: !rec_SpanEmptyFirst = !cir.struct<"SpanEmptyFirst" {empty !u8i, 
pad !cir.array<!u8i x 3>, data !u8i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_SpanEmptyFirst = !cir.struct<"SpanEmptyFirst" {empty !u8i, 
pad !cir.array<!u8i x 3>, bitfield !u8i, pad !cir.array<!u8i x 3>}>
 
 // One unit covering both of these would need more than one register, so they
 // split into two units that are marked independently.
 struct WideSpanMixed { unsigned long a : 64; unsigned long : 64; };
-// CIR-DAG: !rec_WideSpanMixed = !cir.struct<"WideSpanMixed" {data !u64i, 
empty !u64i}>
+// CIR-DAG: !rec_WideSpanMixed = !cir.struct<"WideSpanMixed" {bitfield !u64i, 
empty !u64i}>
 
 struct WideSpanEmptyFirst { unsigned long : 64; unsigned long b : 64; };
-// CIR-DAG: !rec_WideSpanEmptyFirst = !cir.struct<"WideSpanEmptyFirst" {empty 
!u64i, data !u64i}>
+// CIR-DAG: !rec_WideSpanEmptyFirst = !cir.struct<"WideSpanEmptyFirst" {empty 
!u64i, bitfield !u64i}>
 
 struct WideSpanAllEmpty { unsigned long : 64; unsigned long : 64; };
 // CIR-DAG: !rec_WideSpanAllEmpty = !cir.struct<"WideSpanAllEmpty" {empty 
!u64i, empty !u64i}>
@@ -112,13 +112,15 @@ struct NearlyEmptyVBase { virtual ~NearlyEmptyVBase(); };
 struct HasNearlyEmptyVBase : virtual NearlyEmptyVBase { int i; };
 // CIR-DAG: !rec_HasNearlyEmptyVBase = !cir.struct<"HasNearlyEmptyVBase" 
packed {data !rec_NearlyEmptyVBase, data !s32i, pad !cir.array<!u8i x 4>}>
 
-// Both marks appear on one record: the byte array is storage the source
-// declared for its unnamed bit-field, while the byte after it is inserted by
-// the compiler.  The storage keeps its mark in the base subobject type.
+// Only the pad member is reusable, so the unit stays in the base subobject 
type
+// while the byte after it does not.  A named unit stays the same way.
 struct Clipped { Clipped(const Clipped &); int i; int : 24; };
 // CIR-DAG: !rec_Clipped = !cir.struct<"Clipped" packed {data !s32i, empty 
!cir.array<!u8i x 3>, pad !u8i}>
 // CIR-DAG: !rec_Clipped2Ebase = !cir.struct<"Clipped.base" packed {data 
!s32i, empty !cir.array<!u8i x 3>}>
 
+struct NamedClippedTail { NamedClippedTail(const NamedClippedTail &); int i; 
int j : 24; };
+// CIR-DAG: !rec_NamedClippedTail = !cir.struct<"NamedClippedTail" packed 
{data !s32i, bitfield !cir.array<!u8i x 3>, pad !u8i}>
+
 struct DerivedClipped : Clipped { char c; };
 // CIR-DAG: !rec_DerivedClipped = !cir.struct<"DerivedClipped" {data 
!rec_Clipped2Ebase, data !s8i}>
 // LLVM-DAG: %struct.Clipped.base = type <{ i32, [3 x i8] }>
@@ -132,7 +134,7 @@ void useTypes(HoldsEmpty *, NuaEmpty *, NuaEmptyUnion *, 
NuaPolyUnion *,
               NamedFirst *, UnnamedFirst *, SpanMixed *, SpanEmptyFirst *,
               WideSpanMixed *, WideSpanEmptyFirst *, WideSpanAllEmpty *,
               UnnamedBitUnion *, NoMemberUnion *, Pod *, NearlyEmptyVBase *,
-              HasNearlyEmptyVBase *, Clipped *) {}
+              HasNearlyEmptyVBase *, Clipped *, NamedClippedTail *) {}
 
 Empty gEmpty;
 AlignasTail gAlignasTail;

diff  --git a/clang/test/CIR/CodeGen/record-zero-init-padding.c 
b/clang/test/CIR/CodeGen/record-zero-init-padding.c
index 8137f00194271..97ea5087c3746 100644
--- a/clang/test/CIR/CodeGen/record-zero-init-padding.c
+++ b/clang/test/CIR/CodeGen/record-zero-init-padding.c
@@ -35,7 +35,7 @@ void test_zero_init_padding(void) {
 }
 
 // Type definitions for anonymous structs with padding
-// CIR-DAG: !rec_bitfield_with_padding = !cir.struct<"bitfield_with_padding" 
{data !u8i, data !s32i}>
+// CIR-DAG: !rec_bitfield_with_padding = !cir.struct<"bitfield_with_padding" 
{bitfield !u8i, data !s32i}>
 // CIR-DAG: !rec_multiple_padding = !cir.struct<"multiple_padding" {data !s8i, 
data !s16i, data !s64i}>
 // CIR-DAG: !rec_padding_after_field = !cir.struct<"padding_after_field" {data 
!s8i, data !s32i}>
 // CIR-DAG: !rec_tail_padding = !cir.struct<"tail_padding" {data !s32i, data 
!s8i}>

diff  --git a/clang/test/CIR/IR/bitfield_info.cir 
b/clang/test/CIR/IR/bitfield_info.cir
index 43c2ad6e75491..37f07c2f912f9 100644
--- a/clang/test/CIR/IR/bitfield_info.cir
+++ b/clang/test/CIR/IR/bitfield_info.cir
@@ -4,7 +4,7 @@
 !u32i = !cir.int<u, 32>
 
 
-!rec_S = !cir.struct<"S" {data !u32i}>
+!rec_S = !cir.struct<"S" {bitfield !u32i}>
 #bfi_c = #cir.bitfield_info<name = "c", storage_type = !u32i, size = 17, 
offset = 15, is_signed = true>
 
 // CHECK: #bfi_c = #cir.bitfield_info<name = "c", storage_type = !u32i, size = 
17, offset = 15, is_signed = true>

diff  --git a/clang/test/CIR/IR/invalid-record-member-kinds.cir 
b/clang/test/CIR/IR/invalid-record-member-kinds.cir
index 9524b6ea93d70..77e0e00c7664f 100644
--- a/clang/test/CIR/IR/invalid-record-member-kinds.cir
+++ b/clang/test/CIR/IR/invalid-record-member-kinds.cir
@@ -53,6 +53,15 @@ module {}
 
 // -----
 
+!u8i = !cir.int<u, 8>
+!s32i = !cir.int<s, 32>
+// expected-error @below {{a union's tail padding takes no kind mark}}
+!rec_U = !cir.union<"U" {data !s32i}, padding = {bitfield !cir.array<!u8i x 
4>}>
+
+module {}
+
+// -----
+
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
 // expected-error @below {{a union member cannot be marked pad}}

diff  --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index cf22833e211b4..0103da3982b95 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -37,6 +37,9 @@
 !rec_P7 = !cir.struct<"P7" packed {data !u8i, pad !u8i}>
 !rec_P8 = !cir.struct<"P8" {data !u16i, pad !u8i, pad !u8i}>
 
+// Bit-field access units
+!rec_P9 = !cir.struct<"P9" {bitfield !u8i, empty !u8i}>
+
 // CHECK-DAG: !rec_P1 = !cir.struct<"P1" packed {data !s32i, data !s32i}>
 // CHECK-DAG: !rec_P2 = !cir.struct<"P2" {data !u8i, pad !u8i, data !u16i, 
data !u32i}>
 // CHECK-DAG: !rec_P3 = !cir.struct<"P3" packed {data !u8i, pad !u8i, data 
!u16i, data !u32i}>
@@ -45,6 +48,7 @@
 // CHECK-DAG: !rec_P6 = !cir.struct<"P6" {data !u32i, empty !cir.array<!u8i x 
3>, pad !u8i}>
 // CHECK-DAG: !rec_P7 = !cir.struct<"P7" packed {data !u8i, pad !u8i}>
 // CHECK-DAG: !rec_P8 = !cir.struct<"P8" {data !u16i, pad !u8i, pad !u8i}>
+// CHECK-DAG: !rec_P9 = !cir.struct<"P9" {bitfield !u8i, empty !u8i}>
 
 // Records with identical member types, spelled apart by their kinds.
 !rec_M1 = !cir.struct<"M1" {data !u8i, pad !u8i}>
@@ -63,6 +67,9 @@
 !rec_anon_u_plain = !cir.union<{data !s32i, data !u8i}>
 !rec_U3 = !cir.union<"U3" {empty !u8i}>
 !rec_U4 = !cir.union<"U4" {data !s32i, empty !u8i}, padding = {!cir.array<!u8i 
x 4>}>
+// A unit-marked union with a tail-padding slot, which is the shape CIRGen 
emits
+// for a union of bit-fields.
+!rec_U5 = !cir.union<"U5" {bitfield !u8i, empty !u8i}, padding = 
{!cir.array<!u8i x 3>}>
 
 // CHECK-DAG: !rec_U1 = !cir.union<"U1" {data !s32i, data !u8i}, padding = 
{!u8i}>
 // CHECK-DAG: !rec_U2 = !cir.union<"U2" packed {data !s32i}, padding = 
{!cir.array<!u8i x 4>}>
@@ -70,6 +77,7 @@
 // CHECK-DAG: !cir.union<{data !s32i, data !u8i}>
 // CHECK-DAG: !rec_U3 = !cir.union<"U3" {empty !u8i}>
 // CHECK-DAG: !rec_U4 = !cir.union<"U4" {data !s32i, empty !u8i}, padding = 
{!cir.array<!u8i x 4>}>
+// CHECK-DAG: !rec_U5 = !cir.union<"U5" {bitfield !u8i, empty !u8i}, padding = 
{!cir.array<!u8i x 3>}>
 
 
 // Complete a previously incomplete record
@@ -127,7 +135,9 @@ module  {
                      %arg21: !rec_P7,
                      %arg22: !rec_anon_u_empty,
                      %arg23: !rec_anon_u_plain,
-                     %arg24: !rec_P8) {
+                     %arg24: !rec_P8,
+                     %arg25: !rec_P9,
+                     %arg26: !rec_U5) {
     cir.return
   }
 

diff  --git a/clang/unittests/CIR/RecordMemberKindTest.cpp 
b/clang/unittests/CIR/RecordMemberKindTest.cpp
index 9bbaf39c2ca17..d266078eb9129 100644
--- a/clang/unittests/CIR/RecordMemberKindTest.cpp
+++ b/clang/unittests/CIR/RecordMemberKindTest.cpp
@@ -73,12 +73,20 @@ TEST_F(RecordMemberKindTest, 
EmptyForTheABIWhenNoMemberHoldsData) {
   EXPECT_FALSE(makeStruct("dp", {u8, u8},
                           {RecordMemberKind::Data, RecordMemberKind::Pad})
                    .isEmptyForABI());
+  // A unit with a named occupant holds data the same way a field does.
+  EXPECT_FALSE(
+      makeStruct("b1", {u8}, {RecordMemberKind::BitField}).isEmptyForABI());
+  EXPECT_FALSE(makeStruct("be", {u8, u8},
+                          {RecordMemberKind::BitField, 
RecordMemberKind::Empty})
+                   .isEmptyForABI());
 }
 
 TEST_F(RecordMemberKindTest, PaddedFollowsThePadKinds) {
   IntType u8 = getU8();
   EXPECT_FALSE(makeStruct("d", {u8}, {RecordMemberKind::Data}).getPadded());
   EXPECT_FALSE(makeStruct("e", {u8}, {RecordMemberKind::Empty}).getPadded());
+  // Bit-field storage is declared, so an access unit is not padding.
+  EXPECT_FALSE(makeStruct("b", {u8}, 
{RecordMemberKind::BitField}).getPadded());
   EXPECT_TRUE(makeStruct("p", {u8}, {RecordMemberKind::Pad}).getPadded());
   // Interior padding counts too, not just a trailing run.
   EXPECT_TRUE(makeStruct("dpd", {u8, u8, u8},
@@ -156,6 +164,13 @@ TEST_F(RecordMemberKindTest, RejectsPadOnAUnionMember) {
                                     /*packed=*/false, /*padding=*/mlir::Type{},
                                     llvm::ArrayRef<RecordMemberKind>(empty)));
   EXPECT_EQ(diags.count, 1u);
+  // A union variant can be a bit-field access unit, which is not padding.
+  llvm::SmallVector<RecordMemberKind> bitField{RecordMemberKind::BitField};
+  EXPECT_TRUE(
+      UnionType::getChecked(getLoc(), &context, membersRef,
+                            /*packed=*/false, /*padding=*/mlir::Type{},
+                            llvm::ArrayRef<RecordMemberKind>(bitField)));
+  EXPECT_EQ(diags.count, 1u);
 }
 
 TEST_F(RecordMemberKindTest, AnIncompleteRecordIsNotEmptyForTheABI) {
@@ -192,6 +207,12 @@ TEST_F(RecordMemberKindTest, 
KindsTakePartInAnonymousTypeIdentity) {
   // Kinds are provenance rather than layout.
   EXPECT_TRUE(kindsPad.isLayoutIdentical(kindsEmpty));
 
+  auto kindsBitField =
+      StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false,
+                      {RecordMemberKind::BitField, RecordMemberKind::Pad});
+  EXPECT_NE(kindsPad, kindsBitField);
+  EXPECT_TRUE(kindsPad.isLayoutIdentical(kindsBitField));
+
   llvm::SmallVector<mlir::Type> unionMembers{u8, u8};
   llvm::SmallVector<RecordMemberKind> unionEmpty{RecordMemberKind::Data,
                                                  RecordMemberKind::Empty};


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

Reply via email to