diff mbox

Scalar vector binary operation

Message ID AANLkTikEihhuCipZwHYMzPjAvL9H7h0rxy9ovTsYThfc@mail.gmail.com
State New
Headers show

Commit Message

Artem Shinkarov Aug. 14, 2010, 4:37 p.m. UTC
This patch allows to write a vector binary expression where one
operand is a scalar. This is done according to the OpenCL standard.

The scalar operand in the expression is converted to the vector where
each element is the scalar form the expression. We check whether the
scalar fits in the type of vector element.

ChangeLog:

2010-06-18  Artem Shinkarov <artyom.shinakroff@gmail.com>

        /gcc
        * tree.c (build_vector_from_val): Build vector from scalar
        * tree.h (build_vector_from_val): New declaration
        * c-typeck.c (expr_fits_type_p): New function. Check if expression
        fits into the type considering constants.
        (scalar_to_vector): New function. Try scalar to vector conversion.
        (build_binary_op): Adjust.

        /gcc/testsuite
        * gcc.c-torture/execute/scal-to-vec1.c: New test.
        * gcc.dg/scal-to-vec1.c: New test.

        /gcc/doc
        * extend.texi: Adjust


bootstrapped and tested on x86_64_unknown-linux




Artem Shinkarov

Comments

Richard Henderson Aug. 16, 2010, 10:12 p.m. UTC | #1
On 08/14/2010 09:37 AM, Artem Shinkarov wrote:
> +/* Build a vector of type VECTYPE where all the elements are SCs.  */
> +tree
> +build_vector_from_val (const tree sc, const tree vectype) 
> +{
> +  tree t = NULL_TREE;
> +  int i, nunits = TYPE_VECTOR_SUBPARTS (vectype);
> +
> +  if (sc == error_mark_node)
> +    return sc;
> +
> +  gcc_assert (TREE_TYPE (sc) == TREE_TYPE (vectype));
> +
> +  for (i = 0; i < nunits; ++i)
> +    t = tree_cons (NULL_TREE, sc, t);
> +
> +  if (CONSTANT_CLASS_P (sc))
> +    return build_vector (vectype, t);
> +  else 
> +    return build_constructor_from_list (vectype, t);
> +}
> +

Better to use build_constructor and build_vector_from_ctor.
We've been trying to eliminate TREE_LIST.

> +static inline bool
> +expr_fits_type_p (const tree expr, const tree type )

No explicit inline marker.

This function doesn't handle real/integer type crosses.
I.e. v4sf * long or v4si * 2.0.  It will crash for some
of the combinations.

> +static inline int
> +scalar_to_vector (location_t loc, enum tree_code code, tree op0, tree op1)

No explicit inline marker.

> +#define MSG "Conversion of scalar to vector involves truncation"
> +#define MSG_FLOAT "Truncating floating point constant to vector " \
> +                  "precision does not preserve value"
> +#define ERROR(MSG, LOC, RET) { error_at (LOC, MSG); return RET; }

This is, frankly, gross.

> +      case RSHIFT_EXPR:
> +      case LSHIFT_EXPR:
> +        if (TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE)
> +          return 0;

			  code0

I think you also want to check for vect << int, which is directly
supported by most cpus.  Also, you don't want a fallthru to the
real format checks.

You could probably simplify the code a bit for the arithmetic ops
if you first force op0 to be the vector operation.  Since we're not
actually emitting code here, it doesn't matter if we temporarily
"swap" non-commutative operands.

Some commentary that one of op0 or op1 is known to be vector type
and the other is known to be scalar type would also help here.

Using FP scalar types with integer vector types is probably suspect
and should generate an error.


> +  /* For 'vector <shift> scalar' or 'scalar <shift> vector', we convert 
> +     a scalar to a vector. Truncating the shift amount is ok.  */
> +  if ((code0 == VECTOR_TYPE || code1 == VECTOR_TYPE)
> +      && (code0 != code1))

  if ((code0 == VECTOR_TYPE) != (code1 == VECTOR_TYPE))

