Patchwork [C++] Handle ?: for vectors

login
register
mail settings
Submitter Marc Glisse
Date Oct. 17, 2012, 7:56 p.m.
Message ID <alpine.DEB.2.02.1210161606580.7761@stedding.saclay.inria.fr>
Download mbox | patch
Permalink /patch/192134/
State New
Headers show

Comments

Marc Glisse - Oct. 17, 2012, 7:56 p.m.
Hello,

this patch adds support for vector conditions to the C++ front-end. Note 
that the testcase currently only applies to x86. This is because there is 
work remaining in the middle-end for the case where vector selection for 
this vector mode is not provided by the target. There are also many 
middle-end bugs, so the testcase will crash with -O. I believe it makes 
sense to introduce front-end support early, because this way I'll be able 
to add testcases when I fix middle-end issues. Not documenting until this 
is actually usable.

gcc/
 	* tree.c (signed_or_unsigned_type_for): Handle vectors.

gcc/cp/
 	* typeck.c (build_x_conditional_expr): Handle VEC_COND_EXPR.
 	* call.c (build_conditional_expr_1): Likewise.

gcc/c-family/
 	* c-common.c (scalar_to_vector): Handle VEC_COND_EXPR.

gcc/testsuite/
 	* g++.dg/ext/vector19.C: New testcase.
Jason Merrill - Oct. 18, 2012, 1:15 a.m.
On 10/17/2012 12:56 PM, Marc Glisse wrote:
> +      if (!COMPARISON_CLASS_P (arg1))
> +	{
> +	  if (TYPE_UNSIGNED (TREE_TYPE (arg1_type)))
> +	    {
> +	      arg1_type = signed_type_for (arg1_type);
> +	      arg1 = convert (arg1_type, arg1);
> +	    }
> +	  arg1 = build2 (LT_EXPR, arg1_type, arg1,
> +			 build_zero_cst (arg1_type));
> +	}

Why LT_EXPR rather than NE_EXPR?

Jason
Marc Glisse - Oct. 18, 2012, 5:30 a.m.
On Wed, 17 Oct 2012, Jason Merrill wrote:

> On 10/17/2012 12:56 PM, Marc Glisse wrote:
>> +      if (!COMPARISON_CLASS_P (arg1))
>> +	{
>> +	  if (TYPE_UNSIGNED (TREE_TYPE (arg1_type)))
>> +	    {
>> +	      arg1_type = signed_type_for (arg1_type);
>> +	      arg1 = convert (arg1_type, arg1);
>> +	    }
>> +	  arg1 = build2 (LT_EXPR, arg1_type, arg1,
>> +			 build_zero_cst (arg1_type));
>> +	}
>
> Why LT_EXPR rather than NE_EXPR?

Assuming we try to follow OpenCL:

6.3i

The ternary selection operator (?:) operates on three expressions (exp1 ? 
exp2 : exp3). This operator evaluates the first expression exp1, which can 
be a scalar or vector result except float. If the result is a scalar value 
then it selects to evaluate the second expression if the result compares 
unequal to 0, otherwise it selects to evaluate the third expression. If 
the result is a vector value, then this is equivalent to calling 
select(exp3, exp2, exp1). The select function is described in table 6.14. 
The second and third expressions can be any type, as long their types 
match, or there is a conversion in section 6.2.1 Implicit Conversions that 
can be applied to one of the expressions to make their types match, or one 
is a vector and the other is a scalar and the scalar may be subject to the 
usual arithmetic conversion to the element type used by the vector operand 
and widened to the same type as the vector type. This resulting matching 
type is the type of the entire expression.


table 6.14:

gentype select (gentype a, gentype b, igentype c)

For each component of a vector type,
result[i] = if MSB of c[i] is set ? b[i] : a[i].

===

We could deviate from OpenCL in a few places though, if you prefer.

Note that operator! (not implemented) still means == 0.


Thanks for the quick comment despite the meeting,
Jason Merrill - Oct. 18, 2012, 5:50 a.m.
On 10/17/2012 10:30 PM, Marc Glisse wrote:
> For each component of a vector type,
> result[i] = if MSB of c[i] is set ? b[i] : a[i].

Curious.  Do you know why they produce and expect -1 for true?  It 
certainly seems to be a deliberate design choice, so I wonder what 
motivated it.

I think people will expect a >0 value to be considered true, so I am 
inclined to deviate in this case.

