https://github.com/yronglin created https://github.com/llvm/llvm-project/pull/105996
None >From 0e4c511107f76da085a8019cf2eca78c3a5a0754 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Mon, 26 Aug 2024 02:09:31 +0800 Subject: [PATCH] [Clang][Interp] Implement constexpr vector unary operators +, -, ~, ! Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/AST/ASTContext.h | 8 + clang/include/clang/Sema/Sema.h | 8 - clang/lib/AST/ASTContext.cpp | 50 +++++++ clang/lib/AST/ByteCode/Compiler.cpp | 141 ++++++++++++++++++ clang/lib/AST/ByteCode/Compiler.h | 6 + clang/lib/Sema/SemaChecking.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 67 +-------- clang/test/AST/ByteCode/constexpr-vectors.cpp | 88 +++++++++++ 8 files changed, 300 insertions(+), 70 deletions(-) create mode 100644 clang/test/AST/ByteCode/constexpr-vectors.cpp diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 58a820508da42b..01dbf62a3db88c 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -1558,6 +1558,14 @@ class ASTContext : public RefCountedBase<ASTContext> { /// Return a WebAssembly externref type. QualType getWebAssemblyExternrefType() const; + /// Return a signed ext_vector_type that is of identical size and number of + /// elements. For floating point vectors, return an integer type of identical + /// size and number of elements. In the non ext_vector_type case, search from + /// the largest type to the smallest type to avoid cases where long long == + /// long, where long gets picked over long long. + QualType GetSignedVectorType(QualType V); + QualType GetSignedSizelessVectorType(QualType V); + /// Return the unique reference to a vector type of the specified /// element type and size. /// diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1f7e555d1b8717..dd637ef3dd822b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7393,14 +7393,6 @@ class Sema final : public SemaBase { bool AllowBothBool, bool AllowBoolConversion, bool AllowBoolOperation, bool ReportInvalid); - /// Return a signed ext_vector_type that is of identical size and number of - /// elements. For floating point vectors, return an integer type of identical - /// size and number of elements. In the non ext_vector_type case, search from - /// the largest type to the smallest type to avoid cases where long long == - /// long, where long gets picked over long long. - QualType GetSignedVectorType(QualType V); - QualType GetSignedSizelessVectorType(QualType V); - /// CheckVectorCompareOperands - vector comparisons are a clang extension that /// operates on extended vector types. Instead of producing an IntTy result, /// like a scalar comparison, a vector comparison produces a vector of integer diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b201d201e1ea6a..408d4b006a7469 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -4444,6 +4444,56 @@ QualType ASTContext::getScalableVectorType(QualType EltTy, unsigned NumElts, return QualType(); } +QualType ASTContext::GetSignedVectorType(QualType V) { + const VectorType *VTy = V->castAs<VectorType>(); + unsigned TypeSize = getTypeSize(VTy->getElementType()); + + if (isa<ExtVectorType>(VTy)) { + if (VTy->isExtVectorBoolType()) + return getExtVectorType(BoolTy, VTy->getNumElements()); + if (TypeSize == getTypeSize(CharTy)) + return getExtVectorType(CharTy, VTy->getNumElements()); + if (TypeSize == getTypeSize(ShortTy)) + return getExtVectorType(ShortTy, VTy->getNumElements()); + if (TypeSize == getTypeSize(IntTy)) + return getExtVectorType(IntTy, VTy->getNumElements()); + if (TypeSize == getTypeSize(Int128Ty)) + return getExtVectorType(Int128Ty, VTy->getNumElements()); + if (TypeSize == getTypeSize(LongTy)) + return getExtVectorType(LongTy, VTy->getNumElements()); + assert(TypeSize == getTypeSize(LongLongTy) && + "Unhandled vector element size in vector compare"); + return getExtVectorType(LongLongTy, VTy->getNumElements()); + } + + if (TypeSize == getTypeSize(Int128Ty)) + return getVectorType(Int128Ty, VTy->getNumElements(), VectorKind::Generic); + if (TypeSize == getTypeSize(LongLongTy)) + return getVectorType(LongLongTy, VTy->getNumElements(), + VectorKind::Generic); + if (TypeSize == getTypeSize(LongTy)) + return getVectorType(LongTy, VTy->getNumElements(), VectorKind::Generic); + if (TypeSize == getTypeSize(IntTy)) + return getVectorType(IntTy, VTy->getNumElements(), VectorKind::Generic); + if (TypeSize == getTypeSize(ShortTy)) + return getVectorType(ShortTy, VTy->getNumElements(), VectorKind::Generic); + assert(TypeSize == getTypeSize(CharTy) && + "Unhandled vector element size in vector compare"); + return getVectorType(CharTy, VTy->getNumElements(), VectorKind::Generic); +} + +QualType ASTContext::GetSignedSizelessVectorType(QualType V) { + const BuiltinType *VTy = V->castAs<BuiltinType>(); + assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); + + const QualType ETy = V->getSveEltType(*this); + const auto TypeSize = getTypeSize(ETy); + + const QualType IntTy = getIntTypeForBitwidth(TypeSize, true); + const llvm::ElementCount VecSize = getBuiltinVectorTypeInfo(VTy).EC; + return getScalableVectorType(IntTy, VecSize.getKnownMinValue()); +} + /// getVectorType - Return the unique reference to a vector type of /// the specified element type and size. VectorType must be a built-in type. QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts, diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 0fc942a4f1bc4f..fa329fa8ccc979 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -4991,6 +4991,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) { const Expr *SubExpr = E->getSubExpr(); if (SubExpr->getType()->isAnyComplexType()) return this->VisitComplexUnaryOperator(E); + if (SubExpr->getType()->isVectorType()) + return this->VisitVectorUnaryOp(E); std::optional<PrimType> T = classify(SubExpr->getType()); switch (E->getOpcode()) { @@ -5312,6 +5314,145 @@ bool Compiler<Emitter>::VisitComplexUnaryOperator(const UnaryOperator *E) { return true; } +template <class Emitter> +bool Compiler<Emitter>::VisitVectorUnaryOp(const UnaryOperator *E) { + const Expr *SubExpr = E->getSubExpr(); + assert(SubExpr->getType()->isVectorType()); + + if (DiscardResult) + return this->discard(SubExpr); + + std::optional<PrimType> ResT = classify(E); + auto prepareResult = [=]() -> bool { + if (!ResT && !Initializing) { + std::optional<unsigned> LocalIndex = allocateLocal(SubExpr); + if (!LocalIndex) + return false; + return this->emitGetPtrLocal(*LocalIndex, E); + } + + return true; + }; + + // The offset of the temporary, if we created one. + unsigned SubExprOffset = ~0u; + auto createTemp = [=, &SubExprOffset]() -> bool { + SubExprOffset = this->allocateLocalPrimitive(SubExpr, PT_Ptr, true, false); + if (!this->visit(SubExpr)) + return false; + return this->emitSetLocal(PT_Ptr, SubExprOffset, E); + }; + + const auto *VecT = SubExpr->getType()->getAs<VectorType>(); + PrimType ElemT = classifyVectorElementType(SubExpr->getType()); + auto getElem = [=](unsigned Offset, unsigned Index) -> bool { + if (!this->emitGetLocal(PT_Ptr, Offset, E)) + return false; + return this->emitArrayElemPop(ElemT, Index, E); + }; + + switch (E->getOpcode()) { + case UO_Plus: // +x + return this->delegate(SubExpr); + case UO_Minus: + if (!prepareResult()) + return false; + if (!createTemp()) + return false; + for (unsigned I = 0; I != VecT->getNumElements(); ++I) { + if (!getElem(SubExprOffset, I)) + return false; + if (!this->emitNeg(ElemT, E)) + return false; + if (!this->emitInitElem(ElemT, I, E)) + return false; + } + break; + case UO_LNot: { // !x + if (!prepareResult()) + return false; + if (!createTemp()) + return false; + + // In C++, the logic operators !, &&, || are available for vectors. !v is equivalent to v == 0. + // https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html + QualType SignedVecT = Ctx.getASTContext().GetSignedVectorType(SubExpr->getType()); + PrimType SignedElemT = classifyPrim(SignedVecT->getAs<VectorType>()->getElementType()); + for (unsigned I = 0; I != VecT->getNumElements(); ++I) { + if (!getElem(SubExprOffset, I)) + return false; + + // operator ! on vectors returns -1 for 'truth', so negate it. + if (isIntegralType(ElemT)) { + if (!this->emitPrimCast(ElemT, PT_Bool, Ctx.getASTContext().BoolTy, E)) + return false; + if (!this->emitInv(E)) + return false; + if (!this->emitPrimCast(PT_Bool, ElemT, VecT->getElementType(), E)) + return false; + if (!this->emitNeg(ElemT, E)) + return false; + if (ElemT != SignedElemT && + !this->emitPrimCast(ElemT, SignedElemT, SignedVecT, E)) + return false; + } else { + // Float types result in an int of the same size, but -1 for true, or 0 for + // false. + auto &FpSemantics = Ctx.getFloatSemantics(VecT->getElementType()); + unsigned NumBits = Ctx.getBitWidth(SignedVecT->getAs<VectorType>()->getElementType()); + auto Zero = APFloat::getZero(FpSemantics); + APSInt SIntZero(APSInt::getZero(NumBits)); + APSInt SIntAllOne(APSInt::getAllOnes(NumBits)); + // Emit operations equivalent to isZero(Vec[I]) ? -1 : 0 + if (!this->emitConstFloat(Zero, E)) + return false; + if (!this->emitEQ(ElemT, E)) + return false; + LabelTy LabelFalse = this->getLabel(); + LabelTy LabelEnd = this->getLabel(); + if (!this->jumpFalse(LabelFalse)) + return false; + if (!this->emitConst(SIntAllOne, SignedElemT, E)) + return false; + if (!this->jump(LabelEnd)) + return false; + this->emitLabel(LabelFalse); + if (!this->emitConst(SIntZero, SignedElemT, E)) + return false; + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + } + if (!this->emitInitElem(SignedElemT, I, E)) + return false; + } + break; + } + case UO_Not: // ~x + if (!prepareResult()) + return false; + if (!createTemp()) + return false; + for (unsigned I = 0; I != VecT->getNumElements(); ++I) { + if (!getElem(SubExprOffset, I)) + return false; + if (ElemT == PT_Bool) { + if (!this->emitInv(E)) + return false; + } else { + if (!this->emitComp(ElemT, E)) + return false; + } + if (!this->emitInitElem(ElemT, I, E)) + return false; + } + break; + default: + return this->emitInvalid(E); + } + + return true; +} + template <class Emitter> bool Compiler<Emitter>::visitDeclRef(const ValueDecl *D, const Expr *E) { if (DiscardResult) diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 5acfe3c41796c4..b695b3e8e592f9 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -139,6 +139,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, bool VisitGNUNullExpr(const GNUNullExpr *E); bool VisitCXXThisExpr(const CXXThisExpr *E); bool VisitUnaryOperator(const UnaryOperator *E); + bool VisitVectorUnaryOp(const UnaryOperator *E); bool VisitComplexUnaryOperator(const UnaryOperator *E); bool VisitDeclRefExpr(const DeclRefExpr *E); bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E); @@ -349,6 +350,11 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>, return *this->classify(ElemType); } + PrimType classifyVectorElementType(QualType T) const { + assert(T->isVectorType()); + return *this->classify(T->getAs<VectorType>()->getElementType()); + } + bool emitComplexReal(const Expr *SubExpr); bool emitComplexBoolCast(const Expr *E); bool emitComplexComparison(const Expr *LHS, const Expr *RHS, diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index ee143381cf4f79..6fec88da40e526 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4906,7 +4906,7 @@ bool Sema::BuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs, // TODO: When all classification function are implemented with is_fpclass, // vector argument can be supported in all of them. if (ElementTy->isVectorType() && IsFPClass) { - VectorResultTy = GetSignedVectorType(ElementTy); + VectorResultTy = Context.GetSignedVectorType(ElementTy); ElementTy = ElementTy->castAs<VectorType>()->getElementType(); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ea57316ad8014e..bd2ff6c828525a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -12534,61 +12534,6 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, return InvalidOperands(Loc, LHS, RHS); } -QualType Sema::GetSignedVectorType(QualType V) { - const VectorType *VTy = V->castAs<VectorType>(); - unsigned TypeSize = Context.getTypeSize(VTy->getElementType()); - - if (isa<ExtVectorType>(VTy)) { - if (VTy->isExtVectorBoolType()) - return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.CharTy)) - return Context.getExtVectorType(Context.CharTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getExtVectorType(Context.ShortTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getExtVectorType(Context.IntTy, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getExtVectorType(Context.Int128Ty, VTy->getNumElements()); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getExtVectorType(Context.LongTy, VTy->getNumElements()); - assert(TypeSize == Context.getTypeSize(Context.LongLongTy) && - "Unhandled vector element size in vector compare"); - return Context.getExtVectorType(Context.LongLongTy, VTy->getNumElements()); - } - - if (TypeSize == Context.getTypeSize(Context.Int128Ty)) - return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongLongTy)) - return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.LongTy)) - return Context.getVectorType(Context.LongTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.IntTy)) - return Context.getVectorType(Context.IntTy, VTy->getNumElements(), - VectorKind::Generic); - if (TypeSize == Context.getTypeSize(Context.ShortTy)) - return Context.getVectorType(Context.ShortTy, VTy->getNumElements(), - VectorKind::Generic); - assert(TypeSize == Context.getTypeSize(Context.CharTy) && - "Unhandled vector element size in vector compare"); - return Context.getVectorType(Context.CharTy, VTy->getNumElements(), - VectorKind::Generic); -} - -QualType Sema::GetSignedSizelessVectorType(QualType V) { - const BuiltinType *VTy = V->castAs<BuiltinType>(); - assert(VTy->isSizelessBuiltinType() && "expected sizeless type"); - - const QualType ETy = V->getSveEltType(Context); - const auto TypeSize = Context.getTypeSize(ETy); - - const QualType IntTy = Context.getIntTypeForBitwidth(TypeSize, true); - const llvm::ElementCount VecSize = Context.getBuiltinVectorTypeInfo(VTy).EC; - return Context.getScalableVectorType(IntTy, VecSize.getKnownMinValue()); -} - QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc) { @@ -12647,7 +12592,7 @@ QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, } // Return a signed type for the vector. - return GetSignedVectorType(vType); + return Context.GetSignedVectorType(vType); } QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, @@ -12688,7 +12633,7 @@ QualType Sema::CheckSizelessVectorCompareOperands(ExprResult &LHS, return LHSType; // Return a signed type for the vector. - return GetSignedSizelessVectorType(vType); + return Context.GetSignedSizelessVectorType(vType); } static void diagnoseXorMisusedAsPow(Sema &S, const ExprResult &XorLHS, @@ -12835,7 +12780,7 @@ QualType Sema::CheckVectorLogicalOperands(ExprResult &LHS, ExprResult &RHS, !(isa<ExtVectorType>(vType->getAs<VectorType>()))) return InvalidLogicalVectorOperands(Loc, LHS, RHS); - return GetSignedVectorType(LHS.get()->getType()); + return Context.GetSignedVectorType(LHS.get()->getType()); } QualType Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS, @@ -14535,7 +14480,7 @@ static ExprResult convertHalfVecBinOp(Sema &S, ExprResult LHS, ExprResult RHS, // If Opc is a comparison, ResultType is a vector of shorts. In that case, // change BinOpResTy to a vector of ints. if (isVector(ResultTy, Context.ShortTy)) - BinOpResTy = S.GetSignedVectorType(BinOpResTy); + BinOpResTy = S.Context.GetSignedVectorType(BinOpResTy); if (IsCompAssign) return CompoundAssignOperator::Create(Context, LHS.get(), RHS.get(), Opc, @@ -15449,7 +15394,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, << resultType << Input.get()->getSourceRange()); } // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); + resultType = Context.GetSignedVectorType(resultType); break; } else if (Context.getLangOpts().CPlusPlus && resultType->isVectorType()) { @@ -15459,7 +15404,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, << resultType << Input.get()->getSourceRange()); // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); + resultType = Context.GetSignedVectorType(resultType); break; } else { return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) diff --git a/clang/test/AST/ByteCode/constexpr-vectors.cpp b/clang/test/AST/ByteCode/constexpr-vectors.cpp new file mode 100644 index 00000000000000..9c396d580bfc5d --- /dev/null +++ b/clang/test/AST/ByteCode/constexpr-vectors.cpp @@ -0,0 +1,88 @@ +// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter -Wno-uninitialized -std=c++14 -fsyntax-only -verify + +// expected-no-diagnostics + +using FourCharsVecSize __attribute__((vector_size(4))) = char; +using FourIntsVecSize __attribute__((vector_size(16))) = int; +using FourLongLongsVecSize __attribute__((vector_size(32))) = long long; +using FourFloatsVecSize __attribute__((vector_size(16))) = float; +using FourDoublesVecSize __attribute__((vector_size(32))) = double; +using FourI128VecSize __attribute__((vector_size(64))) = __int128; + +using FourCharsExtVec __attribute__((ext_vector_type(4))) = char; +using FourIntsExtVec __attribute__((ext_vector_type(4))) = int; +using FourI128ExtVec __attribute__((ext_vector_type(4))) = __int128; + +// Only int vs float makes a difference here, so we only need to test 1 of each. +// Test Char to make sure the mixed-nature of shifts around char is evident. +void CharUsage() { + constexpr auto H = FourCharsVecSize{-1, -1, 0, -1}; + constexpr auto InvH = -H; + static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, ""); + + constexpr auto ae = ~FourCharsVecSize{1, 2, 10, 20}; + static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, ""); + + constexpr auto af = !FourCharsVecSize{0, 1, 8, -1}; + static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, ""); +} + +void CharExtVecUsage() { + constexpr auto H = FourCharsExtVec{-1, -1, 0, -1}; + constexpr auto InvH = -H; + static_assert(InvH[0] == 1 && InvH[1] == 1 && InvH[2] == 0 && InvH[3] == 1, ""); + + constexpr auto ae = ~FourCharsExtVec{1, 2, 10, 20}; + static_assert(ae[0] == -2 && ae[1] == -3 && ae[2] == -11 && ae[3] == -21, ""); + + constexpr auto af = !FourCharsExtVec{0, 1, 8, -1}; + static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, ""); +} + +void FloatUsage() { + constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00}; + constexpr auto Z = -Y; + static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, ""); + + // Operator ~ is illegal on floats, so no test for that. + constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1}; + static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, ""); +} + +void FloatVecUsage() { + constexpr auto Y = FourFloatsVecSize{1.200000e+01, 1.700000e+01, -1.000000e+00, -1.000000e+00}; + constexpr auto Z = -Y; + static_assert(Z[0] == -1.200000e+01 && Z[1] == -1.700000e+01 && Z[2] == 1.000000e+00 && Z[3] == 1.000000e+00, ""); + + // Operator ~ is illegal on floats, so no test for that. + constexpr auto af = !FourFloatsVecSize{0, 1, 8, -1}; + static_assert(af[0] == -1 && af[1] == 0 && af[2] == 0 && af[3] == 0, ""); +} + +// FIXME: Int128 vector element comparsion will cause new interpreter crash. +void I128Usage() { + // Operator ~ is illegal on floats, so no test for that. + constexpr auto c = ~FourI128VecSize{1, 2, 10, 20}; + // static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, ""); + + constexpr auto d = !FourI128VecSize{0, 1, 8, -1}; + // static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, ""); +} + +void I128VecUsage() { + // Operator ~ is illegal on floats, so no test for that. + constexpr auto c = ~FourI128ExtVec{1, 2, 10, 20}; + // static_assert(c[0] == -2 && c[1] == -3 && c[2] == -11 && c[3] == -21, ""); + + constexpr auto d = !FourI128ExtVec{0, 1, 8, -1}; + // static_assert(d[0] == -1 && d[1] == 0 && d[2] == 0 && d[3] == 0, ""); +} + +using FourBoolsExtVec __attribute__((ext_vector_type(4))) = bool; +void BoolVecUsage() { + // constexpr auto j = !FourBoolsExtVec{true, false, true, false}; + // static_assert(j[0] == false && j[1] == true && j[2] == false && j[3] == true, ""); + + constexpr auto k = ~FourBoolsExtVec{true, false, true, false}; + static_assert(k[0] == false && k[1] == true && k[2] == false && k[3] == true, ""); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits