llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: Lawrence Benson (lawben) <details> <summary>Changes</summary> Adds a new `__builtin_vectorelements()` function which returns the number of elements for a given vector either at compile-time for fixed-sized vectors, e.g., created via `__attribute__((vector_size(N)))` or at runtime via a call to `@<!-- -->llvm.vscale.i32()` for scalable vectors, e.g., SVE or RISCV V. The new builtin follows a similar path as `sizeof()`, as it essentially does the same thing but for the number of elements in vector instead of the number of bytes. This allows us to re-use a lot of the existing logic to handle types etc. A small side addition is `Type::isSizelessVectorType()`, which we need to distinguish between sizeless vectors (SVE, RISCV V) and sizeless types (WASM). This is the [corresponding discussion](https://discourse.llvm.org/t/new-builtin-function-to-get-number-of-lanes-in-simd-vectors/73911). --- Full diff: https://github.com/llvm/llvm-project/pull/69010.diff 13 Files Affected: - (modified) clang/include/clang/AST/Type.h (+3) - (modified) clang/include/clang/Basic/Builtins.def (+1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3) - (modified) clang/include/clang/Basic/TokenKinds.def (+1) - (modified) clang/lib/AST/ExprConstant.cpp (+9) - (modified) clang/lib/AST/ItaniumMangle.cpp (+8) - (modified) clang/lib/AST/Type.cpp (+5-1) - (modified) clang/lib/CodeGen/CGExprScalar.cpp (+13) - (modified) clang/lib/Parse/ParseExpr.cpp (+7-2) - (modified) clang/lib/Sema/SemaExpr.cpp (+22) - (added) clang/test/CodeGen/builtin_vectorelements.c (+121) - (added) clang/test/Sema/builtin_vectorelements.c (+23) - (added) clang/test/SemaCXX/builtin_vectorelements.cpp (+33) ``````````diff 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/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/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..2ca080915a2367a 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -13595,6 +13595,15 @@ 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..f7a70881545f8ab 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -3083,6 +3083,19 @@ 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())); + 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)); + } } // 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..4d267c915ff2478 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,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_omp_required_simd_align, + tok::kw___builtin_vectorelements) && "Not a typeof/sizeof/alignof/vec_step expression!"); ExprResult Operand; @@ -2460,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_omp_required_simd_align, + tok::kw___builtin_vectorelements) && "Not a sizeof/alignof/vec_step expression!"); Token OpTok = Tok; ConsumeToken(); @@ -2539,6 +2542,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/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index cf45fc388083ce6..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" @@ -4351,6 +4352,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_vectorelements_non_vector) << T << ArgRange; + return true; + } + return false; +} + static bool CheckExtensionTraitOperandType(Sema &S, QualType T, SourceLocation Loc, SourceRange ArgRange, @@ -4452,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)) @@ -4743,6 +4759,10 @@ 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)) @@ -4849,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 new file mode 100644 index 000000000000000..d9b7dd14e1dad89 --- /dev/null +++ b/clang/test/CodeGen/builtin_vectorelements.c @@ -0,0 +1,121 @@ +// 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); +} + +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> + +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 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>); +} + `````````` </details> https://github.com/llvm/llvm-project/pull/69010 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits