diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -445,6 +445,67 @@ return v; } +GCC vector types are created using the ``vector_size(N)`` attribute. The +argument ``N`` specifies the number of bytes that will be allocated for an +object of this type. The size has to be multiple of the size of the vector +element type. For example: + +.. code-block:: c++ + + // OK: This declares a vector type with four 'int' elements + typedef int int4 __attribute__((vector_size(4 * sizeof(int)))); + + // ERROR: '11' is not a multiple of sizeof(int) + typedef int int_impossible __attribute__((vector_size(11))); + + int4 foo(int4 a) { + int4 v; + v = a; + return v; + } + + +Boolean Vectors +--------------- + +Clang also supports the ext_vector_type attribute with boolean element types in +C and C++. For example: + +.. code-block:: c++ + + // legal for Clang, error for GCC: + typedef bool bool4 __attribute__((ext_vector_type(4))); + // Objects of bool4 type hold 8 bits, sizeof(bool4) == 1 + + bool4 foo(bool4 a) { + bool4 v; + v = a; + return v; + } + +Boolean vectors are a Clang extension of the ext vector type. Boolean vectors +are intended, though not guaranteed, to map to vector mask registers. The size +parameter of a boolean vector type is the number of bits in the vector. The +boolean vector is dense and each bit in the boolean vector is one vector +element. + +The semantics of boolean vectors borrows from C bit-fields with the following +differences: + +* Distinct boolean vectors are always distinct memory objects (there is no + packing). +* Only the operators `?:`, `!`, `~`, `|`, `&`, `^` and comparison are allowed on + boolean vectors. +* Casting a scalar bool value to a boolean vector type means broadcasting the + scalar value onto all lanes (same as general ext_vector_type). +* It is not possible to access or swizzle elements of a boolean vector + (different than general ext_vector_type). + +The size and alignment are both the number of bits rounded up to the next power +of two, but the alignment is at most the maximum vector alignment of the +target. + + Vector Literals --------------- @@ -496,6 +557,7 @@ reinterpret_cast yes no yes no static_cast yes no yes no const_cast no no no no +address &v[i] no no no [#]_ no ============================== ======= ======= ============= ======= See also :ref:`langext-__builtin_shufflevector`, :ref:`langext-__builtin_convertvector`. @@ -505,6 +567,9 @@ it's only available in C++ and uses normal bool conversions (that is, != 0). If it's an extension (OpenCL) vector, it's only available in C and OpenCL C. And it selects base on signedness of the condition operands (OpenCL v1.1 s6.3.9). +.. [#] Clang does not allow the address of an element to be taken while GCC + allows this. This is intentional for vectors with a boolean element type and + not implemented otherwise. Vector Builtins --------------- diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -2046,6 +2046,7 @@ bool isComplexIntegerType() const; // GCC _Complex integer type. bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. + bool isExtVectorBoolType() const; // Extended vector type with bool element. bool isMatrixType() const; // Matrix type. bool isConstantMatrixType() const; // Constant matrix type. bool isDependentAddressSpaceType() const; // value-dependent address space qualifier @@ -6805,6 +6806,12 @@ return isa(CanonicalType); } +inline bool Type::isExtVectorBoolType() const { + if (!isExtVectorType()) + return false; + return cast(CanonicalType)->getElementType()->isBooleanType(); +} + inline bool Type::isMatrixType() const { return isa(CanonicalType); } diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11872,7 +11872,8 @@ /// type checking for vector binary operators. QualType CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign, - bool AllowBothBool, bool AllowBoolConversion); + bool AllowBothBool, bool AllowBoolConversion, + bool AllowBoolOperation, bool ReportInvalid); QualType GetSignedVectorType(QualType V); QualType CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1981,8 +1981,11 @@ case Type::Vector: { const auto *VT = cast(T); TypeInfo EltInfo = getTypeInfo(VT->getElementType()); - Width = EltInfo.Width * VT->getNumElements(); - Align = Width; + Width = VT->isExtVectorBoolType() ? VT->getNumElements() + : EltInfo.Width * VT->getNumElements(); + // Enforce at least byte alignment. + Align = std::max(8, Width); + // If the alignment is not a power of 2, round up to the next power of 2. // This happens for non-power-of-2 length vectors. if (Align & (Align-1)) { diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3039,6 +3039,23 @@ llvm::DIType *CGDebugInfo::CreateType(const VectorType *Ty, llvm::DIFile *Unit) { + if (Ty->isExtVectorBoolType()) { + // Boolean ext_vector_type(N) are special because their real element type + // (bits of bit size) is not their Clang element type (_Bool of size byte). + // For now, we pretend the boolean vector were actually a vector of bytes + // (where each byte represents 8 bits of the actual vector). + // FIXME Debug info should actually represent this proper as a vector mask + // type. + auto &Ctx = CGM.getContext(); + uint64_t Size = CGM.getContext().getTypeSize(Ty); + uint64_t NumVectorBytes = Size / Ctx.getCharWidth(); + + // Construct the vector of 'char' type. + QualType CharVecTy = Ctx.getVectorType(Ctx.CharTy, NumVectorBytes, + VectorType::GenericVector); + return CreateType(CharVecTy->getAs(), Unit); + } + llvm::DIType *ElementTy = getOrCreateType(Ty->getElementType(), Unit); int64_t Count = Ty->getNumElements(); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1703,27 +1703,42 @@ LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo, bool isNontemporal) { - if (!CGM.getCodeGenOpts().PreserveVec3Type) { - // For better performance, handle vector loads differently. - if (Ty->isVectorType()) { - const llvm::Type *EltTy = Addr.getElementType(); - - const auto *VTy = cast(EltTy); - - // Handle vectors of size 3 like size 4 for better performance. - if (VTy->getNumElements() == 3) { - - // Bitcast to vec4 type. - auto *vec4Ty = llvm::FixedVectorType::get(VTy->getElementType(), 4); - Address Cast = Builder.CreateElementBitCast(Addr, vec4Ty, "castToVec4"); - // Now load value. - llvm::Value *V = Builder.CreateLoad(Cast, Volatile, "loadVec4"); - - // Shuffle vector to get vec3. - V = Builder.CreateShuffleVector(V, ArrayRef{0, 1, 2}, - "extractVec"); - return EmitFromMemory(V, Ty); - } + if (const auto *ClangVecTy = Ty->getAs()) { + // Boolean vectors use `iN` as storage type. + if (ClangVecTy->isExtVectorBoolType()) { + llvm::Type *ValTy = ConvertType(Ty); + unsigned ValNumElems = + cast(ValTy)->getNumElements(); + // Load the `iP` storage object (P is the padded vector size). + auto *RawIntV = Builder.CreateLoad(Addr, Volatile, "load_bits"); + const auto *RawIntTy = RawIntV->getType(); + assert(RawIntTy->isIntegerTy() && "compressed iN storage for bitvectors"); + // Bitcast iP -->

