From df8d0a53a31e1351bb6cd3b340e9012b489e9885 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Wed, 11 Oct 2023 17:26:11 +0200 Subject: [PATCH 01/10] Add __builtin_vectorelements to get the number of elements in a fixed-sized vector at compile-time or via a @llvm.vscale call at runtime. --- clang/include/clang/AST/Type.h | 3 +++ clang/include/clang/Basic/Builtins.def | 1 + clang/include/clang/Basic/TokenKinds.def | 1 + clang/lib/AST/ExprConstant.cpp | 8 ++++++++ clang/lib/AST/ItaniumMangle.cpp | 8 ++++++++ clang/lib/AST/Type.cpp | 6 +++++- clang/lib/CodeGen/CGExprScalar.cpp | 12 ++++++++++++ clang/lib/Parse/ParseExpr.cpp | 7 +++++-- clang/lib/Sema/SemaChecking.cpp | 18 ++++++++++++++++++ clang/lib/Sema/SemaExpr.cpp | 14 ++++++++++++++ 10 files changed, 75 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index a78d8f60462b231..f6e425783176ba2 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2058,6 +2058,9 @@ class alignas(8) Type : public ExtQualsTypeCommonBase { bool isSizelessType() const; bool isSizelessBuiltinType() const; + /// Returns true for all scalable vector types. + bool isSizelessVectorType() const; + /// Returns true for SVE scalable vector types. bool isSVESizelessBuiltinType() const; diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 6ea8484606cfd5d..6033e8a955fb8bd 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -674,6 +674,7 @@ BUILTIN(__builtin_debugtrap, "v", "n") BUILTIN(__builtin_unreachable, "v", "nr") BUILTIN(__builtin_shufflevector, "v." , "nct") BUILTIN(__builtin_convertvector, "v." , "nct") +BUILTIN(__builtin_vectorelements, "v." , "nct") BUILTIN(__builtin_alloca, "v*z" , "Fn") BUILTIN(__builtin_alloca_uninitialized, "v*z", "Fn") BUILTIN(__builtin_alloca_with_align, "v*zIz", "Fn") diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 94db56a9fd5d78c..bbae1200d376c0d 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -746,6 +746,7 @@ ALIAS("_pascal" , __pascal , KEYBORLAND) // Clang Extensions. KEYWORD(__builtin_convertvector , KEYALL) +UNARY_EXPR_OR_TYPE_TRAIT(__builtin_vectorelements, VectorElements, KEYALL) ALIAS("__char16_t" , char16_t , KEYCXX) ALIAS("__char32_t" , char32_t , KEYCXX) KEYWORD(__builtin_bit_cast , KEYALL) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index e5539dedec02a4b..eb36a57e462f3f1 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13595,6 +13595,14 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( Info.Ctx.getOpenMPDefaultSimdAlign(E->getArgumentType())) .getQuantity(), E); + case UETT_VectorElements: { + QualType Ty = E->getTypeOfArgument(); + // If the vector has a fixed size, we can determine the number of elements at compile time. + if (Ty->isVectorType()) + return Success(Ty->castAs()->getNumElements(), E); + + return false; + } } llvm_unreachable("unknown expr/type trait"); diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index 23ec35cae4b7b40..171dfe429c12d31 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -5126,6 +5126,14 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity, Diags.Report(DiagID); return; } + case UETT_VectorElements: { + DiagnosticsEngine &Diags = Context.getDiags(); + unsigned DiagID = Diags.getCustomDiagID( + DiagnosticsEngine::Error, + "cannot yet mangle __builtin_vectorelements expression"); + Diags.Report(DiagID); + return; + } } break; } diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 4c433f7fe9daca0..050761784498a9c 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2369,7 +2369,7 @@ bool Type::isIncompleteType(NamedDecl **Def) const { } bool Type::isSizelessBuiltinType() const { - if (isSVESizelessBuiltinType() || isRVVSizelessBuiltinType()) + if (isSizelessVectorType()) return true; if (const BuiltinType *BT = getAs()) { @@ -2403,6 +2403,10 @@ bool Type::isWebAssemblyTableType() const { bool Type::isSizelessType() const { return isSizelessBuiltinType(); } +bool Type::isSizelessVectorType() const { + return isSVESizelessBuiltinType() || isRVVSizelessBuiltinType(); +} + bool Type::isSVESizelessBuiltinType() const { if (const BuiltinType *BT = getAs()) { switch (BT->getKind()) { diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 93ab064bdf3915d..c1b66062877ef8b 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3083,6 +3083,18 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( E->getTypeOfArgument()->getPointeeType())) .getQuantity(); return llvm::ConstantInt::get(CGF.SizeTy, Alignment); + } else if (E->getKind() == UETT_VectorElements) { + // For scalable vectors, we don't know the size at compile time. We can use @llvm.vscale to calculate it at runtime. + if (E->getTypeOfArgument()->isSizelessVectorType()) { + auto *VecTy = dyn_cast(ConvertType(E->getTypeOfArgument())); + llvm::Type *ElementTy = VecTy->getElementType(); + uint64_t NumUnscaledElements = VecTy->getMinNumElements(); + + llvm::Value *VScale = Builder.CreateVScale(llvm::ConstantInt::get(ElementTy, 1)); + // We need to pass the element type to the vscale call. As it may be small, like i8, we need to extend here to avoid an overflow for large vectors. + VScale = Builder.CreateZExt(VScale, CGF.SizeTy); + return Builder.CreateMul(VScale, llvm::ConstantInt::get(CGF.SizeTy, NumUnscaledElements)); + } } // If this isn't sizeof(vla), the result must be constant; use the constant diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 9dbfc1c8c5e9ffe..e01252f722b662e 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1463,6 +1463,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind, case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')' case tok::kw___builtin_omp_required_simd_align: + case tok::kw___builtin_vectorelements: if (NotPrimaryExpression) *NotPrimaryExpression = true; AllowSuffix = false; @@ -2339,7 +2340,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, - tok::kw___builtin_omp_required_simd_align) && + tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && "Not a typeof/sizeof/alignof/vec_step expression!"); ExprResult Operand; @@ -2460,7 +2461,7 @@ ExprResult Parser::ParseSYCLUniqueStableNameExpression() { ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, - tok::kw___builtin_omp_required_simd_align) && + tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && "Not a sizeof/alignof/vec_step expression!"); Token OpTok = Tok; ConsumeToken(); @@ -2539,6 +2540,8 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { ExprKind = UETT_VecStep; else if (OpTok.is(tok::kw___builtin_omp_required_simd_align)) ExprKind = UETT_OpenMPRequiredSimdAlign; + else if (OpTok.is(tok::kw___builtin_vectorelements)) + ExprKind = UETT_VectorElements; if (isCastExpr) return Actions.ActOnUnaryExprOrTypeTraitExpr(OpTok.getLocation(), diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 2594a8f97f7d94e..70767a048eddaf9 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2836,6 +2836,24 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, break; } +// case Builtin::BI__builtin_vectorelements: { +// if (checkArgCount(*this, TheCall, 1)) +// return ExprError(); +// +// const Expr *Arg = TheCall->getArg(0); +// QualType Ty = Arg->getType(); +// const auto *VecTy = Ty->getAs(); +// if (!VecTy && !Ty->isSizelessVectorType()) { +// Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type) +// << 1 << Arg->getType(); +// return ExprError(); +// } +// +// // The number of elements in a vector is always an integer. +// TheCall->setType(Context.IntTy); +// break; +// } + case Builtin::BI__builtin_matrix_transpose: return SemaBuiltinMatrixTranspose(TheCall, TheCallResult); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index cf45fc388083ce6..191897ee71f48f8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4351,6 +4351,17 @@ static bool CheckVecStepTraitOperandType(Sema &S, QualType T, return false; } +static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, + SourceLocation Loc, + SourceRange ArgRange) { + // builtin_vectorelements supports both fixed-sized and scalable vectors. + if (!T->isVectorType() && !T->isSizelessVectorType()) { + S.Diag(Loc, diag::err_vec_elements_non_vector) << T << ArgRange; + return true; + } + return false; +} + static bool CheckExtensionTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, @@ -4743,6 +4754,9 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, if (ExprKind == UETT_VecStep) return CheckVecStepTraitOperandType(*this, ExprType, OpLoc, ExprRange); + if (ExprKind == UETT_VectorElements) + return CheckVectorElementsTraitOperandType(*this, ExprType, OpLoc, ExprRange); + // Explicitly list some types as extensions. if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange, ExprKind)) From 550f0ca8678866efd558b0226e2877921e3680d1 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Wed, 11 Oct 2023 17:36:19 +0200 Subject: [PATCH 02/10] fixup! Add __builtin_vectorelements to get the number of elements in a fixed-sized vector at compile-time or via a @llvm.vscale call at runtime. --- clang/lib/AST/ExprConstant.cpp | 3 ++- clang/lib/CodeGen/CGExprScalar.cpp | 16 +++++++++++----- clang/lib/Parse/ParseExpr.cpp | 6 ++++-- clang/lib/Sema/SemaChecking.cpp | 18 ------------------ clang/lib/Sema/SemaExpr.cpp | 7 ++++--- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index eb36a57e462f3f1..2ca080915a2367a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13597,7 +13597,8 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( E); case UETT_VectorElements: { QualType Ty = E->getTypeOfArgument(); - // If the vector has a fixed size, we can determine the number of elements at compile time. + // If the vector has a fixed size, we can determine the number of elements + // at compile time. if (Ty->isVectorType()) return Success(Ty->castAs()->getNumElements(), E); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index c1b66062877ef8b..e35f40833ec962f 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3084,16 +3084,22 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( .getQuantity(); return llvm::ConstantInt::get(CGF.SizeTy, Alignment); } else if (E->getKind() == UETT_VectorElements) { - // For scalable vectors, we don't know the size at compile time. We can use @llvm.vscale to calculate it at runtime. + // For scalable vectors, we don't know the size at compile time. We can use + // @llvm.vscale to calculate it at runtime. if (E->getTypeOfArgument()->isSizelessVectorType()) { - auto *VecTy = dyn_cast(ConvertType(E->getTypeOfArgument())); + auto *VecTy = dyn_cast( + ConvertType(E->getTypeOfArgument())); llvm::Type *ElementTy = VecTy->getElementType(); uint64_t NumUnscaledElements = VecTy->getMinNumElements(); - llvm::Value *VScale = Builder.CreateVScale(llvm::ConstantInt::get(ElementTy, 1)); - // We need to pass the element type to the vscale call. As it may be small, like i8, we need to extend here to avoid an overflow for large vectors. + llvm::Value *VScale = + Builder.CreateVScale(llvm::ConstantInt::get(ElementTy, 1)); + // We need to pass the element type to the vscale call. As it may be + // small, like i8, we need to extend here to avoid an overflow for large + // vectors. VScale = Builder.CreateZExt(VScale, CGF.SizeTy); - return Builder.CreateMul(VScale, llvm::ConstantInt::get(CGF.SizeTy, NumUnscaledElements)); + return Builder.CreateMul( + VScale, llvm::ConstantInt::get(CGF.SizeTy, NumUnscaledElements)); } } diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index e01252f722b662e..4d267c915ff2478 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -2340,7 +2340,8 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_typeof_unqual, tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, - tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && + tok::kw___builtin_omp_required_simd_align, + tok::kw___builtin_vectorelements) && "Not a typeof/sizeof/alignof/vec_step expression!"); ExprResult Operand; @@ -2461,7 +2462,8 @@ ExprResult Parser::ParseSYCLUniqueStableNameExpression() { ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() { assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step, - tok::kw___builtin_omp_required_simd_align, tok::kw___builtin_vectorelements) && + tok::kw___builtin_omp_required_simd_align, + tok::kw___builtin_vectorelements) && "Not a sizeof/alignof/vec_step expression!"); Token OpTok = Tok; ConsumeToken(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 70767a048eddaf9..2594a8f97f7d94e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2836,24 +2836,6 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, break; } -// case Builtin::BI__builtin_vectorelements: { -// if (checkArgCount(*this, TheCall, 1)) -// return ExprError(); -// -// const Expr *Arg = TheCall->getArg(0); -// QualType Ty = Arg->getType(); -// const auto *VecTy = Ty->getAs(); -// if (!VecTy && !Ty->isSizelessVectorType()) { -// Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type) -// << 1 << Arg->getType(); -// return ExprError(); -// } -// -// // The number of elements in a vector is always an integer. -// TheCall->setType(Context.IntTy); -// break; -// } - case Builtin::BI__builtin_matrix_transpose: return SemaBuiltinMatrixTranspose(TheCall, TheCallResult); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 191897ee71f48f8..b48e915ade82fc2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4352,8 +4352,8 @@ static bool CheckVecStepTraitOperandType(Sema &S, QualType T, } static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, - SourceLocation Loc, - SourceRange ArgRange) { + SourceLocation Loc, + SourceRange ArgRange) { // builtin_vectorelements supports both fixed-sized and scalable vectors. if (!T->isVectorType() && !T->isSizelessVectorType()) { S.Diag(Loc, diag::err_vec_elements_non_vector) << T << ArgRange; @@ -4755,7 +4755,8 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType, return CheckVecStepTraitOperandType(*this, ExprType, OpLoc, ExprRange); if (ExprKind == UETT_VectorElements) - return CheckVectorElementsTraitOperandType(*this, ExprType, OpLoc, ExprRange); + return CheckVectorElementsTraitOperandType(*this, ExprType, OpLoc, + ExprRange); // Explicitly list some types as extensions. if (!CheckExtensionTraitOperandType(*this, ExprType, OpLoc, ExprRange, From eb387d62a1648da4f18ecc5f5c969de4b9a2ab7b Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Fri, 13 Oct 2023 14:35:49 +0200 Subject: [PATCH 03/10] Add tests --- clang/lib/CodeGen/CGExprScalar.cpp | 7 +- clang/test/CodeGen/builtin_vectorelements.c | 115 ++++++++++++++++++++ 2 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGen/builtin_vectorelements.c diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index e35f40833ec962f..f7a70881545f8ab 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3089,15 +3089,10 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( if (E->getTypeOfArgument()->isSizelessVectorType()) { auto *VecTy = dyn_cast( ConvertType(E->getTypeOfArgument())); - llvm::Type *ElementTy = VecTy->getElementType(); uint64_t NumUnscaledElements = VecTy->getMinNumElements(); llvm::Value *VScale = - Builder.CreateVScale(llvm::ConstantInt::get(ElementTy, 1)); - // We need to pass the element type to the vscale call. As it may be - // small, like i8, we need to extend here to avoid an overflow for large - // vectors. - VScale = Builder.CreateZExt(VScale, CGF.SizeTy); + Builder.CreateVScale(llvm::ConstantInt::get(CGF.SizeTy, 1)); return Builder.CreateMul( VScale, llvm::ConstantInt::get(CGF.SizeTy, NumUnscaledElements)); } diff --git a/clang/test/CodeGen/builtin_vectorelements.c b/clang/test/CodeGen/builtin_vectorelements.c new file mode 100644 index 000000000000000..afd81a2ea4732fa --- /dev/null +++ b/clang/test/CodeGen/builtin_vectorelements.c @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +neon %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,NEON %s +// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +sve %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,SVE %s +// RUN: %clang_cc1 -O1 -triple riscv64 -target-feature +v %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,RISCV %s + +// Note that this does not make sense to check for x86 SIMD types, because +// __m128i, __m256i, and __m512i do not specify the element type. There are no +// "logical" number of elements in them. + +typedef int int1 __attribute__((vector_size(4))); +typedef int int4 __attribute__((vector_size(16))); +typedef int int8 __attribute__((vector_size(32))); +typedef int int16 __attribute__((vector_size(64))); +typedef float float2 __attribute__((vector_size(8))); +typedef long extLong4 __attribute__((ext_vector_type(4)));; + + +int test_builtin_vectorelements_int1() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_int1( + // CHECK: ret i32 1 + return __builtin_vectorelements(int1); +} + +int test_builtin_vectorelements_int4() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_int4( + // CHECK: ret i32 4 + return __builtin_vectorelements(int4); +} + +int test_builtin_vectorelements_int8() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_int8( + // CHECK: ret i32 8 + return __builtin_vectorelements(int8); +} + +int test_builtin_vectorelements_int16() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_int16( + // CHECK: ret i32 16 + return __builtin_vectorelements(int16); +} + +int test_builtin_vectorelements_float2() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_float2( + // CHECK: ret i32 2 + return __builtin_vectorelements(float2); +} + +int test_builtin_vectorelements_extLong4() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_extLong4( + // CHECK: ret i32 4 + return __builtin_vectorelements(extLong4); +} + + +#if defined(__ARM_NEON) +#include + +int test_builtin_vectorelements_neon32x4() { + // NEON: i32 @test_builtin_vectorelements_neon32x4( + // NEON: ret i32 4 + return __builtin_vectorelements(uint32x4_t); +} + +int test_builtin_vectorelements_neon64x1() { + // NEON: i32 @test_builtin_vectorelements_neon64x1( + // NEON: ret i32 1 + return __builtin_vectorelements(uint64x1_t); +} +#endif + +#if defined(__ARM_FEATURE_SVE) +#include + +int test_builtin_vectorelements_sve32() { + // SVE: i32 @test_builtin_vectorelements_sve32( + // SVE: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() + // SVE: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 2 + // SVE: ret i32 [[RES]] + return __builtin_vectorelements(svuint32_t); +} + +int test_builtin_vectorelements_sve16() { + // SVE: i32 @test_builtin_vectorelements_sve16( + // SVE: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() + // SVE: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 4 + // SVE: ret i32 [[RES]] + return __builtin_vectorelements(svuint8_t); +} +#endif + +#if defined(__riscv) +#include + +int test_builtin_vectorelements_riscv8() { + // RISCV: i32 @test_builtin_vectorelements_riscv8( + // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() + // RISCV: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 3 + // RISCV: ret i32 [[RES]] + return __builtin_vectorelements(vuint8m1_t); +} + +int test_builtin_vectorelements_riscv64() { + // RISCV: i32 @test_builtin_vectorelements_riscv64( + // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() + // RISCV: ret i32 [[VSCALE]] + return __builtin_vectorelements(vuint64m1_t); +} + +int test_builtin_vectorelements_riscv32m2() { + // RISCV: i32 @test_builtin_vectorelements_riscv32m2( + // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() + // RISCV: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 2 + // RISCV: ret i32 [[RES]] + return __builtin_vectorelements(vuint32m2_t); +} +#endif From 6e8f1f0ea3777fb143ab7bc93be46a4e4c331983 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Fri, 13 Oct 2023 18:17:32 +0200 Subject: [PATCH 04/10] Fix passing value as argument --- .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/lib/Sema/SemaExpr.cpp | 9 ++++- clang/test/CodeGen/builtin_vectorelements.c | 8 ++++- clang/test/Sema/builtin_vectorelements.c | 23 +++++++++++++ clang/test/SemaCXX/builtin_vectorelements.cpp | 33 +++++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 clang/test/Sema/builtin_vectorelements.c create mode 100644 clang/test/SemaCXX/builtin_vectorelements.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c1a6e3831127e56..1543d7fecfe032c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10136,6 +10136,9 @@ def err_vec_builtin_incompatible_vector : Error< def err_vsx_builtin_nonconstant_argument : Error< "argument %0 to %1 must be a 2-bit unsigned literal (i.e. 0, 1, 2 or 3)">; +def err_vectorelements_non_vector : Error< + "'__builtin_vectorelements' argument must be a vector">; + def err_shufflevector_nonconstant_argument : Error< "index for __builtin_shufflevector must be a constant integer">; def err_shufflevector_argument_too_large : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index b48e915ade82fc2..8710708a4889ca2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -35,6 +35,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TypeTraits.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/AnalysisBasedWarnings.h" @@ -4356,7 +4357,7 @@ static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, SourceRange ArgRange) { // builtin_vectorelements supports both fixed-sized and scalable vectors. if (!T->isVectorType() && !T->isSizelessVectorType()) { - S.Diag(Loc, diag::err_vec_elements_non_vector) << T << ArgRange; + S.Diag(Loc, diag::err_vectorelements_non_vector) << T << ArgRange; return true; } return false; @@ -4463,6 +4464,10 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E, return CheckVecStepTraitOperandType(*this, ExprTy, E->getExprLoc(), E->getSourceRange()); + if (ExprKind == UETT_VectorElements) + return CheckVectorElementsTraitOperandType(*this, ExprTy, E->getExprLoc(), + E->getSourceRange()); + // Explicitly list some types as extensions. if (!CheckExtensionTraitOperandType(*this, ExprTy, E->getExprLoc(), E->getSourceRange(), ExprKind)) @@ -4864,6 +4869,8 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, } else if (E->refersToBitField()) { // C99 6.5.3.4p1. Diag(E->getExprLoc(), diag::err_sizeof_alignof_typeof_bitfield) << 0; isInvalid = true; + } else if (ExprKind == UETT_VectorElements) { + isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_VectorElements); } else { isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_SizeOf); } diff --git a/clang/test/CodeGen/builtin_vectorelements.c b/clang/test/CodeGen/builtin_vectorelements.c index afd81a2ea4732fa..d9b7dd14e1dad89 100644 --- a/clang/test/CodeGen/builtin_vectorelements.c +++ b/clang/test/CodeGen/builtin_vectorelements.c @@ -11,7 +11,7 @@ typedef int int4 __attribute__((vector_size(16))); typedef int int8 __attribute__((vector_size(32))); typedef int int16 __attribute__((vector_size(64))); typedef float float2 __attribute__((vector_size(8))); -typedef long extLong4 __attribute__((ext_vector_type(4)));; +typedef long extLong4 __attribute__((ext_vector_type(4))); int test_builtin_vectorelements_int1() { @@ -50,6 +50,12 @@ int test_builtin_vectorelements_extLong4() { return __builtin_vectorelements(extLong4); } +int test_builtin_vectorelements_multiply_constant() { + // CHECK-LABEL: i32 @test_builtin_vectorelements_multiply_constant( + // CHECK: ret i32 32 + return __builtin_vectorelements(int16) * 2; +} + #if defined(__ARM_NEON) #include diff --git a/clang/test/Sema/builtin_vectorelements.c b/clang/test/Sema/builtin_vectorelements.c new file mode 100644 index 000000000000000..650d74cf4ee6e6b --- /dev/null +++ b/clang/test/Sema/builtin_vectorelements.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify %s + +void test_builtin_vectorelements() { + __builtin_vectorelements(int); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(float); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(long*); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + + int a; + __builtin_vectorelements(a); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + + typedef int veci4 __attribute__((vector_size(16))); + (void) __builtin_vectorelements(veci4); + + veci4 vec; + (void) __builtin_vectorelements(vec); + + typedef veci4 some_other_vec; + (void) __builtin_vectorelements(some_other_vec); + + struct Foo { int a; }; + __builtin_vectorelements(struct Foo); // expected-error {{'__builtin_vectorelements' argument must be a vector}} +} + diff --git a/clang/test/SemaCXX/builtin_vectorelements.cpp b/clang/test/SemaCXX/builtin_vectorelements.cpp new file mode 100644 index 000000000000000..df67722708b6f34 --- /dev/null +++ b/clang/test/SemaCXX/builtin_vectorelements.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple aarch64 -std=c++17 -fsyntax-only -verify %s + +template +using VecT __attribute__((vector_size(16))) = T; + +struct FooT { + template + using VecT __attribute__((vector_size(8))) = T; +}; + +void test_builtin_vectorelements() { + using veci4 __attribute__((vector_size(16))) = int; + (void) __builtin_vectorelements(veci4); + + using some_other_vec = veci4; + (void) __builtin_vectorelements(some_other_vec); + + using some_int = int; + (void) __builtin_vectorelements(some_int); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + + class Foo {}; + __builtin_vectorelements(Foo); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + + struct Bar { veci4 vec; }; + (void) __builtin_vectorelements(Bar{}.vec); + + struct Baz { using VecT = veci4; }; + (void) __builtin_vectorelements(Baz::VecT); + + (void) __builtin_vectorelements(FooT::VecT); + (void) __builtin_vectorelements(VecT); +} + From 323c0180cf9b0d556d5364643e96fcf88d167078 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Fri, 13 Oct 2023 18:44:59 +0200 Subject: [PATCH 05/10] Fix formatting --- clang/include/clang/AST/Type.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index f6e425783176ba2..8bafffee2c5970e 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -5459,9 +5459,8 @@ class DeducedTemplateSpecializationType : public DeducedType, /// TemplateArguments, followed by a QualType representing the /// non-canonical aliased type when the template is a type alias /// template. -class alignas(8) TemplateSpecializationType - : public Type, - public llvm::FoldingSetNode { +class alignas(8) TemplateSpecializationType : public Type, + public llvm::FoldingSetNode { friend class ASTContext; // ASTContext creates these /// The name of the template being specialized. This is From bbc063bb225f34b154184391a82eaf494aaa9f1a Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Mon, 16 Oct 2023 17:28:16 +0200 Subject: [PATCH 06/10] Unify non vector type error --- .../clang/Basic/DiagnosticSemaKinds.td | 7 +-- clang/lib/Sema/SemaChecking.cpp | 5 +- clang/lib/Sema/SemaExpr.cpp | 9 +-- clang/test/CodeGen/builtin_vectorelements.c | 55 ++++++++++--------- clang/test/Sema/builtin_vectorelements.c | 12 ++-- clang/test/Sema/convertvector.c | 2 +- clang/test/SemaCXX/builtin_vectorelements.cpp | 6 +- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1543d7fecfe032c..d6a03dd83c76fb3 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10136,9 +10136,6 @@ def err_vec_builtin_incompatible_vector : Error< def err_vsx_builtin_nonconstant_argument : Error< "argument %0 to %1 must be a 2-bit unsigned literal (i.e. 0, 1, 2 or 3)">; -def err_vectorelements_non_vector : Error< - "'__builtin_vectorelements' argument must be a vector">; - def err_shufflevector_nonconstant_argument : Error< "index for __builtin_shufflevector must be a constant integer">; def err_shufflevector_argument_too_large : Error< @@ -10147,8 +10144,8 @@ def err_shufflevector_argument_too_large : Error< def err_convertvector_non_vector : Error< "first argument to __builtin_convertvector must be a vector">; -def err_convertvector_non_vector_type : Error< - "second argument to __builtin_convertvector must be a vector type">; +def err_builtin_non_vector_type : Error< + "%0 argument to %1 must be of vector type">; def err_convertvector_incompatible_vector : Error< "first two arguments to __builtin_convertvector must have the same number of elements">; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 35b36db2049db09..cffee28f8796178 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -8719,8 +8719,9 @@ ExprResult Sema::SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo, diag::err_convertvector_non_vector) << E->getSourceRange()); if (!DstTy->isVectorType() && !DstTy->isDependentType()) - return ExprError(Diag(BuiltinLoc, - diag::err_convertvector_non_vector_type)); + return ExprError(Diag(BuiltinLoc, diag::err_builtin_non_vector_type) + << "second" + << "__builtin_convertvector"); if (!SrcTy->isDependentType() && !DstTy->isDependentType()) { unsigned SrcElts = SrcTy->castAs()->getNumElements(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8710708a4889ca2..3b77e12978dd6f0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4356,10 +4356,11 @@ static bool CheckVectorElementsTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange) { // builtin_vectorelements supports both fixed-sized and scalable vectors. - if (!T->isVectorType() && !T->isSizelessVectorType()) { - S.Diag(Loc, diag::err_vectorelements_non_vector) << T << ArgRange; - return true; - } + if (!T->isVectorType() && !T->isSizelessVectorType()) + return S.Diag(Loc, diag::err_builtin_non_vector_type) + << "" + << "__builtin_vectorelements" << T << ArgRange; + return false; } diff --git a/clang/test/CodeGen/builtin_vectorelements.c b/clang/test/CodeGen/builtin_vectorelements.c index d9b7dd14e1dad89..727de47b93ecc0a 100644 --- a/clang/test/CodeGen/builtin_vectorelements.c +++ b/clang/test/CodeGen/builtin_vectorelements.c @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +neon %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,NEON %s -// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +sve %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,SVE %s -// RUN: %clang_cc1 -O1 -triple riscv64 -target-feature +v %s -emit-llvm -o - | FileCheck --check-prefixes=CHECK,RISCV %s +// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +neon %s -emit-llvm -disable-llvm-passes -o - | FileCheck --check-prefixes=CHECK,NEON %s +// RUN: %clang_cc1 -O1 -triple aarch64 -target-feature +sve %s -emit-llvm -disable-llvm-passes -o - | FileCheck --check-prefixes=CHECK,SVE %s +// RUN: %clang_cc1 -O1 -triple riscv64 -target-feature +v %s -emit-llvm -disable-llvm-passes -o - | FileCheck --check-prefixes=CHECK,RISCV %s // Note that this does not make sense to check for x86 SIMD types, because // __m128i, __m256i, and __m512i do not specify the element type. There are no @@ -76,19 +76,19 @@ int test_builtin_vectorelements_neon64x1() { #if defined(__ARM_FEATURE_SVE) #include -int test_builtin_vectorelements_sve32() { - // SVE: i32 @test_builtin_vectorelements_sve32( - // SVE: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() - // SVE: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 2 - // SVE: ret i32 [[RES]] +long test_builtin_vectorelements_sve32() { + // SVE: i64 @test_builtin_vectorelements_sve32( + // SVE: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() + // SVE: [[RES:%.+]] = mul i64 [[VSCALE]], 4 + // SVE: ret i64 [[RES]] return __builtin_vectorelements(svuint32_t); } -int test_builtin_vectorelements_sve16() { - // SVE: i32 @test_builtin_vectorelements_sve16( - // SVE: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() - // SVE: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 4 - // SVE: ret i32 [[RES]] +long test_builtin_vectorelements_sve8() { + // SVE: i64 @test_builtin_vectorelements_sve8( + // SVE: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() + // SVE: [[RES:%.+]] = mul i64 [[VSCALE]], 16 + // SVE: ret i64 [[RES]] return __builtin_vectorelements(svuint8_t); } #endif @@ -96,26 +96,27 @@ int test_builtin_vectorelements_sve16() { #if defined(__riscv) #include -int test_builtin_vectorelements_riscv8() { - // RISCV: i32 @test_builtin_vectorelements_riscv8( - // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() - // RISCV: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 3 - // RISCV: ret i32 [[RES]] +long test_builtin_vectorelements_riscv8() { + // RISCV: i64 @test_builtin_vectorelements_riscv8( + // RISCV: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() + // RISCV: [[RES:%.+]] = mul i64 [[VSCALE]], 8 + // RISCV: ret i64 [[RES]] return __builtin_vectorelements(vuint8m1_t); } -int test_builtin_vectorelements_riscv64() { - // RISCV: i32 @test_builtin_vectorelements_riscv64( - // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() - // RISCV: ret i32 [[VSCALE]] +long test_builtin_vectorelements_riscv64() { + // RISCV: i64 @test_builtin_vectorelements_riscv64( + // RISCV: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() + // RISCV: [[RES:%.+]] = mul i64 [[VSCALE]], 1 + // RISCV: ret i64 [[RES]] return __builtin_vectorelements(vuint64m1_t); } -int test_builtin_vectorelements_riscv32m2() { - // RISCV: i32 @test_builtin_vectorelements_riscv32m2( - // RISCV: [[VSCALE:%.+]] = tail call i32 @llvm.vscale.i32() - // RISCV: [[RES:%.+]] = shl nuw nsw i32 [[VSCALE]], 2 - // RISCV: ret i32 [[RES]] +long test_builtin_vectorelements_riscv32m2() { + // RISCV: i64 @test_builtin_vectorelements_riscv32m2( + // RISCV: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() + // RISCV: [[RES:%.+]] = mul i64 [[VSCALE]], 4 + // RISCV: ret i64 [[RES]] return __builtin_vectorelements(vuint32m2_t); } #endif diff --git a/clang/test/Sema/builtin_vectorelements.c b/clang/test/Sema/builtin_vectorelements.c index 650d74cf4ee6e6b..8f669075bcee5ac 100644 --- a/clang/test/Sema/builtin_vectorelements.c +++ b/clang/test/Sema/builtin_vectorelements.c @@ -1,12 +1,12 @@ -// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple aarch64 -fsyntax-only -verify -disable-llvm-passes %s void test_builtin_vectorelements() { - __builtin_vectorelements(int); // expected-error {{'__builtin_vectorelements' argument must be a vector}} - __builtin_vectorelements(float); // expected-error {{'__builtin_vectorelements' argument must be a vector}} - __builtin_vectorelements(long*); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(int); // expected-error {{argument to __builtin_vectorelements must be of vector type}} + __builtin_vectorelements(float); // expected-error {{argument to __builtin_vectorelements must be of vector type}} + __builtin_vectorelements(long*); // expected-error {{argument to __builtin_vectorelements must be of vector type}} int a; - __builtin_vectorelements(a); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(a); // expected-error {{argument to __builtin_vectorelements must be of vector type}} typedef int veci4 __attribute__((vector_size(16))); (void) __builtin_vectorelements(veci4); @@ -18,6 +18,6 @@ void test_builtin_vectorelements() { (void) __builtin_vectorelements(some_other_vec); struct Foo { int a; }; - __builtin_vectorelements(struct Foo); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(struct Foo); // expected-error {{argument to __builtin_vectorelements must be of vector type}} } diff --git a/clang/test/Sema/convertvector.c b/clang/test/Sema/convertvector.c index ccdd87f9e40c1d4..8ae43c3ba3d4937 100644 --- a/clang/test/Sema/convertvector.c +++ b/clang/test/Sema/convertvector.c @@ -8,7 +8,7 @@ vector8float foo1(vector4double x) { } float foo2(vector4double x) { - return __builtin_convertvector(x, float); // expected-error {{must be a vector type}} + return __builtin_convertvector(x, float); // expected-error {{second argument to __builtin_convertvector must be of vector type}} } vector8float foo3(double x) { diff --git a/clang/test/SemaCXX/builtin_vectorelements.cpp b/clang/test/SemaCXX/builtin_vectorelements.cpp index df67722708b6f34..046f3dc306d48e1 100644 --- a/clang/test/SemaCXX/builtin_vectorelements.cpp +++ b/clang/test/SemaCXX/builtin_vectorelements.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple aarch64 -std=c++17 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple aarch64 -std=c++17 -fsyntax-only -verify -disable-llvm-passes %s template using VecT __attribute__((vector_size(16))) = T; @@ -16,10 +16,10 @@ void test_builtin_vectorelements() { (void) __builtin_vectorelements(some_other_vec); using some_int = int; - (void) __builtin_vectorelements(some_int); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + (void) __builtin_vectorelements(some_int); // expected-error {{argument to __builtin_vectorelements must be of vector type}} class Foo {}; - __builtin_vectorelements(Foo); // expected-error {{'__builtin_vectorelements' argument must be a vector}} + __builtin_vectorelements(Foo); // expected-error {{argument to __builtin_vectorelements must be of vector type}} struct Bar { veci4 vec; }; (void) __builtin_vectorelements(Bar{}.vec); From f19e1deb6326aee140be010ed6df2eca8a952ebc Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Mon, 16 Oct 2023 18:56:29 +0200 Subject: [PATCH 07/10] Use Builder.CreateElementCount() for both fixed-sized and scalable vectors --- clang/lib/AST/ExprConstant.cpp | 1 + clang/lib/CodeGen/CGExprScalar.cpp | 15 +++------------ clang/test/CodeGen/builtin_vectorelements.c | 3 +-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 2ca080915a2367a..01b8e7566e58014 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13602,6 +13602,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( if (Ty->isVectorType()) return Success(Ty->castAs()->getNumElements(), E); + assert(Ty->isSizelessVectorType()); return false; } } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index f7a70881545f8ab..5ab0d38ba4fcd54 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3084,18 +3084,9 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( .getQuantity(); return llvm::ConstantInt::get(CGF.SizeTy, Alignment); } else if (E->getKind() == UETT_VectorElements) { - // For scalable vectors, we don't know the size at compile time. We can use - // @llvm.vscale to calculate it at runtime. - if (E->getTypeOfArgument()->isSizelessVectorType()) { - auto *VecTy = dyn_cast( - ConvertType(E->getTypeOfArgument())); - uint64_t NumUnscaledElements = VecTy->getMinNumElements(); - - llvm::Value *VScale = - Builder.CreateVScale(llvm::ConstantInt::get(CGF.SizeTy, 1)); - return Builder.CreateMul( - VScale, llvm::ConstantInt::get(CGF.SizeTy, NumUnscaledElements)); - } + auto *VecTy = + dyn_cast(ConvertType(E->getTypeOfArgument())); + return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount()); } // If this isn't sizeof(vla), the result must be constant; use the constant diff --git a/clang/test/CodeGen/builtin_vectorelements.c b/clang/test/CodeGen/builtin_vectorelements.c index 727de47b93ecc0a..a825ab2b7273d52 100644 --- a/clang/test/CodeGen/builtin_vectorelements.c +++ b/clang/test/CodeGen/builtin_vectorelements.c @@ -107,8 +107,7 @@ long test_builtin_vectorelements_riscv8() { long test_builtin_vectorelements_riscv64() { // RISCV: i64 @test_builtin_vectorelements_riscv64( // RISCV: [[VSCALE:%.+]] = call i64 @llvm.vscale.i64() - // RISCV: [[RES:%.+]] = mul i64 [[VSCALE]], 1 - // RISCV: ret i64 [[RES]] + // RISCV: ret i64 [[VSCALE]] return __builtin_vectorelements(vuint64m1_t); } From 8d78389c2efe94ed7a5004022f4da5766fc52628 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Tue, 17 Oct 2023 10:39:44 +0200 Subject: [PATCH 08/10] Add Release Note and documentation --- clang/docs/LanguageExtensions.rst | 8 ++++++++ clang/docs/ReleaseNotes.rst | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index b9466b5a0bc2087..30e288f986782fd 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -619,6 +619,14 @@ Let ``T`` be one of the following types: For scalar types, consider the operation applied to a vector with a single element. +*Vector Size* +To determine the number of elements in a vector, use ``__builtin_vectorelements()``. +For fixed-sized vectors, e.g., defined via ``__attribute__((vector_size(N)))`` or ARM +NEON's vector types (e.g., ``uint16x8_t``), this returns the constant number of +elements at compile-time. For scalable vectors, e.g., SVE or RISC-V V, the number of +elements is not known at compile-time and is determined at runtime. This builtin can +be used, e.g., to increment the loop-counter in vector-type agnostic loops. + *Elementwise Builtins* Each builtin returns a vector equivalent to applying the specified operation diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2d918967e7f0b02..da7ce988d2d9ebd 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -170,6 +170,12 @@ C23 Feature Support Non-comprehensive list of changes in this release ------------------------------------------------- +* Clang now has a ``__builtin_vectorelements()`` function that determines the number of elements in a vector. + For fixed-sized vectors, e.g., defined via ``__attribute__((vector_size(N)))`` or ARM NEON's vector types + (e.g., ``uint16x8_t``), this returns the constant number of elements at compile-time. + For scalable vectors, e.g., SVE or RISC-V V, the number of elements is not known at compile-time and is + determined at runtime. + New Compiler Flags ------------------ From b1ff89ae7008a7674f34897dcfd96324e42d1026 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Tue, 17 Oct 2023 13:18:40 +0200 Subject: [PATCH 09/10] Add constexpr tests and diagnostic --- .../include/clang/Basic/DiagnosticASTKinds.td | 2 ++ clang/lib/AST/ExprConstant.cpp | 4 ++++ clang/test/SemaCXX/builtin_vectorelements.cpp | 21 ++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index d2656310e79c9b8..c3c92f28d8742e5 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -394,6 +394,8 @@ def note_constexpr_unsupported_layout : Note< "type %0 has unexpected layout">; def note_constexpr_unsupported_flexible_array : Note< "flexible array initialization is not yet supported">; +def note_constexpr_non_const_vectorelements : Note< + "cannot determine number of elements for sizeless vectors in a constant expression">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 01b8e7566e58014..ce69bad36a1a1e3 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13603,6 +13603,10 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr( return Success(Ty->castAs()->getNumElements(), E); assert(Ty->isSizelessVectorType()); + if (Info.InConstantContext) + Info.CCEDiag(E, diag::note_constexpr_non_const_vectorelements) + << E->getSourceRange(); + return false; } } diff --git a/clang/test/SemaCXX/builtin_vectorelements.cpp b/clang/test/SemaCXX/builtin_vectorelements.cpp index 046f3dc306d48e1..423051def7f7c29 100644 --- a/clang/test/SemaCXX/builtin_vectorelements.cpp +++ b/clang/test/SemaCXX/builtin_vectorelements.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple aarch64 -std=c++17 -fsyntax-only -verify -disable-llvm-passes %s +// RUN: %clang_cc1 -triple aarch64 -target-feature +sve -std=c++20 -fsyntax-only -verify -disable-llvm-passes %s template using VecT __attribute__((vector_size(16))) = T; @@ -29,5 +29,24 @@ void test_builtin_vectorelements() { (void) __builtin_vectorelements(FooT::VecT); (void) __builtin_vectorelements(VecT); + + constexpr int i4 = __builtin_vectorelements(veci4); + constexpr int i4p8 = __builtin_vectorelements(veci4) + 8; +} + + +#if defined(__ARM_FEATURE_SVE) +#include + +consteval int consteval_elements() { // expected-error {{consteval function never produces a constant expression}} + return __builtin_vectorelements(svuint64_t); // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} +} + +void test_bad_constexpr() { + constexpr int eval = consteval_elements(); // expected-error {{initialized by a constant expression}} // expected-error {{not a constant expression}} // expected-note {{in call}} // expected-note {{in call}} + constexpr int i32 = __builtin_vectorelements(svuint32_t); // expected-error {{initialized by a constant expression}} // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} + constexpr int i16p8 = __builtin_vectorelements(svuint16_t) + 16; // expected-error {{initialized by a constant expression}} // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} + constexpr int lambda = [] { return __builtin_vectorelements(svuint16_t); }(); // expected-error {{initialized by a constant expression}} // expected-note {{cannot determine number of elements for sizeless vectors in a constant expression}} // expected-note {{in call}} } +#endif From 2c51c600ac8c356188adb92ed96ad769caac03b4 Mon Sep 17 00:00:00 2001 From: Lawrence Benson Date: Tue, 17 Oct 2023 17:54:48 +0200 Subject: [PATCH 10/10] Use cast() instead of dyn_cast() --- clang/lib/CodeGen/CGExprScalar.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 5ab0d38ba4fcd54..c25ddeff9adc3a7 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3084,8 +3084,7 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( .getQuantity(); return llvm::ConstantInt::get(CGF.SizeTy, Alignment); } else if (E->getKind() == UETT_VectorElements) { - auto *VecTy = - dyn_cast(ConvertType(E->getTypeOfArgument())); + auto *VecTy = cast(ConvertType(E->getTypeOfArgument())); return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount()); }