https://github.com/mattst88 updated 
https://github.com/llvm/llvm-project/pull/223481

>From 06e418f744a2d37ab641f78d826a7500802c4b35 Mon Sep 17 00:00:00 2001
From: Matt Turner <[email protected]>
Date: Thu, 15 Oct 2026 18:29:13 -0400
Subject: [PATCH 1/3] [clang][CodeGen] Rename isSingleElementStruct to
 isSingleElementRecord (NFC)

isSingleElementStruct decomposes unions as well as structures, both at
the top level and in nested fields and bases. Rename it to
isSingleElementRecord and say so in its comment, so that a later
isSingleElementStruct can refuse unions.

Assisted-by: Claude Code
---
 clang/lib/CodeGen/ABIInfoImpl.cpp         |  6 +++---
 clang/lib/CodeGen/ABIInfoImpl.h           | 11 ++++++-----
 clang/lib/CodeGen/Targets/AMDGPU.cpp      |  6 +++---
 clang/lib/CodeGen/Targets/CSKY.cpp        |  2 +-
 clang/lib/CodeGen/Targets/PPC.cpp         |  6 +++---
 clang/lib/CodeGen/Targets/SPIR.cpp        |  6 +++---
 clang/lib/CodeGen/Targets/SystemZ.cpp     |  8 ++++----
 clang/lib/CodeGen/Targets/WebAssembly.cpp |  6 +++---
 clang/lib/CodeGen/Targets/X86.cpp         |  8 ++++----
 9 files changed, 30 insertions(+), 29 deletions(-)

diff --git a/clang/lib/CodeGen/ABIInfoImpl.cpp 
b/clang/lib/CodeGen/ABIInfoImpl.cpp
index 23b6b7e8200da1..a7f88411d06262 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.cpp
+++ b/clang/lib/CodeGen/ABIInfoImpl.cpp
@@ -302,7 +302,7 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType 
T, bool AllowArrays,
   return true;
 }
 