> +    {
> +        int convert_flag = scalar_to_vector (location, code, op0, op1);
> +        
> +        if (convert_flag == -2)

Definitely use a switch here.  Possibly better with an enum.


r~
diff mbox

Patch

Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(revision 163244)
+++ gcc/doc/extend.texi	(working copy)
@@ -6135,6 +6135,25 @@  v4si a, b, c;
 c = a + b;
 @end smallexample
 
+For the convenience it is allowed to use a binary vector operation
+where one operand is a scalar. In that case the compiler will 
+transform the scalar operand into a vector where each element is the 
+scalar from the operation. The transformation will happen only 
+if the scalar could be safely converted to the vector-element type.
+Consider the following code.
+
+@smallexample
+typedef int v4si __attribute__ ((vector_size (16)));
+
+v4si a, b, c;
+long l;
+
+a = b + 1;    /* a = b + {1,1,1,1}; */
+a = 2 * b;    /* a = {2,2,2,2} * b; */
+
+a = l + a;    /* Error, cannot convert long to int. */
+@end smallexample
+
 Subtraction, multiplication, division, and the logical operations
 operate in a similar manner.  Likewise, the result of using the unary
 minus or complement operators on a vector type is a vector whose
Index: gcc/tree.c
===================================================================
--- gcc/tree.c	(revision 163244)
+++ gcc/tree.c	(working copy)
@@ -1358,6 +1358,28 @@  build_vector_from_ctor (tree type, VEC(c
   return build_vector (type, nreverse (list));
 }
 
+/* Build a vector of type VECTYPE where all the elements are SCs.  */
+tree
+build_vector_from_val (const tree sc, const tree vectype) 
+{
+  tree t = NULL_TREE;
+  int i, nunits = TYPE_VECTOR_SUBPARTS (vectype);
+
+  if (sc == error_mark_node)
+    return sc;
+
+  gcc_assert (TREE_TYPE (sc) == TREE_TYPE (vectype));
+
+  for (i = 0; i < nunits; ++i)
+    t = tree_cons (NULL_TREE, sc, t);
+
+  if (CONSTANT_CLASS_P (sc))
+    return build_vector (vectype, t);
+  else 
+    return build_constructor_from_list (vectype, t);
+}
+
+
 /* Return a new CONSTRUCTOR node whose type is TYPE and whose values
    are in the VEC pointed to by VALS.  */
 tree
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(revision 163244)
+++ gcc/tree.h	(working copy)
@@ -4027,6 +4027,7 @@  extern tree build_int_cst_type (tree, HO
 extern tree build_int_cst_wide (tree, unsigned HOST_WIDE_INT, HOST_WIDE_INT);
 extern tree build_vector (tree, tree);
 extern tree build_vector_from_ctor (tree, VEC(constructor_elt,gc) *);
+extern tree build_vector_from_val (const tree, const tree);
 extern tree build_constructor (tree, VEC(constructor_elt,gc) *);
 extern tree build_constructor_single (tree, tree, tree);
 extern tree build_constructor_from_list (tree, tree);
Index: gcc/testsuite/gcc.c-torture/execute/scal-to-vec1.c
===================================================================
--- gcc/testsuite/gcc.c-torture/execute/scal-to-vec1.c	(revision 0)
+++ gcc/testsuite/gcc.c-torture/execute/scal-to-vec1.c	(revision 0)
@@ -0,0 +1,77 @@ 
+#define vector(elcount, type)  \
+__attribute__((vector_size((elcount)*sizeof(type)))) type
+
+#define vidx(type, vec, idx) (*((type *) &(vec) + idx))
+
+#define operl(a, b, op) a op b
+#define operr(a, b, op) b op a
+
+#define check(type, count, vec0, vec1, num, op, lr) \
+do {\
+    int __i; \
+    for (__i = 0; __i < count; __i++) {\
+        if (vidx (type, vec1, __i) != oper##lr (num, vidx (type, vec0, __i), op)) \
+            __builtin_abort (); \
+    }\
+} while (0)
+
+#define veccompare(type, count, v0, v1) \
+do {\
+    int __i; \
+    for (__i = 0; __i < count; __i++) { \
+        if (vidx (type, v0, __i) != vidx (type, v1, __i)) \
+            __builtin_abort (); \
+    } \
+} while (0)
+
+
+int main (int argc, char *argv[]) {
+#define fvec_2 (vector(4, float)){2., 2., 2., 2.}
+#define dvec_2 (vector(2, double)){2., 2.}
+
+
+    vector(8, short) v0 = {argc, 1,2,3,4,5,6,7};
+    vector(8, short) v1;
+
+    vector(4, float) f0 = {1., 2., 3., 4.};
+    vector(4, float) f1, f2;
+
+    vector(2, double) d0 = {1., 2.};
+    vector(2, double) d1, d2;
+
+
+
+    v1 = 2 + v0;   check (short, 8, v0, v1, 2, +, l);
+    v1 = 2 - v0;   check (short, 8, v0, v1, 2, -, l);
+    v1 = 2 * v0;   check (short, 8, v0, v1, 2, *, l);
+    v1 = 2 / v0;   check (short, 8, v0, v1, 2, /, l);
+    v1 = 2 % v0;   check (short, 8, v0, v1, 2, %, l);
+
+    v1 = v0 + 2;   check (short, 8, v0, v1, 2, +, r);
+    v1 = v0 - 2;   check (short, 8, v0, v1, 2, -, r);
+    v1 = v0 * 2;   check (short, 8, v0, v1, 2, *, r);
+    v1 = v0 / 2;   check (short, 8, v0, v1, 2, /, r);
+    v1 = v0 % 2;   check (short, 8, v0, v1, 2, %, r);
+
+    f1 = 2. + f0;  f2 = fvec_2 + f0; veccompare (float, 4, f1, f2);
+    f1 = 2. - f0;  f2 = fvec_2 - f0; veccompare (float, 4, f1, f2);
+    f1 = 2. * f0;  f2 = fvec_2 * f0; veccompare (float, 4, f1, f2);
+    f1 = 2. / f0;  f2 = fvec_2 / f0; veccompare (float, 4, f1, f2);
+
+    f1 = f0 + 2.;  f2 = f0 + fvec_2; veccompare (float, 4, f1, f2);
+    f1 = f0 - 2.;  f2 = f0 - fvec_2; veccompare (float, 4, f1, f2);
+    f1 = f0 * 2.;  f2 = f0 * fvec_2; veccompare (float, 4, f1, f2);
+    f1 = f0 / 2.;  f2 = f0 / fvec_2; veccompare (float, 4, f1, f2);
+
+    d1 = 2. + d0;  d2 = dvec_2 + d0; veccompare (double, 2, d1, d2);
+    d1 = 2. - d0;  d2 = dvec_2 - d0; veccompare (double, 2, d1, d2);
+    d1 = 2. * d0;  d2 = dvec_2 * d0; veccompare (double, 2, d1, d2);
+    d1 = 2. / d0;  d2 = dvec_2 / d0; veccompare (double, 2, d1, d2);
+
+    d1 = d0 + 2.;  d2 = d0 + dvec_2; veccompare (double, 2, d1, d2);
+    d1 = d0 - 2.;  d2 = d0 - dvec_2; veccompare (double, 2, d1, d2);
+    d1 = d0 * 2.;  d2 = d0 * dvec_2; veccompare (double, 2, d1, d2);
+    d1 = d0 / 2.;  d2 = d0 / dvec_2; veccompare (double, 2, d1, d2);
+
+    return 0;
+}
Index: gcc/testsuite/gcc.dg/scal-to-vec1.c
===================================================================
--- gcc/testsuite/gcc.dg/scal-to-vec1.c	(revision 0)
+++ gcc/testsuite/gcc.dg/scal-to-vec1.c	(revision 0)
@@ -0,0 +1,25 @@ 
+/* { dg-do compile } */
+#define vector(elcount, type)  \
+__attribute__((vector_size((elcount)*sizeof(type)))) type
+
+#define vidx(type, vec, idx) (*((type *) &(vec) + idx))
+
+
+int main (int argc, char *argv[]) {
+    vector(8, short) v0 = {argc, 1,2,3,4,5,6,7};
+    vector(8, short) v1;
+
+    vector(4, float) f0 = {1., 2., 3., 4.};
+    vector(4, float) f1, f2;
+    
+    int     i = 12;
+    double  d = 3.;
+
+    v1 = i + v0;        /* { dg-error "" } */
+    v1 = 99999 + v0;    /* { dg-error "" } */
+
+    f1 = d + f0;        /* { dg-error "" } */
+    f1 = 1.3 + f0;      /* { dg-error "" } */
+
+    return 0;
+}
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(revision 163244)
+++ gcc/c-typeck.c	(working copy)
@@ -9196,6 +9196,102 @@  push_cleanup (tree decl, tree cleanup, b
   TREE_OPERAND (stmt, 0) = list;
   STATEMENT_LIST_STMT_EXPR (list) = stmt_expr;
 }
+
+/* Check whether expression EXPR can be converted to the TYPE, 
+   considering the case when EXPR is a constant.  */
+static inline bool
+expr_fits_type_p (const tree expr, const tree type )
+{
+  enum machine_mode mode = TYPE_MODE (TREE_TYPE (type));
+  if (TYPE_MODE (TREE_TYPE (expr)) == mode)
+    return true;
+
+  if (TREE_CODE (expr) == INTEGER_CST)
+    return int_fits_type_p (expr, TREE_TYPE (type));
+  else if (TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE)
+    return !tree_int_cst_lt 
+                (TYPE_SIZE (TREE_TYPE (type)),
+                 TYPE_SIZE (TREE_TYPE (expr)));
+  else if (TREE_CODE (expr) == REAL_CST)
+    {
+      REAL_VALUE_TYPE c, c1;
+      c = TREE_REAL_CST (expr);
+      real_convert (&c1, mode, &c);
+      return real_identical (&c, &c1);
+    }
+  else if (TREE_CODE (TREE_TYPE (expr)) == REAL_TYPE)
+    return (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (expr))) 
+            <= GET_MODE_SIZE (mode));
+  return false;
+}
+
+/* Convert scalar to vector for the range of operations.  
+   Function can return:
+        -2  --  Error ocured
+        -1  --  Nothing happened
+         0  --  First argument must be expanded
+         1  --  Second argument must be expanded  */
+static inline int
+scalar_to_vector (location_t loc, enum tree_code code, tree op0, tree op1)
+{
+#define MSG "Conversion of scalar to vector involves truncation"
+#define MSG_FLOAT "Truncating floating point constant to vector " \
+                  "precision does not preserve value"
+#define ERROR(MSG, LOC, RET) { error_at (LOC, MSG); return RET; }
+  
+  tree type0 = TREE_TYPE (op0);
+  tree type1 = TREE_TYPE (op1);
+  enum tree_code code0 = TREE_CODE (type0);
+  enum tree_code code1 = TREE_CODE (type1);
+  
+  switch (code)
+    {
+      case RSHIFT_EXPR:
+      case LSHIFT_EXPR:
+        if (TREE_CODE (TREE_TYPE (op0)) == INTEGER_TYPE)
+          return 0;
+      
+      case PLUS_EXPR:
+      case MINUS_EXPR:
+      case MULT_EXPR:
+      case TRUNC_DIV_EXPR:
+      case TRUNC_MOD_EXPR:
+      case RDIV_EXPR:
+        if (code0 == INTEGER_TYPE
+            && TREE_CODE (TREE_TYPE (type1)) == INTEGER_TYPE) 
+          {
+            if (!expr_fits_type_p (op0, type1))
+              ERROR (MSG, loc, -2);
+            return 0;
+          }
+        else if (code1 == INTEGER_TYPE
+                 &&  TREE_CODE (TREE_TYPE (type0)) == INTEGER_TYPE)
+          {
+            if (!expr_fits_type_p (op1, type0))
+              ERROR (MSG, loc, -2);
+            return 1;
+          }
+
+        else if (code0 == REAL_TYPE
+                 && SCALAR_FLOAT_TYPE_P (TREE_TYPE (type1)))
+          {
+            if (!expr_fits_type_p (op0, type1))
+              ERROR (MSG_FLOAT, loc, -2);
+            return 0;
+          }
+        else if (code1 == REAL_TYPE
+                 && SCALAR_FLOAT_TYPE_P (TREE_TYPE (type0)))
+          {
+            if (!expr_fits_type_p (op1, type0))
+              ERROR (MSG_FLOAT, loc, -2);
+            return 1;
+          }
+      default:
+        break;
+    }
+ 
+  return -1;
+}
 
 /* Build a binary-operation expression without default conversions.
    CODE is the kind of expression to build.
@@ -9375,6 +9471,35 @@  build_binary_op (location_t location, en
 
   objc_ok = objc_compare_types (type0, type1, -3, NULL_TREE);
 
+  /* For 'vector <shift> scalar' or 'scalar <shift> vector', we convert 
+     a scalar to a vector. Truncating the shift amount is ok.  */
+  if ((code0 == VECTOR_TYPE || code1 == VECTOR_TYPE)
+      && (code0 != code1))
+    {
+        int convert_flag = scalar_to_vector (location, code, op0, op1);
+        
+        if (convert_flag == -2)
+          return error_mark_node;
+        else if (convert_flag == 0)
+          {
+            tree sc = save_expr(op0);
+            sc = convert (TREE_TYPE (type1), sc);
+            op0 = build_vector_from_val (sc, type1);
+            orig_type0 = type0 = TREE_TYPE (op0);
+            code0 = TREE_CODE (type0);
+            converted = 1;
+          }
+        else if (convert_flag == 1)
+          {
+            tree sc = save_expr(op1);
+            sc = convert (TREE_TYPE (type0), sc);
+            op1 = build_vector_from_val (sc, type0);
+            orig_type1 = type1 = TREE_TYPE (op1);
+            code1 = TREE_CODE (type1);
+            converted = 1;
+          }
+    }
+
   switch (code)
     {
     case PLUS_EXPR: