[libcxx-commits] [libcxx] [Clang] Add __builtin_vectorelements to get number of elements in vector (PR #69010)

Lawrence Benson via libcxx-commits libcxx-commits at lists.llvm.org
Tue Oct 17 08:55:21 PDT 2023


https://github.com/lawben updated https://github.com/llvm/llvm-project/pull/69010

>From df8d0a53a31e1351bb6cd3b340e9012b489e9885 Mon Sep 17 00:00:00 2001
From: Lawrence Benson <github at lawben.com>
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<VectorType>()->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<BuiltinType>()) {
@@ -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<BuiltinType>()) {
     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<llvm::ScalableVectorType>(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<VectorType>();
+//    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 <github at lawben.com>
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<VectorType>()->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<llvm::ScalableVectorType>(ConvertType(E->getTypeOfArgument()));
+      auto *VecTy = dyn_cast<llvm::ScalableVectorType>(
+          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<VectorType>();
-//    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 <github at lawben.com>
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<llvm::ScalableVectorType>(
           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 <arm_neon.h>
+
+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 <arm_sve.h>
+
+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 <riscv_vector.h>
+
+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 <github at lawben.com>
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 <arm_neon.h>
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 <typename T>
+using VecT __attribute__((vector_size(16))) = T;
+
+struct FooT {
+  template <typename T>
+  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<long>);
+  (void) __builtin_vectorelements(VecT<char>);
+}
+

>From 323c0180cf9b0d556d5364643e96fcf88d167078 Mon Sep 17 00:00:00 2001
From: Lawrence Benson <github at lawben.com>
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 <github at lawben.com>
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<VectorType>()->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 <arm_sve.h>
 
-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 <riscv_vector.h>
 
-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 <typename T>
 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 <github at lawben.com>
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<VectorType>()->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<llvm::ScalableVectorType>(
-          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<llvm::VectorType>(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 <github at lawben.com>
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 <github at lawben.com>
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<VectorType>()->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 <typename T>
 using VecT __attribute__((vector_size(16))) = T;
@@ -29,5 +29,24 @@ void test_builtin_vectorelements() {
 
   (void) __builtin_vectorelements(FooT::VecT<long>);
   (void) __builtin_vectorelements(VecT<char>);
+
+  constexpr int i4 = __builtin_vectorelements(veci4);
+  constexpr int i4p8 = __builtin_vectorelements(veci4) + 8;
+}
+
+
+#if defined(__ARM_FEATURE_SVE)
+#include <arm_sve.h>
+
+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 <github at lawben.com>
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<llvm::VectorType>(ConvertType(E->getTypeOfArgument()));
+    auto *VecTy = cast<llvm::VectorType>(ConvertType(E->getTypeOfArgument()));
     return Builder.CreateElementCount(CGF.SizeTy, VecTy->getElementCount());
   }
 



More information about the libcxx-commits mailing list