From c2e93b903ffee0e691757284c53e5f81615bdc55 Mon Sep 17 00:00:00 2001 From: Tom Stellard Date: Mon, 22 Jun 2015 20:39:00 +0000 Subject: Merging r228118: ------------------------------------------------------------------------ r228118 | sameer.sahasrabuddhe | 2015-02-04 01:38:18 -0500 (Wed, 04 Feb 2015) | 8 lines OpenCL: handle ternary operator when the condition is a vector When the condition is a vector, OpenCL specifies additional requirements on the operand types, and also the operations required to determine the result type of the operator. This is a combination of OpenCL v1.1 s6.3.i and s6.11.6, and the semantics remain unchanged in later versions of OpenCL. ------------------------------------------------------------------------ git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/release_36@240324 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 10 ++ lib/Sema/SemaExpr.cpp | 236 ++++++++++++++++++++++++----- test/SemaOpenCL/cond.cl | 132 +++++++++++++++- 3 files changed, 333 insertions(+), 45 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1a27e7cd3d..75430e1cdd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5467,6 +5467,12 @@ def err_conditional_ambiguous : Error< def err_conditional_ambiguous_ovl : Error< "conditional expression is ambiguous; %diff{$ and $|types}0,1 " "can be converted to several common types">; +def err_conditional_vector_size : Error< + "vector condition type %0 and result type %1 do not have the same number " + "of elements">; +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_throw_incomplete : Error< "cannot throw object of incomplete type %0">; @@ -6049,8 +6055,12 @@ def err_typecheck_call_invalid_ordered_compare : Error< def err_typecheck_call_invalid_unary_fp : Error< "floating point classification requires argument of floating point type " "(passed in %0)">; +def err_typecheck_cond_expect_int_float : Error< + "used type %0 where integer or floating point type is required">; def err_typecheck_cond_expect_scalar : Error< "used type %0 where arithmetic or pointer type is required">; +def err_typecheck_cond_expect_nonfloat : Error< + "used type %0 where floating point type is not allowed">; def ext_typecheck_cond_one_void : Extension< "C99 forbids conditional expressions with only one void side">; def err_typecheck_cond_expect_scalar_or_vector : Error< diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 8be11572b2..b54a5163e4 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5516,47 +5516,24 @@ bool Sema::DiagnoseConditionalForNull(Expr *LHSExpr, Expr *RHSExpr, } /// \brief Return false if the condition expression is valid, true otherwise. -static bool checkCondition(Sema &S, Expr *Cond) { +static bool checkCondition(Sema &S, Expr *Cond, SourceLocation QuestionLoc) { QualType CondTy = Cond->getType(); + // OpenCL v1.1 s6.3.i says the condition cannot be a floating point type. + if (S.getLangOpts().OpenCL && CondTy->isFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << CondTy << Cond->getSourceRange(); + return true; + } + // C99 6.5.15p2 if (CondTy->isScalarType()) return false; - // OpenCL v1.1 s6.3.i says the condition is allowed to be a vector or scalar. - if (S.getLangOpts().OpenCL && CondTy->isVectorType()) - return false; - - // Emit the proper error message. - S.Diag(Cond->getLocStart(), S.getLangOpts().OpenCL ? - diag::err_typecheck_cond_expect_scalar : - diag::err_typecheck_cond_expect_scalar_or_vector) - << CondTy; + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar) + << CondTy << Cond->getSourceRange(); return true; } -/// \brief Return false if the two expressions can be converted to a vector, -/// true otherwise -static bool checkConditionalConvertScalarsToVectors(Sema &S, ExprResult &LHS, - ExprResult &RHS, - QualType CondTy) { - // Both operands should be of scalar type. - if (!LHS.get()->getType()->isScalarType()) { - S.Diag(LHS.get()->getLocStart(), diag::err_typecheck_cond_expect_scalar) - << CondTy; - return true; - } - if (!RHS.get()->getType()->isScalarType()) { - S.Diag(RHS.get()->getLocStart(), diag::err_typecheck_cond_expect_scalar) - << CondTy; - return true; - } - - // Implicity convert these scalars to the type of the condition. - LHS = S.ImpCastExprToType(LHS.get(), CondTy, CK_IntegralCast); - RHS = S.ImpCastExprToType(RHS.get(), CondTy, CK_IntegralCast); - return false; -} - /// \brief Handle when one or both operands are void type. static QualType checkConditionalVoidType(Sema &S, ExprResult &LHS, ExprResult &RHS) { @@ -5773,6 +5750,184 @@ static bool checkPointerIntegerMismatch(Sema &S, ExprResult &Int, return true; } +/// \brief Simple conversion between integer and floating point types. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// OpenCL v1.1 s6.3.i and s6.11.6 together require that the scalar +/// types are either integer or floating type. Between the two +/// operands, the type with the higher rank is defined as the "result +/// type". The other operand needs to be promoted to the same type. No +/// other type promotion is allowed. We cannot use +/// UsualArithmeticConversions() for this purpose, since it always +/// promotes promotable types. +static QualType OpenCLArithmeticConversions(Sema &S, ExprResult &LHS, + ExprResult &RHS, + SourceLocation QuestionLoc) { + LHS = S.DefaultFunctionArrayLvalueConversion(LHS.get()); + if (LHS.isInvalid()) + return QualType(); + RHS = S.DefaultFunctionArrayLvalueConversion(RHS.get()); + if (RHS.isInvalid()) + return QualType(); + + // For conversion purposes, we ignore any qualifiers. + // For example, "const float" and "float" are equivalent. + QualType LHSType = + S.Context.getCanonicalType(LHS.get()->getType()).getUnqualifiedType(); + QualType RHSType = + S.Context.getCanonicalType(RHS.get()->getType()).getUnqualifiedType(); + + if (!LHSType->isIntegerType() && !LHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << LHSType << LHS.get()->getSourceRange(); + return QualType(); + } + + if (!RHSType->isIntegerType() && !RHSType->isRealFloatingType()) { + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_int_float) + << RHSType << RHS.get()->getSourceRange(); + return QualType(); + } + + // If both types are identical, no conversion is needed. + if (LHSType == RHSType) + return LHSType; + + // Now handle "real" floating types (i.e. float, double, long double). + if (LHSType->isRealFloatingType() || RHSType->isRealFloatingType()) + return handleFloatConversion(S, LHS, RHS, LHSType, RHSType, + /*IsCompAssign = */ false); + + // Finally, we have two differing integer types. + return handleIntegerConversion + (S, LHS, RHS, LHSType, RHSType, /*IsCompAssign = */ false); +} + +/// \brief Convert scalar operands to a vector that matches the +/// condition in length. +/// +/// Used when handling the OpenCL conditional operator where the +/// condition is a vector while the other operands are scalar. +/// +/// We first compute the "result type" for the scalar operands +/// according to OpenCL v1.1 s6.3.i. Both operands are then converted +/// into a vector of that type where the length matches the condition +/// vector type. s6.11.6 requires that the element types of the result +/// and the condition must have the same number of bits. +static QualType +OpenCLConvertScalarsToVectors(Sema &S, ExprResult &LHS, ExprResult &RHS, + QualType CondTy, SourceLocation QuestionLoc) { + QualType ResTy = OpenCLArithmeticConversions(S, LHS, RHS, QuestionLoc); + if (ResTy.isNull()) return QualType(); + + const VectorType *CV = CondTy->getAs(); + assert(CV); + + // Determine the vector result type + unsigned NumElements = CV->getNumElements(); + QualType VectorTy = S.Context.getExtVectorType(ResTy, NumElements); + + // Ensure that all types have the same number of bits + if (S.Context.getTypeSize(CV->getElementType()) + != S.Context.getTypeSize(ResTy)) { + // Since VectorTy is created internally, it does not pretty print + // with an OpenCL name. Instead, we just print a description. + std::string EleTyName = ResTy.getUnqualifiedType().getAsString(); + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + OS << "(vector of " << NumElements << " '" << EleTyName << "' values)"; + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << OS.str(); + return QualType(); + } + + // Convert operands to the vector result type + LHS = S.ImpCastExprToType(LHS.get(), VectorTy, CK_VectorSplat); + RHS = S.ImpCastExprToType(RHS.get(), VectorTy, CK_VectorSplat); + + return VectorTy; +} + +/// \brief Return false if this is a valid OpenCL condition vector +static bool checkOpenCLConditionVector(Sema &S, Expr *Cond, + SourceLocation QuestionLoc) { + // OpenCL v1.1 s6.11.6 says the elements of the vector must be of + // integral type. + const VectorType *CondTy = Cond->getType()->getAs(); + assert(CondTy); + QualType EleTy = CondTy->getElementType(); + if (EleTy->isIntegerType()) return false; + + S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_nonfloat) + << Cond->getType() << Cond->getSourceRange(); + return true; +} + +/// \brief Return false if the vector condition type and the vector +/// result type are compatible. +/// +/// OpenCL v1.1 s6.11.6 requires that both vector types have the same +/// number of elements, and their element types have the same number +/// of bits. +static bool checkVectorResult(Sema &S, QualType CondTy, QualType VecResTy, + SourceLocation QuestionLoc) { + const VectorType *CV = CondTy->getAs(); + const VectorType *RV = VecResTy->getAs(); + assert(CV && RV); + + if (CV->getNumElements() != RV->getNumElements()) { + S.Diag(QuestionLoc, diag::err_conditional_vector_size) + << CondTy << VecResTy; + return true; + } + + QualType CVE = CV->getElementType(); + QualType RVE = RV->getElementType(); + + if (S.Context.getTypeSize(CVE) != S.Context.getTypeSize(RVE)) { + S.Diag(QuestionLoc, diag::err_conditional_vector_element_size) + << CondTy << VecResTy; + return true; + } + + return false; +} + +/// \brief Return the resulting type for the conditional operator in +/// OpenCL (aka "ternary selection operator", OpenCL v1.1 +/// s6.3.i) when the condition is a vector type. +static QualType +OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond, + ExprResult &LHS, ExprResult &RHS, + SourceLocation QuestionLoc) { + Cond = S.DefaultFunctionArrayLvalueConversion(Cond.get()); + if (Cond.isInvalid()) + return QualType(); + QualType CondTy = Cond.get()->getType(); + + if (checkOpenCLConditionVector(S, Cond.get(), QuestionLoc)) + return QualType(); + + // If either operand is a vector then find the vector type of the + // 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); + 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)) + return QualType(); + return VecResTy; + } + + // Both operands are scalar. + return OpenCLConvertScalarsToVectors(S, LHS, RHS, CondTy, QuestionLoc); +} + /// Note that LHS is not null here, even if this is the gnu "x ?: y" extension. /// In that case, LHS = cond. /// C99 6.5.15 @@ -5796,11 +5951,16 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, VK = VK_RValue; OK = OK_Ordinary; + // The OpenCL operator with a vector condition is sufficiently + // different to merit its own checker. + if (getLangOpts().OpenCL && Cond.get()->getType()->isVectorType()) + return OpenCLCheckVectorConditional(*this, Cond, LHS, RHS, QuestionLoc); + // First, check the condition. Cond = UsualUnaryConversions(Cond.get()); if (Cond.isInvalid()) return QualType(); - if (checkCondition(*this, Cond.get())) + if (checkCondition(*this, Cond.get(), QuestionLoc)) return QualType(); // Now check the two expressions. @@ -5812,17 +5972,9 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); - QualType CondTy = Cond.get()->getType(); QualType LHSTy = LHS.get()->getType(); QualType RHSTy = RHS.get()->getType(); - // If the condition is a vector, and both operands are scalar, - // attempt to implicity convert them to the vector type to act like the - // built in select. (OpenCL v1.1 s6.3.i) - if (getLangOpts().OpenCL && CondTy->isVectorType()) - if (checkConditionalConvertScalarsToVectors(*this, LHS, RHS, CondTy)) - return QualType(); - // If both operands have arithmetic type, do the usual arithmetic conversions // to find a common type: C99 6.5.15p3,5. if (LHSTy->isArithmeticType() && RHSTy->isArithmeticType()) { diff --git a/test/SemaOpenCL/cond.cl b/test/SemaOpenCL/cond.cl index 802ad9b785..a1e32df16d 100644 --- a/test/SemaOpenCL/cond.cl +++ b/test/SemaOpenCL/cond.cl @@ -1,6 +1,132 @@ // RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only -// expected-no-diagnostics -typedef __attribute__((ext_vector_type(4))) float float4; +typedef unsigned char uchar; +typedef unsigned char uchar2 __attribute__((ext_vector_type(2))); -float4 foo(float4 a, float4 b, float4 c, float4 d) { return a < b ? c : d; } +typedef char char2 __attribute__((ext_vector_type(2))); +typedef char char3 __attribute__((ext_vector_type(3))); + +typedef int int2 __attribute__((ext_vector_type(2))); + +typedef float float2 __attribute__((ext_vector_type(2))); + +// ** Positive tests ** + +// all scalars, but widths do not match. +int ptest01(char C, char X, int Y) +{ + return C ? X : Y; +} + +char ptest02(int C, char X, char Y) +{ + return C ? X : Y; +} + +// scalar condition and mixed-width vectors and scalars +int2 ptest03(char C, char X, int2 Y) +{ + return C ? X : Y; +} + +// uniform vectors +char2 ptest04(char2 X, char2 Y, char2 C) +{ + return C ? X : Y; +} + +// vector condition and mixed scalar operands +int2 ptest05(int2 C, int X, char Y) +{ + return C ? X : Y; +} + +// vector condition and matching scalar operands +float2 ptest06(int2 C, float X, float Y) +{ + return C ? X : Y; +} + +// vector condition and mixed scalar operands +float2 ptest07(int2 C, int X, float Y) +{ + return C ? X : Y; +} + +// vector condition and mixed scalar and vector operands +float2 ptest08(int2 C, int X, float2 Y) +{ + return C ? X : Y; +} + +// Actual comparison expression +float2 ptest09(float2 A, float2 B, float2 C, float2 D) +{ + return A < B ? C : D; +} + +// ** Negative tests ** + +int2 ntest01(char2 C, int X, int Y) +{ + return C ? X : Y; // expected-error {{vector condition type 'char2' (vector of 2 'char' values) and result type (vector of 2 'int' values) do not have elements of the same size}} +} + +int2 ntest02(char2 C, int2 X, int2 Y) +{ + return C ? X : Y; // expected-error {{vector condition type 'char2' (vector of 2 'char' values) and result type 'int2' (vector of 2 'int' values) do not have elements of the same size}} +} + +uchar2 ntest03(int2 C, uchar X, uchar Y) +{ + return C ? X : Y; // expected-error {{vector condition type 'int2' (vector of 2 'int' values) and result type (vector of 2 'unsigned char' values) do not have elements of the same size}} +} + +float2 ntest04(int2 C, int2 X, float2 Y) +{ + return C ? X : Y; // expected-error {{can't convert between vector values of different size ('int2' (vector of 2 'int' values) and 'float2' (vector of 2 'float' values))}} +} + +float2 ntest05(int2 C, int2 X, float Y) +{ + return C ? X : Y; // expected-error {{can't convert between vector values of different size ('int2' (vector of 2 'int' values) and 'float')}} +} + +char2 ntest06(int2 C, char2 X, char2 Y) +{ + return C ? X : Y; // expected-error {{vector condition type 'int2' (vector of 2 'int' values) and result type 'char2' (vector of 2 'char' values) do not have elements of the same size}} +} + +float ntest07(float C, float X, float Y) +{ + return C ? X : Y; // expected-error {{used type 'float' where floating point type is not allowed}} +} + +float2 ntest08(float2 C, float2 X, float2 Y) +{ + return C ? X : Y; // expected-error {{used type 'float2' (vector of 2 'float' values) where floating point type is not allowed}} +} + +// Trying to create a int2 vector out of pointers. +int2 ntest09(int2 C, global int *X, global int *Y) +{ + return C ? X : Y; // expected-error {{used type '__global int *' where integer or floating point type is required}} +} + +char3 ntest10(char C, char3 X, char2 Y) +{ + return C ? X : Y; // expected-error {{can't convert between vector values of different size ('char3' (vector of 3 'char' values) and 'char2' (vector of 2 'char' values))}} +} + +char3 ntest11(char2 C, char3 X, char Y) +{ + return C ? X : Y; // expected-error {{vector condition type 'char2' (vector of 2 'char' values) and result type 'char3' (vector of 3 'char' values) do not have the same number of elements}} +} + +int foo1(int); +int foo2(int); + +unsigned int ntest12(int2 C) +{ + return (unsigned int)(C ? foo1 : foo2); // expected-error {{taking address of function is not allowed}} +} -- cgit v1.2.3