Jason
Marc Glisse - Oct. 18, 2012, 6:15 a.m.
On Wed, 17 Oct 2012, Jason Merrill wrote:

> On 10/17/2012 10:30 PM, Marc Glisse wrote:
>> For each component of a vector type,
>> result[i] = if MSB of c[i] is set ? b[i] : a[i].
>
> Curious.  Do you know why they produce and expect -1 for true?  It certainly 
> seems to be a deliberate design choice, so I wonder what motivated it.

(just guessing)
First a look at hardware. Vector units in x86, power and arm all produce 
-1 from comparisons. For selection, arm expects a vector of 0 and -1, 
power looks at x!=0, and x86 at x<0 (so opencl matches x86). A vector of 
-1 can be convenient on platforms without a selection instruction so it 
can be implemented with and+not+or. Looking at the MSB has the advantage 
(compared to !=0) that using & and | has the expected result.

In the middle-end, VEC_COND_EXPR is documented to take a vector of 0 and 
-1, and at expansion time, it gives vcond(x<0,y,z) to the target if its 
first argument is not a comparison.

> I think people will expect a >0 value to be considered true, so I am inclined 
> to deviate in this case.

Clang's webpage says they support ?: for vector types, but my version 
(3.1) rejects it, I'll compile a new one to check what they do.
Jason Merrill - Oct. 18, 2012, 6:35 a.m.
On 10/17/2012 11:15 PM, Marc Glisse wrote:
> In the middle-end, VEC_COND_EXPR is documented to take a vector of 0 and
> -1, and at expansion time, it gives vcond(x<0,y,z) to the target if its
> first argument is not a comparison.

Maybe we should leave the first argument alone and let the middle end 
mess with it, then?

Jason
Marc Glisse - Oct. 19, 2012, 8:40 p.m.
(Adding Joseph in Cc: because we'll want the same behavior in C 
afterwards. Conversation starts here: 
http://gcc.gnu.org/ml/gcc-patches/2012-10/msg01665.html )

On Thu, 18 Oct 2012, Marc Glisse wrote:

> Clang's webpage says they support ?: for vector types, but my version (3.1) 
> rejects it, I'll compile a new one to check what they do.

They implement ?: for vectors only in OpenCL mode, and in that mode 
implement the OpenCL semantics (<0).


OpenCL can be confusing. x?y:z means (x<0)?y:z, but !x means x==0 and x&&y 
seems to mean (x!=0)&(y!=0) (the text is not extremely clear).


On Wed, 17 Oct 2012, Jason Merrill wrote:

> On 10/17/2012 11:15 PM, Marc Glisse wrote:
>> In the middle-end, VEC_COND_EXPR is documented to take a vector of 0 and
>> -1, and at expansion time, it gives vcond(x<0,y,z) to the target if its
>> first argument is not a comparison.

I shouldn't have mentioned the <0 in vcond which is rather random: x is 
necessarily a vector of -1 and 0 there, so anything like <0, !=0, ==-1 is 
equivalent, we just need one because of a currently missing target 
interface.

> Maybe we should leave the first argument alone and let the middle end mess 
> with it, then?

VEC_COND_EXPR only accepts a vector of -1 and 0, so we can't just pass 
arbitrary input from the front-end. Richard B didn't want to make 
VEC_COND_EXPR more general either.

Besides, I believe it is the front-end's responsibility to decide this. If 
we don't want to decide on a specific semantic, I think I'd rather sorry() 
if a user passes an argument that is not directly a comparison (with a 
note explaining that they can write <0 for OpenCL-like behavior) than 
have an unspecified behavior.

So between the following:
a) x?y:z means (x<0)?y:z
b) x?y:z means (x!=0)?y:z
c) x?y:z is rejected, only things like (x==t)?y:z are accepted
d) other

is the choice still b) ? That's an easy one, only 2 characters to change 
in the patch (assuming the rest is correct) :-)
(well, no, actually, I'd also remove the conversion to a signed type)
Jason Merrill - Oct. 24, 2012, 6:02 p.m.
On 10/19/2012 04:40 PM, Marc Glisse wrote:
> So between the following:
> a) x?y:z means (x<0)?y:z
> b) x?y:z means (x!=0)?y:z
> c) x?y:z is rejected, only things like (x==t)?y:z are accepted
> d) other
>
> is the choice still b) ? That's an easy one, only 2 characters to change
> in the patch (assuming the rest is correct) :-)

