Author: Victor Campos
Date: 2026-09-03T17:04:41+01:00
New Revision: d8217655d8c338927ef37462b561d1a61ac7f64a

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

LOG: [Clang] Fix BitInt padding clearing on big-endian targets (#215809)

This patch fixes the padding clearing logic of `_BitInt`s.

Before this patch, the clearing logic assumed little endian. But the
memory layout of BitInts differs between little and big endian:

- In LE, the occupied bits start from the lowest address and go on
contiguously up until the BitInt's declared size. The padding bits then
start from that point and go contiguously until the end of the storage
unit.
- In BE, since the byte order is reversed, the occupied bit interval is
not contiguous if the storage unit is larger than the BitInt's size.

Therefore, the logic must tell the two cases apart and perform the
calculations accordingly.

Added: 
    

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/AST/ASTContext.cpp
    clang/test/CodeGen/builtin-clear-padding-codegen.c
    clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 64bc17a9ab545..1e4e1ea684501 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -501,6 +501,8 @@ features cannot lower the translation-unit ABI level;
   such as when the call is used as an `auto` non-type template argument.
 - Fixed a crash in ``__builtin_dump_struct`` when ``-Werror`` promotes
   format warnings to errors. (#GH211943)
+- Fixed a wrong code generation in `__builtin_clear_padding` wherein the
+  wrong bits of the `_BitInt` type were cleared in big-endian mode.
 
 #### Bug Fixes to Attribute Support
 

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b7e771595e86e..6aef972dc5ceb 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15839,6 +15839,11 @@ struct PaddingCalculator {
       return;
     }
 
+    if (const auto *BITy = D.Ty->getAs<BitIntType>()) {
+      VisitBitInt(BITy, D.StartBitOffset);
+      return;
+    }
+
     uint64_t SizeBit = getScalarOccupiedSizeInBits(D.Ty);
     OccuppiedIntervals.push_back(
         ASTContext::BitInterval{D.StartBitOffset, D.StartBitOffset + SizeBit});
@@ -15949,6 +15954,47 @@ struct PaddingCalculator {
         ASTContext::BitInterval{StartBitOffset, StartBitOffset + SizeBit});
   }
 
+  /// Compute the occupied bit intervals for a BitInt.
+  ///
+  /// In the case of little endian, the occupied bits are always contiguous so 
a
+  /// single interval is sufficient. However in big endian, the intervals can 
be
+  /// disjoint.
+  void VisitBitInt(const BitIntType *Ty, uint64_t StartBitOffset) {
+    const uint64_t OccupiedSizeInBits = Ty->getNumBits();
+
+    if (Ctx.getTargetInfo().isLittleEndian()) {
+      OccuppiedIntervals.push_back(
+          {StartBitOffset, StartBitOffset + OccupiedSizeInBits});
+      return;
+    }
+
+    // In big endian mode, the layout of a BitInt in memory has its bytes in
+    // reverse order, and is pictured in this order:
+    //   1. Fully padding bytes.
+    //   2. One partially occupied byte, with padding at the most significant
+    //   bits. ("remaining occupied bits")
+    //   3. A sequence of fully occupied bytes up until the end of the storage.
+    const uint64_t StorageSizeInBits = Ctx.getTypeSize(Ty);
+    const uint64_t CharWidth = Ctx.getCharWidth();
+    const uint64_t NumFullyPaddingBytes =
+        (StorageSizeInBits - OccupiedSizeInBits) / CharWidth;
+    const uint64_t NumFullyOccupiedBytes = OccupiedSizeInBits / CharWidth;
+    const uint64_t NumRemainingOccupiedBits = OccupiedSizeInBits % CharWidth;
+
+    // Partially occupied byte
+    if (NumRemainingOccupiedBits > 0)
+      OccuppiedIntervals.push_back(
+          {StartBitOffset + NumFullyPaddingBytes * CharWidth,
+           StartBitOffset + NumFullyPaddingBytes * CharWidth +
+               NumRemainingOccupiedBits});
+
+    // Fully occupied bytes
+    if (NumFullyOccupiedBytes > 0)
+      OccuppiedIntervals.push_back({StartBitOffset + StorageSizeInBits -
+                                        NumFullyOccupiedBytes * CharWidth,
+                                    StartBitOffset + StorageSizeInBits});
+  }
+
   void MergeOccuppiedIntervals() {
     std::sort(OccuppiedIntervals.begin(), OccuppiedIntervals.end(),
               [](const ASTContext::BitInterval &lhs,

diff  --git a/clang/test/CodeGen/builtin-clear-padding-codegen.c 
b/clang/test/CodeGen/builtin-clear-padding-codegen.c
index 0564c52dd2a49..fceb7b623ba6d 100644
--- a/clang/test/CodeGen/builtin-clear-padding-codegen.c
+++ b/clang/test/CodeGen/builtin-clear-padding-codegen.c
@@ -1,6 +1,10 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 5
 // RUN: %clang_cc1 -std=c11 -triple=x86_64-linux-gnu -emit-llvm -o - %s | 
FileCheck %s --check-prefix=LINUX
 // 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
 
 struct Empty {};
 
@@ -122,11 +126,88 @@ void testPrimitiveLongDouble(long double *ld) {
 // WINDOWS-NEXT:   store i8 0, ptr %6, align 1
 // 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-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
+//
 void testBitInt(_BitInt(97) *bi) {
   // Storage is widened to 128 bits; clear bits [97, 128).
   __builtin_clear_padding(bi);
 }
 
+// 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-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
+//
+void testBitIntByteAligned(_BitInt(24) *bi) {
+  // Storage is widened to 32 bits; clear the leading or trailing byte.
+  __builtin_clear_padding(bi);
+}
+
+// ARM-LE-LABEL: define dso_local void @testVolatileBitInt(
+// 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-BE-LABEL: define dso_local void @testVolatileBitInt(
+// 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
+//
+void testVolatileBitInt(volatile _BitInt(97) *bi) {
+  __builtin_clear_padding(bi);
+}
+
 
 // LINUX-LABEL: define dso_local void @testPrimitiveComplexLongDouble(
 // LINUX-SAME: ptr noundef [[C:%.*]]) #[[ATTR0]] {

diff  --git a/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp 
b/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp
index 155ffc441df76..2de4645fa62a8 100644
--- a/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp
+++ b/clang/test/CodeGenCXX/builtin-clear-padding-codegen.cpp
@@ -1,6 +1,10 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 5
 // RUN: %clang_cc1 -std=c++20 -triple=x86_64-linux-gnu -emit-llvm -o - %s | 
FileCheck %s --check-prefix=LINUX
 // RUN: %clang_cc1 -std=c++20 -triple=x86_64-pc-windows-msvc -emit-llvm -o - 
%s | FileCheck %s --check-prefix=WINDOWS
+// RUN: %clang_cc1 -std=c++20 -triple=armv7a-none-eabi -emit-llvm -o - %s | 
FileCheck %s --check-prefix=ARM-LE
+// RUN: %clang_cc1 -std=c++20 -triple=armebv7a-none-eabi -emit-llvm -o - %s | 
FileCheck %s --check-prefix=ARM-BE
+// RUN: %clang_cc1 -std=c++20 -triple=aarch64-none-eabi -emit-llvm -o - %s | 
FileCheck %s --check-prefix=ARM-LE
+// RUN: %clang_cc1 -std=c++20 -triple=aarch64_be-none-eabi -emit-llvm -o - %s 
| FileCheck %s --check-prefix=ARM-BE
 
 
 struct Empty {};
@@ -118,11 +122,88 @@ void testPrimitiveLongDouble(long double *ld) {
 // WINDOWS-NEXT:   store i8 0, ptr %6, align 1
 // WINDOWS-NEXT:   ret void
 //
+// ARM-LE-LABEL: define dso_local void @_Z10testBitIntPDB97_(
+// 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-BE-LABEL: define dso_local void @_Z10testBitIntPDB97_(
+// 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
+//
 void testBitInt(_BitInt(97) *bi) {
   // Storage is widened to 128 bits; clear bits [97, 128).
   __builtin_clear_padding(bi);
 }
 
+// ARM-LE-LABEL: define dso_local void @_Z21testBitIntByteAlignedPDB24_(
+// 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-BE-LABEL: define dso_local void @_Z21testBitIntByteAlignedPDB24_(
+// 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
+//
+void testBitIntByteAligned(_BitInt(24) *bi) {
+  // Storage is widened to 32 bits; clear the leading or trailing byte.
+  __builtin_clear_padding(bi);
+}
+
+// ARM-LE-LABEL: define dso_local void @_Z18testVolatileBitIntPVDB97_(
+// 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-BE-LABEL: define dso_local void @_Z18testVolatileBitIntPVDB97_(
+// 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
+//
+void testVolatileBitInt(volatile _BitInt(97) *bi) {
+  __builtin_clear_padding(bi);
+}
+
 
 // LINUX-LABEL: define dso_local void @_Z30testPrimitiveComplexLongDoublePCe(
 // LINUX-SAME: ptr noundef [[C:%.*]]) #[[ATTR0]] {


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

Reply via email to