[cfe-dev] [PATCH] [OpenCL] Conversions for ternary operations between scalar and vector

Sahasrabuddhe, Sameer sameer.sahasrabuddhe at amd.com
Wed Dec 10 22:57:05 PST 2014


On 12/10/2014 9:26 PM, Colin Riddell wrote:
>
>> Also my mistake about the error not showing up. The actual example 
>> that I tried was "float2 = char2 ? float : float". Attached the 
>> modified copy that I had created from your original lit test. This 
>> requires your patch to avoid the assertion. I believe this assignment 
>> should not cause any error at all.
> Agreed, and thanks for the test. Added that to the patch.

Erm ... turns out that my test is wrong for a different reason! The 
expression "char2 ? float : float" is not compatible with the select 
builtin because the number of bits in the element types needs to be the 
same. I am guessing that this bitness issue might have been the original 
inspiration for casting to CondTy instead of ResTy, that you fixed. But 
your fix is correct within its context.

> It's worth pointing out that the select generation is handled in 
> CGExprScalar.cpp Value *ScalarExprEmitter::
> VisitAbstractConditionalOperator()

Thanks! That actually saved me from embarking on a spelunking expedition! :D

> Attached is another patch for review, including both tests. Now ignore 
> the first patch I submitted. This new change passes all the tests run 
> with check-clang.

There could be more to this than the existing tests. Please see the 
attached document where I have described my understanding of how the 
ternary operator is supposed to work. Would appreciate any faults that 
you find! I hope it is not overkill, but the effort was already useful 
--- it helped me discover that my testcase above is invalid. Another 
example which we discussed in previous emails is also wrong: "float ? 
char : unsigned", because the condition cannot be a floating point type.

Sameer.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20141211/0eaa297d/attachment.html>
-------------- next part --------------
      Implementing OpenCL ternary operator with vector operands
      =========================================================

Author: Sameer Sahasrabuddhe <sameer.sahsrabuddhe at amd.com>
Date: 2014-12-11 

The ternary operator (?:) in OpenCL 1.2 has the following form:

  result = exp1 ? exp2 : exp3

The operator first evaluates exp1, and compares its value to zero. If
the value is not equal to zero, then the operator selects to evaluate
exp2, otherwise it evaluates exp3. The situation when one or more of
the three operands is a vector needs clarification.

Three places in the OpenCL 1.2 spec are relevant here:
1. Page 220, which defines the behaviour of the ternary operator.
2. Section 6.2.6, which defines the usual arithmetic operations.
3. Page 268, which defines the select builtin.

In particular, this is an attempt to interpret page 220 in the
broadest possible way:

  "The second and third expressions can be any type, as long their
   types match, or there is a conversion <snip> that can be applied to
   one of the expressions to make their types match <snip>. This
   resulting matching type is the type of the entire expression."

The two snips refer to the applicable conversion rules that can be
used to determine the resulting matching type.

The semantics described on page 220 state that when exp1 is a vector,
then the operator is "equivalent to calling the select builtin". The
nature of this equivalence is open to interpretation. In particular,
the following inconsistencies stand out:

1. The select builtin does not compare the condition to zero, but
   instead checks only the MSB of the value. This seems strange
   anyway, because OpenCL 1.2 also defines the boolean "true" value to
   be the integer constant "1". It seems reasonable to resolve this in
   favour of comparing to zero for the ternary operator.

2. If exp1 is a vector and either one or both of exp2 and exp3 are
   scalar, then page 220 leaves room for performing "usual arithmetic
   conversions" on exp2 and exp3.

It seems reasonable to assume that the intended meaning of the
"equivalance" refers two aspects:

1. If exp1 is a vector, then both exp2 and exp3 need to be evaluated,
   since it is to be treated like a function call. This can cause
   surprising results, but it is required for an element-wise
   construction of the result vector as seen below.

2. All operands exp1, exp2, and exp3 are assumed to be vectors of the
   same length (possibly after usual arithmetic conversions), and the
   resulting vector is computed as:

   result[i] = exp1[i] ? exp2[i] : exp3[i] 

   where i is every valid index into the vectors.

Section 6.2.6 "Usual Arithmetic Conversions" seems to say that for a
given operator, either all operands are of matching vector types, or
at most one operand is a vector. This can be overly restrictive for
the ternary operator because exp1 is treated specially as seen below.
Page 220 seems to over-ride this by saying that the result type is
determined only by exp2 and exp3. But this is insufficient, because
exp1 can affect the vector size of the result as seen below.

The three pieces in the OpenCL 1.2 spec could be synthesised into a
consistent behaviour for the ternary operator as follows:

1. If one or more operands are vectors, then their lengths must match.

2. The element type of exp1 cannot be a floating point type.

3. If exp1 is a scalar:

   1. No restrictions apply to the types of exp2 and exp3 with respect
      to exp1.

   2. Either exp2 or exp3 is evaluated depending on the value of exp1.

   3. If the result type is a vector, there is no need to expand exp1.

4. If exp1 is a vector:

   1. The number of bits in the element types of all vector types must
      match. Scalar types will be handled by usual arithmetic
      conversions. See point (6.1) below for additional rules.

   2. Both exp2 and exp3 are evaluated.

5. The result type is obtained by usual arithmetic conversions on exp2
   and exp3, as specified in Section 6.2.6.

6. If exp1 is a vector, and the result type is a scalar, then

   1. The scalar result type must have the same number of bits as the
      element type in exp1, as required by the select builtin. Page
      220 does not provide for further changing either the result type
      or exp1 to match the number of bits.

   2. The scalar result type is expanded to a vector having the same
      length as exp1. The spec does not actually say this, and it
      could be considered undefined behaviour. It is a useful but
      tricky mix-and-match between all three referred sections to
      arrive at a "reasonable behaviour" for the ternary operator.
      This is how Clang does it, anyway.

Some notable examples (needs fixed width font to view correctly):

  exp1     exp2   exp3        result type   evalutation  
 --------+------+-----------+-------------+-------------
  char     char   unsigned    unsigned      either       
  char     char   unsigned2   unsigned2     either       
  int      char   char        char          either       
  float    char   char        error         -            
  char2    int    float       error         -            
  int2     int    float       float2        both         
  int2     char   char        error         -            
  int2     char   int         int2          both         
  int2     int2   float2      error         -            
  int2     int2   float       error         -            
  int2     int    float2      float2        both         
  float2   char   char        error         -            


More information about the cfe-dev mailing list