That would be my inclination, but I'm not a vector programming expert, 
so I guess let's go with the OpenCL semantics.

Jason
Marc Glisse - Oct. 24, 2012, 6:36 p.m.
On Wed, 24 Oct 2012, Jason Merrill wrote:

> On 10/19/2012 04:40 PM, Marc Glisse wrote:
>> So between the following:
>> a) x?y:z means (x<0)?y:z
>> b) x?y:z means (x!=0)?y:z
>> c) x?y:z is rejected, only things like (x==t)?y:z are accepted
>> d) other
>> 
>> is the choice still b) ? That's an easy one, only 2 characters to change
>> in the patch (assuming the rest is correct) :-)
>
> That would be my inclination, but I'm not a vector programming expert,

I'm not an expert at all either, and I find the OpenCL semantics quite 
confusing, where truth is implicitly x<0 for x?y:z but x!=0 for x&&y or 
!x, so in particular (x&&y)?z:t is not equivalent to x?y?z:t:t, and I am 
quite happy with your suggestion to deviate. I was surprised because I 
hadn't considered the possibility, but now I actually like it better than 
OpenCL ;-)

> so I guess let's go with the OpenCL semantics.

Ok. I take it as: let's wait a few days in case people want to comment, 
then you'll review the original patch?

Thanks,
Jakub Jelinek - Oct. 24, 2012, 7:38 p.m.
On Wed, Oct 24, 2012 at 08:36:19PM +0200, Marc Glisse wrote:
> On Wed, 24 Oct 2012, Jason Merrill wrote:
> 
> >On 10/19/2012 04:40 PM, Marc Glisse wrote:
> >>So between the following:
> >>a) x?y:z means (x<0)?y:z
> >>b) x?y:z means (x!=0)?y:z
> >>c) x?y:z is rejected, only things like (x==t)?y:z are accepted
> >>d) other
> >>
> >>is the choice still b) ? That's an easy one, only 2 characters to change
> >>in the patch (assuming the rest is correct) :-)
> >
> >That would be my inclination, but I'm not a vector programming expert,
> 
> I'm not an expert at all either, and I find the OpenCL semantics
> quite confusing, where truth is implicitly x<0 for x?y:z but x!=0
> for x&&y or !x, so in particular (x&&y)?z:t is not equivalent to
> x?y?z:t:t, and I am quite happy with your suggestion to deviate. I
> was surprised because I hadn't considered the possibility, but now I
> actually like it better than OpenCL ;-)
> 
> >so I guess let's go with the OpenCL semantics.
> 
> Ok. I take it as: let's wait a few days in case people want to
> comment, then you'll review the original patch?

I'd prefer b) as well, but I'm not a vector programming expert either.

	Jakub
Jason Merrill - Oct. 24, 2012, 7:51 p.m.
My guess for the reason why OpenCL has the semantics it does is that if 
you stored a boolean result in a variable earlier and then use it as the 
? condition, that would require an extra comparison whereas if it's 
already a vector of 0 and -1 as expected it can be used directly.

Jason
Marc Glisse - Oct. 24, 2012, 8:15 p.m.
On Wed, 24 Oct 2012, Jason Merrill wrote:

> My guess for the reason why OpenCL has the semantics it does is that if you 
> stored a boolean result in a variable earlier and then use it as the ? 
> condition, that would require an extra comparison whereas if it's already a 
> vector of 0 and -1 as expected it can be used directly.

I don't understand how < 0 differs from != 0 for that purpose. In both 
cases, the front-end will produce the extra comparison, and the middle-end 
will need an optimization to detect "truth values" (vectors of -1 and 0) 
and simplify trivial operations on those (<0, !=0, etc).