. + auto *PaddedVecTy = llvm::FixedVectorType::get( + Builder.getInt1Ty(), RawIntTy->getPrimitiveSizeInBits()); + llvm::Value *V = Builder.CreateBitCast(RawIntV, PaddedVecTy); + // Shuffle

--> (N is the actual bit size). + V = emitBoolVecConversion(V, ValNumElems, "extractvec"); + + return EmitFromMemory(V, Ty); + } + + // Handle vectors of size 3 like size 4 for better performance. + const llvm::Type *EltTy = Addr.getElementType(); + const auto *VTy = cast(EltTy); + + if (!CGM.getCodeGenOpts().PreserveVec3Type && VTy->getNumElements() == 3) { + + // Bitcast to vec4 type. + llvm::VectorType *vec4Ty = + llvm::FixedVectorType::get(VTy->getElementType(), 4); + Address Cast = Builder.CreateElementBitCast(Addr, vec4Ty, "castToVec4"); + // Now load value. + llvm::Value *V = Builder.CreateLoad(Cast, Volatile, "loadVec4"); + + // Shuffle vector to get vec3. + V = Builder.CreateShuffleVector(V, ArrayRef{0, 1, 2}, "extractVec"); + return EmitFromMemory(V, Ty); } } @@ -1774,6 +1789,17 @@ "wrong value rep of bool"); return Builder.CreateTrunc(Value, Builder.getInt1Ty(), "tobool"); } + if (Ty->isExtVectorBoolType()) { + const auto *RawIntTy = Value->getType(); + // Bitcast iP -->

. + auto *PaddedVecTy = llvm::FixedVectorType::get( + Builder.getInt1Ty(), RawIntTy->getPrimitiveSizeInBits()); + auto *V = Builder.CreateBitCast(Value, PaddedVecTy); + // Shuffle

