[llvm-branch-commits] [clang] [Clang] Fix __builtin_clear_padding() for bitfields (PR #216063)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Aug 13 07:25:31 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: Victor Campos (vhscampos)

<details>
<summary>Changes</summary>

This patch fixes the padding clearing logic for bitfields.

The padding clearing logic for bitfields is wrong for these cases:

 - Over-sized bitfields don't have their padding bits recognised as such.
 - In big endian mode, the occupied bits are wrongly determined because the different byte order is dismissed.

Assisted-by: Codex. Used to help debugging.

---

Patch is 311.48 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/216063.diff


3 Files Affected:

- (modified) clang/lib/CodeGen/CGBuiltin.cpp (+66-4) 
- (modified) clang/test/CodeGen/builtin-clear-padding-codegen.c (+1909-58) 
- (modified) clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp (+2941-74) 


``````````diff
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 13aff2ef13eb9..24b2987923e26 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2672,6 +2672,69 @@ struct PaddingClearer {
     return CGF.getContext().getTypeSize(Ty);
   }
 
+  /// Compute the occupied bit intervals for a bitfield.
+  ///
+  /// In the case of little endian, the occupied bits are always contiguous so a
+  /// single interval is sufficient; whereas in big endian mode, the occupied
+  /// bits are not necessarily contiguous since the byte order is reversed. Care
+  /// is also taken if the bitfield is volatile because the field size and
+  /// offset can be different in this case.
+  SmallVector<BitInterval>
+  computeBitfieldOccupiedIntervals(const RecordDecl *RD, const FieldDecl *Field,
+                                   uint64_t StartBitOffset) const {
+    const CGRecordLayout &Layout = CGF.CGM.getTypes().getCGRecordLayout(RD);
+    const CGBitFieldInfo &Info = Layout.getBitFieldInfo(Field);
+    bool IsLittleEndian = CGF.getTarget().isLittleEndian();
+    bool IsVolatile = Field->getType().isVolatileQualified() &&
+                      Info.VolatileStorageSize != 0 &&
+                      CGF.CGM.getTarget().getABI().starts_with("aapcs");
+
+    uint64_t DeclaredTySizeInBits =
+        getScalarOccupiedSizeInBits(Field->getType());
+    // In case of over-sized bitfields, the number of bits actually occupied
+    // with values is smaller than the declared bitfield width (Info.Size).
+    uint64_t ValueSizeInBits =
+        std::min(static_cast<uint64_t>(Info.Size), DeclaredTySizeInBits);
+
+    const uint64_t Offset = IsVolatile ? Info.VolatileOffset : Info.Offset;
+
+    // This calculation looks different between volatile and non-volatile
+    // because StorageOffset is in bytes, whereas VolatileStorageOffset is in
+    // VolatileStorageSize units.
+    const uint64_t StorageOffset =
+        IsVolatile ? Info.VolatileStorageOffset.getQuantity() *
+                         Info.VolatileStorageSize
+                   : CGF.getContext().toBits(Info.StorageOffset);
+
+    if (IsLittleEndian)
+      return {BitInterval{StartBitOffset + StorageOffset + Offset,
+                          StartBitOffset + StorageOffset + Offset +
+                              ValueSizeInBits}};
+
+    const uint64_t StorageSize =
+        IsVolatile ? Info.VolatileStorageSize : Info.StorageSize;
+    const uint64_t BitsInByte = CGF.getContext().getCharWidth();
+
+    SmallVector<BitInterval> Intervals;
+
+    for (uint64_t Start = Offset, End = Start + ValueSizeInBits; Start < End;
+         Start = (Start + BitsInByte) & (~0UL << 3)) {
+      uint64_t ByteBoundaryStart = Start & (~0UL << 3);
+      uint64_t ByteBoundaryEnd = ByteBoundaryStart + BitsInByte;
+      uint64_t OffsetWithinByte = Start - ByteBoundaryStart;
+      uint64_t SizeWithinByte =
+          std::min(End - Start, BitsInByte - OffsetWithinByte);
+      uint64_t ByteOffset = StorageSize - ByteBoundaryEnd;
+
+      BitInterval BI{StartBitOffset + StorageOffset + ByteOffset +
+                         OffsetWithinByte,
+                     StartBitOffset + StorageOffset + ByteOffset +
+                         OffsetWithinByte + SizeWithinByte};
+      Intervals.push_back(std::move(BI));
+    }
+    return Intervals;
+  }
+
   /// Compute the occupied bit intervals for a BitInt.
   ///
   /// In the case of little endian, the occupied bits are always contiguous so a
@@ -2816,12 +2879,11 @@ struct PaddingClearer {
       if (Field->isUnnamedBitField())
         continue;
 
-      auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex());
       if (Field->isBitField()) {
-        OccuppiedIntervals.push_back(BitInterval{
-            StartBitOffset + FieldOffset,
-            StartBitOffset + FieldOffset + Field->getBitWidthValue()});
+        OccuppiedIntervals.append(
+            computeBitfieldOccupiedIntervals(R, Field, StartBitOffset));
       } else {
+        auto FieldOffset = ASTLayout.getFieldOffset(Field->getFieldIndex());
         Stack.push_back(Data{StartBitOffset + FieldOffset, Field->getType(),
                              /*VisitVirtualBase*/ true});
       }
diff --git a/clang/test/CodeGen/builtin-clear-padding-codegen.c b/clang/test/CodeGen/builtin-clear-padding-codegen.c
index fceb7b623ba6d..7e709f4fcbf98 100644
--- a/clang/test/CodeGen/builtin-clear-padding-codegen.c
+++ b/clang/test/CodeGen/builtin-clear-padding-codegen.c
@@ -3,8 +3,8 @@
 // RUN: %clang_cc1 -std=c11 -triple=x86_64-pc-windows-msvc -emit-llvm -o - %s | FileCheck %s --check-prefix=WINDOWS
 // RUN: %clang_cc1 -std=c11 -triple=armv7a-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=ARM-LE
 // RUN: %clang_cc1 -std=c11 -triple=armebv7a-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=ARM-BE
-// RUN: %clang_cc1 -std=c11 -triple=aarch64-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=ARM-LE
-// RUN: %clang_cc1 -std=c11 -triple=aarch64_be-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=ARM-BE
+// RUN: %clang_cc1 -std=c11 -triple=aarch64-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=AARCH64-LE
+// RUN: %clang_cc1 -std=c11 -triple=aarch64_be-none-eabi -emit-llvm -o - %s | FileCheck %s --check-prefix=AARCH64-BE
 
 struct Empty {};
 
@@ -31,6 +31,38 @@ struct Empty {};
 // WINDOWS-NEXT:   store i8 0, ptr %4, align 1
 // WINDOWS-NEXT:   ret void
 //
+// ARM-LE-LABEL: define dso_local void @testEmpty(
+// ARM-LE-SAME: ptr noundef [[E:%.*]]) #[[ATTR0:[0-9]+]] {
+// ARM-LE-NEXT:  [[ENTRY:.*:]]
+// ARM-LE-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 4
+// ARM-LE-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 4
+// ARM-LE-NEXT:    ret void
+//
+// ARM-BE-LABEL: define dso_local void @testEmpty(
+// ARM-BE-SAME: ptr noundef [[E:%.*]]) #[[ATTR0:[0-9]+]] {
+// ARM-BE-NEXT:  [[ENTRY:.*:]]
+// ARM-BE-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 4
+// ARM-BE-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 4
+// ARM-BE-NEXT:    ret void
+//
+// AARCH64-LE-LABEL: define dso_local void @testEmpty(
+// AARCH64-LE-SAME: ptr noundef [[E:%.*]]) #[[ATTR0:[0-9]+]] {
+// AARCH64-LE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-LE-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-LE-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 8
+// AARCH64-LE-NEXT:    ret void
+//
+// AARCH64-BE-LABEL: define dso_local void @testEmpty(
+// AARCH64-BE-SAME: ptr noundef [[E:%.*]]) #[[ATTR0:[0-9]+]] {
+// AARCH64-BE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-BE-NEXT:    [[E_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-BE-NEXT:    store ptr [[E]], ptr [[E_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[E_ADDR]], align 8
+// AARCH64-BE-NEXT:    ret void
+//
 void testEmpty(struct Empty *e) {
   // Emtpy struct is empty in C in Itanium ABI, no padding
   // Emtpy struct has 4 bytes in C in clang's MSVC ABI
@@ -53,6 +85,38 @@ void testEmpty(struct Empty *e) {
 // WINDOWS-NEXT:   %0 = load ptr, ptr %i.addr, align 8
 // WINDOWS-NEXT:   ret void
 //
+// ARM-LE-LABEL: define dso_local void @testPrimitiveNoPadding(
+// ARM-LE-SAME: ptr noundef [[I:%.*]]) #[[ATTR0]] {
+// ARM-LE-NEXT:  [[ENTRY:.*:]]
+// ARM-LE-NEXT:    [[I_ADDR:%.*]] = alloca ptr, align 4
+// ARM-LE-NEXT:    store ptr [[I]], ptr [[I_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[I_ADDR]], align 4
+// ARM-LE-NEXT:    ret void
+//
+// ARM-BE-LABEL: define dso_local void @testPrimitiveNoPadding(
+// ARM-BE-SAME: ptr noundef [[I:%.*]]) #[[ATTR0]] {
+// ARM-BE-NEXT:  [[ENTRY:.*:]]
+// ARM-BE-NEXT:    [[I_ADDR:%.*]] = alloca ptr, align 4
+// ARM-BE-NEXT:    store ptr [[I]], ptr [[I_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[I_ADDR]], align 4
+// ARM-BE-NEXT:    ret void
+//
+// AARCH64-LE-LABEL: define dso_local void @testPrimitiveNoPadding(
+// AARCH64-LE-SAME: ptr noundef [[I:%.*]]) #[[ATTR0]] {
+// AARCH64-LE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-LE-NEXT:    [[I_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-LE-NEXT:    store ptr [[I]], ptr [[I_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[I_ADDR]], align 8
+// AARCH64-LE-NEXT:    ret void
+//
+// AARCH64-BE-LABEL: define dso_local void @testPrimitiveNoPadding(
+// AARCH64-BE-SAME: ptr noundef [[I:%.*]]) #[[ATTR0]] {
+// AARCH64-BE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-BE-NEXT:    [[I_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-BE-NEXT:    store ptr [[I]], ptr [[I_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[I_ADDR]], align 8
+// AARCH64-BE-NEXT:    ret void
+//
 void testPrimitiveNoPadding(int *i) {
   // This should not clear any padding, since int has no padding.
   __builtin_clear_padding(i);
@@ -86,6 +150,38 @@ void testPrimitiveNoPadding(int *i) {
 // WINDOWS-NEXT:   %0 = load ptr, ptr %ld.addr, align 8
 // WINDOWS-NEXT:   ret void
 //
+// ARM-LE-LABEL: define dso_local void @testPrimitiveLongDouble(
+// ARM-LE-SAME: ptr noundef [[LD:%.*]]) #[[ATTR0]] {
+// ARM-LE-NEXT:  [[ENTRY:.*:]]
+// ARM-LE-NEXT:    [[LD_ADDR:%.*]] = alloca ptr, align 4
+// ARM-LE-NEXT:    store ptr [[LD]], ptr [[LD_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[LD_ADDR]], align 4
+// ARM-LE-NEXT:    ret void
+//
+// ARM-BE-LABEL: define dso_local void @testPrimitiveLongDouble(
+// ARM-BE-SAME: ptr noundef [[LD:%.*]]) #[[ATTR0]] {
+// ARM-BE-NEXT:  [[ENTRY:.*:]]
+// ARM-BE-NEXT:    [[LD_ADDR:%.*]] = alloca ptr, align 4
+// ARM-BE-NEXT:    store ptr [[LD]], ptr [[LD_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[LD_ADDR]], align 4
+// ARM-BE-NEXT:    ret void
+//
+// AARCH64-LE-LABEL: define dso_local void @testPrimitiveLongDouble(
+// AARCH64-LE-SAME: ptr noundef [[LD:%.*]]) #[[ATTR0]] {
+// AARCH64-LE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-LE-NEXT:    [[LD_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-LE-NEXT:    store ptr [[LD]], ptr [[LD_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[LD_ADDR]], align 8
+// AARCH64-LE-NEXT:    ret void
+//
+// AARCH64-BE-LABEL: define dso_local void @testPrimitiveLongDouble(
+// AARCH64-BE-SAME: ptr noundef [[LD:%.*]]) #[[ATTR0]] {
+// AARCH64-BE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-BE-NEXT:    [[LD_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-BE-NEXT:    store ptr [[LD]], ptr [[LD_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[LD_ADDR]], align 8
+// AARCH64-BE-NEXT:    ret void
+//
 void testPrimitiveLongDouble(long double *ld) {
   // padding [10, 15] on x86
   __builtin_clear_padding(ld);
@@ -127,82 +223,254 @@ void testPrimitiveLongDouble(long double *ld) {
 // WINDOWS-NEXT:   ret void
 //
 // ARM-LE-LABEL: define dso_local void @testBitInt(
-// ARM-LE:      [[OBJ:%.*]] = load ptr, ptr {{.*}}, align {{[0-9]+}}
-// ARM-LE-NEXT: [[BYTE12:%.*]] = getelementptr i8, ptr [[OBJ]], i32 12
-// ARM-LE-NEXT: [[VALUE:%.*]] = load i8, ptr [[BYTE12]], align 4
-// ARM-LE-NEXT: [[MASKED:%.*]] = and i8 [[VALUE]], 1
-// ARM-LE-NEXT: store i8 [[MASKED]], ptr [[BYTE12]], align 4
-// ARM-LE-NEXT: [[BYTE13:%.*]] = getelementptr i8, ptr [[OBJ]], i32 13
-// ARM-LE-NEXT: store i8 0, ptr [[BYTE13]], align 1
-// ARM-LE-NEXT: [[BYTE14:%.*]] = getelementptr i8, ptr [[OBJ]], i32 14
-// ARM-LE-NEXT: store i8 0, ptr [[BYTE14]], align 2
-// ARM-LE-NEXT: [[BYTE15:%.*]] = getelementptr i8, ptr [[OBJ]], i32 15
-// ARM-LE-NEXT: store i8 0, ptr [[BYTE15]], align 1
-// ARM-LE-NEXT: ret void
+// ARM-LE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// ARM-LE-NEXT:  [[ENTRY:.*:]]
+// ARM-LE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 4
+// ARM-LE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 12
+// ARM-LE-NEXT:    [[TMP2:%.*]] = load i8, ptr [[TMP1]], align 4
+// ARM-LE-NEXT:    [[TMP3:%.*]] = and i8 [[TMP2]], 1
+// ARM-LE-NEXT:    store i8 [[TMP3]], ptr [[TMP1]], align 4
+// ARM-LE-NEXT:    [[TMP4:%.*]] = getelementptr i8, ptr [[TMP0]], i32 13
+// ARM-LE-NEXT:    store i8 0, ptr [[TMP4]], align 1
+// ARM-LE-NEXT:    [[TMP5:%.*]] = getelementptr i8, ptr [[TMP0]], i32 14
+// ARM-LE-NEXT:    store i8 0, ptr [[TMP5]], align 2
+// ARM-LE-NEXT:    [[TMP6:%.*]] = getelementptr i8, ptr [[TMP0]], i32 15
+// ARM-LE-NEXT:    store i8 0, ptr [[TMP6]], align 1
+// ARM-LE-NEXT:    ret void
 //
 // ARM-BE-LABEL: define dso_local void @testBitInt(
-// ARM-BE:      [[OBJ:%.*]] = load ptr, ptr {{.*}}, align {{[0-9]+}}
-// ARM-BE-NEXT: [[BYTE0:%.*]] = getelementptr i8, ptr [[OBJ]], i32 0
-// ARM-BE-NEXT: store i8 0, ptr [[BYTE0]], align {{[0-9]+}}
-// ARM-BE-NEXT: [[BYTE1:%.*]] = getelementptr i8, ptr [[OBJ]], i32 1
-// ARM-BE-NEXT: store i8 0, ptr [[BYTE1]], align 1
-// ARM-BE-NEXT: [[BYTE2:%.*]] = getelementptr i8, ptr [[OBJ]], i32 2
-// ARM-BE-NEXT: store i8 0, ptr [[BYTE2]], align 2
-// ARM-BE-NEXT: [[BYTE3:%.*]] = getelementptr i8, ptr [[OBJ]], i32 3
-// ARM-BE-NEXT: [[VALUE:%.*]] = load i8, ptr [[BYTE3]], align 1
-// ARM-BE-NEXT: [[MASKED:%.*]] = and i8 [[VALUE]], 1
-// ARM-BE-NEXT: store i8 [[MASKED]], ptr [[BYTE3]], align 1
-// ARM-BE-NEXT: ret void
+// ARM-BE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// ARM-BE-NEXT:  [[ENTRY:.*:]]
+// ARM-BE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 4
+// ARM-BE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 0
+// ARM-BE-NEXT:    store i8 0, ptr [[TMP1]], align 8
+// ARM-BE-NEXT:    [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i32 1
+// ARM-BE-NEXT:    store i8 0, ptr [[TMP2]], align 1
+// ARM-BE-NEXT:    [[TMP3:%.*]] = getelementptr i8, ptr [[TMP0]], i32 2
+// ARM-BE-NEXT:    store i8 0, ptr [[TMP3]], align 2
+// ARM-BE-NEXT:    [[TMP4:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// ARM-BE-NEXT:    [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1
+// ARM-BE-NEXT:    [[TMP6:%.*]] = and i8 [[TMP5]], 1
+// ARM-BE-NEXT:    store i8 [[TMP6]], ptr [[TMP4]], align 1
+// ARM-BE-NEXT:    ret void
+//
+// AARCH64-LE-LABEL: define dso_local void @testBitInt(
+// AARCH64-LE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// AARCH64-LE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-LE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-LE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 12
+// AARCH64-LE-NEXT:    [[TMP2:%.*]] = load i8, ptr [[TMP1]], align 4
+// AARCH64-LE-NEXT:    [[TMP3:%.*]] = and i8 [[TMP2]], 1
+// AARCH64-LE-NEXT:    store i8 [[TMP3]], ptr [[TMP1]], align 4
+// AARCH64-LE-NEXT:    [[TMP4:%.*]] = getelementptr i8, ptr [[TMP0]], i32 13
+// AARCH64-LE-NEXT:    store i8 0, ptr [[TMP4]], align 1
+// AARCH64-LE-NEXT:    [[TMP5:%.*]] = getelementptr i8, ptr [[TMP0]], i32 14
+// AARCH64-LE-NEXT:    store i8 0, ptr [[TMP5]], align 2
+// AARCH64-LE-NEXT:    [[TMP6:%.*]] = getelementptr i8, ptr [[TMP0]], i32 15
+// AARCH64-LE-NEXT:    store i8 0, ptr [[TMP6]], align 1
+// AARCH64-LE-NEXT:    ret void
+//
+// AARCH64-BE-LABEL: define dso_local void @testBitInt(
+// AARCH64-BE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// AARCH64-BE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-BE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-BE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 0
+// AARCH64-BE-NEXT:    store i8 0, ptr [[TMP1]], align 16
+// AARCH64-BE-NEXT:    [[TMP2:%.*]] = getelementptr i8, ptr [[TMP0]], i32 1
+// AARCH64-BE-NEXT:    store i8 0, ptr [[TMP2]], align 1
+// AARCH64-BE-NEXT:    [[TMP3:%.*]] = getelementptr i8, ptr [[TMP0]], i32 2
+// AARCH64-BE-NEXT:    store i8 0, ptr [[TMP3]], align 2
+// AARCH64-BE-NEXT:    [[TMP4:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// AARCH64-BE-NEXT:    [[TMP5:%.*]] = load i8, ptr [[TMP4]], align 1
+// AARCH64-BE-NEXT:    [[TMP6:%.*]] = and i8 [[TMP5]], 1
+// AARCH64-BE-NEXT:    store i8 [[TMP6]], ptr [[TMP4]], align 1
+// AARCH64-BE-NEXT:    ret void
 //
 void testBitInt(_BitInt(97) *bi) {
   // Storage is widened to 128 bits; clear bits [97, 128).
   __builtin_clear_padding(bi);
 }
 
+// LINUX-LABEL: define dso_local void @testBitIntByteAligned(
+// LINUX-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// LINUX-NEXT:  [[ENTRY:.*:]]
+// LINUX-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// LINUX-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// LINUX-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// LINUX-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// LINUX-NEXT:    store i8 0, ptr [[TMP1]], align 1
+// LINUX-NEXT:    ret void
+//
+// WINDOWS-LABEL: define dso_local void @testBitIntByteAligned(
+// WINDOWS-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// WINDOWS-NEXT:  [[ENTRY:.*:]]
+// WINDOWS-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// WINDOWS-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// WINDOWS-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// WINDOWS-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// WINDOWS-NEXT:    store i8 0, ptr [[TMP1]], align 1
+// WINDOWS-NEXT:    ret void
+//
 // ARM-LE-LABEL: define dso_local void @testBitIntByteAligned(
-// ARM-LE:      [[OBJ:%.*]] = load ptr, ptr {{.*}}, align {{[0-9]+}}
-// ARM-LE-NEXT: [[BYTE3:%.*]] = getelementptr i8, ptr [[OBJ]], i32 3
-// ARM-LE-NEXT: store i8 0, ptr [[BYTE3]], align 1
-// ARM-LE-NEXT: ret void
+// ARM-LE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// ARM-LE-NEXT:  [[ENTRY:.*:]]
+// ARM-LE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 4
+// ARM-LE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 4
+// ARM-LE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// ARM-LE-NEXT:    store i8 0, ptr [[TMP1]], align 1
+// ARM-LE-NEXT:    ret void
 //
 // ARM-BE-LABEL: define dso_local void @testBitIntByteAligned(
-// ARM-BE:      [[OBJ:%.*]] = load ptr, ptr {{.*}}, align {{[0-9]+}}
-// ARM-BE-NEXT: [[BYTE0:%.*]] = getelementptr i8, ptr [[OBJ]], i32 0
-// ARM-BE-NEXT: store i8 0, ptr [[BYTE0]], align 4
-// ARM-BE-NEXT: ret void
+// ARM-BE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// ARM-BE-NEXT:  [[ENTRY:.*:]]
+// ARM-BE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 4
+// ARM-BE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 4
+// ARM-BE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 0
+// ARM-BE-NEXT:    store i8 0, ptr [[TMP1]], align 4
+// ARM-BE-NEXT:    ret void
+//
+// AARCH64-LE-LABEL: define dso_local void @testBitIntByteAligned(
+// AARCH64-LE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// AARCH64-LE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-LE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-LE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// AARCH64-LE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 3
+// AARCH64-LE-NEXT:    store i8 0, ptr [[TMP1]], align 1
+// AARCH64-LE-NEXT:    ret void
+//
+// AARCH64-BE-LABEL: define dso_local void @testBitIntByteAligned(
+// AARCH64-BE-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// AARCH64-BE-NEXT:  [[ENTRY:.*:]]
+// AARCH64-BE-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// AARCH64-BE-NEXT:    store ptr [[BI]], ptr [[BI_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[BI_ADDR]], align 8
+// AARCH64-BE-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i32 0
+// AARCH64-BE-NEXT:    store i8 0, ptr [[TMP1]], align 4
+// AARCH64-BE-NEXT:    ret void
 //
 void testBitIntByteAligned(_BitInt(24) *bi) {
   // Storage is widened to 32 bits; clear the leading or trailing byte.
   __builtin_clear_padding(bi);
 }
 
+// LINUX-LABEL: define dso_local void @testVolatileBitInt(
+// LINUX-SAME: ptr noundef [[BI:%.*]]) #[[ATTR0]] {
+// LINUX-NEXT:  [[ENTRY:.*:]]
+// LINUX-NEXT:    [[BI_ADDR:%.*]] = alloca ptr, align 8
+// LI...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/216063


More information about the llvm-branch-commits mailing list