https://github.com/vhscampos updated 
https://github.com/llvm/llvm-project/pull/215809

>From 9ad190e2a75e407951aa97f0b16658e8a801ec5a Mon Sep 17 00:00:00 2001
From: Victor Campos <[email protected]>
Date: Wed, 5 Aug 2026 16:05:21 +0100
Subject: [PATCH] [Clang] Fix BitInt padding clearing on big-endian targets

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.
---
 clang/lib/AST/ASTContext.cpp                  | 46 +++++++++++
 .../CodeGen/builtin-clear-padding-codegen.c   | 81 +++++++++++++++++++
 .../builtin-clear-padding-codegen.cpp         | 81 +++++++++++++++++++
 3 files changed, 208 insertions(+)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 35a2a0b131a3f..51bc3cc3c4b45 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15850,6 +15850,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});
@@ -15960,6 +15965,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 01475ca5154a3..bd52b0e10662e 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