https://github.com/freaknbigpanda updated 
https://github.com/llvm/llvm-project/pull/187814

>From c6e5b660cc6a0fa6a92fdf98701b0214271f610e Mon Sep 17 00:00:00 2001
From: Benjamin Luke <[email protected]>
Date: Fri, 20 Mar 2026 15:21:45 -0700
Subject: [PATCH 1/3] [clang][X86] Ignore ABI-empty fields and base classes in
 X86_64 SysV ABI classification algorithm

Previously if there were empty record fields or base classes the SysV 
classification algorithm would incorrectly pass and return indirect. This adds 
code to correct the ABI and maintain the previous behaviour for PlayStation.
---
 clang/lib/CodeGen/Targets/X86.cpp             |  6 ++-
 .../x86_64-empty-base-vector-abi.cpp          | 46 +++++++++++++++++++
 2 files changed, 50 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp

diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index e6203db8bc245..a32615c79e1d9 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -2072,6 +2072,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
         assert(!I.isVirtual() && !I.getType()->isDependentType() &&
                "Unexpected base class!");
         const auto *Base = I.getType()->castAsCXXRecordDecl();
+        bool IsEmptyBase = isEmptyRecord(getContext(), I.getType(), true);
         // Classify this field.
         //
         // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate exceeds a
@@ -2083,7 +2084,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
         classify(I.getType(), Offset, FieldLo, FieldHi, isNamedArg);
         Lo = merge(Lo, FieldLo);
         Hi = merge(Hi, FieldHi);