(at a point, based on Richard's words but apparently not on his 
suggestion, I was tempted to introduce a new boolean vector type, whose 
only values are 0 and -1, but that seems a bit complicated and doesn't 
answer whether x?y:z should mean x<0 or x!=0)
Mike Stump - Oct. 24, 2012, 8:23 p.m.
On Oct 24, 2012, at 12:38 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Oct 24, 2012 at 08:36:19PM +0200, Marc Glisse wrote:
>> On Wed, 24 Oct 2012, Jason Merrill wrote:
>> 
>>> On 10/19/2012 04:40 PM, Marc Glisse wrote:
>>>> So between the following:
>>>> a) x?y:z means (x<0)?y:z
>>>> b) x?y:z means (x!=0)?y:z
>>>> c) x?y:z is rejected, only things like (x==t)?y:z are accepted
>>>> d) other
>>>> 
>>>> is the choice still b) ? That's an easy one, only 2 characters to change
>>>> in the patch (assuming the rest is correct) :-)
>>> 
>>> That would be my inclination, but I'm not a vector programming expert,
>> 
>> I'm not an expert at all either, and I find the OpenCL semantics
>> quite confusing, where truth is implicitly x<0 for x?y:z but x!=0
>> for x&&y or !x, so in particular (x&&y)?z:t is not equivalent to
>> x?y?z:t:t, and I am quite happy with your suggestion to deviate. I
>> was surprised because I hadn't considered the possibility, but now I
>> actually like it better than OpenCL ;-)
>> 
>>> so I guess let's go with the OpenCL semantics.
>> 
>> Ok. I take it as: let's wait a few days in case people want to
>> comment, then you'll review the original patch?
> 
> I'd prefer b) as well, but I'm not a vector programming expert either.

Well, I suspect the OpenCL community had a ton of people sweat over the details and take into consideration the realities and the needs of people.  I'd like to believe they had more people in on this and that this was a compromise for someones vector unit.  I like b myself, however, following OpenCL I think might be better.  Tough choice.  The problem is, what if your vector unit produces 0, 1, to be compatible with C?  Suddenly, spilling this onto ? is annoying, both because it doesn't match hardware, nor the expected semantics of a person that just knows C.  Maybe we run a poll of vector units that prefer -1 or prefer 1 for true, and then decide.  SSE has CMPPS, which likes the -1.  Altivec's vec_cmpgt says true is all bits 1.  Gosh, I guess we can stop there.  Neon, for fun VCGE is defined to set to all ones for true.  OpenCL it is.
Marc Glisse - Oct. 24, 2012, 8:41 p.m.
On Wed, 24 Oct 2012, Mike Stump wrote:

> Well, I suspect the OpenCL community had a ton of people sweat over the 
> details and take into consideration the realities and the needs of 
> people.  I'd like to believe they had more people in on this and that 
> this was a compromise for someones vector unit.

Intel's, apparently...

> The problem is, what if your vector unit produces 0, 1, to be compatible 
> with C?  Suddenly, spilling this onto ? is annoying, both because it 
> doesn't match hardware, nor the expected semantics of a person that just 
> knows C.  Maybe we run a poll of vector units that prefer -1 or prefer 1 
> for true, and then decide.  SSE has CMPPS, which likes the -1. 
> Altivec's vec_cmpgt says true is all bits 1.  Gosh, I guess we can stop 
> there.  Neon, for fun VCGE is defined to set to all ones for true. 
> OpenCL it is.

We already decided that comparisons return -1, most processors agree on 
that except sparc, which doesn't return a vector at all. The question is 
about the selection instructions.

{-2,-1,0,1} ? {x,x,x,x} : {y,y,y,y}

OpenCL says this should be {x,x,y,y}. We are considering making it 
{x,x,y,x} instead. Hardware selection instructions vary a lot. OpenCL 
follows x86, what we are considering matches Power IIRC, and ARM only has 
a bitwise selection (I only quickly glanced at all of these, I may have 
read them wrong).


I am fine with both alternatives, but the choice is important...
Jason Merrill - Oct. 24, 2012, 8:47 p.m.
On 10/24/2012 04:15 PM, Marc Glisse wrote:
> I don't understand how < 0 differs from != 0 for that purpose. In both
> cases, the front-end will produce the extra comparison, and the
> middle-end will need an optimization to detect "truth values" (vectors
> of -1 and 0) and simplify trivial operations on those (<0, !=0, etc).

True; if we aren't going to assume that the operand is already a 
truthvalue vector, it would be the same.  So let's go with != 0 after all.

> (at a point, based on Richard's words but apparently not on his
> suggestion, I was tempted to introduce a new boolean vector type, whose
> only values are 0 and -1, but that seems a bit complicated and doesn't
> answer whether x?y:z should mean x<0 or x!=0)

That would make sense to me; it would avoid the extra comparison if the 
condition is a boolean vector.

