erichkeane updated this revision to Diff 234977.
erichkeane marked 4 inline comments as done.
erichkeane added a comment.

@aaron.ballman s comments done.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D71463/new/

https://reviews.llvm.org/D71463

Files:
  clang/docs/LanguageExtensions.rst
  clang/include/clang/AST/Expr.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/ExprConstant.cpp
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/Sema/SemaExprCXX.cpp
  clang/test/CodeGenCXX/vector-conditional.cpp
  clang/test/Sema/vector-gcc-compat.cpp
  clang/test/SemaCXX/vector-conditional.cpp

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 <typename T1, typename T2>
+struct is_same {
+  static constexpr bool value = false;
+};
+template <typename T>
+struct is_same<T, T> {
+  static constexpr bool value = true;
+};
+template <typename T1, typename T2>
+constexpr bool is_same_v = is_same<T1, T2>::value;
+template <typename T>
+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<TwoInts, decltype(declval<TwoInts>() ? declval<TwoInts>() : declval<TwoInts>())>);
+  static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : declval<TwoFloats>())>);
+
+  // When both are scalars, converts to vectors of common type.
+  static_assert(is_same_v<TwoUInts, decltype(declval<TwoInts>() ? declval<int>() : declval<unsigned int>())>);
+
+  // Constant is allowed since it doesn't truncate, and should promote to float.
+  static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<float>() : 5u)>);
+  static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? 5 : declval<float>())>);
+
+  // when only 1 is a scalar, it should convert to a compatible type.
+  static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : declval<float>())>);
+  static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ? declval<TwoInts>() : declval<int>())>);
+  static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : 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<TwoInts, decltype(declval<TwoInts>() ?: declval<TwoInts>())>);
+  static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ?: declval<int>())>);
+}
+
+template <typename Cond>
+void dependent_cond(Cond C) {
+  (void)(C ? 1 : 2);
+}
+
+template <typename Operand>
+void dependent_operand(Operand C) {
+  (void)(two_ints ? 1 : C);
+  (void)(two_ints ? C : 1);
+  (void)(two_ints ? C : C);
+}
+
+template <typename Cond, typename LHS, typename RHS>
+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);
+}
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/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> <i32 5, i32 5, i32 5, i32 5>
+
+  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> <i32 5, i32 5, i32 5, i32 5>
+
+  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> <double 6.{{.+}}, double 6.{{.+}}, double 6.{{.+}}>
+
+  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> <i64 6, i64 6, i64 6, i64 6>
+
+  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> <i64 6, i64 6, i64 6, i64 6>
+
+  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/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<VectorType>(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<VectorType>();
+  QualType CondElementTy = CondVT->getElementType();
+  unsigned CondElementCount = CondVT->getNumElements();
+  QualType LHSType = LHS.get()->getType();
+  const auto *LHSVT = LHSType->getAs<VectorType>();
+  QualType RHSType = RHS.get()->getType();
+  const auto *RHSVT = RHSType->getAs<VectorType>();
+
+  QualType ResultType;
+
+  // FIXME: In the future we should define what the Extvector conditional
+  // operator looks like.
+  if (LHSVT && isa<ExtVectorType>(LHSVT)) {
+    Diag(QuestionLoc, diag::err_conditional_vector_operand_type)
+        << /*isExtVector*/ true << LHSType;
+    return {};
+  }
+
+  if (RHSVT && isa<ExtVectorType>(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<VectorType>()->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<VectorType>()->getElementType();
+  unsigned ResultElementCount =
+      ResultType->getAs<VectorType>()->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<CXXThrowExpr>(LHS.get()->IgnoreParenImpCasts());
     bool RThrow = isa<CXXThrowExpr>(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/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<llvm::VectorType>(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/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/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/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/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/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
 =============================
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to