Index: clang/docs/LanguageExtensions.rst =================================================================== --- clang/docs/LanguageExtensions.rst +++ clang/docs/LanguageExtensions.rst @@ -465,28 +465,33 @@ dash indicates that an operation is not accepted according to a corresponding specification. -============================== ======= ======= ======= ======= - Operator OpenCL AltiVec GCC NEON -============================== ======= ======= ======= ======= -[] yes yes yes -- -unary operators +, -- yes yes yes -- -++, -- -- yes yes yes -- -+,--,*,/,% yes yes yes -- -bitwise operators &,|,^,~ yes yes yes -- ->>,<< yes yes yes -- -!, &&, || yes -- -- -- -==, !=, >, <, >=, <= yes yes -- -- -= yes yes yes yes -:? yes -- -- -- -sizeof yes yes yes yes -C-style cast yes yes yes no -reinterpret_cast yes no yes no -static_cast yes no yes no -const_cast no no no no -============================== ======= ======= ======= ======= +============================== ======= ======= ============= ======= + Operator OpenCL AltiVec GCC NEON +============================== ======= ======= ============= ======= +[] yes yes yes -- +unary operators +, -- yes yes yes -- +++, -- -- yes yes yes -- ++,--,*,/,% yes yes yes -- +bitwise operators &,|,^,~ yes yes yes -- +>>,<< yes yes yes -- +!, &&, || yes -- yes [#]_ -- +==, !=, >, <, >=, <= yes yes yes -- += yes yes yes yes +:? [#]_ yes -- yes -- +sizeof yes yes yes yes +C-style cast yes yes yes no +reinterpret_cast yes no yes no +static_cast yes no yes no +const_cast no no no no +============================== ======= ======= ============= ======= See also :ref:`langext-__builtin_shufflevector`, :ref:`langext-__builtin_convertvector`. +.. [#] unary operator ! is not implemented, however && and || are. +.. [#] While OpenCL and GCC vectors both implement the comparison operator(?:) as a + 'select', they operate somewhat differently. OpenCL selects based on signedness of + the condition operands, but GCC vectors use normal bool conversions (that is, != 0). + Half-Precision Floating Point ============================= Index: clang/include/clang/AST/Expr.h =================================================================== --- clang/include/clang/AST/Expr.h +++ clang/include/clang/AST/Expr.h @@ -3727,22 +3727,25 @@ friend class ASTStmtReader; public: ConditionalOperator(Expr *cond, SourceLocation QLoc, Expr *lhs, - SourceLocation CLoc, Expr *rhs, - QualType t, ExprValueKind VK, ExprObjectKind OK) - : AbstractConditionalOperator(ConditionalOperatorClass, t, VK, OK, - // FIXME: the type of the conditional operator doesn't - // depend on the type of the conditional, but the standard - // seems to imply that it could. File a bug! - (lhs->isTypeDependent() || rhs->isTypeDependent()), - (cond->isValueDependent() || lhs->isValueDependent() || - rhs->isValueDependent()), - (cond->isInstantiationDependent() || - lhs->isInstantiationDependent() || - rhs->isInstantiationDependent()), - (cond->containsUnexpandedParameterPack() || - lhs->containsUnexpandedParameterPack() || - rhs->containsUnexpandedParameterPack()), - QLoc, CLoc) { + SourceLocation CLoc, Expr *rhs, QualType t, + ExprValueKind VK, ExprObjectKind OK) + : AbstractConditionalOperator( + ConditionalOperatorClass, t, VK, OK, + // The type of the conditional operator depends on the type + // of the conditional to support the GCC vector conditional + // extension. Additionally, [temp.dep.expr] does specify state that + // this should be dependent on ALL sub expressions. + (cond->isTypeDependent() || lhs->isTypeDependent() || + rhs->isTypeDependent()), + (cond->isValueDependent() || lhs->isValueDependent() || + rhs->isValueDependent()), + (cond->isInstantiationDependent() || + lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), + (cond->containsUnexpandedParameterPack() || + lhs->containsUnexpandedParameterPack() || + rhs->containsUnexpandedParameterPack()), + QLoc, CLoc) { SubExprs[COND] = cond; SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6876,6 +6876,14 @@ def err_conditional_vector_element_size : Error< "vector condition type %0 and result type %1 do not have elements of the " "same size">; +def err_conditional_vector_has_void : Error< + "GNU vector conditional operand cannot be %select{void|a throw expression}0">; +def err_conditional_vector_operand_type + : Error<"%select{enumeration|extended vector}0 type %1 is not allowed in a " + "vector conditional">; +def err_conditional_vector_mismatched_vectors + : Error<"vector operands to the vector conditional must be the same type " + "%diff{($ and $)|}0,1}">; def err_throw_incomplete : Error< "cannot throw object of incomplete type %0">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -10555,6 +10555,9 @@ QualType CXXCheckConditionalOperands( // C++ 5.16 ExprResult &cond, ExprResult &lhs, ExprResult &rhs, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc); + QualType CheckGNUVectorConditionalTypes(ExprResult &Cond, ExprResult &LHS, + ExprResult &RHS, + SourceLocation QuestionLoc); QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, bool ConvertArgs = true); QualType FindCompositePointerType(SourceLocation Loc, Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -9288,6 +9288,7 @@ bool VisitUnaryImag(const UnaryOperator *E); // FIXME: Missing: unary -, unary ~, binary add/sub/mul/div, // binary comparisons, binary and/or/xor, + // conditional operator (for GNU conditional select), // shufflevector, ExtVectorElementExpr }; } // end anonymous namespace Index: clang/lib/CodeGen/CGExprScalar.cpp =================================================================== --- clang/lib/CodeGen/CGExprScalar.cpp +++ clang/lib/CodeGen/CGExprScalar.cpp @@ -4290,6 +4290,21 @@ return tmp5; } + if (condExpr->getType()->isVectorType()) { + CGF.incrementProfileCounter(E); + + llvm::Value *CondV = CGF.EmitScalarExpr(condExpr); + llvm::Value *LHS = Visit(lhsExpr); + llvm::Value *RHS = Visit(rhsExpr); + + llvm::Type *CondType = ConvertType(condExpr->getType()); + auto *VecTy = cast(CondType); + llvm::Value *ZeroVec = llvm::Constant::getNullValue(VecTy); + + CondV = Builder.CreateICmpNE(CondV, ZeroVec, "vector_cond"); + return Builder.CreateSelect(CondV, LHS, RHS, "vector_select"); + } + // If this is a really simple expression (like x ? 4 : 5), emit this as a // select instead of as control flow. We can only do this if it is cheap and // safe to evaluate the LHS and RHS unconditionally. Index: clang/lib/Sema/SemaExprCXX.cpp =================================================================== --- clang/lib/Sema/SemaExprCXX.cpp +++ clang/lib/Sema/SemaExprCXX.cpp @@ -5747,38 +5747,156 @@ return false; } +// Check the condition operand of ?: to see if it is valid for the GCC +// extension. +static bool isValidVectorForConditionalCondition(ASTContext &Ctx, + QualType CondTy) { + if (!CondTy->isVectorType() || CondTy->isExtVectorType()) + 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); +} + +QualType Sema::CheckGNUVectorConditionalTypes(ExprResult &Cond, ExprResult &LHS, + ExprResult &RHS, + SourceLocation QuestionLoc) { + LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); + RHS = DefaultFunctionArrayLvalueConversion(RHS.get()); + + QualType CondType = Cond.get()->getType(); + const auto *CondVT = CondType->getAs(); + QualType CondElementTy = CondVT->getElementType(); + unsigned CondElementCount = CondVT->getNumElements(); + QualType LHSType = LHS.get()->getType(); + const auto *LHSVT = LHSType->getAs(); + QualType RHSType = RHS.get()->getType(); + const auto *RHSVT = RHSType->getAs(); + + QualType ResultType; + + // FIXME: In the future we should define what the Extvector conditional + // operator looks like. + if (LHSVT && isa(LHSVT)) { + Diag(QuestionLoc, diag::err_conditional_vector_operand_type) + << /*isExtVector*/ true << LHSType; + return {}; + } + + if (RHSVT && isa(RHSVT)) { + Diag(QuestionLoc, diag::err_conditional_vector_operand_type) + << /*isExtVector*/ true << RHSType; + return {}; + } + + if (LHSVT && RHSVT) { + // If both are vector types, they must be the same type. + if (!Context.hasSameType(LHSType, RHSType)) { + Diag(QuestionLoc, diag::err_conditional_vector_mismatched_vectors) + << LHSType << RHSType; + return {}; + } + ResultType = LHSType; + } else if (LHSVT || RHSVT) { + ResultType = CheckVectorOperands( + LHS, RHS, QuestionLoc, /*isCompAssign*/ false, /*AllowBothBool*/ true, + /*AllowBoolConversions*/ false); + if (ResultType.isNull()) + return {}; + } else { + // Both are scalar. + QualType ResultElementTy; + LHSType = LHSType.getCanonicalType().getUnqualifiedType(); + RHSType = RHSType.getCanonicalType().getUnqualifiedType(); + + if (Context.hasSameType(LHSType, RHSType)) + ResultElementTy = LHSType; + else + ResultElementTy = UsualArithmeticConversions(LHS, RHS); + + if (ResultElementTy->isEnumeralType()) { + Diag(QuestionLoc, diag::err_conditional_vector_operand_type) + << /*isExtVector*/ false << ResultElementTy; + return {}; + } + ResultType = Context.getVectorType( + ResultElementTy, CondType->getAs()->getNumElements(), + VectorType::GenericVector); + + LHS = ImpCastExprToType(LHS.get(), ResultType, CK_VectorSplat); + RHS = ImpCastExprToType(RHS.get(), ResultType, CK_VectorSplat); + } + + assert(!ResultType.isNull() && ResultType->isVectorType() && + "Result should have been a vector type"); + QualType ResultElementTy = ResultType->getAs()->getElementType(); + unsigned ResultElementCount = + ResultType->getAs()->getNumElements(); + + if (ResultElementCount != CondElementCount) { + Diag(QuestionLoc, diag::err_conditional_vector_size) << CondType + << ResultType; + return {}; + } + + if (Context.getTypeSize(ResultElementTy) != + Context.getTypeSize(CondElementTy)) { + Diag(QuestionLoc, diag::err_conditional_vector_element_size) << CondType + << ResultType; + return {}; + } + + return ResultType; +} + /// Check the operands of ?: under C++ semantics. /// /// See C++ [expr.cond]. Note that LHS is never null, even for the GNU x ?: y /// extension. In this case, LHS == Cond. (But they're not aliases.) +/// +/// This function also implements GCC's vector extension for conditionals. +/// GCC's vector extension permits the use of a?b:c where the type of +/// a is that of a integer vector with the same number of elements and +/// size as the vectors of b and c. If one of either b or c is a scalar +/// it is implicitly converted to match the type of the vector. +/// Otherwise the expression is ill-formed. If both b and c are scalars, +/// then b and c are checked and converted to the type of a if possible. +/// Unlike the OpenCL ?: operator, the expression is evaluated as +/// (a[0] != 0 ? b[0] : c[0], .. , a[n] != 0 ? b[n] : c[n]). QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, ExprResult &RHS, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation QuestionLoc) { - // FIXME: Handle C99's complex types, vector types, block pointers and Obj-C++ - // interface pointers. + // FIXME: Handle C99's complex types, block pointers and Obj-C++ interface + // pointers. + + // Assume r-value. + VK = VK_RValue; + OK = OK_Ordinary; + bool IsVectorConditional = + isValidVectorForConditionalCondition(Context, Cond.get()->getType()); // C++11 [expr.cond]p1 // The first expression is contextually converted to bool. - // - // FIXME; GCC's vector extension permits the use of a?b:c where the type of - // a is that of a integer vector with the same number of elements and - // size as the vectors of b and c. If one of either b or c is a scalar - // it is implicitly converted to match the type of the vector. - // Otherwise the expression is ill-formed. If both b and c are scalars, - // then b and c are checked and converted to the type of a if possible. - // Unlike the OpenCL ?: operator, the expression is evaluated as - // (a[0] != 0 ? b[0] : c[0], .. , a[n] != 0 ? b[n] : c[n]). if (!Cond.get()->isTypeDependent()) { - ExprResult CondRes = CheckCXXBooleanCondition(Cond.get()); + ExprResult CondRes = IsVectorConditional + ? DefaultFunctionArrayLvalueConversion(Cond.get()) + : CheckCXXBooleanCondition(Cond.get()); if (CondRes.isInvalid()) return QualType(); Cond = CondRes; + } else { + // To implement C++, the first expression typically doesn't alter the result + // type of the conditional, however the GCC compatible vector extension + // changes the result type to be that of the conditional. Since we cannot + // know if this is a vector extension here, delay the conversion of the + // LHS/RHS below until later. + return Context.DependentTy; } - // Assume r-value. - VK = VK_RValue; - OK = OK_Ordinary; // Either of the arguments dependent? if (LHS.get()->isTypeDependent() || RHS.get()->isTypeDependent()) @@ -5797,6 +5915,17 @@ // and value category of the other. bool LThrow = isa(LHS.get()->IgnoreParenImpCasts()); bool RThrow = isa(RHS.get()->IgnoreParenImpCasts()); + + // Void expressions aren't legal in the vector-conditional expressions. + if (IsVectorConditional) { + SourceRange DiagLoc = + LVoid ? LHS.get()->getSourceRange() : RHS.get()->getSourceRange(); + bool IsThrow = LVoid ? LThrow : RThrow; + Diag(DiagLoc.getBegin(), diag::err_conditional_vector_has_void) + << DiagLoc << IsThrow; + return QualType(); + } + if (LThrow != RThrow) { Expr *NonThrow = LThrow ? RHS.get() : LHS.get(); VK = NonThrow->getValueKind(); @@ -5819,6 +5948,8 @@ } // Neither is void. + if (IsVectorConditional) + return CheckGNUVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc); // C++11 [expr.cond]p3 // Otherwise, if the second and third operand have different types, and Index: clang/test/CodeGenCXX/vector-conditional.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/vector-conditional.cpp @@ -0,0 +1,231 @@ +// RUN: %clang_cc1 %s -triple x86_64-linux-gnu -Wno-unused -std=c++11 -emit-llvm -o - | FileCheck %s + +using FourShorts = short __attribute__((__vector_size__(8))); +using TwoInts = int __attribute__((__vector_size__(8))); +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))); + +FourShorts four_shorts; +TwoInts two_ints; +TwoUInts two_uints; +FourInts four_ints; +FourUInts four_uints; +TwoLongLong two_ll; +FourLongLong four_ll; +TwoFloats two_floats; +FourFloats four_floats; +TwoDoubles two_doubles; +FourDoubles four_doubles; + +short some_short; +unsigned short some_ushort; +int some_int; +float some_float; +unsigned int some_uint; +long long some_ll; +unsigned long long some_ull; +double some_double; + +// CHECK: TwoVectorOps +void TwoVectorOps() { + two_ints ? two_ints : two_ints; + // CHECK: %[[COND:.+]] = load <2 x i32> + // CHECK: %[[LHS:.+]] = load <2 x i32> + // CHECK: %[[RHS:.+]] = load <2 x i32> + // CHECK: %[[NEZERO:.+]] = icmp ne <2 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <2 x i1> %[[NEZERO]], <2 x i32> %[[LHS]], <2 x i32> %[[RHS]] + + two_ints ? two_floats : two_floats; + // CHECK: %[[COND:.+]] = load <2 x i32> + // CHECK: %[[LHS:.+]] = load <2 x float> + // CHECK: %[[RHS:.+]] = load <2 x float> + // CHECK: %[[NEZERO:.+]] = icmp ne <2 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <2 x i1> %[[NEZERO]], <2 x float> %[[LHS]], <2 x float> %[[RHS]] + + two_ll ? two_doubles : two_doubles; + // CHECK: %[[COND:.+]] = load <2 x i64> + // CHECK: %[[LHS:.+]] = load <2 x double> + // CHECK: %[[RHS:.+]] = load <2 x double> + // CHECK: %[[NEZERO:.+]] = icmp ne <2 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <2 x i1> %[[NEZERO]], <2 x double> %[[LHS]], <2 x double> %[[RHS]] +} + +// CHECK: TwoScalarOps +void TwoScalarOps() { + four_shorts ? some_short : some_short; + // CHECK: %[[COND:.+]] = load <4 x i16> + // CHECK: %[[LHS:.+]] = load i16 + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x i16> undef, i16 %[[LHS]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x i16> %[[LHS_SPLAT_INSERT]], <4 x i16> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load i16 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i16> undef, i16 %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i16> %[[RHS_SPLAT_INSERT]], <4 x i16> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i16> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i16> %[[LHS_SPLAT]], <4 x i16> %[[RHS_SPLAT]] + + four_shorts ? some_ushort : some_ushort; + // CHECK: %[[COND:.+]] = load <4 x i16> + // CHECK: %[[LHS:.+]] = load i16 + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x i16> undef, i16 %[[LHS]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x i16> %[[LHS_SPLAT_INSERT]], <4 x i16> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load i16 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i16> undef, i16 %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i16> %[[RHS_SPLAT_INSERT]], <4 x i16> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i16> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i16> %[[LHS_SPLAT]], <4 x i16> %[[RHS_SPLAT]] + + four_ints ? some_ushort : some_short; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load i16 + // CHECK: %[[LHS_ZEXT:.+]] = zext i16 %[[LHS]] to i32 + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[LHS_ZEXT]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x i32> %[[LHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load i16 + // CHECK: %[[RHS_SEXT:.+]] = sext i16 %[[RHS]] to i32 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[RHS_SEXT]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i32> %[[RHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS_SPLAT]], <4 x i32> %[[RHS_SPLAT]] + + four_ints ? some_int : some_float; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load i32 + // CHECK: %[[LHS_CONV:.+]] = sitofp i32 %[[LHS]] to float + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x float> undef, float %[[LHS_CONV]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x float> %[[LHS_SPLAT_INSERT]], <4 x float> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load float + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x float> undef, float %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x float> %[[RHS_SPLAT_INSERT]], <4 x float> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x float> %[[LHS_SPLAT]], <4 x float> %[[RHS_SPLAT]] + + four_ll ? some_double : some_ll; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load double + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x double> undef, double %[[LHS]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x double> %[[LHS_SPLAT_INSERT]], <4 x double> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load i64 + // CHECK: %[[RHS_CONV:.+]] = sitofp i64 %[[RHS]] to double + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x double> undef, double %[[RHS_CONV]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x double> %[[RHS_SPLAT_INSERT]], <4 x double> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x double> %[[LHS_SPLAT]], <4 x double> %[[RHS_SPLAT]] + + four_ints ? some_int : some_short; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load i32 + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[LHS]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x i32> %[[LHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load i16 + // CHECK: %[[RHS_SEXT:.+]] = sext i16 %[[RHS]] to i32 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[RHS_SEXT]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i32> %[[RHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS_SPLAT]], <4 x i32> %[[RHS_SPLAT]] +} + +// CHECK: OneScalarOp +void OneScalarOp() { + four_ints ? four_ints : some_int; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load <4 x i32> + // CHECK: %[[RHS:.+]] = load i32 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i32> %[[RHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS]], <4 x i32> %[[RHS_SPLAT]] + + four_ints ? four_ints : 5; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load <4 x i32> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS]], <4 x i32> + + four_ints ?: some_float; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[RHS:.+]] = load float + // CHECK: %[[RHS_CONV:.+]] = fptosi float %[[RHS]] to i32 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[RHS_CONV]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i32> %[[RHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[COND]], <4 x i32> %[[RHS_SPLAT]] + + four_ints ? four_ints : 5.0f; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load <4 x i32> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS]], <4 x i32> + + four_ints ? some_float : four_ints; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load float + // CHECK: %[[LHS_CONV:.+]] = fptosi float %[[LHS]] to i32 + // CHECK: %[[LHS_SPLAT_INSERT:.+]] = insertelement <4 x i32> undef, i32 %[[LHS_CONV]], i32 0 + // CHECK: %[[LHS_SPLAT:.+]] = shufflevector <4 x i32> %[[LHS_SPLAT_INSERT]], <4 x i32> undef, <4 x i32> zeroinitializer + // CHECK: %[[RHS:.+]] = load <4 x i32> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i32> %[[LHS_SPLAT]], <4 x i32> %[[RHS]] + + four_ints ? four_floats : some_float; + // CHECK: %[[COND:.+]] = load <4 x i32> + // CHECK: %[[LHS:.+]] = load <4 x float> + // CHECK: %[[RHS:.+]] = load float + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x float> undef, float %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x float> %[[RHS_SPLAT_INSERT]], <4 x float> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i32> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x float> %[[LHS]], <4 x float> %[[RHS_SPLAT]] + + four_ll ? four_doubles : 6.0; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x double> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x double> %[[LHS]], <4 x double> + + four_ll ? four_ll : 6.0; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x i64> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i64> %[[LHS]], <4 x i64> + + four_ll ? four_ll : 6; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x i64> + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i64> %[[LHS]], <4 x i64> + + four_ll ? four_ll : some_int; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x i64> + // CHECK: %[[RHS:.+]] = load i32 + // CHECK: %[[RHS_CONV:.+]] = sext i32 %[[RHS]] to i64 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i64> undef, i64 %[[RHS_CONV]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i64> %[[RHS_SPLAT_INSERT]], <4 x i64> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i64> %[[LHS]], <4 x i64> %[[RHS_SPLAT]] + + four_ll ? four_ll : some_ll; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x i64> + // CHECK: %[[RHS:.+]] = load i64 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i64> undef, i64 %[[RHS]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i64> %[[RHS_SPLAT_INSERT]], <4 x i64> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i64> %[[LHS]], <4 x i64> %[[RHS_SPLAT]] + + four_ll ? four_ll : some_double; + // CHECK: %[[COND:.+]] = load <4 x i64> + // CHECK: %[[LHS:.+]] = load <4 x i64> + // CHECK: %[[RHS:.+]] = load double + // CHECK: %[[RHS_CONV:.+]] = fptosi double %[[RHS]] to i64 + // CHECK: %[[RHS_SPLAT_INSERT:.+]] = insertelement <4 x i64> undef, i64 %[[RHS_CONV]], i32 0 + // CHECK: %[[RHS_SPLAT:.+]] = shufflevector <4 x i64> %[[RHS_SPLAT_INSERT]], <4 x i64> undef, <4 x i32> zeroinitializer + // CHECK: %[[NEZERO:.+]] = icmp ne <4 x i64> %[[COND]], zeroinitializer + // CHECK: %[[SELECT:.+]] = select <4 x i1> %[[NEZERO]], <4 x i64> %[[LHS]], <4 x i64> %[[RHS_SPLAT]] +} Index: clang/test/Sema/vector-gcc-compat.cpp =================================================================== --- clang/test/Sema/vector-gcc-compat.cpp +++ clang/test/Sema/vector-gcc-compat.cpp @@ -86,7 +86,7 @@ v2i64_r = !v2i64_a; // expected-error {{invalid argument type 'v2i64' (vector of 2 'long long' values) to unary expression}} v2i64_r = ~v2i64_a; - v2i64_r = v2i64_a ? v2i64_b : v2i64_c; // expected-error {{value of type 'v2i64' (vector of 2 'long long' values) is not contextually convertible to 'bool'}} + v2i64_r = v2i64_a ? v2i64_b : v2i64_c; v2i64_r = v2i64_a & 1; v2i64_r = v2i64_a | 1; Index: clang/test/SemaCXX/vector-conditional.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/vector-conditional.cpp @@ -0,0 +1,172 @@ +// 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 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))); + +FourShorts four_shorts; +TwoInts two_ints; +TwoUInts two_uints; +FourInts four_ints; +FourUInts four_uints; +TwoLongLong two_ll; +FourLongLong four_ll; +TwoFloats two_floats; +FourFloats four_floats; +TwoDoubles two_doubles; +FourDoubles four_doubles; + +enum E {}; +enum class SE {}; +E e; +SE se; + +// Check the rules of the condition of the conditional operator. +void Condition() { + // Only int types are allowed here, the rest should fail to convert to bool. + (void)(four_floats ? 1 : 1); // expected-error {{is not contextually convertible to 'bool'}}} + (void)(two_doubles ? 1 : 1); // expected-error {{is not contextually convertible to 'bool'}}} +} + +// Check the rules of the LHS/RHS of the conditional operator. +void Operands() { + (void)(four_ints ? four_ints : throw 1); // expected-error {{GNU vector conditional operand cannot be a throw expression}} + (void)(four_ints ? throw 1 : four_ints); // expected-error {{GNU vector conditional operand cannot be a throw expression}} + (void)(four_ints ?: throw 1); // expected-error {{GNU vector conditional operand cannot be a throw expression}} + (void)(four_ints ? (void)1 : four_ints); // expected-error {{GNU vector conditional operand cannot be void}} + (void)(four_ints ?: (void)1); // expected-error {{GNU vector conditional operand cannot be void}} + + // Vector types must be the same element size as the condition. + (void)(four_ints ? two_ll : two_ll); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'TwoLongLong' (vector of 2 'long long' values) do not have the same number of elements}} + (void)(four_ints ? four_ll : four_ll); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'FourLongLong' (vector of 4 'long long' values) do not have elements of the same size}} + (void)(four_ints ? two_doubles : two_doubles); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'TwoDoubles' (vector of 2 'double' values) do not have the same number of elements}} + (void)(four_ints ? four_doubles : four_doubles); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'FourDoubles' (vector of 4 'double' values) do not have elements of the same size}} + (void)(four_ints ?: two_ints); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'TwoInts' (vector of 2 'int' values)}} + (void)(four_ints ?: four_doubles); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'FourDoubles' (vector of 4 'double' values)}} + + // Scalars are promoted, but must be the same element size. + (void)(four_ints ? 3.0f : 3.0); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(double)))) double' (vector of 4 'double' values) do not have elements of the same size}} + (void)(four_ints ? 5ll : 5); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(long long)))) long long' (vector of 4 'long long' values) do not have elements of the same size}} + (void)(four_ints ?: 3.0); // expected-error {{cannot convert between scalar type 'double' and vector type 'FourInts' (vector of 4 'int' values) as implicit conversion would cause truncation}} + (void)(four_ints ?: 5ll); // We allow this despite GCc not allowing this since we support integral->vector-integral conversions despite integer rank. + + // This one would be allowed in GCC, but we don't allow vectors of enum. Also, + // the error message isn't perfect, since it is only going to be a problem + // when both sides are an enum, otherwise it'll be promoted to whatever type + // the other side causes. + (void)(four_ints ? e : e); // expected-error {{enumeral type 'E' is not allowed in a vector conditional}} + (void)(four_ints ? se : se); // expected-error {{enumeral type 'SE' is not allowed in a vector conditional}} + (void)(four_shorts ? (short)5 : (unsigned short)5); // expected-error {{vector condition type 'FourShorts' (vector of 4 'short' values) and result type '__attribute__((__vector_size__(4 * sizeof(int)))) int' (vector of 4 'int' values) do not have elements of the same size}} + + // They must also be convertible. + (void)(four_ints ? 3.0f : 5u); + (void)(four_ints ? 3.0f : 5); + unsigned us = 5u; + int sint = 5; + short shrt = 5; + unsigned short uss = 5u; + // The following 2 error in GCC for truncation errors, but it seems + // unimportant and inconsistent to enforce that rule. + (void)(four_ints ? 3.0f : us); + (void)(four_ints ? 3.0f : sint); + + // Test promotion: + (void)(four_shorts ? uss : shrt); // expected-error {{vector condition type 'FourShorts' (vector of 4 'short' values) and result type '__attribute__((__vector_size__(4 * sizeof(int)))) int' (vector of 4 'int' values) do not have elements of the same size}} + (void)(four_shorts ? shrt : shrt); // should be fine. + (void)(four_ints ? uss : shrt); // should be fine, since they get promoted to int. + (void)(four_ints ? shrt : shrt); //expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(short)))) short' (vector of 4 'short' values) do not have elements of the same size}} + + // Vectors must be the same type as eachother. + (void)(four_ints ? four_uints : four_floats); // expected-error {{vector operands to the vector conditional must be the same type ('FourUInts' (vector of 4 'unsigned int' values) and 'FourFloats' (vector of 4 'float' values))}} + (void)(four_ints ? four_uints : four_ints); // expected-error {{vector operands to the vector conditional must be the same type ('FourUInts' (vector of 4 'unsigned int' values) and 'FourInts' (vector of 4 'int' values))}} + (void)(four_ints ? four_ints : four_uints); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'FourUInts' (vector of 4 'unsigned int' values))}} + + // GCC rejects these, but our lax vector conversions don't seem to have a problem with them. Allow conversion of the float to an int as an extension. + (void)(four_ints ? four_uints : 3.0f); + (void)(four_ints ? four_ints : 3.0f); + + // 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); // should work, non-scoped enum can convert to uint. + (void)(four_ints ? four_uints : se); // expected-error {{cannot convert between vector and non-scalar values ('FourUInts' (vector of 4 'unsigned int' values) and 'SE'}} + // GCC permits this, but our conversion rules reject this for truncation. + (void)(two_ints ? two_ints : us); // expected-error {{cannot convert between scalar type 'unsigned int' and vector type 'TwoInts'}} + (void)(four_shorts ? four_shorts : uss); // expected-error {{cannot convert between scalar type 'unsigned short' and vector type 'FourShorts'}} + (void)(four_ints ? four_floats : us); // expected-error {{cannot convert between scalar type 'unsigned int' and vector type 'FourFloats'}} + (void)(four_ints ? four_floats : sint); // expected-error {{cannot convert between scalar type 'int' and vector type 'FourFloats'}} +} + +template +struct is_same { + static constexpr bool value = false; +}; +template +struct is_same { + static constexpr bool value = true; +}; +template +constexpr bool is_same_v = is_same::value; +template +T &&declval(); + +// Check the result types when given two vector types. +void ResultTypes() { + // Vectors must be the same, but result is the type of the LHS/RHS. + static_assert(is_same_v() ? declval() : declval())>); + static_assert(is_same_v() ? declval() : declval())>); + + // When both are scalars, converts to vectors of common type. + static_assert(is_same_v() ? declval() : declval())>); + + // Constant is allowed since it doesn't truncate, and should promote to float. + static_assert(is_same_v() ? declval() : 5u)>); + static_assert(is_same_v() ? 5 : declval())>); + + // when only 1 is a scalar, it should convert to a compatible type. + static_assert(is_same_v() ? declval() : declval())>); + static_assert(is_same_v() ? declval() : declval())>); + static_assert(is_same_v() ? declval() : 5)>); + + // For the Binary conditional operator, the result type is either the vector on the RHS (that fits the rules on size/count), or the scalar extended to the correct count. + static_assert(is_same_v() ?: declval())>); + static_assert(is_same_v() ?: declval())>); +} + +template +void dependent_cond(Cond C) { + (void)(C ? 1 : 2); +} + +template +void dependent_operand(Operand C) { + (void)(two_ints ? 1 : C); + (void)(two_ints ? C : 1); + (void)(two_ints ? C : C); +} + +template +void all_dependent(Cond C, LHS L, RHS R) { + (void)(C ? L : R); +} + +// Check dependent cases. +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))}}} + 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))}}} + all_dependent(four_ints, four_uints, two_uints); // expected-note {{in instantiation of}} + all_dependent(four_ints, four_uints, four_uints); +}