[OPENCL][PATCH] conditional operator when the condition is a vector

Sameer Sahasrabuddhe sameer.sahasrabuddhe at amd.com
Thu Jan 22 19:26:49 PST 2015


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. The attached patch implements these requirements.

Sameer.
-------------- next part --------------
commit 9e1b90c4b3bdce4ac88ac630f9b9c6b621908561
Author: Sameer Sahasrabuddhe <sameer.sahasrabuddhe at amd.com>
Date:   Thu Jan 22 13:52:57 2015 +0530

    OpenCL: conditional 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.

diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 19e6861..fd6b1ff 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5477,6 +5477,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">;
@@ -6059,8 +6065,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 bf6c780..d3817d8 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -5520,47 +5520,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) {
@@ -5777,6 +5754,176 @@ 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.DefaultLvalueConversion(LHS.get());
+  RHS = S.DefaultLvalueConversion(RHS.get());
+
+  // 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) {
+  // Both operands should be of scalar type.
+  assert(LHS.get()->getType()->isScalarType());
+  assert(RHS.get()->getType()->isScalarType());
+
+  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
+  QualType vectorTy = S.Context.getExtVectorType(ResTy, CV->getNumElements());
+
+  // Ensure that all types have the same number of bits
+  if (S.Context.getTypeSize(CV->getElementType())
+      != S.Context.getTypeSize(ResTy)) {
+    S.Diag(QuestionLoc, diag::err_conditional_vector_element_size)
+      << CondTy << vectorTy;
+    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 Handle OpenCL conditional operator when the condition is a
+///        vector type.
+static QualType
+OpenCLCheckVectorConditional(Sema &S, ExprResult &Cond,
+                             ExprResult &LHS, ExprResult &RHS,
+                             ExprValueKind &VK, ExprObjectKind &OK,
+                             SourceLocation QuestionLoc) {
+  Cond = S.DefaultLvalueConversion(Cond.get());
+  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
@@ -5806,6 +5953,12 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
   if (getLangOpts().CPlusPlus)
     return CXXCheckConditionalOperands(Cond, LHS, RHS, VK, OK, QuestionLoc);
 
+  // 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, VK, OK,
+                                        QuestionLoc);
+
   VK = VK_RValue;
   OK = OK_Ordinary;
 
@@ -5813,7 +5966,7 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS,
   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.
@@ -5825,17 +5978,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 802ad9b..a32bba3 100644
--- a/test/SemaOpenCL/cond.cl
+++ b/test/SemaOpenCL/cond.cl
@@ -1,6 +1,121 @@
 // RUN: %clang_cc1 %s -verify -pedantic -fsyntax-only
-// expected-no-diagnostics
 
-typedef __attribute__((ext_vector_type(4))) float float4;
+typedef char char2 __attribute__((ext_vector_type(2)));
+typedef char char3 __attribute__((ext_vector_type(3)));
 
-float4 foo(float4 a, float4 b, float4 c, float4 d) { return a < b ? c : d; }
+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 'int __attribute__((ext_vector_type(2)))' (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}}
+}
+
+char2 ntest03(int2 C, char X, char Y)
+{
+  return C ? X : Y; // expected-error {{vector condition type 'int2' (vector of 2 'int' values) and result type 'char __attribute__((ext_vector_type(2)))' (vector of 2 '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}}
+}


More information about the cfe-commits mailing list