Commit a578220f authored by Tom Stellard's avatar Tom Stellard
Browse files

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.

------------------------------------------------------------------------

llvm-svn: 240324
parent d70b4247
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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<
+194 −42
Original line number Diff line number Diff line
@@ -5516,45 +5516,22 @@ 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();
  // 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;
  // 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;
  }
/// \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;
  }
  // C99 6.5.15p2
  if (CondTy->isScalarType()) return false;
  // 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;
  S.Diag(QuestionLoc, diag::err_typecheck_cond_expect_scalar)
    << CondTy << Cond->getSourceRange();
  return true;
}
/// \brief Handle when one or both operands are void type.
@@ -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<doIntegralCast, doIntegralCast>
  (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<VectorType>();
  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<VectorType>();
  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<VectorType>();
  const VectorType *RV = VecResTy->getAs<VectorType>();
  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()) {
+129 −3
Original line number Diff line number Diff line
// 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}}
}