-const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) {
+const Type *CodeGen::isSingleElementRecord(QualType T, ASTContext &Context) {
   const auto *RD = T->getAsRecordDecl();
   if (!RD)
     return nullptr;
@@ -325,7 +325,7 @@ const Type *CodeGen::isSingleElementStruct(QualType T, 
ASTContext &Context) {
 
       // If this is non-empty and not a single element struct, the composite
       // cannot be a single element struct.
-      Found = isSingleElementStruct(I.getType(), Context);
+      Found = isSingleElementRecord(I.getType(), Context);
       if (!Found)
         return nullptr;
     }
@@ -354,7 +354,7 @@ const Type *CodeGen::isSingleElementStruct(QualType T, 
ASTContext &Context) {
     if (!isAggregateTypeForABI(FT)) {
       Found = FT.getTypePtr();
     } else {
-      Found = isSingleElementStruct(FT, Context);
+      Found = isSingleElementRecord(FT, Context);
       if (!Found)
         return nullptr;
     }
diff --git a/clang/lib/CodeGen/ABIInfoImpl.h b/clang/lib/CodeGen/ABIInfoImpl.h
index f0276be8cb97fb..f9cec462620de8 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.h
+++ b/clang/lib/CodeGen/ABIInfoImpl.h
@@ -120,15 +120,16 @@ bool isEmptyField(ASTContext &Context, const FieldDecl 
*FD, bool AllowArrays,
 bool isEmptyRecord(ASTContext &Context, QualType T, bool AllowArrays,
                    bool AsIfNoUniqueAddr = false);
 
-/// isSingleElementStruct - Determine if a structure is a "single
-/// element struct", i.e. it has exactly one non-empty field or
+/// isSingleElementRecord - Determine if a record is a "single
+/// element record", i.e. it has exactly one non-empty field or
 /// exactly one field which is itself a single element
-/// struct. Structures with flexible array members are never
-/// considered single element structs.
+/// record. Both structures and unions are decomposed, at the top level
+/// and at every level of nesting. Records with flexible array members
+/// are never considered single element records.
 ///
 /// \return The field declaration for the single non-empty field, if
 /// it exists.
-const Type *isSingleElementStruct(QualType T, ASTContext &Context);
+const Type *isSingleElementRecord(QualType T, ASTContext &Context);
 
 Address EmitVAArgInstr(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
                        const ABIArgInfo &AI);
diff --git a/clang/lib/CodeGen/Targets/AMDGPU.cpp 
b/clang/lib/CodeGen/Targets/AMDGPU.cpp
index d54efe81f8cee3..fd9182adda47db 100644
--- a/clang/lib/CodeGen/Targets/AMDGPU.cpp
+++ b/clang/lib/CodeGen/Targets/AMDGPU.cpp
@@ -152,7 +152,7 @@ ABIArgInfo AMDGPUABIInfo::classifyReturnType(QualType 
RetTy) const {
         return ABIArgInfo::getIgnore();
 
       // Lower single-element structs to just return a regular value.
-      if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
+      if (const Type *SeltTy = isSingleElementRecord(RetTy, getContext()))
         return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
 
       if (const auto *RD = RetTy->getAsRecordDecl();
@@ -188,7 +188,7 @@ ABIArgInfo 
AMDGPUABIInfo::classifyKernelArgumentType(QualType Ty) const {
 
   // TODO: Can we omit empty structs?
 
-  if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+  if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
     Ty = QualType(SeltTy, 0);
 
   llvm::Type *OrigLTy = CGT.ConvertType(Ty);
@@ -243,7 +243,7 @@ ABIArgInfo AMDGPUABIInfo::classifyArgumentType(QualType Ty, 
bool Variadic,
     // Lower single-element structs to just pass a regular value. TODO: We
     // could do reasonable-size multiple-element structs too, using 
getExpand(),
     // though watch out for things like bitfields.
-    if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+    if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
       return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
 
     if (const auto *RD = Ty->getAsRecordDecl();
diff --git a/clang/lib/CodeGen/Targets/CSKY.cpp 
b/clang/lib/CodeGen/Targets/CSKY.cpp
index b9254208f912a3..a7455f6b39fb47 100644
--- a/clang/lib/CodeGen/Targets/CSKY.cpp
+++ b/clang/lib/CodeGen/Targets/CSKY.cpp
@@ -92,7 +92,7 @@ ABIArgInfo CSKYABIInfo::classifyArgumentType(QualType Ty, int 
&ArgGPRsLeft,
     return ABIArgInfo::getIgnore();
 
   if (!Ty->isUnionType())
-    if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+    if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
       return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
 
   uint64_t Size = getContext().getTypeSize(Ty);
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp 
b/clang/lib/CodeGen/Targets/PPC.cpp
index df14719a8c6a87..5818c3c6ca9ff2 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -428,7 +428,7 @@ CharUnits 
PPC32_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
   // For single-element float/vector structs, we consider the whole type
   // to have the same alignment requirements as its single element.
   const Type *AlignTy = nullptr;
-  if (const Type *EltType = isSingleElementStruct(Ty, getContext())) {
+  if (const Type *EltType = isSingleElementRecord(Ty, getContext())) {
     const BuiltinType *BT = EltType->getAs<BuiltinType>();
     if ((EltType->isVectorType() && getContext().getTypeSize(EltType) == 128) 
||
         (BT && BT->isFloatingPoint()))
@@ -688,7 +688,7 @@ class PPC64_SVR4_ABIInfo : public ABIInfo {
       // We rely on the default argument classification for the most part.
       // One exception:  An aggregate containing a single floating-point
       // or vector item must be passed in a register if one is available.
-      const Type *T = isSingleElementStruct(I.type, getContext());
+      const Type *T = isSingleElementRecord(I.type, getContext());
       if (T) {
         const BuiltinType *BT = T->getAs<BuiltinType>();
         if ((T->isVectorType() && getContext().getTypeSize(T) == 128) ||
@@ -797,7 +797,7 @@ CharUnits 
PPC64_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
   // For single-element float/vector structs, we consider the whole type
   // to have the same alignment requirements as its single element.
   const Type *AlignAsType = nullptr;
-  const Type *EltType = isSingleElementStruct(Ty, getContext());
+  const Type *EltType = isSingleElementRecord(Ty, getContext());
   if (EltType) {
     const BuiltinType *BT = EltType->getAs<BuiltinType>();
     if ((EltType->isVectorType() && getContext().getTypeSize(EltType) == 128) 
||
diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp 
b/clang/lib/CodeGen/Targets/SPIR.cpp
index 19936d44208ddc..8088a09ecdced7 100644
--- a/clang/lib/CodeGen/Targets/SPIR.cpp
+++ b/clang/lib/CodeGen/Targets/SPIR.cpp
@@ -262,7 +262,7 @@ ABIArgInfo AMDGCNSPIRVABIInfo::classifyReturnType(QualType 
RetTy) const {
     return ABIArgInfo::getIgnore();
 
   // Lower single-element structs to just return a regular value.
-  if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
+  if (const Type *SeltTy = isSingleElementRecord(RetTy, getContext()))
     return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
 
   if (const auto *RD = RetTy->getAsRecordDecl();
@@ -296,7 +296,7 @@ ABIArgInfo 
AMDGCNSPIRVABIInfo::classifyKernelArgumentType(QualType Ty) const {
 
   // TODO: Can we omit empty structs?
 
-  if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+  if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
     Ty = QualType(SeltTy, 0);
 
   llvm::Type *OrigLTy = CGT.ConvertType(Ty);
@@ -359,7 +359,7 @@ ABIArgInfo 
AMDGCNSPIRVABIInfo::classifyArgumentType(QualType Ty,
   // Lower single-element structs to just pass a regular value. TODO: We
   // could do reasonable-size multiple-element structs too, using getExpand(),
   // though watch out for things like bitfields.
-  if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+  if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
     return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
 
   if (const auto *RD = Ty->getAsRecordDecl();
diff --git a/clang/lib/CodeGen/Targets/SystemZ.cpp 
b/clang/lib/CodeGen/Targets/SystemZ.cpp
index a27fee633ead23..6c5c2e65dcaf1e 100644
--- a/clang/lib/CodeGen/Targets/SystemZ.cpp
+++ b/clang/lib/CodeGen/Targets/SystemZ.cpp
@@ -229,22 +229,22 @@ QualType SystemZABIInfo::getSingleElementType(QualType 
Ty) const {
 
     // Check the fields.
     for (const auto *FD : RD->fields()) {
-      // Unlike isSingleElementStruct(), empty structure and array fields
+      // Unlike isSingleElementRecord(), empty structure and array fields
       // do count.  So do anonymous bitfields that aren't zero-sized.
 
-      // Like isSingleElementStruct(), ignore C++20 empty data members.
+      // Like isSingleElementRecord(), ignore C++20 empty data members.
       if (FD->hasAttr<NoUniqueAddressAttr>() &&
           isEmptyRecord(getContext(), FD->getType(), true))
         continue;
 
-      // Unlike isSingleElementStruct(), arrays do not count.
+      // Unlike isSingleElementRecord(), arrays do not count.
       // Nested structures still do though.
       if (!Found.isNull())
         return Ty;
       Found = getSingleElementType(FD->getType());
     }
 
-    // Unlike isSingleElementStruct(), trailing padding is allowed.
+    // Unlike isSingleElementRecord(), trailing padding is allowed.
     // An 8-byte aligned struct s { float f; } is passed as a double.
     if (!Found.isNull())
       return Found;
diff --git a/clang/lib/CodeGen/Targets/WebAssembly.cpp 
b/clang/lib/CodeGen/Targets/WebAssembly.cpp
index 8f6f58bfb807e8..b892923eddc519 100644
--- a/clang/lib/CodeGen/Targets/WebAssembly.cpp
+++ b/clang/lib/CodeGen/Targets/WebAssembly.cpp
@@ -150,7 +150,7 @@ ABIArgInfo 
WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
     // Lower single-element structs to just pass a regular value. TODO: We
     // could do reasonable-size multiple-element structs too, using 
getExpand(),
     // though watch out for things like bitfields.
-    if (const Type *SeltTy = isSingleElementStruct(Ty, getContext()))
+    if (const Type *SeltTy = isSingleElementRecord(Ty, getContext()))
       return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
     // For the experimental multivalue ABI, fully expand all other aggregates
     if (Kind == WebAssemblyABIKind::ExperimentalMV) {
@@ -186,7 +186,7 @@ ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType 
RetTy) const {
       // Lower single-element structs to just return a regular value. TODO: We
       // could do reasonable-size multiple-element structs too, using
       // ABIArgInfo::getDirect().
-      if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
+      if (const Type *SeltTy = isSingleElementRecord(RetTy, getContext()))
         return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
       // For the experimental multivalue ABI, return all other aggregates
       if (Kind == WebAssemblyABIKind::ExperimentalMV)
@@ -202,7 +202,7 @@ RValue WebAssemblyABIInfo::EmitVAArg(CodeGenFunction &CGF, 
Address VAListAddr,
                                      QualType Ty, AggValueSlot Slot) const {
   bool IsIndirect = isAggregateTypeForABI(Ty) &&
                     !isEmptyRecord(getContext(), Ty, true) &&
-                    !isSingleElementStruct(Ty, getContext());
+                    !isSingleElementRecord(Ty, getContext());
   return emitVoidPtrVAArg(CGF, VAListAddr, Ty, IsIndirect,
                           getContext().getTypeInfoInChars(Ty),
                           CharUnits::fromQuantity(4),
diff --git a/clang/lib/CodeGen/Targets/X86.cpp 
b/clang/lib/CodeGen/Targets/X86.cpp
index c0947c481d2b07..c5c8bd4c0b27d2 100644
--- a/clang/lib/CodeGen/Targets/X86.cpp
+++ b/clang/lib/CodeGen/Targets/X86.cpp
@@ -544,7 +544,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy,
       // floating-point register. (MSVC does not apply this special case.)
       // We apply a similar transformation for pointer types to improve the
       // quality of the generated IR.
-      if (const Type *SeltTy = isSingleElementStruct(RetTy, getContext()))
+      if (const Type *SeltTy = isSingleElementRecord(RetTy, getContext()))
         if ((!IsWin32StructABI && SeltTy->isRealFloatingType())
             || SeltTy->hasPointerRepresentation())
           return ABIArgInfo::getDirect(CGT.ConvertType(QualType(SeltTy, 0)));
@@ -631,7 +631,7 @@ ABIArgInfo X86_32ABIInfo::getIndirectResult(QualType Ty, 
bool ByVal,
       Realign);
 }
 
-/// Like isSingleElementStruct, but only looks through one level of records.
+/// Like isSingleElementRecord, but only looks through one level of records.
 /// This mirrors how GCC gives a record the mode of its only field.
 static const Type *getSingleFieldType(QualType T, ASTContext &Context) {
   const auto *RD = T->getAsRecordDecl();
@@ -692,7 +692,7 @@ static const Type *getSingleFieldType(QualType T, 
ASTContext &Context) {
 X86_32ABIInfo::Class X86_32ABIInfo::classify(QualType Ty) const {
   if (getContext().getLangOpts().isCompatibleWith(
           LangOptions::ClangABI::Ver23)) {
-    const Type *T = isSingleElementStruct(Ty, getContext());
+    const Type *T = isSingleElementRecord(Ty, getContext());
     if (!T)
       T = Ty.getTypePtr();
 
@@ -2477,7 +2477,7 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty,
 llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const {
   // Wrapper structs/arrays that only contain vectors are passed just like
   // vectors; strip them off if present.
-  if (const Type *InnerTy = isSingleElementStruct(Ty, getContext()))
+  if (const Type *InnerTy = isSingleElementRecord(Ty, getContext()))
     Ty = QualType(InnerTy, 0);
 
   llvm::Type *IRType = CGT.ConvertType(Ty);

>From 2a749612a768f5bcf60bdf366cb2cc37ac04110b Mon Sep 17 00:00:00 2001
From: Matt Turner <[email protected]>
Date: Mon, 14 Sep 2026 07:47:46 -0400
Subject: [PATCH 2/3] [clang][PowerPC] Factor out single-element FP/vector
 aggregate check

PPC64_SVR4_ABIInfo::computeInfo and getParamTypeAlignment each tested
whether an aggregate wraps a single floating-point or 128-bit vector
value. Move the test into getSingleElementFPOrVectorType.

Assisted-by: Claude Code
---
 clang/lib/CodeGen/Targets/PPC.cpp | 36 ++++++++++++++++---------------
 1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/clang/lib/CodeGen/Targets/PPC.cpp 
b/clang/lib/CodeGen/Targets/PPC.cpp
index 5818c3c6ca9ff2..cac2ee86f7d1f4 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -667,6 +667,7 @@ class PPC64_SVR4_ABIInfo : public ABIInfo {
 
   bool isPromotableTypeForABI(QualType Ty) const;
   CharUnits getParamTypeAlignment(QualType Ty) const;
+  const Type *getSingleElementFPOrVectorType(QualType Ty) const;
 
   ABIArgInfo classifyReturnType(QualType RetTy) const;
   ABIArgInfo classifyArgumentType(QualType Ty) const;
@@ -688,15 +689,9 @@ class PPC64_SVR4_ABIInfo : public ABIInfo {
       // We rely on the default argument classification for the most part.
       // One exception:  An aggregate containing a single floating-point
       // or vector item must be passed in a register if one is available.
-      const Type *T = isSingleElementRecord(I.type, getContext());
-      if (T) {
-        const BuiltinType *BT = T->getAs<BuiltinType>();
-        if ((T->isVectorType() && getContext().getTypeSize(T) == 128) ||
-            (BT && BT->isFloatingPoint())) {
-          QualType QT(T, 0);
-          I.info = ABIArgInfo::getDirectInReg(CGT.ConvertType(QT));
-          continue;
-        }
+      if (const Type *T = getSingleElementFPOrVectorType(I.type)) {
+        I.info = ABIArgInfo::getDirectInReg(CGT.ConvertType(QualType(T, 0)));
+        continue;
       }
       I.info = classifyArgumentType(I.type);
     }
@@ -796,14 +791,7 @@ CharUnits 
PPC64_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
 
   // For single-element float/vector structs, we consider the whole type
   // to have the same alignment requirements as its single element.
-  const Type *AlignAsType = nullptr;
-  const Type *EltType = isSingleElementRecord(Ty, getContext());
-  if (EltType) {
-    const BuiltinType *BT = EltType->getAs<BuiltinType>();
-    if ((EltType->isVectorType() && getContext().getTypeSize(EltType) == 128) 
||
-        (BT && BT->isFloatingPoint()))
-      AlignAsType = EltType;
-  }
+  const Type *AlignAsType = getSingleElementFPOrVectorType(Ty);
 
   // Likewise for ELFv2 homogeneous aggregates.
   const Type *Base = nullptr;
@@ -828,6 +816,20 @@ CharUnits 
PPC64_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
   return CharUnits::fromQuantity(8);
 }
 
+/// If Ty is a one-member aggregate wrapping a floating-point or 128-bit vector
+/// type, return that type.
+const Type *
+PPC64_SVR4_ABIInfo::getSingleElementFPOrVectorType(QualType Ty) const {
+  const Type *EltType = isSingleElementRecord(Ty, getContext());
+  if (!EltType)
+    return nullptr;
+  const BuiltinType *BT = EltType->getAs<BuiltinType>();
+  if ((EltType->isVectorType() && getContext().getTypeSize(EltType) == 128) ||
+      (BT && BT->isFloatingPoint()))
+    return EltType;
+  return nullptr;
+}
+
 bool PPC64_SVR4_ABIInfo::isHomogeneousAggregateBaseType(QualType Ty) const {
   // Homogeneous aggregates for ELFv2 must have base types of float,
   // double, long double, or 128-bit vectors.

>From 238941ce5ef589a24ba9b70242c5584e789b8b50 Mon Sep 17 00:00:00 2001
From: Matt Turner <[email protected]>
Date: Thu, 15 Oct 2026 18:29:52 -0400
Subject: [PATCH 3/3] [clang][PowerPC] Pass single-member unions in GPRs on
 ELFv1

The 64-bit PowerPC ELFv1 ABI (section 3.2.3) passes "one member
aggregates passed by value containing a floating point value" in FPRs,
and an aggregate containing one vector value in a VR. Section 3.1.6
defines aggregates as structures and arrays and lists unions separately,
so a union is never unwrapped this way.

Clang used isSingleElementRecord, which accepts unions, so it
passed `union { float a; }`, `struct { union { float a; } u; }` and
`union { struct { float a; } s; }` in f1 where GCC passes them in r3.
The same happened for double, long double and 128-bit vector members.
GCC reaches its result through TYPE_MODE: compute_record_mode gives a
single-field RECORD_TYPE its field's mode but a UNION_TYPE an integer
mode, so USE_FP_FOR_ARG_P and USE_ALTIVEC_FOR_ARG_P reject it.

Add isSingleElementStruct, which refuses unions at every level, and use
it on ELFv1. ELFv2 is unchanged: it defines homogeneous aggregates to
include unions, and clang already agrees with GCC there.

This is an ABI break for existing clang-compiled ELFv1 code that passes
such unions; -fclang-abi-compat=23 restores the previous behavior. rustc
has the same bug (rust-lang/rust#162011).

Assisted-by: Claude Code
---
 clang/docs/ReleaseNotes.md                    |  6 ++
 clang/lib/CodeGen/ABIInfoImpl.cpp             | 17 +++--
 clang/lib/CodeGen/ABIInfoImpl.h               |  4 ++
 clang/lib/CodeGen/Targets/PPC.cpp             |  9 ++-
 .../CodeGen/PowerPC/ppc64-union-onefloat.c    | 63 +++++++++++++++++++
 5 files changed, 93 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/CodeGen/PowerPC/ppc64-union-onefloat.c

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3f0c48cf36644d..d9dce1b66f6ab3 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -97,6 +97,12 @@ 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 64-bit PowerPC ELFv1, a union argument that wraps a single floating-point
+  or vector value, or a struct that wraps such a union, is now passed in
+  general-purpose registers rather than in a floating-point or vector register,
+  matching GCC. ELFv2 is unchanged. `-fclang-abi-compat=23` restores the
+  previous behavior.
+
 - 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
diff --git a/clang/lib/CodeGen/ABIInfoImpl.cpp 
b/clang/lib/CodeGen/ABIInfoImpl.cpp
index a7f88411d06262..1787b3eda097a7 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.cpp
+++ b/clang/lib/CodeGen/ABIInfoImpl.cpp
@@ -302,9 +302,10 @@ bool CodeGen::isEmptyRecord(ASTContext &Context, QualType 
T, bool AllowArrays,
   return true;
 }
 
-const Type *CodeGen::isSingleElementRecord(QualType T, ASTContext &Context) {
+static const Type *getSingleElement(QualType T, ASTContext &Context,
+                                    bool AllowUnions) {
   const auto *RD = T->getAsRecordDecl();
-  if (!RD)
+  if (!RD || (!AllowUnions && RD->isUnion()))
     return nullptr;
 
   if (RD->hasFlexibleArrayMember())
@@ -325,7 +326,7 @@ const Type *CodeGen::isSingleElementRecord(QualType T, 
ASTContext &Context) {
 
       // If this is non-empty and not a single element struct, the composite
       // cannot be a single element struct.
-      Found = isSingleElementRecord(I.getType(), Context);
+      Found = getSingleElement(I.getType(), Context, AllowUnions);
       if (!Found)
         return nullptr;
     }
@@ -354,7 +355,7 @@ const Type *CodeGen::isSingleElementRecord(QualType T, 
ASTContext &Context) {
     if (!isAggregateTypeForABI(FT)) {
       Found = FT.getTypePtr();
     } else {
-      Found = isSingleElementRecord(FT, Context);
+      Found = getSingleElement(FT, Context, AllowUnions);
       if (!Found)
         return nullptr;
     }
@@ -368,6 +369,14 @@ const Type *CodeGen::isSingleElementRecord(QualType T, 
ASTContext &Context) {
   return Found;
 }
 
+const Type *CodeGen::isSingleElementRecord(QualType T, ASTContext &Context) {
+  return getSingleElement(T, Context, /*AllowUnions=*/true);
+}
+
+const Type *CodeGen::isSingleElementStruct(QualType T, ASTContext &Context) {
+  return getSingleElement(T, Context, /*AllowUnions=*/false);
+}
+
 Address CodeGen::EmitVAArgInstr(CodeGenFunction &CGF, Address VAListAddr,
                                 QualType Ty, const ABIArgInfo &AI) {
   // This default implementation defers to the llvm backend's va_arg
diff --git a/clang/lib/CodeGen/ABIInfoImpl.h b/clang/lib/CodeGen/ABIInfoImpl.h
index f9cec462620de8..37f856598ddc89 100644
--- a/clang/lib/CodeGen/ABIInfoImpl.h
+++ b/clang/lib/CodeGen/ABIInfoImpl.h
@@ -131,6 +131,10 @@ bool isEmptyRecord(ASTContext &Context, QualType T, bool 
AllowArrays,
 /// it exists.
 const Type *isSingleElementRecord(QualType T, ASTContext &Context);
 
+/// isSingleElementStruct - Like isSingleElementRecord, but neither \p T nor
+/// any record nested in it may be a union.
+const Type *isSingleElementStruct(QualType T, ASTContext &Context);
+
 Address EmitVAArgInstr(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
                        const ABIArgInfo &AI);
 
diff --git a/clang/lib/CodeGen/Targets/PPC.cpp 
b/clang/lib/CodeGen/Targets/PPC.cpp
index cac2ee86f7d1f4..de296680ce6af2 100644
--- a/clang/lib/CodeGen/Targets/PPC.cpp
+++ b/clang/lib/CodeGen/Targets/PPC.cpp
@@ -817,10 +817,15 @@ CharUnits 
PPC64_SVR4_ABIInfo::getParamTypeAlignment(QualType Ty) const {
 }
 
 /// If Ty is a one-member aggregate wrapping a floating-point or 128-bit vector
-/// type, return that type.
+/// type, return that type. ELFv1 only unwraps structs and arrays; GCC passes
+/// unions in GPRs.
 const Type *
 PPC64_SVR4_ABIInfo::getSingleElementFPOrVectorType(QualType Ty) const {
-  const Type *EltType = isSingleElementRecord(Ty, getContext());
+  bool AllowUnions =
+      Kind != PPC64_SVR4_ABIKind::ELFv1 ||
+      
getContext().getLangOpts().isCompatibleWith(LangOptions::ClangABI::Ver23);
+  const Type *EltType = AllowUnions ? isSingleElementRecord(Ty, getContext())
+                                    : isSingleElementStruct(Ty, getContext());
   if (!EltType)
     return nullptr;
   const BuiltinType *BT = EltType->getAs<BuiltinType>();
diff --git a/clang/test/CodeGen/PowerPC/ppc64-union-onefloat.c 
b/clang/test/CodeGen/PowerPC/ppc64-union-onefloat.c
new file mode 100644
index 00000000000000..a6893f365ced8d
--- /dev/null
+++ b/clang/test/CodeGen/PowerPC/ppc64-union-onefloat.c
@@ -0,0 +1,63 @@
+// REQUIRES: powerpc-registered-target
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN:   | FileCheck %s --check-prefixes=ALL,GPR
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu -fclang-abi-compat=23 \
+// RUN:   -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,UNWRAP
+// RUN: %clang_cc1 -triple powerpc64-unknown-linux-gnu -target-abi elfv2 \
+// RUN:   -emit-llvm -o - %s | FileCheck %s --check-prefixes=ALL,UNWRAP
+// RUN: %clang_cc1 -triple powerpc64le-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN:   | FileCheck %s --check-prefixes=ALL,UNWRAP
+
+// ELFv1 does not unwrap unions for FPR/VR passing; ELFv2 does.
+
+typedef float v4sf __attribute__((vector_size(16)));
+
+typedef struct { float a; } S1;
+typedef union { float a; } U1;
+typedef union { double a; } UD;
+typedef union { long double a; } ULD;
+typedef union { v4sf v; } UV;
+typedef struct { U1 u; } SU1;
+typedef union { S1 s; } US1;
+typedef struct { U1 a[1]; } SAU1;
+
+// ALL-LABEL: define{{.*}} void @s1(float inreg %
+void s1(S1 x) {}
+
+// GPR-LABEL:    define{{.*}} void @u1(i32 %
+// UNWRAP-LABEL: define{{.*}} void @u1(float inreg %
+void u1(U1 x) {}
+
+// GPR-LABEL:    define{{.*}} void @ud(i64 %
+// UNWRAP-LABEL: define{{.*}} void @ud(double inreg %
+void ud(UD x) {}
+
+// GPR-LABEL:    define{{.*}} void @uld([1 x i128] %
+// UNWRAP-LABEL: define{{.*}} void @uld(ppc_fp128 inreg %
+void uld(ULD x) {}
+
+// GPR-LABEL:    define{{.*}} void @uv([1 x i128] %
+// UNWRAP-LABEL: define{{.*}} void @uv(<4 x float> inreg %
+void uv(UV x) {}
+
+// GPR-LABEL:    define{{.*}} void @su1(i32 %
+// UNWRAP-LABEL: define{{.*}} void @su1(float inreg %
+void su1(SU1 x) {}
+
+// GPR-LABEL:    define{{.*}} void @us1(i32 %
+// UNWRAP-LABEL: define{{.*}} void @us1(float inreg %
+void us1(US1 x) {}
+
+// GPR-LABEL:    define{{.*}} void @sau1(i32 %
+// UNWRAP-LABEL: define{{.*}} void @sau1(float inreg %
+void sau1(SAU1 x) {}
+
+// A union of a vector is still quadword-aligned in the parameter save area.
+// ALL-LABEL: define{{.*}} void @uva(
+// ALL:       call ptr @llvm.ptrmask.p0.i64(ptr %{{.*}}, i64 -16)
+void uva(int n, ...) {
+  __builtin_va_list ap;
+  __builtin_va_start(ap, n);
+  UV x = __builtin_va_arg(ap, UV);
+  __builtin_va_end(ap);
+}

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

Reply via email to