This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Scalar vector binary operation
- From: Artem Shinkarov <artyom dot shinkaroff at gmail dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: Richard Guenther <richard dot guenther at gmail dot com>
- Date: Sat, 14 Aug 2010 17:37:50 +0100
- Subject: Scalar vector binary operation
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
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: