Author: Phoebe Wang
Date: 2026-09-07T01:24:25Z
New Revision: 91d3532e2261e168e85ebcf5c5112419b47ef530

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

LOG: [Clang][X86] Add Clang23Compat for __int128 bit-field ABI change (#220788)

Follow up of #216777.

Assisted-by: Claude Opus 4.8

Added: 
    clang/test/CodeGen/X86/x86_64-unnamed-bitfield-abi-compat.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/include/clang/Basic/ABIVersions.def
    clang/lib/CodeGen/CodeGenModule.cpp
    clang/lib/CodeGen/Targets/X86.cpp
    llvm/include/llvm/ABI/TargetInfo.h
    llvm/lib/ABI/Targets/X86.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a43ed2b924622..d9ac67d1a2824 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -101,12 +101,13 @@ features cannot lower the translation-unit ABI level;
 - On MIPS N32/N64, an `__int128` now correctly start in an even-numbered 
register
   or 16-byte aligned stack slot, matching GCC.
 
-- On x86-64 System V, a non-zero-width unnamed bit-field now classifies the
-  eightbytes it occupies as INTEGER, like a named bit-field, matching GCC.
-  Aggregates where this changes the classification may be passed or returned
-  
diff erently -- a struct holding a run of `__int128` bit-fields, for example,
-  now travels in the two integer registers the ABI assigns it. This also fixes
-  a crash when such a struct was passed or returned. (#GH202205)
+- Except on PlayStation, on x86-64 System V a non-zero-width unnamed bit-field
+  now classifies the eightbytes it occupies as INTEGER, like a named bit-field,
+  matching GCC. Aggregates where this changes the classification may be passed
+  or returned 
diff erently -- a struct holding a run of `__int128` bit-fields,
+  for example, now travels in the two integer registers the ABI assigns it.
+  This also fixes a crash when such a struct was passed or returned.
+  `-fclang-abi-compat=23` restores the previous behavior. (#GH202205)
 
 ### AST Dumping Potentially Breaking Changes
 

diff  --git a/clang/include/clang/Basic/ABIVersions.def 
b/clang/include/clang/Basic/ABIVersions.def
index e1017a1547773..3c434da91bfab 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -161,6 +161,12 @@ ABI_VER_MAJOR(22)
 ///   - On MIPS N32/N64, always pass a `_Complex float` or `_Complex double`
 ///     argument as its two parts, one floating-point register each, instead of
 ///     packing it into integer registers once there is no room for both.
+///   - On x86-64 System V, skip every unnamed bit-field as padding when
+///     classifying an aggregate, instead of treating a non-zero-width unnamed
+///     bit-field as INTEGER storage like a named one the way GCC does. (This
+///     faithfully reproduces Clang 23, including its crash on aggregates such
+///     as a run of `__int128` bit-fields, where skipping the unnamed field
+///     leaves part of a wider access unit unclassified.)
 ABI_VER_MAJOR(23)
 
 /// Conform to the underlying platform's C and C++ ABIs as closely as we can.

diff  --git a/clang/lib/CodeGen/CodeGenModule.cpp 
b/clang/lib/CodeGen/CodeGenModule.cpp
index ca110afe9edc8..8890d2a4b1b7e 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -434,6 +434,8 @@ CodeGenModule::getLLVMABITargetInfo(llvm::abi::TypeBuilder 
&TB) {
         Compat > LangOptions::ClangABI::Ver20 && !T.isPS();
     CompatInfo.Clang11Compat =
         Compat <= LangOptions::ClangABI::Ver11 || T.isPS();
+    CompatInfo.ClassifyUnnamedBitFields =
+        Compat > LangOptions::ClangABI::Ver23 && !T.isPS();
 
     bool Has64BitPointers = getTarget().getPointerWidth(LangAS::Default) == 64;
 

diff  --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index 90028b64f8efa..14fe5ffae8372 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2204,6 +2204,10 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
     bool UseClang11Compat = getContext().getLangOpts().isCompatibleWith(
                                 LangOptions::ClangABI::Ver11) ||
                             getContext().getTargetInfo().getTriple().isPS();
+    bool ClassifyUnnamedBitFields =
+        getContext().getLangOpts().getClangABICompat() >
+            LangOptions::ClangABI::Ver23 &&
+        !getContext().getTargetInfo().getTriple().isPS();
     bool IsUnion = RT->isUnionType() && !UseClang11Compat;
 
     for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end();
@@ -2211,9 +2215,13 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
       uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx);
       bool BitField = i->isBitField();
 
-      // Ignore zero-length bit-fields. Other unnamed bit-fields are real
-      // storage and classify like named ones, matching GCC.
-      if (BitField && i->isZeroLengthBitField())
+      // Ignore padding bit-fields. Normally only zero-length bit-fields are
+      // padding, but under -fclang-abi-compat=23 every unnamed bit-field is,
+      // faithfully reproducing Clang 23 -- including its crash on aggregates
+      // where skipping one leaves part of a wider access unit (e.g. an
+      // __int128 bit-field run) unclassified.
+      if (BitField && (ClassifyUnnamedBitFields ? i->isZeroLengthBitField()
+                                                : i->isUnnamedBitField()))
         continue;
 
       // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger than
@@ -2254,7 +2262,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
       // structure to be passed in memory even if unaligned, and
       // therefore they can straddle an eightbyte.
       if (BitField) {
-        assert(!i->isZeroLengthBitField());
+        assert(ClassifyUnnamedBitFields ? !i->isZeroLengthBitField()
+                                        : !i->isUnnamedBitField());
         uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx);
         uint64_t Size = i->getBitWidthValue();
 

diff  --git a/clang/test/CodeGen/X86/x86_64-unnamed-bitfield-abi-compat.c 
b/clang/test/CodeGen/X86/x86_64-unnamed-bitfield-abi-compat.c
new file mode 100644
index 0000000000000..a1ead52af0396
--- /dev/null
+++ b/clang/test/CodeGen/X86/x86_64-unnamed-bitfield-abi-compat.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | \
+// RUN:   FileCheck %s -check-prefix=NEW
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm 
-fclang-abi-compat=23 %s -o - | \
+// RUN:   FileCheck %s -check-prefix=COMPAT
+// RUN: %clang_cc1 -triple x86_64-scei-ps4 -emit-llvm %s -o - | \
+// RUN:   FileCheck %s -check-prefix=COMPAT
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -emit-llvm %s -o - | \
+// RUN:   FileCheck %s -check-prefix=COMPAT
+
+// A non-zero-width unnamed bit-field classifies the eightbyte it occupies as
+// INTEGER like a named one, matching GCC, so this struct travels in two 
integer
+// registers. Under -fclang-abi-compat=23 (and on PlayStation) the unnamed
+// bit-field is padding, so the low eightbyte stays NO_CLASS and only the high
+// eightbyte is passed.
+struct s {
+  long : 64;
+  long a;
+};
+
+// NEW-LABEL:    define{{.*}} { i64, i64 } @get()
+// COMPAT-LABEL: define{{.*}} i64 @get()
+struct s get(void) {
+  return (struct s){0};
+}
+
+// NEW-LABEL:    define{{.*}} void @put(i64 %a.coerce0, i64 %a.coerce1)
+// COMPAT-LABEL: define{{.*}} void @put(i64 %a.coerce)
+void put(struct s a) {
+}
+
+// Note: -fclang-abi-compat=23 faithfully reproduces Clang 23, so it also
+// reproduces Clang 23's crash on a run of __int128 bit-fields (skipping the
+// unnamed field leaves half of the i128 access unit unclassified). That shape
+// is deliberately not exercised here; the corrected classification is covered
+// in x86_64-arguments.c.

diff  --git a/llvm/include/llvm/ABI/TargetInfo.h 
b/llvm/include/llvm/ABI/TargetInfo.h
index 8132de140064a..ea621597e5b22 100644
--- a/llvm/include/llvm/ABI/TargetInfo.h
+++ b/llvm/include/llvm/ABI/TargetInfo.h
@@ -46,11 +46,12 @@ struct ABICompatInfo {
   bool ClassifyIntegerMMXAsSSE : 1;
   bool HonorsRevision98 : 1;
   bool Clang11Compat : 1;
+  bool ClassifyUnnamedBitFields : 1;
 
   ABICompatInfo()
       : PassInt128VectorsInMem(true), ReturnCXXRecordGreaterThan128InMem(true),
         ClassifyIntegerMMXAsSSE(true), HonorsRevision98(true),
-        Clang11Compat(true) {}
+        Clang11Compat(true), ClassifyUnnamedBitFields(true) {}
 
   /// Return flags matching the ABI emitted by the given Clang major version.
   // TODO: fill in per-version flag overrides.

diff  --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp
index 88bfb8ad453cc..fd7a5e15b3548 100644
--- a/llvm/lib/ABI/Targets/X86.cpp
+++ b/llvm/lib/ABI/Targets/X86.cpp
@@ -557,9 +557,12 @@ void X86_64TargetInfo::classify(const Type *T, uint64_t 
OffsetBase, Class &Lo,
       uint64_t Offset = OffsetBase + Field.OffsetInBits;
       bool BitField = Field.IsBitField;
 
-      // Ignore zero-length bit-fields. Other unnamed bit-fields are real
-      // storage and classify like named ones, matching GCC.
-      if (BitField && Field.BitFieldWidth == 0)
+      // Ignore padding bit-fields. Normally only zero-length bit-fields are
+      // padding, but under Clang 23 compatibility every unnamed bit-field is,
+      // faithfully reproducing Clang 23.
+      if (BitField && (getABICompatInfo().ClassifyUnnamedBitFields
+                           ? Field.BitFieldWidth == 0
+                           : Field.IsUnnamedBitfield))
         continue;
 
       if (Size > 128 &&


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

Reply via email to