https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/218799
>From 62a5d3ad43f6d26c82f2fbde6deb6b9267353b60 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 17 Aug 2026 15:49:58 -0700 Subject: [PATCH 1/3] [LLVMAABI][AARCH64] Handle homogeneous aggregate return types This change implements the IsHomogenousAggregate() function, which is shared across targets, and uses it to implement direct passthrough of return values that meet the homogeneous aggregate criteria. I'm also adding a new AArch64ABIOptions object that will be used to track various conditions that are derived from target settings and must be accounted for in the ABI handling. This change adds only two options, IsILP32 and IsMicrosoftCXXABI. Other options are expected and will be added as they are needed. This change also revises the not-yet-implemented cases, having them return Ignore rather than Direct so that they fail in more obvious ways. This was necessary in order to be able to distinguish a return type that was classified as Direct because it was a homogeneous aggregate from one that was classified as Direct because it was an unhandled type. Assisted-by: Cursor / various models --- clang/lib/CodeGen/CodeGenModule.cpp | 16 +- .../AArch64/abi-classify-return-types.c | 28 +++ .../AArch64/abi-classify-return-types.cpp | 78 ++++++++ llvm/include/llvm/ABI/TargetInfo.h | 51 +++++- llvm/lib/ABI/TargetInfo.cpp | 127 ++++++++++++++ llvm/lib/ABI/Targets/AArch64.cpp | 94 ++++++++-- llvm/lib/ABI/Targets/BPF.cpp | 4 +- llvm/lib/ABI/Targets/X86.cpp | 3 +- llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 166 ++++++++++++++++-- 9 files changed, 522 insertions(+), 45 deletions(-) create mode 100644 clang/test/CodeGen/AArch64/abi-classify-return-types.cpp diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index b7bd5744bcb09..0992c97768946 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -397,15 +397,21 @@ CodeGenModule::getLLVMABITargetInfo(llvm::abi::TypeBuilder &TB) { case llvm::Triple::aarch64: case llvm::Triple::aarch64_32: case llvm::Triple::aarch64_be: { + llvm::abi::AArch64ABIOptions Opts; StringRef ABI = getTarget().getABI(); - llvm::abi::AArch64ABIKind Kind = llvm::abi::AArch64ABIKind::AAPCS; if (ABI == "darwinpcs") - Kind = llvm::abi::AArch64ABIKind::DarwinPCS; + Opts.Kind = llvm::abi::AArch64ABIKind::DarwinPCS; else if (T.isOSWindows()) - Kind = llvm::abi::AArch64ABIKind::Win64; + Opts.Kind = llvm::abi::AArch64ABIKind::Win64; else if (ABI == "aapcs-soft") - Kind = llvm::abi::AArch64ABIKind::AAPCSSoft; - TheLLVMABITargetInfo = llvm::abi::createAArch64TargetInfo(TB, Kind); + Opts.Kind = llvm::abi::AArch64ABIKind::AAPCSSoft; + else + Opts.Kind = llvm::abi::AArch64ABIKind::AAPCS; + + Opts.IsILP32 = T.getArch() == llvm::Triple::aarch64_32; + Opts.IsMicrosoftCXXABI = getTarget().getCXXABI().isMicrosoft(); + + TheLLVMABITargetInfo = llvm::abi::createAArch64TargetInfo(TB, Opts); return *TheLLVMABITargetInfo; } diff --git a/clang/test/CodeGen/AArch64/abi-classify-return-types.c b/clang/test/CodeGen/AArch64/abi-classify-return-types.c index 61b2332993087..18c033f0bd692 100644 --- a/clang/test/CodeGen/AArch64/abi-classify-return-types.c +++ b/clang/test/CodeGen/AArch64/abi-classify-return-types.c @@ -86,3 +86,31 @@ _BitInt(128) ret_bitint128(void) { return 0; } _BitInt(129) ret_bitint129(void) { return 0; } // CHECK: define{{.*}} void @ret_bitint129(ptr dead_on_unwind noalias writable sret(i256) align 16 %{{.*}}) + +// Homogeneous floating-point aggregates are returned directly. +_Complex float ret_complex_float() { return 1.0f; } +// CHECK: define{{.*}} { float, float } @ret_complex_float + +typedef struct { + float a, b; +} HFA2f; +HFA2f ret_hfa2f() { return (HFA2f){1.0f, 2.0f}; } +// CHECK: define{{.*}} %struct.HFA2f @ret_hfa2f + +typedef struct { + double a, b, c, d; +} HFA4d; +HFA4d ret_hfa4d() { return (HFA4d){1.0, 2.0, 3.0, 4.0}; } +// CHECK: define{{.*}} %struct.HFA4d @ret_hfa4d + +typedef struct { + float v[3]; +} HFA3arr; +HFA3arr ret_hfa3arr() { return (HFA3arr){{1.0f, 2.0f, 3.0f}}; } +// CHECK: define{{.*}} %struct.HFA3arr @ret_hfa3arr + +typedef struct { + _Float16 a, b; +} HFA2h; +HFA2h ret_hfa2h() { return (HFA2h){1.0f, 2.0f}; } +// CHECK: define{{.*}} %struct.HFA2h @ret_hfa2h diff --git a/clang/test/CodeGen/AArch64/abi-classify-return-types.cpp b/clang/test/CodeGen/AArch64/abi-classify-return-types.cpp new file mode 100644 index 0000000000000..185c471c94a21 --- /dev/null +++ b/clang/test/CodeGen/AArch64/abi-classify-return-types.cpp @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented" +// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -std=c++20 -fexperimental-abi-lowering -emit-llvm -o - %s 2>&1 | FileCheck %s --implicit-check-not="not yet implemented" + +// Verify C++ homogeneous floating-point aggregate return classification matches +// between classic CodeGen and the LLVM ABI library. + +struct HFA2f { + float a, b; +}; +HFA2f ret_hfa2f() { return {}; } +// CHECK: define{{.*}} %struct.HFA2f @_Z9ret_hfa2fv() + +struct EmptyBase {}; +struct HFAEmptyBase : EmptyBase { + float a, b; +}; +HFAEmptyBase ret_hfa_empty_base() { return {}; } +// CHECK: define{{.*}} %struct.HFAEmptyBase @_Z18ret_hfa_empty_basev() + +struct EmptyBase1 {}; +struct EmptyBase2 {}; +struct HFAMultiEmptyBase : EmptyBase1, EmptyBase2 { + float a, b; +}; +HFAMultiEmptyBase ret_hfa_multi_empty_base() { return {}; } +// CHECK: define{{.*}} %struct.HFAMultiEmptyBase @_Z24ret_hfa_multi_empty_basev() + +struct HFABase { + float a; +}; +struct HFADerived : HFABase { + float b; +}; +HFADerived ret_hfa_derived() { return {}; } +// CHECK: define{{.*}} %struct.HFADerived @_Z15ret_hfa_derivedv() + +struct HFABaseAndFields : HFABase { + float b, c; +}; +HFABaseAndFields ret_hfa_base_fields() { return {}; } +// CHECK: define{{.*}} %struct.HFABaseAndFields @_Z19ret_hfa_base_fieldsv() + +struct FloatBase1 { + float a; +}; +struct FloatBase2 { + float b; +}; +struct HFATwoBases : FloatBase1, FloatBase2 {}; +HFATwoBases ret_hfa_two_bases() { return {}; } +// CHECK: define{{.*}} %struct.HFATwoBases @_Z17ret_hfa_two_basesv() + +struct HFANested { + HFA2f inner; + float c; +}; +HFANested ret_hfa_nested() { return {}; } +// CHECK: define{{.*}} %struct.HFANested @_Z14ret_hfa_nestedv() + +struct HFAZeroBF { + int : 0; + float a, b; +}; +HFAZeroBF ret_hfa_zerobf() { return {}; } +// CHECK: define{{.*}} %struct.HFAZeroBF @_Z14ret_hfa_zerobfv() + +struct Empty {}; +struct HFANoUniqueEmpty { + [[no_unique_address]] Empty e; + float a, b; +}; +HFANoUniqueEmpty ret_hfa_nua_empty() { return {}; } +// CHECK: define{{.*}} %struct.HFANoUniqueEmpty @_Z17ret_hfa_nua_emptyv() + +_Complex float ret_complex_float() { return 1.0f; } +// CHECK: define{{.*}} { float, float } @_Z17ret_complex_floatv() diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h index ea621597e5b22..92b20cce3eb48 100644 --- a/llvm/include/llvm/ABI/TargetInfo.h +++ b/llvm/include/llvm/ABI/TargetInfo.h @@ -64,9 +64,13 @@ class TargetInfo { private: ABICompatInfo CompatInfo; +protected: + TypeBuilder &TB; + public: - TargetInfo() : CompatInfo() {} - explicit TargetInfo(const ABICompatInfo &Info) : CompatInfo(Info) {} + explicit TargetInfo(TypeBuilder &Builder) : CompatInfo(), TB(Builder) {} + TargetInfo(TypeBuilder &Builder, const ABICompatInfo &Info) + : CompatInfo(Info), TB(Builder) {} virtual ~TargetInfo() = default; @@ -90,6 +94,35 @@ class TargetInfo { /// Apply rules for classifying return types that are common to all targets. LLVM_ABI bool maybeCommonClassifyReturnType(FunctionInfo &FI) const; + + /// Return true if \p Ty is a valid base type for a homogeneous aggregate. + virtual bool isHomogeneousAggregateBaseType(const Type *Ty) const { + return false; + } + + /// Return true if a homogeneous aggregate with \p Members copies of \p Base + /// is small enough to be passed in registers for this ABI. + virtual bool isHomogeneousAggregateSmallEnough(const Type *Base, + uint64_t Members) const { + return false; + } + + /// Return true if zero-length bitfields should be ignored when deciding + /// whether an aggregate is homogeneous. + virtual bool isZeroLengthBitfieldPermittedInHomogeneousAggregate() const { + return false; + } + + /// Return true if the C++ ABI permits \p RT to be a homogeneous aggregate. + virtual bool isPermittedToBeHomogeneousAggregate(const RecordType *RT) const { + return true; + } + + /// Return true if \p Ty is an ELFv2-style homogeneous aggregate. \p Base is + /// set to the base element type and \p Members to the number of base + /// elements. + LLVM_ABI bool isHomogeneousAggregate(const Type *Ty, const Type *&Base, + uint64_t &Members) const; }; LLVM_ABI std::unique_ptr<TargetInfo> createBPFTargetInfo(TypeBuilder &TB); @@ -113,8 +146,20 @@ enum class AArch64ABIKind { AAPCSSoft, }; +/// Target / language flags that affect AArch64 ABI classification. +/// Callers (e.g. Clang) resolve Triple and LangOptions into these flags +/// rather than passing a Triple into the ABI library. +struct AArch64ABIOptions { + AArch64ABIKind Kind = AArch64ABIKind::AAPCS; + bool IsILP32 = false; + bool IsMicrosoftCXXABI = false; + + AArch64ABIOptions() = default; + AArch64ABIOptions(AArch64ABIKind Kind) : Kind(Kind) {} +}; + LLVM_ABI std::unique_ptr<TargetInfo> -createAArch64TargetInfo(TypeBuilder &TB, AArch64ABIKind Kind); +createAArch64TargetInfo(TypeBuilder &TB, const AArch64ABIOptions &Opts); } // namespace abi } // namespace llvm diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp index 507e3eb5bc120..b8cf81c2a621e 100644 --- a/llvm/lib/ABI/TargetInfo.cpp +++ b/llvm/lib/ABI/TargetInfo.cpp @@ -7,8 +7,15 @@ //===----------------------------------------------------------------------===// #include "llvm/ABI/TargetInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/MathExtras.h" +#include <algorithm> +#include <cstdint> using namespace llvm::abi; +using llvm::alignTo; +using llvm::bit_ceil; +using llvm::dyn_cast; bool TargetInfo::isAggregateTypeForABI(const Type *Ty) const { // Atomic values use the evaluation kind of their underlying value type. @@ -86,3 +93,123 @@ bool TargetInfo::maybeCommonClassifyReturnType(FunctionInfo &FI) const { return false; } + +namespace { + +bool isEmptyRecordForHA(const Type *Ty) { + const auto *RT = dyn_cast<RecordType>(Ty); + return RT && RT->isEmpty(); +} + +/// Storage-container width mirroring Clang's ASTContext::getTypeSize for the +/// types that matter to homogeneous-aggregate detection. +uint64_t getHATypeSizeInBits(const Type *Ty) { + if (const auto *VT = dyn_cast<VectorType>(Ty)) { + uint64_t EltWidth = VT->getElementType()->getSizeInBits().getFixedValue(); + uint64_t Width = std::max<uint64_t>( + 8, EltWidth * VT->getNumElements().getKnownMinValue()); + if (Width & (Width - 1)) + Width = alignTo(Width, bit_ceil(Width)); + return Width; + } + return Ty->getSizeInBits().getFixedValue(); +} + +} // namespace + +bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, + uint64_t &Members) const { + if (const auto *AT = dyn_cast<ArrayType>(Ty)) { + uint64_t NElements = AT->getNumElements(); + if (NElements == 0) + return false; + if (!isHomogeneousAggregate(AT->getElementType(), Base, Members)) + return false; + Members *= NElements; + } else if (const auto *RT = dyn_cast<RecordType>(Ty)) { + if (RT->hasFlexibleArrayMember()) + return false; + + Members = 0; + + // If this is a C++ record, check bases and ABI-specific restrictions. + if (RT->isCXXRecord()) { + if (!isPermittedToBeHomogeneousAggregate(RT)) + return false; + + for (const FieldInfo &BaseField : RT->getBaseClasses()) { + if (isEmptyRecordForHA(BaseField.FieldType)) + continue; + + uint64_t FldMembers = 0; + if (!isHomogeneousAggregate(BaseField.FieldType, Base, FldMembers)) + return false; + + Members += FldMembers; + } + } + + for (const FieldInfo &FD : RT->getFields()) { + // Ignore (non-zero arrays of) empty records. + const Type *FT = FD.FieldType; + while (const auto *AT = dyn_cast<ArrayType>(FT)) { + if (AT->getNumElements() == 0) + return false; + FT = AT->getElementType(); + } + if (isEmptyRecordForHA(FT)) + continue; + + if (isZeroLengthBitfieldPermittedInHomogeneousAggregate() && + FD.IsBitField && FD.BitFieldWidth == 0) + continue; + + uint64_t FldMembers = 0; + if (!isHomogeneousAggregate(FD.FieldType, Base, FldMembers)) + return false; + + Members = + RT->isUnion() ? std::max(Members, FldMembers) : Members + FldMembers; + } + + if (!Base) + return false; + + // Ensure there is no padding. + if (getHATypeSizeInBits(Base) * Members != getHATypeSizeInBits(Ty)) + return false; + } else { + Members = 1; + const Type *ElemTy = Ty; + if (const auto *CT = dyn_cast<ComplexType>(Ty)) { + Members = 2; + ElemTy = CT->getElementType(); + } + + // Most ABIs only support float, double, and some vector type widths. + if (!isHomogeneousAggregateBaseType(ElemTy)) + return false; + + // The base type must be the same for all members. Types that agree in both + // total size and mode (float vs. vector) are treated as equivalent here. + if (!Base) { + Base = ElemTy; + // If it's a non-power-of-2 vector, its ABI size is already a power-of-2, + // so widen it explicitly to match Clang. + if (const auto *VT = dyn_cast<VectorType>(Base)) { + uint64_t EltSize = + VT->getElementType()->getSizeInBits().getFixedValue(); + unsigned NumElements = getHATypeSizeInBits(VT) / EltSize; + if (NumElements != VT->getNumElements().getKnownMinValue()) + Base = TB.getVectorType(VT->getElementType(), + ElementCount::getFixed(NumElements), + VT->getAlignment()); + } + } + + if (Base->isVector() != ElemTy->isVector() || + getHATypeSizeInBits(Base) != getHATypeSizeInBits(ElemTy)) + return false; + } + return Members > 0 && isHomogeneousAggregateSmallEnough(Base, Members); +} diff --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp index bf48f6a47dabf..4fb7ad941fa91 100644 --- a/llvm/lib/ABI/Targets/AArch64.cpp +++ b/llvm/lib/ABI/Targets/AArch64.cpp @@ -9,16 +9,20 @@ #include "llvm/ABI/FunctionInfo.h" #include "llvm/ABI/TargetInfo.h" #include "llvm/ABI/Types.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" #include "llvm/Support/WithColor.h" +#include <algorithm> +#include <cstdint> namespace llvm { namespace abi { class AArch64TargetInfo : public TargetInfo { public: - AArch64TargetInfo(TypeBuilder &TB, AArch64ABIKind Kind) - : TB(TB), Kind(Kind) {} + AArch64TargetInfo(TypeBuilder &TB, const AArch64ABIOptions &Opts) + : TargetInfo(TB), Opts(Opts) {} void computeInfo(FunctionInfo &FI) const override { if (!maybeCommonClassifyReturnType(FI)) @@ -37,21 +41,27 @@ class AArch64TargetInfo : public TargetInfo { } private: - [[maybe_unused]] TypeBuilder &TB; - AArch64ABIKind Kind; + AArch64ABIOptions Opts; ArgInfo classifyReturnType(const Type *RetTy, bool IsVariadicFn) const; ArgInfo classifyArgumentType(const Type *Ty, bool IsVariadicFn, bool IsNamedArg, unsigned CallingConvention, unsigned &NSRN, unsigned &NPRN) const; - bool isDarwinPCS() const { return Kind == AArch64ABIKind::DarwinPCS; } + bool isDarwinPCS() const { return Opts.Kind == AArch64ABIKind::DarwinPCS; } + bool isSoftFloat() const { return Opts.Kind == AArch64ABIKind::AAPCSSoft; } bool passAsAggregateType(const Type *Ty) const; + + bool isHomogeneousAggregateBaseType(const Type *Ty) const override; + bool isHomogeneousAggregateSmallEnough(const Type *Base, + uint64_t Members) const override; + bool isZeroLengthBitfieldPermittedInHomogeneousAggregate() const override; + bool isPermittedToBeHomogeneousAggregate(const RecordType *RT) const override; }; -std::unique_ptr<TargetInfo> createAArch64TargetInfo(TypeBuilder &TB, - AArch64ABIKind Kind) { - return std::make_unique<AArch64TargetInfo>(TB, Kind); +std::unique_ptr<TargetInfo> +createAArch64TargetInfo(TypeBuilder &TB, const AArch64ABIOptions &Opts) { + return std::make_unique<AArch64TargetInfo>(TB, Opts); } static void reportNYI(StringRef Feature) { @@ -67,7 +77,7 @@ ArgInfo AArch64TargetInfo::classifyReturnType(const Type *RetTy, if (RetTy->isVector()) { reportNYI("Vector return type handling"); - return ArgInfo::getDirect(); + return ArgInfo::getIgnore(); } if (!passAsAggregateType(RetTy)) { @@ -84,8 +94,18 @@ ArgInfo AArch64TargetInfo::classifyReturnType(const Type *RetTy, return ArgInfo::getDirect(); } + // TODO: Handle empty records and zero-size non-SVE types. + + const Type *Base = nullptr; + uint64_t Members = 0; + if (isHomogeneousAggregate(RetTy, Base, Members) && + !(Opts.IsILP32 && IsVariadicFn)) { + // Homogeneous Floating-point Aggregates (HFAs) are returned directly. + return ArgInfo::getDirect(); + } + reportNYI("Aggregate return type handling"); - return ArgInfo::getDirect(); + return ArgInfo::getIgnore(); } ArgInfo AArch64TargetInfo::classifyArgumentType( @@ -93,11 +113,9 @@ ArgInfo AArch64TargetInfo::classifyArgumentType( unsigned CallingConvention, unsigned &NSRN, unsigned &NPRN) const { Ty = useFirstFieldIfTransparentUnion(Ty); - // TODO: Handle variadic functins here when Windows Arm64 EC is supported. - if (Ty->isVector()) { reportNYI("Vector argument type handling"); - return ArgInfo::getDirect(); + return ArgInfo::getIgnore(); } if (!passAsAggregateType(Ty)) { @@ -127,7 +145,7 @@ ArgInfo AArch64TargetInfo::classifyArgumentType( } reportNYI("Aggregate argument type handling"); - return ArgInfo::getDirect(); + return ArgInfo::getIgnore(); } bool AArch64TargetInfo::passAsAggregateType(const Type *Ty) const { @@ -135,5 +153,53 @@ bool AArch64TargetInfo::passAsAggregateType(const Type *Ty) const { return isAggregateTypeForABI(Ty); } +bool AArch64TargetInfo::isHomogeneousAggregateBaseType(const Type *Ty) const { + // Soft-float ABI: no types are homogeneous aggregates. + if (isSoftFloat()) + return false; + + // Homogeneous aggregates for AAPCS64 must have base types of a floating + // point type or a short-vector type. + if (Ty->isFloat()) + return true; + + if (const auto *VT = dyn_cast<VectorType>(Ty)) { + // TODO: Reject SVE fixed-length data/predicate vectors once the type + // mapper can express them. + uint64_t EltWidth = VT->getElementType()->getSizeInBits().getFixedValue(); + uint64_t VecSize = std::max<uint64_t>( + 8, EltWidth * VT->getNumElements().getKnownMinValue()); + if (VecSize & (VecSize - 1)) + VecSize = alignTo(VecSize, bit_ceil(VecSize)); + if (VecSize == 64 || VecSize == 128) + return true; + } + return false; +} + +bool AArch64TargetInfo::isHomogeneousAggregateSmallEnough( + const Type * /*Base*/, uint64_t Members) const { + return Members <= 4; +} + +bool AArch64TargetInfo::isZeroLengthBitfieldPermittedInHomogeneousAggregate() + const { + // AAPCS64 applies homogeneity to the output of the data layout decision, so + // zero-length bitfields do not affect homogeneity. + return true; +} + +bool AArch64TargetInfo::isPermittedToBeHomogeneousAggregate( + const RecordType *RT) const { + if (Opts.IsMicrosoftCXXABI && RT->isCXXRecord()) { + // This won't always return false, but we don't have enough information to + // perform the full check correctly yet. + reportNYI("MicrosoftCXXABI homogeneous record classification"); + return false; + } + + return true; +} + } // namespace abi } // namespace llvm diff --git a/llvm/lib/ABI/Targets/BPF.cpp b/llvm/lib/ABI/Targets/BPF.cpp index 08127938d1747..70b838a5abd20 100644 --- a/llvm/lib/ABI/Targets/BPF.cpp +++ b/llvm/lib/ABI/Targets/BPF.cpp @@ -16,8 +16,6 @@ namespace llvm::abi { class BPFTargetInfo : public TargetInfo { private: - TypeBuilder &TB; - ArgInfo classifyReturnType(const Type *RetTy) const { if (RetTy->isVoid()) return ArgInfo::getIgnore(); @@ -72,7 +70,7 @@ class BPFTargetInfo : public TargetInfo { } public: - BPFTargetInfo(TypeBuilder &TB) : TB(TB) {} + BPFTargetInfo(TypeBuilder &Builder) : TargetInfo(Builder) {} void computeInfo(FunctionInfo &FI) const override { FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); diff --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp index fd7a5e15b3548..e2233f20074c3 100644 --- a/llvm/lib/ABI/Targets/X86.cpp +++ b/llvm/lib/ABI/Targets/X86.cpp @@ -74,7 +74,6 @@ class X86_64TargetInfo : public TargetInfo { enum Class { Integer, Sse, SseUp, X87, X87Up, ComplexX87, NoClass, Memory }; private: - TypeBuilder &TB; X86AVXABILevel AVXLevel; bool Has64BitPointers; @@ -115,7 +114,7 @@ class X86_64TargetInfo : public TargetInfo { public: X86_64TargetInfo(TypeBuilder &TypeBuilder, X86AVXABILevel AVXABILevel, bool Has64BitPtrs, const ABICompatInfo &Compat) - : TargetInfo(Compat), TB(TypeBuilder), AVXLevel(AVXABILevel), + : TargetInfo(TypeBuilder, Compat), AVXLevel(AVXABILevel), Has64BitPointers(Has64BitPtrs) {} bool has64BitPointers() const { return Has64BitPointers; } diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp index 0da1c00d4c9f4..0a3b54fae43ee 100644 --- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp +++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp @@ -13,18 +13,30 @@ #include "llvm/IR/CallingConv.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/Allocator.h" +#include "llvm/Support/TypeSize.h" #include "gtest/gtest.h" +#include <cstdint> namespace { using ABIType = llvm::abi::Type; using llvm::abi::AArch64ABIKind; +using llvm::abi::AArch64ABIOptions; using llvm::abi::ArgInfo; using llvm::abi::createAArch64TargetInfo; +using llvm::abi::FieldInfo; using llvm::abi::FunctionInfo; +using llvm::abi::RecordFlags; +using llvm::abi::RequiredArgs; +using llvm::abi::StructPacking; using llvm::abi::TargetInfo; using llvm::abi::TypeBuilder; +static void expectUncoercedDirect(const ArgInfo &Info); +static void expectExtendInteger(const ArgInfo &Info, const ABIType *Ty, + bool IsSigned); +static void expectIndirect(const ArgInfo &Info); + class AArch64TargetInfoTest : public ::testing::Test { protected: llvm::BumpPtrAllocator Alloc; @@ -38,6 +50,7 @@ class AArch64TargetInfoTest : public ::testing::Test { const ABIType *U32; const ABIType *I64; const ABIType *U64; + const ABIType *F16; const ABIType *F32; const ABIType *F64; const ABIType *Ptr; @@ -48,6 +61,9 @@ class AArch64TargetInfoTest : public ::testing::Test { const ABIType *BitInt65; const ABIType *BitInt128; const ABIType *BitInt129; + const ABIType *ComplexFloat; + const ABIType *V2F32; + const ABIType *V4F32; AArch64TargetInfoTest() : TB(Alloc), Bool(TB.getIntegerType(1, llvm::Align(1), /*Signed=*/false)), @@ -59,6 +75,7 @@ class AArch64TargetInfoTest : public ::testing::Test { U32(TB.getIntegerType(32, llvm::Align(4), /*Signed=*/false)), I64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/true)), U64(TB.getIntegerType(64, llvm::Align(8), /*Signed=*/false)), + F16(TB.getFloatType(llvm::APFloat::IEEEhalf(), llvm::Align(2))), F32(TB.getFloatType(llvm::APFloat::IEEEsingle(), llvm::Align(4))), F64(TB.getFloatType(llvm::APFloat::IEEEdouble(), llvm::Align(8))), Ptr(TB.getPointerType(64, llvm::Align(8))), Void(TB.getVoidType()), @@ -73,7 +90,28 @@ class AArch64TargetInfoTest : public ::testing::Test { BitInt128(TB.getIntegerType(128, llvm::Align(16), /*Signed=*/true, /*IsBitInt=*/true)), BitInt129(TB.getIntegerType(129, llvm::Align(16), /*Signed=*/true, - /*IsBitInt=*/true)) {} + /*IsBitInt=*/true)), + ComplexFloat(TB.getComplexType(F32, llvm::Align(4))), + V2F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(2), + llvm::Align(8))), + V4F32(TB.getVectorType(F32, llvm::ElementCount::getFixed(4), + llvm::Align(16))) {} + + static RecordFlags passableRecordFlags(bool IsCXX = false) { + unsigned Flags = RecordFlags::CanPassInRegisters; + if (IsCXX) + Flags |= RecordFlags::IsCXXRecord; + return static_cast<RecordFlags>(Flags); + } + + const ABIType *makeRecord(llvm::ArrayRef<FieldInfo> Fields, uint64_t SizeBits, + llvm::Align Align, + RecordFlags Flags = RecordFlags::CanPassInRegisters, + llvm::ArrayRef<FieldInfo> Bases = {}, + llvm::ArrayRef<FieldInfo> VBases = {}) { + return TB.getRecordType(Fields, llvm::TypeSize::getFixed(SizeBits), Align, + StructPacking::Default, Bases, VBases, Flags); + } }; static void expectUncoercedDirect(const ArgInfo &Info) { @@ -95,9 +133,13 @@ static void expectAlignedIndirect(const ArgInfo &Info, llvm::Align Align, EXPECT_EQ(Info.getIndirectByVal(), ByVal); } +static void expectIndirect(const ArgInfo &Info) { + EXPECT_TRUE(Info.isIndirect()); +} + TEST_F(AArch64TargetInfoTest, ClassifyReturnVoidIsIgnore) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {}); @@ -112,10 +154,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnVoidIsIgnore) { // return path under AAPCS. TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectAAPCS) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS)); - for (const ABIType *RetTy : - {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *RetTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F16, + F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, RetTy, {}); FI->getReturnInfo() = ArgInfo::getIgnore(); @@ -128,9 +170,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectAAPCS) { // returns are extended. TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectOrPromotableDarwin) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); - for (const ABIType *RetTy : {I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *RetTy : + {I32, U32, I64, U64, F16, F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, RetTy, {}); FI->getReturnInfo() = ArgInfo::getIgnore(); @@ -153,10 +196,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectOrPromotableDarwin) { // return path under AAPCSSoft. TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectAAPCSSoft) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCSSoft); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCSSoft)); - for (const ABIType *RetTy : - {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *RetTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F16, + F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, RetTy, {}); FI->getReturnInfo() = ArgInfo::getIgnore(); @@ -243,10 +286,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectWin64) { // argument path under AAPCS. TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectAAPCS) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS)); - for (const ABIType *ArgTy : - {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *ArgTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F16, + F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy}); TI->computeInfo(*FI); @@ -258,9 +301,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectAAPCS) { // arguments are extended. TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectOrPromotableDarwin) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); - for (const ABIType *ArgTy : {I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *ArgTy : + {I32, U32, I64, U64, F16, F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy}); TI->computeInfo(*FI); @@ -281,10 +325,10 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectOrPromotableDarwin) { // argument path under AAPCSSoft. TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectAAPCSSoft) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCSSoft); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCSSoft)); - for (const ABIType *ArgTy : - {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { + for (const ABIType *ArgTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F16, + F32, F64, Ptr, Matrix}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {ArgTy}); TI->computeInfo(*FI); @@ -435,4 +479,90 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentRecordCannotPassInRegisters) { } } +// Homogeneous floating-point aggregates of at most four members are returned +// directly under AAPCS and DarwinPCS. +TEST_F(AArch64TargetInfoTest, ClassifyReturnHFADirect) { + RecordFlags CXXFlags = passableRecordFlags(/*IsCXX=*/true); + + const ABIType *HFA2f = + makeRecord({FieldInfo(F32, 0), FieldInfo(F32, 32)}, 64, llvm::Align(4)); + const ABIType *HFA4d = makeRecord({FieldInfo(F64, 0), FieldInfo(F64, 64), + FieldInfo(F64, 128), FieldInfo(F64, 192)}, + 256, llvm::Align(8)); + const ABIType *HFA3arr = + makeRecord({FieldInfo(TB.getArrayType(F32, 3, /*SizeInBits=*/96), 0)}, 96, + llvm::Align(4)); + const ABIType *HFA2h = + makeRecord({FieldInfo(F16, 0), FieldInfo(F16, 16)}, 32, llvm::Align(2)); + const ABIType *HFANested = + makeRecord({FieldInfo(HFA2f, 0), FieldInfo(F32, 64)}, 96, llvm::Align(4)); + const ABIType *HFAZeroBF = + makeRecord({FieldInfo(I32, 0, /*IsBitField=*/true, /*BitFieldWidth=*/0), + FieldInfo(F32, 0), FieldInfo(F32, 32)}, + 64, llvm::Align(4)); + const ABIType *HFAUnion = TB.getUnionType( + {FieldInfo(F32, 0), + FieldInfo(TB.getArrayType(F32, 3, /*SizeInBits=*/96), 0)}, + llvm::TypeSize::getFixed(96), llvm::Align(4), StructPacking::Default, + RecordFlags::CanPassInRegisters); + + // Short-vector aggregates (HVAs) follow the same rules. + const ABIType *HVA2x64 = makeRecord( + {FieldInfo(V2F32, 0), FieldInfo(V2F32, 64)}, 128, llvm::Align(8)); + const ABIType *HVA2x128 = makeRecord( + {FieldInfo(V4F32, 0), FieldInfo(V4F32, 128)}, 256, llvm::Align(16)); + + // C++ records: empty bases are skipped and non-empty bases contribute + // members, whether inherited normally or as a direct virtual base. + const ABIType *EmptyRecord = makeRecord({}, 0, llvm::Align(1), CXXFlags); + const ABIType *HFAEmptyBase = + makeRecord({FieldInfo(F32, 0), FieldInfo(F32, 32)}, 64, llvm::Align(4), + CXXFlags, {FieldInfo(EmptyRecord, 0)}); + const ABIType *FloatBase = + makeRecord({FieldInfo(F32, 0)}, 32, llvm::Align(4), CXXFlags); + const ABIType *HFADerived = + makeRecord({FieldInfo(F32, 32)}, 64, llvm::Align(4), CXXFlags, + {FieldInfo(FloatBase, 0)}); + FieldInfo VirtualFloatBase(FloatBase, 0, /*IsBitField=*/false, + /*BitFieldWidth=*/0, /*IsUnnamedBitfield=*/false, + /*IsVirtualBase=*/true); + const ABIType *HFAVirtualBase = + makeRecord({}, 32, llvm::Align(4), CXXFlags, /*Bases=*/{VirtualFloatBase}, + /*VBases=*/{VirtualFloatBase}); + + for (AArch64ABIKind Kind : + {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS}) { + std::unique_ptr<TargetInfo> TI = + createAArch64TargetInfo(TB, AArch64ABIOptions(Kind)); + for (const ABIType *RetTy : + {ComplexFloat, HFA2f, HFA4d, HFA3arr, HFA2h, HFANested, HFAZeroBF, + HFAUnion, HVA2x64, HVA2x128, HFAEmptyBase, HFADerived, + HFAVirtualBase}) { + std::unique_ptr<FunctionInfo> FI = + FunctionInfo::create(llvm::CallingConv::C, RetTy, {}); + FI->getReturnInfo() = ArgInfo::getIgnore(); + TI->computeInfo(*FI); + expectUncoercedDirect(FI->getReturnInfo()); + } + } +} + +// Records that cannot pass in registers are returned indirectly before HFA +// classification. +TEST_F(AArch64TargetInfoTest, ClassifyReturnCXXCannotPassInRegistersIndirect) { + std::unique_ptr<TargetInfo> TI = + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS)); + + const ABIType *NonPassableHFA = + makeRecord({FieldInfo(F32, 0), FieldInfo(F32, 32)}, 64, llvm::Align(4), + RecordFlags::IsCXXRecord); + + std::unique_ptr<FunctionInfo> FI = + FunctionInfo::create(llvm::CallingConv::C, NonPassableHFA, {}); + FI->getReturnInfo() = ArgInfo::getIgnore(); + TI->computeInfo(*FI); + expectIndirect(FI->getReturnInfo()); + EXPECT_FALSE(FI->getReturnInfo().getIndirectByVal()); +} + } // namespace >From 501872de854d1110662f629cad0a1fea2717487c Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 1 Sep 2026 16:30:25 -0700 Subject: [PATCH 2/3] Fix unit test's virtual base expectations. --- llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 48 ++++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp index 0a3b54fae43ee..98c695f47ded8 100644 --- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp +++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp @@ -35,7 +35,6 @@ using llvm::abi::TypeBuilder; static void expectUncoercedDirect(const ArgInfo &Info); static void expectExtendInteger(const ArgInfo &Info, const ABIType *Ty, bool IsSigned); -static void expectIndirect(const ArgInfo &Info); class AArch64TargetInfoTest : public ::testing::Test { protected: @@ -133,10 +132,6 @@ static void expectAlignedIndirect(const ArgInfo &Info, llvm::Align Align, EXPECT_EQ(Info.getIndirectByVal(), ByVal); } -static void expectIndirect(const ArgInfo &Info) { - EXPECT_TRUE(Info.isIndirect()); -} - TEST_F(AArch64TargetInfoTest, ClassifyReturnVoidIsIgnore) { std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); @@ -513,7 +508,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnHFADirect) { {FieldInfo(V4F32, 0), FieldInfo(V4F32, 128)}, 256, llvm::Align(16)); // C++ records: empty bases are skipped and non-empty bases contribute - // members, whether inherited normally or as a direct virtual base. + // members. const ABIType *EmptyRecord = makeRecord({}, 0, llvm::Align(1), CXXFlags); const ABIType *HFAEmptyBase = makeRecord({FieldInfo(F32, 0), FieldInfo(F32, 32)}, 64, llvm::Align(4), @@ -523,12 +518,6 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnHFADirect) { const ABIType *HFADerived = makeRecord({FieldInfo(F32, 32)}, 64, llvm::Align(4), CXXFlags, {FieldInfo(FloatBase, 0)}); - FieldInfo VirtualFloatBase(FloatBase, 0, /*IsBitField=*/false, - /*BitFieldWidth=*/0, /*IsUnnamedBitfield=*/false, - /*IsVirtualBase=*/true); - const ABIType *HFAVirtualBase = - makeRecord({}, 32, llvm::Align(4), CXXFlags, /*Bases=*/{VirtualFloatBase}, - /*VBases=*/{VirtualFloatBase}); for (AArch64ABIKind Kind : {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS}) { @@ -536,8 +525,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnHFADirect) { createAArch64TargetInfo(TB, AArch64ABIOptions(Kind)); for (const ABIType *RetTy : {ComplexFloat, HFA2f, HFA4d, HFA3arr, HFA2h, HFANested, HFAZeroBF, - HFAUnion, HVA2x64, HVA2x128, HFAEmptyBase, HFADerived, - HFAVirtualBase}) { + HFAUnion, HVA2x64, HVA2x128, HFAEmptyBase, HFADerived}) { std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, RetTy, {}); FI->getReturnInfo() = ArgInfo::getIgnore(); @@ -557,12 +545,32 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnCXXCannotPassInRegistersIndirect) { makeRecord({FieldInfo(F32, 0), FieldInfo(F32, 32)}, 64, llvm::Align(4), RecordFlags::IsCXXRecord); - std::unique_ptr<FunctionInfo> FI = - FunctionInfo::create(llvm::CallingConv::C, NonPassableHFA, {}); - FI->getReturnInfo() = ArgInfo::getIgnore(); - TI->computeInfo(*FI); - expectIndirect(FI->getReturnInfo()); - EXPECT_FALSE(FI->getReturnInfo().getIndirectByVal()); + // struct FloatBase { float f; }; + // struct VirtualDerived : virtual FloatBase {}; + // Virtual inheritance makes the copy constructor non-trivial, so the derived + // record cannot pass in registers even though its virtual base would + // otherwise supply a homogeneous float member. The vbase pointer at offset 0 + // places the FloatBase subobject at offset 8, giving sizeof == 16. + const ABIType *FloatBase = makeRecord({FieldInfo(F32, 0)}, 32, llvm::Align(4), + passableRecordFlags(/*IsCXX=*/true)); + const ABIType *VirtualDerived = + makeRecord({}, 128, llvm::Align(8), RecordFlags::IsCXXRecord, + /*Bases=*/{}, /*VBases=*/{FieldInfo(FloatBase, 64)}); + + const struct { + const ABIType *RetTy; + llvm::Align ExpectedAlign; + } Cases[] = {{NonPassableHFA, llvm::Align(4)}, + {VirtualDerived, llvm::Align(8)}}; + + for (const auto &Case : Cases) { + std::unique_ptr<FunctionInfo> FI = + FunctionInfo::create(llvm::CallingConv::C, Case.RetTy, {}); + FI->getReturnInfo() = ArgInfo::getIgnore(); + TI->computeInfo(*FI); + expectNaturalAlignIndirect(FI->getReturnInfo(), Case.ExpectedAlign, + /*ByVal=*/false); + } } } // namespace >From 15289852876e51545c0ac3759c56567c13dc1954 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Thu, 10 Sep 2026 17:29:56 -0700 Subject: [PATCH 3/3] Address review feedback and add minimal scalable type handling --- llvm/include/llvm/ABI/TargetInfo.h | 2 +- llvm/lib/ABI/TargetInfo.cpp | 32 +++++++------------- llvm/lib/ABI/Targets/AArch64.cpp | 12 +++----- llvm/lib/ABI/Targets/X86.cpp | 4 +-- llvm/unittests/ABI/AArch64TargetInfoTest.cpp | 25 ++++++++------- 5 files changed, 32 insertions(+), 43 deletions(-) diff --git a/llvm/include/llvm/ABI/TargetInfo.h b/llvm/include/llvm/ABI/TargetInfo.h index 92b20cce3eb48..61f41992f2107 100644 --- a/llvm/include/llvm/ABI/TargetInfo.h +++ b/llvm/include/llvm/ABI/TargetInfo.h @@ -155,7 +155,7 @@ struct AArch64ABIOptions { bool IsMicrosoftCXXABI = false; AArch64ABIOptions() = default; - AArch64ABIOptions(AArch64ABIKind Kind) : Kind(Kind) {} + explicit AArch64ABIOptions(AArch64ABIKind Kind) : Kind(Kind) {} }; LLVM_ABI std::unique_ptr<TargetInfo> diff --git a/llvm/lib/ABI/TargetInfo.cpp b/llvm/lib/ABI/TargetInfo.cpp index b8cf81c2a621e..9a6780cd69f04 100644 --- a/llvm/lib/ABI/TargetInfo.cpp +++ b/llvm/lib/ABI/TargetInfo.cpp @@ -8,13 +8,10 @@ #include "llvm/ABI/TargetInfo.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/MathExtras.h" #include <algorithm> #include <cstdint> using namespace llvm::abi; -using llvm::alignTo; -using llvm::bit_ceil; using llvm::dyn_cast; bool TargetInfo::isAggregateTypeForABI(const Type *Ty) const { @@ -101,25 +98,12 @@ bool isEmptyRecordForHA(const Type *Ty) { return RT && RT->isEmpty(); } -/// Storage-container width mirroring Clang's ASTContext::getTypeSize for the -/// types that matter to homogeneous-aggregate detection. -uint64_t getHATypeSizeInBits(const Type *Ty) { - if (const auto *VT = dyn_cast<VectorType>(Ty)) { - uint64_t EltWidth = VT->getElementType()->getSizeInBits().getFixedValue(); - uint64_t Width = std::max<uint64_t>( - 8, EltWidth * VT->getNumElements().getKnownMinValue()); - if (Width & (Width - 1)) - Width = alignTo(Width, bit_ceil(Width)); - return Width; - } - return Ty->getSizeInBits().getFixedValue(); -} - } // namespace bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, uint64_t &Members) const { - if (const auto *AT = dyn_cast<ArrayType>(Ty)) { + // TODO: Keep this in sync with Clang's handling of matrix types. + if (const auto *AT = dyn_cast<ArrayType>(Ty); AT && !AT->isMatrixType()) { uint64_t NElements = AT->getNumElements(); if (NElements == 0) return false; @@ -153,6 +137,9 @@ bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, // Ignore (non-zero arrays of) empty records. const Type *FT = FD.FieldType; while (const auto *AT = dyn_cast<ArrayType>(FT)) { + // TODO: Keep this in sync with Clang's handling of matrix types. + if (AT->isMatrixType()) + break; if (AT->getNumElements() == 0) return false; FT = AT->getElementType(); @@ -176,7 +163,7 @@ bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, return false; // Ensure there is no padding. - if (getHATypeSizeInBits(Base) * Members != getHATypeSizeInBits(Ty)) + if (Base->getTypeAllocSize() * Members != Ty->getTypeAllocSize()) return false; } else { Members = 1; @@ -197,9 +184,12 @@ bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, // If it's a non-power-of-2 vector, its ABI size is already a power-of-2, // so widen it explicitly to match Clang. if (const auto *VT = dyn_cast<VectorType>(Base)) { + assert(VT->isFixedLength() && + "scalable vectors are never homogeneous aggregates"); uint64_t EltSize = VT->getElementType()->getSizeInBits().getFixedValue(); - unsigned NumElements = getHATypeSizeInBits(VT) / EltSize; + unsigned NumElements = + VT->getTypeAllocSize().getFixedValue() * 8 / EltSize; if (NumElements != VT->getNumElements().getKnownMinValue()) Base = TB.getVectorType(VT->getElementType(), ElementCount::getFixed(NumElements), @@ -208,7 +198,7 @@ bool TargetInfo::isHomogeneousAggregate(const Type *Ty, const Type *&Base, } if (Base->isVector() != ElemTy->isVector() || - getHATypeSizeInBits(Base) != getHATypeSizeInBits(ElemTy)) + Base->getTypeAllocSize() != ElemTy->getTypeAllocSize()) return false; } return Members > 0 && isHomogeneousAggregateSmallEnough(Base, Members); diff --git a/llvm/lib/ABI/Targets/AArch64.cpp b/llvm/lib/ABI/Targets/AArch64.cpp index 4fb7ad941fa91..cdc04d3c5c973 100644 --- a/llvm/lib/ABI/Targets/AArch64.cpp +++ b/llvm/lib/ABI/Targets/AArch64.cpp @@ -164,13 +164,11 @@ bool AArch64TargetInfo::isHomogeneousAggregateBaseType(const Type *Ty) const { return true; if (const auto *VT = dyn_cast<VectorType>(Ty)) { - // TODO: Reject SVE fixed-length data/predicate vectors once the type - // mapper can express them. - uint64_t EltWidth = VT->getElementType()->getSizeInBits().getFixedValue(); - uint64_t VecSize = std::max<uint64_t>( - 8, EltWidth * VT->getNumElements().getKnownMinValue()); - if (VecSize & (VecSize - 1)) - VecSize = alignTo(VecSize, bit_ceil(VecSize)); + if (VT->isScalable()) + return false; + + uint64_t VecSize = + bit_ceil(std::max<uint64_t>(8, VT->getSizeInBits().getFixedValue())); if (VecSize == 64 || VecSize == 128) return true; } diff --git a/llvm/lib/ABI/Targets/X86.cpp b/llvm/lib/ABI/Targets/X86.cpp index e2233f20074c3..9de954cc03518 100644 --- a/llvm/lib/ABI/Targets/X86.cpp +++ b/llvm/lib/ABI/Targets/X86.cpp @@ -53,9 +53,7 @@ static uint64_t getClangVectorWidthInBits(const VectorType *VT) { EltWidth = getClangIntegerWidthInBits(IT); uint64_t Width = std::max<uint64_t>(8, EltWidth * VT->getNumElements().getKnownMinValue()); - if (Width & (Width - 1)) - Width = llvm::alignTo(Width, llvm::bit_ceil(Width)); - return Width; + return llvm::bit_ceil(Width); } // The storage-container width of a type, mirroring Clang's getTypeSize. Used on diff --git a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp index 98c695f47ded8..2e21ada000d84 100644 --- a/llvm/unittests/ABI/AArch64TargetInfoTest.cpp +++ b/llvm/unittests/ABI/AArch64TargetInfoTest.cpp @@ -207,7 +207,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectAAPCSSoft) { // Wider _BitInt types are returned indirectly. TEST_F(AArch64TargetInfoTest, ClassifyReturnBitIntAAPCS) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS)); for (const ABIType *RetTy : {BitInt7, UBitInt7, BitInt65, BitInt128}) { std::unique_ptr<FunctionInfo> FI = @@ -229,7 +229,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnBitIntAAPCS) { // indirectly. TEST_F(AArch64TargetInfoTest, ClassifyReturnBitIntDarwin) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); for (const ABIType *RetTy : {BitInt65, BitInt128}) { std::unique_ptr<FunctionInfo> FI = @@ -265,7 +265,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyReturnBitIntDarwin) { // return path under Win64. TEST_F(AArch64TargetInfoTest, ClassifyReturnScalarsDirectWin64) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::Win64); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::Win64)); for (const ABIType *RetTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { @@ -335,7 +335,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectAAPCSSoft) { // Wider _BitInt types are passed indirectly without byval. TEST_F(AArch64TargetInfoTest, ClassifyArgumentBitIntAAPCS) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::AAPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::AAPCS)); for (const ABIType *ArgTy : {BitInt7, UBitInt7, BitInt65, BitInt128}) { std::unique_ptr<FunctionInfo> FI = @@ -356,7 +356,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentBitIntAAPCS) { // indirectly without byval. TEST_F(AArch64TargetInfoTest, ClassifyArgumentBitIntDarwin) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); for (const ABIType *ArgTy : {BitInt65, BitInt128}) { std::unique_ptr<FunctionInfo> FI = @@ -389,7 +389,7 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentBitIntDarwin) { // argument path under Win64. TEST_F(AArch64TargetInfoTest, ClassifyArgumentScalarsDirectWin64) { std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::Win64); + createAArch64TargetInfo(TB, AArch64ABIOptions(AArch64ABIKind::Win64)); for (const ABIType *ArgTy : {Bool, I8, U8, I16, U16, I32, U32, I64, U64, F32, F64, Ptr, Matrix}) { @@ -414,7 +414,8 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) { for (AArch64ABIKind Kind : {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64, AArch64ABIKind::AAPCSSoft}) { - std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind); + std::unique_ptr<TargetInfo> TI = + createAArch64TargetInfo(TB, AArch64ABIOptions(Kind)); std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {TUInt}); TI->computeInfo(*FI); @@ -427,8 +428,8 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) { llvm::Align(1), StructPacking::Default, RecordFlags::IsTransparent); { - std::unique_ptr<TargetInfo> TI = - createAArch64TargetInfo(TB, AArch64ABIKind::DarwinPCS); + std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo( + TB, AArch64ABIOptions(AArch64ABIKind::DarwinPCS)); std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {TUChar}); TI->computeInfo(*FI); @@ -437,7 +438,8 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentTransparentUnion) { for (AArch64ABIKind Kind : {AArch64ABIKind::AAPCS, AArch64ABIKind::Win64, AArch64ABIKind::AAPCSSoft}) { - std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind); + std::unique_ptr<TargetInfo> TI = + createAArch64TargetInfo(TB, AArch64ABIOptions(Kind)); std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {TUChar}); TI->computeInfo(*FI); @@ -465,7 +467,8 @@ TEST_F(AArch64TargetInfoTest, ClassifyArgumentRecordCannotPassInRegisters) { for (AArch64ABIKind Kind : {AArch64ABIKind::AAPCS, AArch64ABIKind::DarwinPCS, AArch64ABIKind::Win64, AArch64ABIKind::AAPCSSoft}) { - std::unique_ptr<TargetInfo> TI = createAArch64TargetInfo(TB, Kind); + std::unique_ptr<TargetInfo> TI = + createAArch64TargetInfo(TB, AArch64ABIOptions(Kind)); std::unique_ptr<FunctionInfo> FI = FunctionInfo::create(llvm::CallingConv::C, Void, {CannotPass}); TI->computeInfo(*FI); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