-        if (returnCXXRecordGreaterThan128InMem() &&
+        if (returnCXXRecordGreaterThan128InMem() && !IsEmptyBase &&
             (Size > 128 && (Size != getContext().getTypeSize(I.getType()) ||
                             Size > getNativeVectorSizeForAVXABI(AVXLevel)))) {
           // The only case a 256(or 512)-bit wide vector could be used to 
return
@@ -2122,7 +2123,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
       //
       // FIXME: Extended the Lo and Hi logic properly to work for size wider
       // than 128.
-      if (Size > 128 &&
+      bool IsEmptyField = isEmptyField(getContext(), *i, true);
+      if (Size > 128 && (!IsEmptyField || getTarget().getTriple().isPS()) &&
           ((!IsUnion && Size != getContext().getTypeSize(i->getType())) ||
            Size > getNativeVectorSizeForAVXABI(AVXLevel))) {
         Lo = Memory;
diff --git a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp 
b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
new file mode 100644
index 0000000000000..bfd3f66bc1160
--- /dev/null
+++ b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - 
%s -target-feature +avx | FileCheck %s --check-prefix=SYSV
+// RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -o - %s 
-target-feature +avx | FileCheck %s --check-prefix=PS
+
+typedef unsigned long long v4ull __attribute__((vector_size(32)));
+
+struct EmptyBase {};
+
+struct EmptyBaseThenVector : EmptyBase {
+   v4ull Data;
+};
+
+EmptyBaseThenVector return_empty_base_then_vector() {
+  return {};
+}
+
+unsigned long long pass_empty_base_then_vector(EmptyBaseThenVector X) {
+  return X.Data[0];
+}
+
+struct EmptyField {};
+
+struct EmptyFieldThenVector {
+  [[no_unique_address]] EmptyField E;
+  v4ull Data;
+};
+
+EmptyFieldThenVector return_empty_field_then_vector() {
+  return {};
+}
+
+unsigned long long pass_empty_field_then_vector(EmptyFieldThenVector X) {
+  return X.Data[0];
+}
+
+// Both the empty base case and empty-field case are now passed in register as 
per SysV spec
+// SYSV-LABEL: define dso_local <4 x i64> @_Z29return_empty_base_then_vectorv()
+// SYSV-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(<4 x i64> %X.coerce)
+// SYSV-LABEL: define dso_local <4 x i64> 
@_Z30return_empty_field_then_vectorv()
+// SYSV-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(<4 x i64> %X.coerce)
+
+// PlayStation keeps the legacy ABI behavior here: the empty-base case was 
always direct for PlayStation ABI
+// while the [[no_unique_address]] empty-field case is still indirect even 
after this change.
+// PS-LABEL: define dso_local <4 x i64> @_Z29return_empty_base_then_vectorv()
+// PS-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(<4 x i64> %X.coerce)
+// PS-LABEL: define dso_local void @_Z30return_empty_field_then_vectorv(ptr 
dead_on_unwind noalias writable sret(%struct.EmptyFieldThenVector) align 32 
%agg.result)
+// PS-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)

>From 56fd2a0830bab5012724a3f2e0572a504b60a480 Mon Sep 17 00:00:00 2001
From: Benjamin Luke <[email protected]>
Date: Fri, 3 Apr 2026 09:17:35 -0700
Subject: [PATCH 2/3] Comments by Phoebe

---
 clang/include/clang/Basic/ABIVersions.def     |  6 ++++++
 clang/lib/CodeGen/Targets/X86.cpp             | 20 ++++++++++++++++---
 .../x86_64-empty-base-vector-abi.cpp          |  7 +++++++
 3 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Basic/ABIVersions.def 
b/clang/include/clang/Basic/ABIVersions.def
index 92edcd830f031..fbfdbc0830bfa 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -135,6 +135,12 @@ ABI_VER_MAJOR(20)
 ///    operator delete.
 ABI_VER_MAJOR(21)
 
+/// Attempt to be ABI-compatible with code generated by Clang 22.0.x.
+/// This causes clang to:
+///   - Not ignore ABI-empty CXX fields and base classes when classifying 
+///    x86_64 SysV C++ record arguments and returns.
+ABI_VER_MAJOR(22)
+
 /// Conform to the underlying platform's C and C++ ABIs as closely as we can.
 ABI_VER_LATEST(Latest)
 
diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index a32615c79e1d9..b4e84f3d5fcc2 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -1359,6 +1359,18 @@ class X86_64ABIInfo : public ABIInfo {
     return true;
   }
 
+  bool properlyIgnoreEmptyCXXFieldsAndBases() const {
+    // properly ignore empty CXX Fields and Bases in ABI classification
+    // algorithm CLang <= 22.0 did not do this, and PlayStation does not do
+    // this.
+    if (getContext().getLangOpts().getClangABICompat() <=
+            LangOptions::ClangABI::Ver22 ||
+        getTarget().getTriple().isPS())
+      return false;
+
+    return true;
+  }
+
   X86AVXABILevel AVXLevel;
   // Some ABIs (e.g. X32 ABI and Native Client OS) use 32 bit pointers on
   // 64-bit hardware.
@@ -2072,7 +2084,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
         assert(!I.isVirtual() && !I.getType()->isDependentType() &&
                "Unexpected base class!");
         const auto *Base = I.getType()->castAsCXXRecordDecl();
-        bool IsEmptyBase = isEmptyRecord(getContext(), I.getType(), true);
+        bool IsEmptyBase = isEmptyRecord(getContext(), I.getType(), true) &&
+                           properlyIgnoreEmptyCXXFieldsAndBases();
         // Classify this field.
         //
         // AMD64-ABI 3.2.3p2: Rule 3. If the size of the aggregate exceeds a
@@ -2123,8 +2136,9 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t 
OffsetBase, Class &Lo,
       //
       // FIXME: Extended the Lo and Hi logic properly to work for size wider
       // than 128.
-      bool IsEmptyField = isEmptyField(getContext(), *i, true);
-      if (Size > 128 && (!IsEmptyField || getTarget().getTriple().isPS()) &&
+      bool IsEmptyField = isEmptyField(getContext(), *i, true) &&
+                          properlyIgnoreEmptyCXXFieldsAndBases();
+      if (Size > 128 && !IsEmptyField &&
           ((!IsUnion && Size != getContext().getTypeSize(i->getType())) ||
            Size > getNativeVectorSizeForAVXABI(AVXLevel))) {
         Lo = Memory;
diff --git a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp 
b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
index bfd3f66bc1160..54e802e20cf31 100644
--- a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
+++ b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
@@ -1,5 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - 
%s -target-feature +avx | FileCheck %s --check-prefix=SYSV
 // RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -o - %s 
-target-feature +avx | FileCheck %s --check-prefix=PS
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fclang-abi-compat=21 -emit-llvm -o - %s -target-feature +avx | FileCheck %s 
--check-prefix=CLANG21
 
 typedef unsigned long long v4ull __attribute__((vector_size(32)));
 
@@ -44,3 +45,9 @@ unsigned long long 
pass_empty_field_then_vector(EmptyFieldThenVector X) {
 // PS-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(<4 x i64> %X.coerce)
 // PS-LABEL: define dso_local void @_Z30return_empty_field_then_vectorv(ptr 
dead_on_unwind noalias writable sret(%struct.EmptyFieldThenVector) align 32 
%agg.result)
 // PS-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)
+
+// Clang21 ABI compatibility mode keeps the legacy ABI behavior: the 
empty-base case and [[no_unique_address]] empty-field case both as indirect
+// CLANG21-LABEL: define dso_local void 
@_Z29return_empty_base_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyBaseThenVector) align 32 %agg.result) 
+// CLANG21-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(ptr noundef 
byval(%struct.EmptyBaseThenVector) align 32 %X)
+// CLANG21-LABEL: define dso_local void 
@_Z30return_empty_field_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyFieldThenVector) align 32 %agg.result)
+// CLANG21-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)