Jason
Richard Guenther - Oct. 25, 2012, 10:54 a.m.
On Wed, Oct 24, 2012 at 10:41 PM, Marc Glisse <marc.glisse@inria.fr> wrote:
> On Wed, 24 Oct 2012, Mike Stump wrote:
>
>> Well, I suspect the OpenCL community had a ton of people sweat over the
>> details and take into consideration the realities and the needs of people.
>> I'd like to believe they had more people in on this and that this was a
>> compromise for someones vector unit.
>
>
> Intel's, apparently...
>
>
>> The problem is, what if your vector unit produces 0, 1, to be compatible
>> with C?  Suddenly, spilling this onto ? is annoying, both because it doesn't
>> match hardware, nor the expected semantics of a person that just knows C.
>> Maybe we run a poll of vector units that prefer -1 or prefer 1 for true, and
>> then decide.  SSE has CMPPS, which likes the -1. Altivec's vec_cmpgt says
>> true is all bits 1.  Gosh, I guess we can stop there.  Neon, for fun VCGE is
>> defined to set to all ones for true. OpenCL it is.
>
>
> We already decided that comparisons return -1, most processors agree on that
> except sparc, which doesn't return a vector at all. The question is about
> the selection instructions.
>
> {-2,-1,0,1} ? {x,x,x,x} : {y,y,y,y}
>
> OpenCL says this should be {x,x,y,y}. We are considering making it {x,x,y,x}
> instead. Hardware selection instructions vary a lot. OpenCL follows x86,
> what we are considering matches Power IIRC, and ARM only has a bitwise
> selection (I only quickly glanced at all of these, I may have read them
> wrong).
>
>
> I am fine with both alternatives, but the choice is important...

I would go with != 0, too.  It is what people would expect.  We can deviate
when someone introduces -fopencl.

Richard.

> --
> Marc Glisse

Patch

Index: tree.c

===================================================================
--- tree.c	(revision 192542)

+++ tree.c	(working copy)

@@ -10229,20 +10229,31 @@  widest_int_cst_value (const_tree x)

 /* If TYPE is an integral or pointer type, return an integer type with
    the same precision which is unsigned iff UNSIGNEDP is true, or itself
    if TYPE is already an integer type of signedness UNSIGNEDP.  */
 
 tree
 signed_or_unsigned_type_for (int unsignedp, tree type)
 {
   if (TREE_CODE (type) == INTEGER_TYPE && TYPE_UNSIGNED (type) == unsignedp)
     return type;
 
+  if (TREE_CODE (type) == VECTOR_TYPE)

+    {

+      tree inner = TREE_TYPE (type);

+      tree inner2 = signed_or_unsigned_type_for (unsignedp, inner);

+      if (!inner2)

+	return NULL_TREE;

+      if (inner == inner2)

+	return type;

+      return build_vector_type (inner2, TYPE_VECTOR_SUBPARTS (type));

+    }

+

   if (!INTEGRAL_TYPE_P (type)
       && !POINTER_TYPE_P (type))
     return NULL_TREE;
 
   return build_nonstandard_integer_type (TYPE_PRECISION (type), unsignedp);
 }
 
 /* If TYPE is an integral or pointer type, return an integer type with
    the same precision which is unsigned, or itself if TYPE is already an
    unsigned integer type.  */
Index: c-family/c-common.c

===================================================================
--- c-family/c-common.c	(revision 192542)

+++ c-family/c-common.c	(working copy)

@@ -11467,20 +11467,22 @@  scalar_to_vector (location_t loc, enum t

 	      return stv_firstarg;
 	  }
 	break;
 
       case BIT_IOR_EXPR:
       case BIT_XOR_EXPR:
       case BIT_AND_EXPR:
 	integer_only_op = true;
 	/* ... fall through ...  */
 
+      case VEC_COND_EXPR:

+

       case PLUS_EXPR:
       case MINUS_EXPR:
       case MULT_EXPR:
       case TRUNC_DIV_EXPR:
       case CEIL_DIV_EXPR:
       case FLOOR_DIV_EXPR:
       case ROUND_DIV_EXPR:
       case EXACT_DIV_EXPR:
       case TRUNC_MOD_EXPR:
       case FLOOR_MOD_EXPR:
Index: cp/call.c

===================================================================
--- cp/call.c	(revision 192542)

+++ cp/call.c	(working copy)

