https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/221375
This change extends the LLVM ABI library's VectorType to be able to describe SVE types and updates Clang's QualTypeMapper to map them. The AArch64 target info class in the ABI library is still a work in progress. It will continue to report "not yet implemented" for function signatures involving SVE types. This change is a neceasary prerequisite for correctly handling them or correctly deferring handling of these specific types. Assisted-by: Cursor / claude-opus-5 >From a1c7cf7526225cc09ee40a62f95cc1b6f8c2e65a Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Fri, 4 Sep 2026 17:51:00 -0700 Subject: [PATCH] [LLVMABI] Add support for SVE types in the LLVM ABI library This change extends the LLVM ABI library's VectorType to be able to describe SVE types and updates Clang's QualTypeMapper to map them. The AArch64 target info class in the ABI library is still a work in progress. It will continue to report "not yet implemented" for function signatures involving SVE types. This change is a neceasary prerequisite for correctly handling them or correctly deferring handling of these specific types. Assisted-by: Cursor / claude-opus-5 --- clang/lib/CodeGen/QualTypeMapper.cpp | 78 +++++- clang/lib/CodeGen/QualTypeMapper.h | 1 + clang/unittests/CodeGen/CMakeLists.txt | 3 + .../unittests/CodeGen/QualTypeMapperTest.cpp | 258 ++++++++++++++++++ llvm/include/llvm/ABI/Types.h | 80 +++++- llvm/lib/ABI/IRTypeMapper.cpp | 11 +- llvm/unittests/ABI/TypesTest.cpp | 79 ++++++ .../clang/unittests/CodeGen/BUILD.gn | 3 + 8 files changed, 499 insertions(+), 14 deletions(-) create mode 100644 clang/unittests/CodeGen/QualTypeMapperTest.cpp diff --git a/clang/lib/CodeGen/QualTypeMapper.cpp b/clang/lib/CodeGen/QualTypeMapper.cpp index c095b9394f8e5..fadb0d41a1756 100644 --- a/clang/lib/CodeGen/QualTypeMapper.cpp +++ b/clang/lib/CodeGen/QualTypeMapper.cpp @@ -34,6 +34,31 @@ namespace clang { namespace CodeGen { +/// Returns true if \p BT is one of the AArch64 SVE predicate types, i.e. +/// svbool_t or one of its tuples. +static bool isSVEPredicateBuiltinType(const BuiltinType *BT) { + switch (BT->getKind()) { +#define SVE_PREDICATE_TYPE(Name, MangledName, Id, SingletonId) \ + case BuiltinType::Id: \ + return true; +#include "clang/Basic/AArch64ACLETypes.def" + default: + return false; + } +} + +/// Maps a Clang vector kind onto the ABI library's notion of a vector flavor. +static llvm::abi::VectorKind getABIVectorKind(clang::VectorKind Kind) { + switch (Kind) { + case clang::VectorKind::SveFixedLengthData: + return llvm::abi::VectorKind::SVEData; + case clang::VectorKind::SveFixedLengthPredicate: + return llvm::abi::VectorKind::SVEPredicate; + default: + return llvm::abi::VectorKind::Generic; + } +} + /// Main entry point for converting Clang QualType to LLVM ABI Type. /// This method performs type canonicalization, caching, and dispatches /// to specialized conversion methods based on the type kind. @@ -242,11 +267,27 @@ QualTypeMapper::convertBuiltinType(const BuiltinType *BT) { case BuiltinType::ObjCSel: return createPointerTypeForPointee(QT); - // Target-specific vector/matrix types — not yet implemented. -#define SVE_TYPE(Name, Id, SingletonId) case BuiltinType::Id: + // AArch64 SVE data and predicate types, including the x2/x3/x4 tuples. +#define SVE_VECTOR_TYPE(Name, MangledName, Id, SingletonId) \ + case BuiltinType::Id: +#define SVE_PREDICATE_TYPE(Name, MangledName, Id, SingletonId) \ + case BuiltinType::Id: #include "clang/Basic/AArch64ACLETypes.def" + return convertSVEBuiltinType(BT); + + case BuiltinType::SveCount: + return Builder.getSVECountType(getTypeAlign(QT)); + + // TODO: __mfp8 has no floating-point semantics of its own, so representing + // it needs a decision about how the ABI library should model opaque + // floating-point data. As an mfloat8 vector element it is treated as an + // 8-bit integer, but that is not right for the scalar type, which is passed + // in a floating-point register. + case BuiltinType::MFloat8: llvm::reportFatalInternalError( - "AArch64 SVE types not yet supported in ABI lowering library"); + "__mfp8 is not yet supported in the ABI lowering library"); + + // Target-specific vector/matrix types — not yet implemented. #define PPC_VECTOR_TYPE(Name, Id, Size) case BuiltinType::Id: #include "clang/Basic/PPCTypes.def" llvm::reportFatalInternalError( @@ -318,7 +359,36 @@ const llvm::abi::Type *QualTypeMapper::convertVectorType(const VectorType *VT) { llvm::ElementCount NumElements = llvm::ElementCount::getFixed(NElems); llvm::Align VectorAlign = getTypeAlign(VectorQualType); - return Builder.getVectorType(ElementType, NumElements, VectorAlign); + return Builder.getVectorType(ElementType, NumElements, VectorAlign, + getABIVectorKind(VT->getVectorKind())); +} + +/// Converts the sizeless AArch64 SVE data and predicate builtin types, +/// including the x2/x3/x4 tuples, to scalable LLVM ABI vector types. +/// +/// \param BT The SVE BuiltinType to convert +/// \return LLVM ABI VectorType with a scalable element count +const llvm::abi::Type * +QualTypeMapper::convertSVEBuiltinType(const BuiltinType *BT) { + ASTContext::BuiltinVectorTypeInfo Info = ASTCtx.getBuiltinVectorTypeInfo(BT); + assert(Info.NumVectors > 0 && Info.NumVectors <= 4 && + "Expected 1, 2, 3 or 4 vectors!"); + + // __mfp8 carries no floating-point semantics, so mfloat8 vectors use an + // 8-bit integer element type, which is also how they are represented in + // LLVM IR. + const llvm::abi::Type *ElementType = + Info.ElementType->isMFloat8Type() + ? Builder.getIntegerType(8, llvm::Align(1), /*Signed=*/false) + : convertType(Info.ElementType); + + llvm::abi::VectorKind VecKind = isSVEPredicateBuiltinType(BT) + ? llvm::abi::VectorKind::SVEPredicate + : llvm::abi::VectorKind::SVEData; + + return Builder.getVectorType(ElementType, Info.EC, + getTypeAlign(QualType(BT, 0)), VecKind, + Info.NumVectors); } /// Converts complex types to LLVM ABI complex representations. diff --git a/clang/lib/CodeGen/QualTypeMapper.h b/clang/lib/CodeGen/QualTypeMapper.h index 35876f44f3aba..f6bb43482c705 100644 --- a/clang/lib/CodeGen/QualTypeMapper.h +++ b/clang/lib/CodeGen/QualTypeMapper.h @@ -41,6 +41,7 @@ class QualTypeMapper { const llvm::abi::Type *convertBuiltinType(const clang::BuiltinType *BT); const llvm::abi::Type *convertArrayType(const clang::ArrayType *AT); const llvm::abi::Type *convertVectorType(const clang::VectorType *VT); + const llvm::abi::Type *convertSVEBuiltinType(const clang::BuiltinType *BT); const llvm::abi::Type *convertRecordType(const clang::RecordType *RT); const llvm::abi::Type *convertEnumType(const clang::EnumType *ET); const llvm::abi::Type *convertComplexType(const ComplexType *CT); diff --git a/clang/unittests/CodeGen/CMakeLists.txt b/clang/unittests/CodeGen/CMakeLists.txt index d4efb2230a054..7e2782e09117c 100644 --- a/clang/unittests/CodeGen/CMakeLists.txt +++ b/clang/unittests/CodeGen/CMakeLists.txt @@ -4,6 +4,7 @@ add_clang_unittest(ClangCodeGenTests DemangleTrapReasonInDebugInfo.cpp TBAAMetadataTest.cpp CheckTargetFeaturesTest.cpp + QualTypeMapperTest.cpp CLANG_LIBS clangAST clangBasic @@ -12,7 +13,9 @@ add_clang_unittest(ClangCodeGenTests clangLex clangParse clangSerialization + clangTesting LLVM_COMPONENTS + ABI Core Support TargetParser diff --git a/clang/unittests/CodeGen/QualTypeMapperTest.cpp b/clang/unittests/CodeGen/QualTypeMapperTest.cpp new file mode 100644 index 0000000000000..4e354ab641e05 --- /dev/null +++ b/clang/unittests/CodeGen/QualTypeMapperTest.cpp @@ -0,0 +1,258 @@ +//===- QualTypeMapperTest.cpp - Tests for QualType to ABI type mapping ----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Tests that QualTypeMapper maps the AArch64 SVE types onto the expected +/// LLVM ABI type representations. +/// +//===----------------------------------------------------------------------===// + +#include "../../lib/CodeGen/QualTypeMapper.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" +#include "clang/Testing/CommandLineArgs.h" +#include "clang/Testing/TestAST.h" +#include "llvm/ABI/Types.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/Support/Alignment.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/TypeSize.h" +#include "gtest/gtest.h" + +using namespace clang; + +namespace { + +/// Parses a translation unit for an AArch64 target, which makes the SVE +/// builtin types available on the ASTContext, and exposes a QualTypeMapper +/// over the resulting ASTContext. +class QualTypeMapperSVETest : public ::testing::Test { +protected: + QualTypeMapperSVETest() + : AST(makeInputs()), Mapper(AST.context(), DL, Alloc) {} + + ASTContext &context() { return AST.context(); } + + /// Maps \p QT and returns it as an ABI vector type, or null if it did not + /// map to a vector. + const llvm::abi::VectorType *mapToVector(QualType QT) { + return dyn_cast<llvm::abi::VectorType>(Mapper.convertType(QT)); + } + + /// Returns the underlying type of the file-scope typedef named \p Name. + QualType lookupTypedef(StringRef Name) { + for (Decl *D : context().getTranslationUnitDecl()->decls()) + if (const auto *TD = dyn_cast<TypedefNameDecl>(D)) + if (TD->getName() == Name) + return TD->getUnderlyingType(); + ADD_FAILURE() << "no typedef named " << Name; + return QualType(); + } + + /// Checks an SVE data vector of \p NF vectors of \p NumEls elements, each + /// \p ElBits wide. \p IsFP selects whether the element is expected to be a + /// floating-point or an integer type. + void checkDataVector(StringRef Name, QualType QT, unsigned NumEls, + unsigned ElBits, unsigned NF, bool IsFP) { + SCOPED_TRACE(Name); + const llvm::abi::VectorType *VT = mapToVector(QT); + ASSERT_NE(VT, nullptr) << "did not map to a vector type"; + + EXPECT_EQ(VT->getVectorKind(), llvm::abi::VectorKind::SVEData); + EXPECT_TRUE(VT->isScalable()); + EXPECT_EQ(VT->getNumElements(), llvm::ElementCount::getScalable(NumEls)); + EXPECT_EQ(VT->getNumVectors(), NF); + EXPECT_EQ(VT->isTuple(), NF > 1); + EXPECT_EQ(VT->getSizeInBits(), + llvm::TypeSize::getScalable(NumEls * ElBits * NF)); + // AAPCS64 aligns every SVE data vector, including the tuples, to 16 bytes. + EXPECT_EQ(VT->getAlignment(), llvm::Align(16)); + + const llvm::abi::Type *Elt = VT->getElementType(); + EXPECT_EQ(Elt->getSizeInBits(), llvm::TypeSize::getFixed(ElBits)); + EXPECT_EQ(Elt->isFloat(), IsFP); + EXPECT_EQ(Elt->isInteger(), !IsFP); + } + + /// Checks an SVE predicate vector of \p NF vectors of \p NumEls elements. + void checkPredicateVector(StringRef Name, QualType QT, unsigned NumEls, + unsigned NF) { + SCOPED_TRACE(Name); + const llvm::abi::VectorType *VT = mapToVector(QT); + ASSERT_NE(VT, nullptr) << "did not map to a vector type"; + + EXPECT_EQ(VT->getVectorKind(), llvm::abi::VectorKind::SVEPredicate); + EXPECT_TRUE(VT->isScalable()); + EXPECT_EQ(VT->getNumElements(), llvm::ElementCount::getScalable(NumEls)); + EXPECT_EQ(VT->getNumVectors(), NF); + EXPECT_EQ(VT->getSizeInBits(), llvm::TypeSize::getScalable(NumEls * NF)); + EXPECT_EQ(VT->getAlignment(), llvm::Align(2)); + + const llvm::abi::Type *Elt = VT->getElementType(); + ASSERT_TRUE(Elt->isInteger()); + EXPECT_EQ(Elt->getSizeInBits(), llvm::TypeSize::getFixed(1)); + } + +private: + static TestInputs makeInputs() { + TestInputs Inputs(R"c( +typedef __SVInt32_t fixed_int32_t __attribute__((arm_sve_vector_bits(256))); +typedef __SVBool_t fixed_bool_t __attribute__((arm_sve_vector_bits(256))); +typedef int generic_int32x4_t __attribute__((vector_size(16))); +)c"); + Inputs.Language = TestLanguage::Lang_C99; + // The fixed-length SVE typedefs require a known vector length, which must + // agree with the width in the attribute. + Inputs.ExtraArgs = {"-triple", "aarch64-unknown-linux-gnu", + "-target-feature", "+sve", + "-mvscale-min=2", "-mvscale-max=2"}; + return Inputs; + } + + TestAST AST; + llvm::DataLayout DL; + llvm::BumpPtrAllocator Alloc; + CodeGen::QualTypeMapper Mapper; +}; + +// Every SVE data vector, including the x2/x3/x4 tuples, maps to a scalable +// vector tagged as SVE data. +TEST_F(QualTypeMapperSVETest, DataVectors) { +#define SVE_VECTOR_TYPE_INT(Name, MangledName, Id, SingletonId, NumEls, \ + ElBits, NF, IsSigned) \ + checkDataVector(#Name, context().SingletonId, NumEls, ElBits, NF, \ + /*IsFP=*/false); +#define SVE_VECTOR_TYPE_FLOAT(Name, MangledName, Id, SingletonId, NumEls, \ + ElBits, NF) \ + checkDataVector(#Name, context().SingletonId, NumEls, ElBits, NF, \ + /*IsFP=*/true); +#define SVE_VECTOR_TYPE_BFLOAT(Name, MangledName, Id, SingletonId, NumEls, \ + ElBits, NF) \ + checkDataVector(#Name, context().SingletonId, NumEls, ElBits, NF, \ + /*IsFP=*/true); + // mfloat8 vectors use an integer element, since __mfp8 has no + // floating-point semantics of its own. +#define SVE_VECTOR_TYPE_MFLOAT(Name, MangledName, Id, SingletonId, NumEls, \ + ElBits, NF) \ + checkDataVector(#Name, context().SingletonId, NumEls, ElBits, NF, \ + /*IsFP=*/false); +#include "clang/Basic/AArch64ACLETypes.def" +} + +// Every SVE predicate, including the x2/x4 tuples, maps to a scalable vector +// of one-bit elements tagged as an SVE predicate. +TEST_F(QualTypeMapperSVETest, PredicateVectors) { +#define SVE_PREDICATE_TYPE_ALL(Name, MangledName, Id, SingletonId, NumEls, NF) \ + checkPredicateVector(#Name, context().SingletonId, NumEls, NF); +#include "clang/Basic/AArch64ACLETypes.def" +} + +// Spot-check a few representative types against their IR spellings, so that +// the macro-driven tests above are anchored to concrete expectations. +TEST_F(QualTypeMapperSVETest, RepresentativeTypes) { + // svint8_t is <vscale x 16 x i8>. + const llvm::abi::VectorType *SVInt8 = mapToVector(context().SveInt8Ty); + ASSERT_NE(SVInt8, nullptr); + EXPECT_EQ(SVInt8->getNumElements(), llvm::ElementCount::getScalable(16)); + EXPECT_EQ(SVInt8->getSizeInBits(), llvm::TypeSize::getScalable(128)); + EXPECT_TRUE( + cast<llvm::abi::IntegerType>(SVInt8->getElementType())->isSigned()); + + // svuint8_t has the same shape but an unsigned element. + const llvm::abi::VectorType *SVUint8 = mapToVector(context().SveUint8Ty); + ASSERT_NE(SVUint8, nullptr); + EXPECT_FALSE( + cast<llvm::abi::IntegerType>(SVUint8->getElementType())->isSigned()); + + // svfloat64x2_t is two <vscale x 2 x double> vectors. + const llvm::abi::VectorType *SVFloat64x2 = + mapToVector(context().SveFloat64x2Ty); + ASSERT_NE(SVFloat64x2, nullptr); + EXPECT_EQ(SVFloat64x2->getNumVectors(), 2u); + EXPECT_EQ(SVFloat64x2->getNumElements(), llvm::ElementCount::getScalable(2)); + EXPECT_EQ(SVFloat64x2->getSizeInBits(), llvm::TypeSize::getScalable(256)); + + // svbool_t is <vscale x 16 x i1>. + const llvm::abi::VectorType *SVBool = mapToVector(context().SveBoolTy); + ASSERT_NE(SVBool, nullptr); + EXPECT_EQ(SVBool->getNumElements(), llvm::ElementCount::getScalable(16)); + EXPECT_EQ(SVBool->getSizeInBits(), llvm::TypeSize::getScalable(16)); +} + +// __mfp8 has no floating-point semantics, so mfloat8 vectors use an 8-bit +// integer element. That makes svmfloat8_t share a representation with +// svuint8_t, which is also how the two are represented in LLVM IR. +TEST_F(QualTypeMapperSVETest, MFloat8VectorUsesIntegerElement) { + const llvm::abi::VectorType *SVMFloat8 = mapToVector(context().SveMFloat8Ty); + ASSERT_NE(SVMFloat8, nullptr); + + EXPECT_EQ(SVMFloat8->getVectorKind(), llvm::abi::VectorKind::SVEData); + EXPECT_EQ(SVMFloat8->getNumElements(), llvm::ElementCount::getScalable(16)); + + const auto *Elt = + dyn_cast<llvm::abi::IntegerType>(SVMFloat8->getElementType()); + ASSERT_NE(Elt, nullptr); + EXPECT_EQ(Elt->getSizeInBits(), llvm::TypeSize::getFixed(8)); + EXPECT_FALSE(Elt->isSigned()); +} + +// __SVCount_t is opaque, and is given the shape of svbool_t because it +// occupies a predicate register. +TEST_F(QualTypeMapperSVETest, SVECount) { + const llvm::abi::VectorType *SVCount = mapToVector(context().SveCountTy); + ASSERT_NE(SVCount, nullptr); + + EXPECT_EQ(SVCount->getVectorKind(), llvm::abi::VectorKind::SVECount); + EXPECT_TRUE(SVCount->isSVEType()); + EXPECT_FALSE(SVCount->isSVEPredicate()); + EXPECT_TRUE(SVCount->isScalable()); + EXPECT_FALSE(SVCount->isTuple()); + EXPECT_EQ(SVCount->getNumElements(), llvm::ElementCount::getScalable(16)); + EXPECT_EQ(SVCount->getSizeInBits(), llvm::TypeSize::getScalable(16)); + EXPECT_EQ(SVCount->getAlignment(), llvm::Align(2)); +} + +// The arm_sve_vector_bits types are fixed-length, but they still have to be +// distinguishable from an ordinary vector of the same shape. +TEST_F(QualTypeMapperSVETest, FixedLengthSVEVectors) { + const llvm::abi::VectorType *FixedInt32 = + mapToVector(lookupTypedef("fixed_int32_t")); + ASSERT_NE(FixedInt32, nullptr); + EXPECT_EQ(FixedInt32->getVectorKind(), llvm::abi::VectorKind::SVEData); + EXPECT_FALSE(FixedInt32->isScalable()); + EXPECT_FALSE(FixedInt32->isTuple()); + EXPECT_EQ(FixedInt32->getNumElements(), llvm::ElementCount::getFixed(8)); + EXPECT_EQ(FixedInt32->getSizeInBits(), llvm::TypeSize::getFixed(256)); + + // Clang derives the element count of a fixed-length predicate by dividing + // the vector length in bits by the square of the char width, so a 256-bit + // vector length gives 4 elements. + const llvm::abi::VectorType *FixedBool = + mapToVector(lookupTypedef("fixed_bool_t")); + ASSERT_NE(FixedBool, nullptr); + EXPECT_EQ(FixedBool->getVectorKind(), llvm::abi::VectorKind::SVEPredicate); + EXPECT_FALSE(FixedBool->isScalable()); + EXPECT_EQ(FixedBool->getNumElements(), llvm::ElementCount::getFixed(4)); +} + +// An ordinary vector must not be mistaken for an SVE type. +TEST_F(QualTypeMapperSVETest, PlainVectorIsGeneric) { + const llvm::abi::VectorType *Int32x4 = + mapToVector(lookupTypedef("generic_int32x4_t")); + ASSERT_NE(Int32x4, nullptr); + + EXPECT_EQ(Int32x4->getVectorKind(), llvm::abi::VectorKind::Generic); + EXPECT_FALSE(Int32x4->isSVEType()); + EXPECT_FALSE(Int32x4->isScalable()); + EXPECT_EQ(Int32x4->getNumElements(), llvm::ElementCount::getFixed(4)); + EXPECT_EQ(Int32x4->getSizeInBits(), llvm::TypeSize::getFixed(128)); +} + +} // namespace diff --git a/llvm/include/llvm/ABI/Types.h b/llvm/include/llvm/ABI/Types.h index 88b99e9e095d3..d04bd765ab5e6 100644 --- a/llvm/include/llvm/ABI/Types.h +++ b/llvm/include/llvm/ABI/Types.h @@ -81,7 +81,7 @@ class Type { bool isRecord() const { return Kind == TypeKind::Record; } bool isMemberPointer() const { return Kind == TypeKind::MemberPointer; } bool isComplex() const { return Kind == TypeKind::Complex; } - bool isZeroSize() const { return getSizeInBits().getFixedValue() == 0; } + bool isZeroSize() const { return getSizeInBits().isZero(); } }; class VoidType : public Type { @@ -209,23 +209,73 @@ class ArrayType : public Type { static bool classof(const Type *T) { return T->getKind() == TypeKind::Array; } }; +/// Distinguishes the vector flavors that ABIs have to treat differently. +/// Scalability is not part of the kind. It is tracked by the vector's +/// ElementCount, because some flavors have both a scalable and a +/// fixed-length spelling. +enum class VectorKind { + /// A plain vector, such as a Neon vector or a GCC vector_size vector. + Generic, + + /// An AArch64 SVE data vector, such as svint32_t or svfloat64x2_t. Data + /// vectors are passed in Z registers. + SVEData, + + /// An AArch64 SVE predicate vector, such as svbool_t or svboolx4_t. + /// Predicate vectors have one-bit elements and are passed in P registers. + SVEPredicate, + + /// The AArch64 __SVCount_t type. It is opaque rather than a real vector, + /// but it occupies a predicate register, so it is given the same shape as + /// svbool_t. + SVECount, +}; + class VectorType : public Type { private: const Type *ElementType; ElementCount NumElements; + VectorKind VecKind; + unsigned NumVectors; + + static TypeSize computeSizeInBits(const Type *ElementType, + ElementCount NumElements, + unsigned NumVectors) { + return TypeSize(ElementType->getSizeInBits().getFixedValue() * + NumElements.getKnownMinValue() * NumVectors, + NumElements.isScalable()); + } public: - VectorType(const Type *ElementType, ElementCount NumElements, Align ABIAlign) + VectorType(const Type *ElementType, ElementCount NumElements, Align ABIAlign, + VectorKind VecKind = VectorKind::Generic, unsigned NumVectors = 1) : Type(TypeKind::Vector, - TypeSize(ElementType->getSizeInBits().getFixedValue() * - NumElements.getKnownMinValue(), - NumElements.isScalable()), - ABIAlign), - ElementType(ElementType), NumElements(NumElements) {} + computeSizeInBits(ElementType, NumElements, NumVectors), ABIAlign), + ElementType(ElementType), NumElements(NumElements), VecKind(VecKind), + NumVectors(NumVectors) {} const Type *getElementType() const { return ElementType; } + + /// Returns the element count of a single vector. A tuple type holds + /// getNumVectors() vectors of this shape. ElementCount getNumElements() const { return NumElements; } + VectorKind getVectorKind() const { return VecKind; } + + /// Returns the number of vectors making up an SVE tuple type, which is 1, + /// 2, 3, or 4. Every other vector type holds exactly one. + unsigned getNumVectors() const { return NumVectors; } + bool isTuple() const { return NumVectors > 1; } + + bool isScalable() const { return NumElements.isScalable(); } + + bool isSVEData() const { return VecKind == VectorKind::SVEData; } + bool isSVEPredicate() const { return VecKind == VectorKind::SVEPredicate; } + bool isSVECount() const { return VecKind == VectorKind::SVECount; } + + /// Returns true for any of the AArch64 SVE flavors. + bool isSVEType() const { return VecKind != VectorKind::Generic; } + static bool classof(const Type *T) { return T->getKind() == TypeKind::Vector; } @@ -369,9 +419,21 @@ class TypeBuilder { } const VectorType *getVectorType(const Type *ElementType, - ElementCount NumElements, Align Align) { + ElementCount NumElements, Align Align, + VectorKind VecKind = VectorKind::Generic, + unsigned NumVectors = 1) { return new (Allocator.Allocate<VectorType>()) - VectorType(ElementType, NumElements, Align); + VectorType(ElementType, NumElements, Align, VecKind, NumVectors); + } + + /// Creates the AArch64 __SVCount_t type. The type is opaque, so it is + /// modeled with the shape of svbool_t: a scalable vector of 16 one-bit + /// elements. + const VectorType *getSVECountType(Align ABIAlign) { + const Type *PredicateBit = + getIntegerType(1, Align(1), /*Signed=*/false, /*IsBitInt=*/false); + return getVectorType(PredicateBit, ElementCount::getScalable(16), ABIAlign, + VectorKind::SVECount); } const RecordType *getRecordType(ArrayRef<FieldInfo> Fields, TypeSize Size, diff --git a/llvm/lib/ABI/IRTypeMapper.cpp b/llvm/lib/ABI/IRTypeMapper.cpp index bcd133ae30c41..4b1185ce3e2f2 100644 --- a/llvm/lib/ABI/IRTypeMapper.cpp +++ b/llvm/lib/ABI/IRTypeMapper.cpp @@ -77,8 +77,17 @@ llvm::Type *IRTypeMapper::convertArrayType(const abi::ArrayType *AT) { } llvm::Type *IRTypeMapper::convertVectorType(const abi::VectorType *VT) { + if (VT->isSVECount()) + return llvm::TargetExtType::get(Context, "aarch64.svcount"); + llvm::Type *ElementType = convertType(VT->getElementType()); - return llvm::VectorType::get(ElementType, VT->getNumElements()); + llvm::Type *VecTy = llvm::VectorType::get(ElementType, VT->getNumElements()); + if (!VT->isTuple()) + return VecTy; + + // SVE tuples are a struct with one element per vector. + SmallVector<llvm::Type *, 4> Elements(VT->getNumVectors(), VecTy); + return llvm::StructType::get(Context, Elements); } llvm::Type *IRTypeMapper::convertRecordType(const abi::RecordType *RT) { diff --git a/llvm/unittests/ABI/TypesTest.cpp b/llvm/unittests/ABI/TypesTest.cpp index 85a9d368b1e25..ef5cabb98525a 100644 --- a/llvm/unittests/ABI/TypesTest.cpp +++ b/llvm/unittests/ABI/TypesTest.cpp @@ -14,12 +14,15 @@ #include "gtest/gtest.h" using llvm::Align; +using llvm::ElementCount; using llvm::TypeSize; using llvm::abi::FieldInfo; using llvm::abi::RecordFlags; using llvm::abi::RecordType; using llvm::abi::StructPacking; using llvm::abi::TypeBuilder; +using llvm::abi::VectorKind; +using llvm::abi::VectorType; namespace { @@ -125,4 +128,80 @@ TEST_F(ABITypesTest, DirectVirtualBasesAndVTablePointer) { ->isEmpty()); } +TEST_F(ABITypesTest, GenericVector) { + const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), /*Signed=*/true); + const VectorType *V4I32 = + TB.getVectorType(I32, ElementCount::getFixed(4), Align(16)); + + EXPECT_EQ(V4I32->getVectorKind(), VectorKind::Generic); + EXPECT_FALSE(V4I32->isSVEType()); + EXPECT_FALSE(V4I32->isScalable()); + EXPECT_FALSE(V4I32->isTuple()); + EXPECT_EQ(V4I32->getNumVectors(), 1u); + EXPECT_EQ(V4I32->getSizeInBits(), TypeSize::getFixed(128)); +} + +// svint32_t is <vscale x 4 x i32>. +TEST_F(ABITypesTest, SVEDataVector) { + const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), /*Signed=*/true); + const VectorType *SVInt32 = TB.getVectorType( + I32, ElementCount::getScalable(4), Align(16), VectorKind::SVEData); + + EXPECT_TRUE(SVInt32->isSVEData()); + EXPECT_TRUE(SVInt32->isSVEType()); + EXPECT_TRUE(SVInt32->isScalable()); + EXPECT_FALSE(SVInt32->isTuple()); + EXPECT_EQ(SVInt32->getSizeInBits(), TypeSize::getScalable(128)); + EXPECT_EQ(SVInt32->getAlignment(), Align(16)); +} + +// svint32x3_t is three <vscale x 4 x i32> vectors. +TEST_F(ABITypesTest, SVEDataVectorTuple) { + const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), /*Signed=*/true); + const VectorType *SVInt32x3 = + TB.getVectorType(I32, ElementCount::getScalable(4), Align(16), + VectorKind::SVEData, /*NumVectors=*/3); + + EXPECT_TRUE(SVInt32x3->isSVEData()); + EXPECT_TRUE(SVInt32x3->isTuple()); + EXPECT_EQ(SVInt32x3->getNumVectors(), 3u); + // getNumElements() describes one vector of the tuple, while the size covers + // all of them. + EXPECT_EQ(SVInt32x3->getNumElements(), ElementCount::getScalable(4)); + EXPECT_EQ(SVInt32x3->getSizeInBits(), TypeSize::getScalable(384)); +} + +// svbool_t is <vscale x 16 x i1>. +TEST_F(ABITypesTest, SVEPredicateVector) { + const llvm::abi::Type *I1 = TB.getIntegerType(1, Align(1), /*Signed=*/false); + const VectorType *SVBool = TB.getVectorType( + I1, ElementCount::getScalable(16), Align(2), VectorKind::SVEPredicate); + + EXPECT_TRUE(SVBool->isSVEPredicate()); + EXPECT_FALSE(SVBool->isSVEData()); + EXPECT_TRUE(SVBool->isScalable()); + EXPECT_EQ(SVBool->getSizeInBits(), TypeSize::getScalable(16)); + EXPECT_EQ(SVBool->getAlignment(), Align(2)); +} + +TEST_F(ABITypesTest, SVECount) { + const VectorType *SVCount = TB.getSVECountType(Align(2)); + + EXPECT_TRUE(SVCount->isSVECount()); + EXPECT_TRUE(SVCount->isSVEType()); + EXPECT_FALSE(SVCount->isSVEPredicate()); + EXPECT_TRUE(SVCount->isScalable()); + EXPECT_FALSE(SVCount->isTuple()); + EXPECT_EQ(SVCount->getSizeInBits(), TypeSize::getScalable(16)); +} + +// Scalable vectors have no fixed size, so isZeroSize() must not query one. +TEST_F(ABITypesTest, ScalableVectorIsNotZeroSized) { + const llvm::abi::Type *I32 = TB.getIntegerType(32, Align(4), /*Signed=*/true); + const VectorType *SVInt32 = TB.getVectorType( + I32, ElementCount::getScalable(4), Align(16), VectorKind::SVEData); + + EXPECT_FALSE(SVInt32->isZeroSize()); +} + } // namespace diff --git a/llvm/utils/gn/secondary/clang/unittests/CodeGen/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/CodeGen/BUILD.gn index bd8d9610c2a4a..faca44f0f5d95 100644 --- a/llvm/utils/gn/secondary/clang/unittests/CodeGen/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/CodeGen/BUILD.gn @@ -9,6 +9,8 @@ unittest("ClangCodeGenTests") { "//clang/lib/Frontend", "//clang/lib/Lex", "//clang/lib/Parse", + "//clang/lib/Testing", + "//llvm/lib/ABI", "//llvm/lib/IR", "//llvm/lib/Support", "//llvm/lib/TargetParser", @@ -18,6 +20,7 @@ unittest("ClangCodeGenTests") { "CheckTargetFeaturesTest.cpp", "CodeGenExternalTest.cpp", "DemangleTrapReasonInDebugInfo.cpp", + "QualTypeMapperTest.cpp", "TBAAMetadataTest.cpp", ] } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