>From 0024db23822eacacf75f4446b06ccf65e656ac6d Mon Sep 17 00:00:00 2001
From: Benjamin Luke <[email protected]>
Date: Fri, 3 Apr 2026 09:35:16 -0700
Subject: [PATCH 3/3] Fixup the test to use Clang22 ABI compat mode

---
 .../test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp 
b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
index 54e802e20cf31..705b89d68a54f 100644
--- a/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
+++ b/clang/test/CodeGenCXX/x86_64-empty-base-vector-abi.cpp
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm -o - 
%s -target-feature +avx | FileCheck %s --check-prefix=SYSV
 // RUN: %clang_cc1 -triple x86_64-sie-ps5 -std=c++20 -emit-llvm -o - %s 
-target-feature +avx | FileCheck %s --check-prefix=PS
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fclang-abi-compat=21 -emit-llvm -o - %s -target-feature +avx | FileCheck %s 
--check-prefix=CLANG21
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fclang-abi-compat=22 -emit-llvm -o - %s -target-feature +avx | FileCheck %s 
--check-prefix=CLANG22
 
 typedef unsigned long long v4ull __attribute__((vector_size(32)));
 
@@ -46,8 +46,8 @@ unsigned long long 
pass_empty_field_then_vector(EmptyFieldThenVector X) {
 // PS-LABEL: define dso_local void @_Z30return_empty_field_then_vectorv(ptr 
dead_on_unwind noalias writable sret(%struct.EmptyFieldThenVector) align 32 
%agg.result)
 // PS-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)
 
-// Clang21 ABI compatibility mode keeps the legacy ABI behavior: the 
empty-base case and [[no_unique_address]] empty-field case both as indirect
-// CLANG21-LABEL: define dso_local void 
@_Z29return_empty_base_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyBaseThenVector) align 32 %agg.result) 
-// CLANG21-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(ptr noundef 
byval(%struct.EmptyBaseThenVector) align 32 %X)
-// CLANG21-LABEL: define dso_local void 
@_Z30return_empty_field_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyFieldThenVector) align 32 %agg.result)
-// CLANG21-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)
+// Clang22 ABI compatibility mode keeps the legacy ABI behavior: the 
empty-base case and [[no_unique_address]] empty-field case both as indirect
+// CLANG22-LABEL: define dso_local void 
@_Z29return_empty_base_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyBaseThenVector) align 32 %agg.result) 
+// CLANG22-LABEL: define dso_local noundef i64 
@_Z27pass_empty_base_then_vector19EmptyBaseThenVector(ptr noundef 
byval(%struct.EmptyBaseThenVector) align 32 %X)
+// CLANG22-LABEL: define dso_local void 
@_Z30return_empty_field_then_vectorv(ptr dead_on_unwind noalias writable 
sret(%struct.EmptyFieldThenVector) align 32 %agg.result)
+// CLANG22-LABEL: define dso_local noundef i64 
@_Z28pass_empty_field_then_vector20EmptyFieldThenVector(ptr noundef 
byval(%struct.EmptyFieldThenVector) align 32 %X)

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

Reply via email to