@@ -4366,43 +4366,120 @@  build_conditional_expr_1 (tree arg1, tre

 	pedwarn (input_location, OPT_Wpedantic, 
 		 "ISO C++ forbids omitting the middle term of a ?: expression");
 
       /* Make sure that lvalues remain lvalues.  See g++.oliva/ext1.C.  */
       if (real_lvalue_p (arg1))
 	arg2 = arg1 = stabilize_reference (arg1);
       else
 	arg2 = arg1 = save_expr (arg1);
     }
 
+  /* If something has already gone wrong, just pass that fact up the

+     tree.  */

+  if (error_operand_p (arg1)

+      || error_operand_p (arg2)

+      || error_operand_p (arg3))

+    return error_mark_node;

+

+  orig_arg2 = arg2;

+  orig_arg3 = arg3;

+

+  if (VECTOR_INTEGER_TYPE_P (TREE_TYPE (arg1)))

+    {

+      arg1 = force_rvalue (arg1, complain);

+      arg2 = force_rvalue (arg2, complain);

+      arg3 = force_rvalue (arg3, complain);

+

+      tree arg1_type = TREE_TYPE (arg1);

+      arg2_type = TREE_TYPE (arg2);

+      arg3_type = TREE_TYPE (arg3);

+

+      if (TREE_CODE (arg2_type) != VECTOR_TYPE

+	  && TREE_CODE (arg3_type) != VECTOR_TYPE)

+	{

+	  if (complain & tf_error)

+	    error ("at least one operand of a vector conditional operator "

+		   "must be a vector");

+	  return error_mark_node;

+	}

+

+      if ((TREE_CODE (arg2_type) == VECTOR_TYPE)

+	  != (TREE_CODE (arg3_type) == VECTOR_TYPE))

+	{

+	  enum stv_conv convert_flag =

+	    scalar_to_vector (input_location, VEC_COND_EXPR, arg2, arg3,

+			      complain & tf_error);

+

+	  switch (convert_flag)

+	    {

+	      case stv_error:

+		return error_mark_node;

+	      case stv_firstarg:

+		{

+		  arg2 = convert (TREE_TYPE (arg3_type), arg2);

+		  arg2 = build_vector_from_val (arg3_type, arg2);

+		  arg2_type = TREE_TYPE (arg2);

+		  break;

+		}

+	      case stv_secondarg:

+		{

+		  arg3 = convert (TREE_TYPE (arg2_type), arg3);

+		  arg3 = build_vector_from_val (arg2_type, arg3);

+		  arg3_type = TREE_TYPE (arg3);

+		  break;

+		}

+	      default:

+		break;

+	    }

+	}

+

+      if (!same_type_p (arg2_type, arg3_type)

+	  || TYPE_VECTOR_SUBPARTS (arg1_type)

+	     != TYPE_VECTOR_SUBPARTS (arg2_type)

+	  || TYPE_SIZE (arg1_type) != TYPE_SIZE (arg2_type))

+	{

+	  if (complain & tf_error)

+	    error ("incompatible vector types in conditional expression: "

+		   "%qT, %qT and %qT", TREE_TYPE (arg1), TREE_TYPE (orig_arg2),

+		   TREE_TYPE (orig_arg3));

+	  return error_mark_node;

+	}

+

+      if (!COMPARISON_CLASS_P (arg1))

+	{

+	  if (TYPE_UNSIGNED (TREE_TYPE (arg1_type)))

+	    {

+	      arg1_type = signed_type_for (arg1_type);

+	      arg1 = convert (arg1_type, arg1);

+	    }

+	  arg1 = build2 (LT_EXPR, arg1_type, arg1,

+			 build_zero_cst (arg1_type));

+	}

+      return build3 (VEC_COND_EXPR, arg2_type, arg1, arg2, arg3);

+    }

+

   /* [expr.cond]
 
      The first expression is implicitly converted to bool (clause
      _conv_).  */
   arg1 = perform_implicit_conversion_flags (boolean_type_node, arg1, complain,
 					    LOOKUP_NORMAL);
-

-  /* If something has already gone wrong, just pass that fact up the

-     tree.  */

-  if (error_operand_p (arg1)

-      || error_operand_p (arg2)

-      || error_operand_p (arg3))