--> (N is the actual bit size). + llvm::Type *ValTy = ConvertType(Ty); + unsigned ValNumElems = cast(ValTy)->getNumElements(); + return emitBoolVecConversion(V, ValNumElems, "extractvec"); + } return Value; } @@ -1818,11 +1844,19 @@ LValueBaseInfo BaseInfo, TBAAAccessInfo TBAAInfo, bool isInit, bool isNontemporal) { - if (!CGM.getCodeGenOpts().PreserveVec3Type) { - // Handle vectors differently to get better performance. - if (Ty->isVectorType()) { - llvm::Type *SrcTy = Value->getType(); - auto *VecTy = dyn_cast(SrcTy); + llvm::Type *SrcTy = Value->getType(); + if (const auto *ClangVecTy = Ty->getAs()) { + auto *VecTy = dyn_cast(SrcTy); + if (VecTy && ClangVecTy->isExtVectorBoolType()) { + auto *MemIntTy = + cast(Addr.getType()->getPointerElementType()); + // Expand to the memory bit width. + unsigned MemNumElems = MemIntTy->getPrimitiveSizeInBits(); + // -->

. + Value = emitBoolVecConversion(Value, MemNumElems, "insertvec"); + //

--> iP. + Value = Builder.CreateBitCast(Value, MemIntTy); + } else if (!CGM.getCodeGenOpts().PreserveVec3Type) { // Handle vec3 special. if (VecTy && cast(VecTy)->getNumElements() == 3) { // Our source is a vec3, do a shuffle vector to make it a vec4. @@ -2059,8 +2093,19 @@ // Read/modify/write the vector, inserting the new element. llvm::Value *Vec = Builder.CreateLoad(Dst.getVectorAddress(), Dst.isVolatileQualified()); + auto *IRStoreTy = dyn_cast(Vec->getType()); + if (IRStoreTy) { + auto *IRVecTy = llvm::FixedVectorType::get( + Builder.getInt1Ty(), IRStoreTy->getPrimitiveSizeInBits()); + Vec = Builder.CreateBitCast(Vec, IRVecTy); + // iN --> . + } Vec = Builder.CreateInsertElement(Vec, Src.getScalarVal(), Dst.getVectorIdx(), "vecins"); + if (IRStoreTy) { + // --> . + Vec = Builder.CreateBitCast(Vec, IRStoreTy); + } Builder.CreateStore(Vec, Dst.getVectorAddress(), Dst.isVolatileQualified()); return; diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2147,7 +2147,6 @@ DestLV.setTBAAInfo(TBAAAccessInfo::getMayAliasInfo()); return EmitLoadOfLValue(DestLV, CE->getExprLoc()); } - return Builder.CreateBitCast(Src, DstTy); } case CK_AddressSpaceConversion: { @@ -4818,6 +4817,11 @@ ? cast(DstTy)->getNumElements() : 0; + // Use bit vector expansion for ext_vector_type boolean vectors. + if (E->getType()->isExtVectorBoolType()) { + return CGF.emitBoolVecConversion(Src, NumElementsDst, "astype"); + } + // Going from vec3 to non-vec3 is a special case and requires a shuffle // vector to get a vec4, then a bitcast if the target type is different. if (NumElementsSrc == 3 && NumElementsDst != 3) { diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4648,6 +4648,11 @@ /// Set the codegen fast-math flags. void SetFastMathFlags(FPOptions FPFeatures); + // Truncate or extend a boolean vector to the requested number of elements. + llvm::Value *emitBoolVecConversion(llvm::Value *SrcVec, + unsigned NumElementsDst, + const llvm::Twine &Name = ""); + private: llvm::MDNode *getRangeForLoadFromType(QualType Ty); void EmitReturnOfRValue(RValue RV, QualType Ty); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2742,3 +2742,21 @@ } llvm_unreachable("Unknown Likelihood"); } + +llvm::Value *CodeGenFunction::emitBoolVecConversion(llvm::Value *SrcVec, + unsigned NumElementsDst, + const llvm::Twine &Name) { + auto *SrcTy = cast(SrcVec->getType()); + unsigned NumElementsSrc = SrcTy->getNumElements(); + if (NumElementsSrc == NumElementsDst) { + return SrcVec; + } + + std::vector ShuffleMask(NumElementsDst, -1); + for (unsigned MaskIdx = 0; + MaskIdx < std::min<>(NumElementsDst, NumElementsSrc); ++MaskIdx) { + ShuffleMask[MaskIdx] = MaskIdx; + } + + return Builder.CreateShuffleVector(SrcVec, ShuffleMask, Name); +} diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -97,6 +97,14 @@ llvm::Type *R = ConvertType(T); + // Check for the boolean vector case. + if (T->isExtVectorBoolType()) { + auto *FixedVT = cast(R); + // Pad to at least one byte. + uint64_t BytePadded = std::max(FixedVT->getNumElements(), 8); + return llvm::IntegerType::get(FixedVT->getContext(), BytePadded); + } + // If this is a bool type, or a bit-precise integer type in a bitfield // representation, map this integer to the target-specified size. if ((ForBitField && T->isBitIntType()) || @@ -691,9 +699,12 @@ } case Type::ExtVector: case Type::Vector: { - const VectorType *VT = cast(Ty); - ResultType = llvm::FixedVectorType::get(ConvertType(VT->getElementType()), - VT->getNumElements()); + const auto *VT = cast(Ty); + // An ext_vector_type of Bool is really a vector of bits. + llvm::Type *IRElemTy = VT->isExtVectorBoolType() + ? llvm::Type::getInt1Ty(getLLVMContext()) + : ConvertType(VT->getElementType()); + ResultType = llvm::FixedVectorType::get(IRElemTy, VT->getNumElements()); break; } case Type::ConstantMatrix: { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8316,11 +8316,17 @@ // result as specified in OpenCL v1.1 s6.3.i. if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) { - QualType VecResTy = S.CheckVectorOperands(LHS, RHS, QuestionLoc, - /*isCompAssign*/false, - /*AllowBothBool*/true, - /*AllowBoolConversions*/false); - if (VecResTy.isNull()) return QualType(); + bool IsBoolVecLang = + !S.getLangOpts().OpenCL && !S.getLangOpts().OpenCLCPlusPlus; + QualType VecResTy = + S.CheckVectorOperands(LHS, RHS, QuestionLoc, + /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ IsBoolVecLang, + /*ReportInvalid*/ true); + if (VecResTy.isNull()) + return QualType(); // The result type must match the condition type as specified in // OpenCL v1.1 s6.11.6. if (checkVectorResult(S, CondTy, VecResTy, QuestionLoc)) @@ -8393,9 +8399,11 @@ // Now check the two expressions. if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) - return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/false, - /*AllowBothBool*/true, - /*AllowBoolConversions*/false); + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); QualType ResTy = UsualArithmeticConversions(LHS, RHS, QuestionLoc, ACK_Conditional); @@ -10100,10 +10108,16 @@ return false; } +static bool isBoolOrExtVectorBoolType(QualType VT) { + return VT->isBooleanType() || VT->isExtVectorBoolType(); +} + QualType Sema::CheckVectorOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, bool IsCompAssign, bool AllowBothBool, - bool AllowBoolConversions) { + bool AllowBoolConversions, + bool AllowBoolOperation, + bool ReportInvalid) { if (!IsCompAssign) { LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); if (LHS.isInvalid()) @@ -10124,14 +10138,19 @@ if ((LHSVecType && LHSVecType->getElementType()->isBFloat16Type()) || (RHSVecType && RHSVecType->getElementType()->isBFloat16Type())) - return InvalidOperands(Loc, LHS, RHS); + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); // AltiVec-style "vector bool op vector bool" combinations are allowed // for some operators but not others. if (!AllowBothBool && LHSVecType && LHSVecType->getVectorKind() == VectorType::AltiVecBool && RHSVecType && RHSVecType->getVectorKind() == VectorType::AltiVecBool) - return InvalidOperands(Loc, LHS, RHS); + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); + + // This operation may not be performed on boolean vectors. + if (!AllowBoolOperation && (isBoolOrExtVectorBoolType(LHSType) && + isBoolOrExtVectorBoolType(RHSType))) + return ReportInvalid ? InvalidOperands(Loc, LHS, RHS) : QualType(); // If the vector types are identical, return. if (Context.hasSameType(LHSType, RHSType)) @@ -10420,7 +10439,9 @@ if (LHSTy->isVectorType() || RHSTy->isVectorType()) return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, /*AllowBothBool*/getLangOpts().AltiVec, - /*AllowBoolConversions*/false); + /*AllowBoolConversions*/false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); if (!IsDiv && (LHSTy->isConstantMatrixType() || RHSTy->isConstantMatrixType())) return CheckMatrixMultiplyOperands(LHS, RHS, Loc, IsCompAssign); @@ -10453,8 +10474,10 @@ if (LHS.get()->getType()->hasIntegerRepresentation() && RHS.get()->getType()->hasIntegerRepresentation()) return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/getLangOpts().AltiVec, - /*AllowBoolConversions*/false); + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); return InvalidOperands(Loc, LHS, RHS); } @@ -10755,10 +10778,12 @@ if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) { - QualType compType = CheckVectorOperands( - LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/getLangOpts().AltiVec, - /*AllowBoolConversions*/getLangOpts().ZVector); + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -10859,10 +10884,12 @@ if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) { - QualType compType = CheckVectorOperands( - LHS, RHS, Loc, CompLHSTy, - /*AllowBothBool*/getLangOpts().AltiVec, - /*AllowBoolConversions*/getLangOpts().ZVector); + QualType compType = + CheckVectorOperands(LHS, RHS, Loc, CompLHSTy, + /*AllowBothBool*/ getLangOpts().AltiVec, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ true); if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -11113,6 +11140,15 @@ const VectorType *RHSVecTy = RHSType->getAs(); QualType RHSEleType = RHSVecTy ? RHSVecTy->getElementType() : RHSType; + // Do not allow shifts for boolean vectors. + if ((LHSVecTy && LHSVecTy->isExtVectorBoolType()) || + (RHSVecTy && RHSVecTy->isExtVectorBoolType())) { + S.Diag(Loc, diag::err_typecheck_invalid_operands) + << LHS.get()->getType() << RHS.get()->getType() + << LHS.get()->getSourceRange(); + return QualType(); + } + // The operands need to be integers. if (!LHSEleType->isIntegerType()) { S.Diag(Loc, diag::err_typecheck_expect_int) @@ -12313,6 +12349,8 @@ if (TypeSize == Context.getTypeSize(Context.Int128Ty)) return Context.getVectorType(Context.Int128Ty, VTy->getNumElements(), VectorType::GenericVector); + if (VTy->isExtVectorBoolType()) + return Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); if (TypeSize == Context.getTypeSize(Context.LongLongTy)) return Context.getVectorType(Context.LongLongTy, VTy->getNumElements(), VectorType::GenericVector); @@ -12345,9 +12383,12 @@ // Check to make sure we're operating on vectors of the same type and width, // Allowing one side to be a scalar of element type. - QualType vType = CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/false, - /*AllowBothBool*/true, - /*AllowBoolConversions*/getLangOpts().ZVector); + QualType vType = + CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ true, + /*ReportInvalid*/ true); if (vType.isNull()) return vType; @@ -12521,8 +12562,10 @@ // Ensure that either both operands are of the same vector type, or // one operand is of a vector type and the other is of its element type. QualType vType = CheckVectorOperands(LHS, RHS, Loc, false, - /*AllowBothBool*/true, - /*AllowBoolConversions*/false); + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBooleanOperation*/ false, + /*ReportInvalid*/ false); if (vType.isNull()) return InvalidOperands(Loc, LHS, RHS); if (getLangOpts().OpenCL && @@ -12616,6 +12659,20 @@ return CheckMatrixElementwiseOperands(LHS, RHS, Loc, IsCompAssign); } +static bool isLegalBoolVectorBinaryOp(BinaryOperatorKind Opc) { + switch (Opc) { + default: + return false; + case BO_And: + case BO_AndAssign: + case BO_Or: + case BO_OrAssign: + case BO_Xor: + case BO_XorAssign: + return true; + } +} + inline QualType Sema::CheckBitwiseOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc) { @@ -12624,13 +12681,17 @@ bool IsCompAssign = Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign; + bool LegalBoolVecOperator = isLegalBoolVectorBinaryOp(Opc); + if (LHS.get()->getType()->isVectorType() || RHS.get()->getType()->isVectorType()) { if (LHS.get()->getType()->hasIntegerRepresentation() && RHS.get()->getType()->hasIntegerRepresentation()) return CheckVectorOperands(LHS, RHS, Loc, IsCompAssign, - /*AllowBothBool*/true, - /*AllowBoolConversions*/getLangOpts().ZVector); + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ getLangOpts().ZVector, + /*AllowBooleanOperation*/ LegalBoolVecOperator, + /*ReportInvalid*/ true); return InvalidOperands(Loc, LHS, RHS); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6049,9 +6049,8 @@ return false; const QualType EltTy = cast(CondTy.getCanonicalType())->getElementType(); - assert(!EltTy->isBooleanType() && !EltTy->isEnumeralType() && - "Vectors cant be boolean or enum types"); - return EltTy->isIntegralType(Ctx); + assert(!EltTy->isEnumeralType() && "Vectors cant be enum types"); + return EltTy->isIntegralType(Ctx) || EltTy->isBooleanType(); } QualType Sema::CheckVectorConditionalTypes(ExprResult &Cond, ExprResult &LHS, @@ -6071,7 +6070,6 @@ QualType ResultType; - if (LHSVT && RHSVT) { if (isa(CondVT) != isa(LHSVT)) { Diag(QuestionLoc, diag::err_conditional_vector_cond_result_mismatch) @@ -6089,7 +6087,9 @@ } else if (LHSVT || RHSVT) { ResultType = CheckVectorOperands( LHS, RHS, QuestionLoc, /*isCompAssign*/ false, /*AllowBothBool*/ true, - /*AllowBoolConversions*/ false); + /*AllowBoolConversions*/ false, + /*AllowBoolOperation*/ true, + /*ReportInvalid*/ true); if (ResultType.isNull()) return {}; } else { @@ -6416,9 +6416,11 @@ // Extension: conditional operator involving vector types. if (LTy->isVectorType() || RTy->isVectorType()) - return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/false, - /*AllowBothBool*/true, - /*AllowBoolConversions*/false); + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/ false, + /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false, + /*AllowBoolOperation*/ false, + /*ReportInvalid*/ true); // -- The second and third operands have arithmetic or enumeration type; // the usual arithmetic conversions are performed to bring them to a diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1592,6 +1592,16 @@ false); } + if (BaseType->isExtVectorBoolType()) { + // We disallow element access for ext_vector_type bool. There is no way to + // materialize a reference to a vector element as pointer (each element is + // one bit in the vector). + S.Diag(R.getNameLoc(), diag::err_ext_vector_component_name_illegal) + << MemberName + << (BaseExpr.get() ? BaseExpr.get()->getSourceRange() : SourceRange()); + return ExprError(); + } + // Handle 'field access' to vectors, such as 'V.xx'. if (BaseType->isExtVectorType()) { // FIXME: this expr should store IsArrow. diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2639,9 +2639,12 @@ // reserved data type under OpenCL v2.0 s6.1.4), we don't support selects // on bitvectors, and we have no well-defined ABI for bitvectors, so vectors // of bool aren't allowed. + // + // We explictly allow bool elements in ext_vector_type for C/C++. + bool IsNoBoolVecLang = getLangOpts().OpenCL || getLangOpts().OpenCLCPlusPlus; if ((!T->isDependentType() && !T->isIntegerType() && !T->isRealFloatingType()) || - T->isBooleanType()) { + (IsNoBoolVecLang && T->isBooleanType())) { Diag(AttrLoc, diag::err_attribute_invalid_vector_type) << T; return QualType(); } diff --git a/clang/test/AST/ast-print-vector-size-bool.c b/clang/test/AST/ast-print-vector-size-bool.c new file mode 100644 --- /dev/null +++ b/clang/test/AST/ast-print-vector-size-bool.c @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -ast-print %s -o - | FileCheck %s + +// CHECK: typedef _Bool bool32 __attribute__((ext_vector_type(32))); +typedef _Bool bool32 __attribute__((ext_vector_type(32))); diff --git a/clang/test/CodeGen/debug-info-vector-bool.c b/clang/test/CodeGen/debug-info-vector-bool.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/debug-info-vector-bool.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -triple x86_64-linux-pc -emit-llvm -debug-info-kind=limited %s -o - | FileCheck %s +typedef _Bool bool512 __attribute__((ext_vector_type(512))); + +bool512 b; + +// Test that we get bit-sized bool elements on x86 +// CHECK: !DICompositeType(tag: DW_TAG_array_type, +// CHECK-SAME: baseType: ![[BOOL:[0-9]+]] +// CHECK-SAME: size: 512 +// CHECK-SAME: DIFlagVector +// CHECK: ![[BOOL]] = !DIBasicType(name: "char" diff --git a/clang/test/CodeGen/vector-alignment.c b/clang/test/CodeGen/vector-alignment.c --- a/clang/test/CodeGen/vector-alignment.c +++ b/clang/test/CodeGen/vector-alignment.c @@ -22,6 +22,9 @@ // SSE: @v2 {{.*}}, align 16 // AVX: @v2 {{.*}}, align 32 // AVX512: @v2 {{.*}}, align 32 +typedef __attribute__((__ext_vector_type__(16))) _Bool v2b_type; +v2b_type v2b; +// ALL: @v2b {{.*}}, align 2 // Alignment above target max alignment with no aligned attribute should align // based on the target max. @@ -33,6 +36,11 @@ // SSE: @v4 {{.*}}, align 16 // AVX: @v4 {{.*}}, align 32 // AVX512: @v4 {{.*}}, align 64 +typedef __attribute__((__ext_vector_type__(8192))) _Bool v4b_type; +v4b_type v4b; +// SSE: @v4b {{.*}}, align 16 +// AVX: @v4b {{.*}}, align 32 +// AVX512: @v4b {{.*}}, align 64 // Aliged attribute should always override. double __attribute__((vector_size(16), aligned(16))) v5; @@ -43,6 +51,9 @@ // ALL: @v7 {{.*}}, align 16 double __attribute__((vector_size(32), aligned(64))) v8; // ALL: @v8 {{.*}}, align 64 +typedef __attribute__((ext_vector_type(256), aligned(128))) _Bool v8b_type; +v8b_type v8b; +// ALL: @v8b {{.*}}, align 128 // Check non-power of 2 widths. double __attribute__((vector_size(24))) v9; @@ -53,9 +64,17 @@ // SSE: @v10 {{.*}}, align 16 // AVX: @v10 {{.*}}, align 32 // AVX512: @v10 {{.*}}, align 64 +typedef __attribute__((ext_vector_type(248))) _Bool v10b_type; +v10b_type v10b; +// SSE: @v10b {{.*}}, align 16 +// AVX: @v10b {{.*}}, align 32 +// AVX512: @v10b {{.*}}, align 32 // Check non-power of 2 widths with aligned attribute. double __attribute__((vector_size(24), aligned(64))) v11; // ALL: @v11 {{.*}}, align 64 double __attribute__((vector_size(80), aligned(16))) v12; // ALL: @v12 {{.*}}, align 16 +typedef __attribute__((ext_vector_type(248), aligned(4))) _Bool v12b_type; +v12b_type v12b; +// ALL: @v12b {{.*}}, align 4 diff --git a/clang/test/Sema/ext_vector_casts.c b/clang/test/Sema/ext_vector_casts.c --- a/clang/test/Sema/ext_vector_casts.c +++ b/clang/test/Sema/ext_vector_casts.c @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple x86_64-apple-macos10.7.0 -fsyntax-only -verify -flax-vector-conversions=none -Wconversion %s -typedef __attribute__((ext_vector_type(8))) _Bool BoolVector; // expected-error {{invalid vector element type '_Bool'}} +typedef __attribute__((ext_vector_type(8))) _Bool BoolVector; typedef __attribute__(( ext_vector_type(2) )) float float2; typedef __attribute__(( ext_vector_type(3) )) float float3; diff --git a/clang/test/SemaCXX/constexpr-vectors.cpp b/clang/test/SemaCXX/constexpr-vectors.cpp --- a/clang/test/SemaCXX/constexpr-vectors.cpp +++ b/clang/test/SemaCXX/constexpr-vectors.cpp @@ -677,3 +677,18 @@ // CHECK: store <4 x i128> } +using EightBoolsExtVector __attribute__((ext_vector_type(8))) = bool; +void BoolVecUsage() { + constexpr auto a = EightBoolsExtVector{true, false, true, false} < + EightBoolsExtVector{false, false, true, true}; + constexpr auto b = EightBoolsExtVector{true, false, true, false} <= + EightBoolsExtVector{false, false, true, true}; + constexpr auto c = EightBoolsExtVector{true, false, true, false} == + EightBoolsExtVector{false, false, true, true}; + constexpr auto d = EightBoolsExtVector{true, false, true, false} != + EightBoolsExtVector{false, false, true, true}; + constexpr auto e = EightBoolsExtVector{true, false, true, false} >= + EightBoolsExtVector{false, false, true, true}; + constexpr auto f = EightBoolsExtVector{true, false, true, false} > + EightBoolsExtVector{false, false, true, true}; +} diff --git a/clang/test/SemaCXX/vector-bool.cpp b/clang/test/SemaCXX/vector-bool.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/vector-bool.cpp @@ -0,0 +1,92 @@ +// RUN: %clang_cc1 -triple x86_64-linux-pc -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++17 +// Note that this test depends on the size of long-long to be different from +// int, so it specifies a triple. + +using FourShorts = short __attribute__((__vector_size__(8))); +using TwoInts = int __attribute__((__vector_size__(8))); +using EightInts = int __attribute__((__vector_size__(32))); +using TwoUInts = unsigned __attribute__((__vector_size__(8))); +using FourInts = int __attribute__((__vector_size__(16))); +using FourUInts = unsigned __attribute__((__vector_size__(16))); +using TwoLongLong = long long __attribute__((__vector_size__(16))); +using FourLongLong = long long __attribute__((__vector_size__(32))); +using TwoFloats = float __attribute__((__vector_size__(8))); +using FourFloats = float __attribute__((__vector_size__(16))); +using TwoDoubles = double __attribute__((__vector_size__(16))); +using FourDoubles = double __attribute__((__vector_size__(32))); +using EightBools = bool __attribute__((ext_vector_type(8))); + +EightInts eight_ints; +EightBools eight_bools; +EightBools other_eight_bools; +bool one_bool; + +// Check the rules of the LHS/RHS of the conditional operator. +void Operations() { + // Legal binary + // (void)(eight_bools | other_eight_bools); + // (void)(eight_bools & other_eight_bools); + // (void)(eight_bools ^ other_eight_bools); + // (void)(~eight_bools); + // (void)(!eight_bools); + + // // Legal comparison + // (void)(eight_bools == other_eight_bools); + // (void)(eight_bools != other_eight_bools); + // (void)(eight_bools < other_eight_bools); + // (void)(eight_bools <= other_eight_bools); + // (void)(eight_bools > other_eight_bools); + // (void)(eight_bools >= other_eight_bools); + + // // Legal assignments + // (void)(eight_bools |= other_eight_bools); + // (void)(eight_bools &= other_eight_bools); + // (void)(eight_bools ^= other_eight_bools); + + // Illegal operators + (void)(eight_bools || other_eight_bools); // expected-error@47 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools && other_eight_bools); // expected-error@48 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools + other_eight_bools); // expected-error@49 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools - other_eight_bools); // expected-error@50 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools * other_eight_bools); // expected-error@51 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools << other_eight_bools); // expected-error@52 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools >> other_eight_bools); // expected-error@53 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools / other_eight_bools); // expected-error@54 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools % other_eight_bools); // expected-error@55 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + + // Illegal assignment + (void)(eight_bools += other_eight_bools); // expected-error@58 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools -= other_eight_bools); // expected-error@59 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools *= other_eight_bools); // expected-error@60 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools <<= other_eight_bools); // expected-error@61 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools >>= other_eight_bools); // expected-error@62 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools /= other_eight_bools); // expected-error@63 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + (void)(eight_bools %= other_eight_bools); // expected-error@64 {{invalid operands to binary expression ('EightBools' (vector of 8 'bool' values) and 'EightBools')}} + + // Illegal in/decrements + (void)(eight_bools++); // expected-error@67 {{cannot increment value of type 'EightBools' (vector of 8 'bool' values)}} + (void)(++eight_bools); // expected-error@68 {{cannot increment value of type 'EightBools' (vector of 8 'bool' values)}} + (void)(eight_bools--); // expected-error@69 {{cannot decrement value of type 'EightBools' (vector of 8 'bool' values)}} + (void)(--eight_bools); // expected-error@70 {{cannot decrement value of type 'EightBools' (vector of 8 'bool' values)}} + + // No implicit promotion + (void)(eight_bools + eight_ints); // expected-error@73 {{cannot convert between vector type 'EightInts' (vector of 8 'int' values) and vector type 'EightBools' (vector of 8 'bool' values) as implicit conversion would cause truncation}} + (void)(eight_ints - eight_bools); // expected-error@74 {{cannot convert between vector type 'EightBools' (vector of 8 'bool' values) and vector type 'EightInts' (vector of 8 'int' values) as implicit conversion would cause truncation}} +} + +// Allow scalar-to-vector broadcast. Do not allow bool vector conversions. +void Conversions() { + (void)((long)eight_bools); // expected-error@79 {{C-style cast from vector 'EightBools' (vector of 8 'bool' values) to scalar 'long' of different size}} + (void)((EightBools) one_bool); // Scalar-to-vector broadcast. + (void)((char)eight_bools); // expected-error@81 {{C-style cast from vector 'EightBools' (vector of 8 'bool' values) to scalar 'char' of different size}} +} + +void foo(const bool& X); + +// Disallow element-wise access. +bool* ElementRefs() { + eight_bools.y = false; // expected-error@88 {{illegal vector component name ''y''}} + &eight_bools.z; // expected-error@89 {{illegal vector component name ''z''}} + foo(eight_bools.w); // expected-error@90 {{illegal vector component name ''w''}} + foo(eight_bools.wyx); // expected-error@91 {{illegal vector component name ''wyx''}} +} diff --git a/clang/test/SemaCXX/vector-size-conditional.cpp b/clang/test/SemaCXX/vector-size-conditional.cpp --- a/clang/test/SemaCXX/vector-size-conditional.cpp +++ b/clang/test/SemaCXX/vector-size-conditional.cpp @@ -13,6 +13,7 @@ using FourFloats = float __attribute__((__vector_size__(16))); using TwoDoubles = double __attribute__((__vector_size__(16))); using FourDoubles = double __attribute__((__vector_size__(32))); +using EightBools = bool __attribute__((ext_vector_type(8))); FourShorts four_shorts; TwoInts two_ints; @@ -25,6 +26,8 @@ FourFloats four_floats; TwoDoubles two_doubles; FourDoubles four_doubles; +EightBools eight_bools; +EightBools other_eight_bools; enum E {}; enum class SE {}; @@ -95,6 +98,9 @@ (void)(four_ints ? four_uints : 3.0f); (void)(four_ints ? four_ints : 3.0f); + // Allow conditional select on bool vectors. + (void)(eight_bools ? eight_bools : other_eight_bools); + // When there is a vector and a scalar, conversions must be legal. (void)(four_ints ? four_floats : 3); // should work, ints can convert to floats. (void)(four_ints ? four_uints : e); // expected-error {{cannot convert between scalar type 'E' and vector type 'FourUInts'}} @@ -163,10 +169,10 @@ void Templates() { dependent_cond(two_ints); dependent_operand(two_floats); - // expected-error@159 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(4 * sizeof(double)))) double' (vector of 4 'double' values))}}} + // expected-error@165 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(4 * sizeof(double)))) double' (vector of 4 'double' values))}}} all_dependent(four_ints, four_uints, four_doubles); // expected-note {{in instantiation of}} - // expected-error@159 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(2 * sizeof(unsigned int)))) unsigned int' (vector of 2 'unsigned int' values))}}} + // expected-error@165 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(2 * sizeof(unsigned int)))) unsigned int' (vector of 2 'unsigned int' values))}}} all_dependent(four_ints, four_uints, two_uints); // expected-note {{in instantiation of}} all_dependent(four_ints, four_uints, four_uints); } diff --git a/clang/test/SemaOpenCL/ext_vectors.cl b/clang/test/SemaOpenCL/ext_vectors.cl --- a/clang/test/SemaOpenCL/ext_vectors.cl +++ b/clang/test/SemaOpenCL/ext_vectors.cl @@ -5,6 +5,7 @@ // RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -cl-std=clc++2021 typedef float float4 __attribute__((ext_vector_type(4))); +typedef __attribute__((ext_vector_type(8))) bool BoolVector; // expected-error {{invalid vector element type 'bool'}} void test_ext_vector_accessors(float4 V) { V = V.wzyx;