https://github.com/mattst88 created https://github.com/llvm/llvm-project/pull/234054
Two NFC refactors split out of #223481 at review request. - Rename isSingleElementStruct to isSingleElementRecord. It decomposes unions as well as structures, both at the top level and in nested fields and bases; the new name and comment say so, so that a later isSingleElementStruct can refuse unions. - Factor the single-element floating-point/128-bit vector aggregate test that PPC64_SVR4_ABIInfo::computeInfo and getParamTypeAlignment each performed into getSingleElementFPOrVectorType. Assisted-by: Claude Code >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/2] [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 23b6b7e8200da..a7f88411d0626 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 f0276be8cb97f..f9cec462620de 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 d54efe81f8cee..fd9182adda47d 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 b9254208f912a..a7455f6b39fb4 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 df14719a8c6a8..5818c3c6ca9ff 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 19936d44208dd..8088a09ecdced 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 a27fee633ead2..6c5c2e65dcaf1 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 8f6f58bfb807e..b892923eddc51 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 c0947c481d2b0..c5c8bd4c0b27d 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/2] [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 5818c3c6ca9ff..cac2ee86f7d1f 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. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