+  if (error_operand_p (arg1))

     return error_mark_node;
 
   /* [expr.cond]
 
      If either the second or the third operand has type (possibly
      cv-qualified) void, then the lvalue-to-rvalue (_conv.lval_),
      array-to-pointer (_conv.array_), and function-to-pointer
      (_conv.func_) standard conversions are performed on the second
      and third operands.  */
-  orig_arg2 = arg2;

-  orig_arg3 = arg3;

   arg2_type = unlowered_expr_type (arg2);
   arg3_type = unlowered_expr_type (arg3);
   if (VOID_TYPE_P (arg2_type) || VOID_TYPE_P (arg3_type))
     {
       /* Do the conversions.  We don't these for `void' type arguments
 	 since it can't have any effect and since decay_conversion
 	 does not handle that case gracefully.  */
       if (!VOID_TYPE_P (arg2_type))
 	arg2 = decay_conversion (arg2, complain);
       if (!VOID_TYPE_P (arg3_type))
Index: cp/typeck.c

===================================================================
--- cp/typeck.c	(revision 192542)

+++ cp/typeck.c	(working copy)

@@ -5803,21 +5803,22 @@  build_x_conditional_expr (location_t loc

 	  || (op1 && type_dependent_expression_p (op1))
 	  || type_dependent_expression_p (op2))
 	return build_min_nt_loc (loc, COND_EXPR, ifexp, op1, op2);
       ifexp = build_non_dependent_expr (ifexp);
       if (op1)
 	op1 = build_non_dependent_expr (op1);
       op2 = build_non_dependent_expr (op2);
     }
 
   expr = build_conditional_expr (ifexp, op1, op2, complain);
-  if (processing_template_decl && expr != error_mark_node)

+  if (processing_template_decl && expr != error_mark_node

+      && TREE_CODE (expr) != VEC_COND_EXPR)

     {
       tree min = build_min_non_dep (COND_EXPR, expr,
 				    orig_ifexp, orig_op1, orig_op2);
       /* In C++11, remember that the result is an lvalue or xvalue.
          In C++98, lvalue_kind can just assume lvalue in a template.  */
       if (cxx_dialect >= cxx0x
 	  && lvalue_or_rvalue_with_address_p (expr)
 	  && !lvalue_or_rvalue_with_address_p (min))
 	TREE_TYPE (min) = cp_build_reference_type (TREE_TYPE (min),
 						   !real_lvalue_p (expr));
Index: testsuite/g++.dg/ext/vector19.C

===================================================================
--- testsuite/g++.dg/ext/vector19.C	(revision 0)

+++ testsuite/g++.dg/ext/vector19.C	(revision 0)

@@ -0,0 +1,56 @@ 

+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */

+/* { dg-options "-std=c++11 -mavx2" } */

+

+// The target restrictions and the -mavx2 flag are meant to disappear

+// once vector lowering is in place.

+

+typedef double vec __attribute__((vector_size(2*sizeof(double))));

+typedef signed char vec2 __attribute__((vector_size(16)));

+typedef unsigned char vec2u __attribute__((vector_size(16)));

+

+void f (vec *x, vec *y, vec *z)

+{

+  *x = (*y < *z) ? *x : *y;

+}

+

+void g (vec *x, vec *y, vec *z)

+{

+  *x = (*y < *z) ? *x : 42;

+}

+

+void h (vec *x, vec *y, vec *z)

+{

+  *x = (*y < *z) ? 3. : *y;

+}

+

+void i1 (vec *x, vec *y, vec *z)

+{

+  auto c = *y < *z;

+  *x = c ? *x : *y;

+}

+

+void i2 (vec2 *x, vec2 *y, vec2u *z)

+{

+  *x = *y ? *x : *y;

+  *y = *z ? *x : *y;

+}

+

+void j (vec2 *x, vec2 *y, vec2 *z, vec *t)

+{

+  *x = (*y < *z) ? *x : 4.2; /* { dg-error "" } */

+  *y = (*x < *z) ? 2.5 : *y; /* { dg-error "" } */

+  *t = *t ? *t : *t; /* { dg-error "" } */

+  *z = (*x < *z) ? '1' : '0'; /* { dg-error "" } */

+  // The last one may eventually be accepted.

+}

+

+template <class A, class B>

+auto k (A *a, B b) -> decltype (*a ? *a : b);

+

+void k (...) {}

+

+void l (vec2 *v, double x)

+{

+  k (v, x);

+}

+