expr.c (do_store_flag): Expand vector comparison by building an appropriate VEC_COND_EXPR.
2011-09-29 Artjoms Sinkarovs <artyom.shinkaroff@gmail.com> * expr.c (do_store_flag): Expand vector comparison by building an appropriate VEC_COND_EXPR. * c-typeck.c (build_binary_op): Typecheck vector comparisons. (c_objc_common_truthvalue_conversion): Adjust. * tree-vect-generic.c (do_compare): Helper function. (expand_vector_comparison): Check if hardware supports vector comparison of the given type or expand vector piecewise. (expand_vector_operation): Treat comparison as binary operation of vector type. (expand_vector_operations_1): Adjust. * gcc.c-torture/execute/vector-compare-1.c: New testcase. * gcc.c-torture/execute/vector-compare-2.c: Likewise. * gcc.dg/vector-compare-1.c: Likewise. * gcc.dg/vector-compare-2.c: Likewise. From-SVN: r179342
This commit is contained in:
parent
7c99ecef0b
commit
d246ab4f57
@ -1,3 +1,17 @@
|
||||
2011-09-29 Artjoms Sinkarovs <artyom.shinkaroff@gmail.com>
|
||||
|
||||
* expr.c (do_store_flag): Expand vector comparison by
|
||||
building an appropriate VEC_COND_EXPR.
|
||||
* c-typeck.c (build_binary_op): Typecheck vector comparisons.
|
||||
(c_objc_common_truthvalue_conversion): Adjust.
|
||||
* tree-vect-generic.c (do_compare): Helper function.
|
||||
(expand_vector_comparison): Check if hardware supports
|
||||
vector comparison of the given type or expand vector
|
||||
piecewise.
|
||||
(expand_vector_operation): Treat comparison as binary
|
||||
operation of vector type.
|
||||
(expand_vector_operations_1): Adjust.
|
||||
|
||||
2011-09-29 Richard Guenther <rguenther@suse.de>
|
||||
|
||||
* tree.c (build_opaque_vector_type): Make opaque vectors
|
||||
|
@ -9910,6 +9910,31 @@ build_binary_op (location_t location, enum tree_code code,
|
||||
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE)
|
||||
{
|
||||
tree intt;
|
||||
if (TREE_TYPE (type0) != TREE_TYPE (type1))
|
||||
{
|
||||
error_at (location, "comparing vectors with different "
|
||||
"element types");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
if (TYPE_VECTOR_SUBPARTS (type0) != TYPE_VECTOR_SUBPARTS (type1))
|
||||
{
|
||||
error_at (location, "comparing vectors with different "
|
||||
"number of elements");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
/* Always construct signed integer vector type. */
|
||||
intt = c_common_type_for_size (GET_MODE_BITSIZE
|
||||
(TYPE_MODE (TREE_TYPE (type0))), 0);
|
||||
result_type = build_opaque_vector_type (intt,
|
||||
TYPE_VECTOR_SUBPARTS (type0));
|
||||
converted = 1;
|
||||
break;
|
||||
}
|
||||
if (FLOAT_TYPE_P (type0) || FLOAT_TYPE_P (type1))
|
||||
warning_at (location,
|
||||
OPT_Wfloat_equal,
|
||||
@ -10022,6 +10047,31 @@ build_binary_op (location_t location, enum tree_code code,
|
||||
case GE_EXPR:
|
||||
case LT_EXPR:
|
||||
case GT_EXPR:
|
||||
if (code0 == VECTOR_TYPE && code1 == VECTOR_TYPE)
|
||||
{
|
||||
tree intt;
|
||||
if (TREE_TYPE (type0) != TREE_TYPE (type1))
|
||||
{
|
||||
error_at (location, "comparing vectors with different "
|
||||
"element types");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
if (TYPE_VECTOR_SUBPARTS (type0) != TYPE_VECTOR_SUBPARTS (type1))
|
||||
{
|
||||
error_at (location, "comparing vectors with different "
|
||||
"number of elements");
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
/* Always construct signed integer vector type. */
|
||||
intt = c_common_type_for_size (GET_MODE_BITSIZE
|
||||
(TYPE_MODE (TREE_TYPE (type0))), 0);
|
||||
result_type = build_opaque_vector_type (intt,
|
||||
TYPE_VECTOR_SUBPARTS (type0));
|
||||
converted = 1;
|
||||
break;
|
||||
}
|
||||
build_type = integer_type_node;
|
||||
if ((code0 == INTEGER_TYPE || code0 == REAL_TYPE
|
||||
|| code0 == FIXED_POINT_TYPE)
|
||||
@ -10429,6 +10479,10 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr)
|
||||
case FUNCTION_TYPE:
|
||||
gcc_unreachable ();
|
||||
|
||||
case VECTOR_TYPE:
|
||||
error_at (location, "used vector type where scalar is required");
|
||||
return error_mark_node;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -18890,7 +18890,12 @@ ix86_expand_sse_movcc (rtx dest, rtx cmp, rtx op_true, rtx op_false)
|
||||
enum machine_mode mode = GET_MODE (dest);
|
||||
rtx t2, t3, x;
|
||||
|
||||
if (op_false == CONST0_RTX (mode))
|
||||
if (vector_all_ones_operand (op_true, GET_MODE (op_true))
|
||||
&& rtx_equal_p (op_false, CONST0_RTX (mode)))
|
||||
{
|
||||
emit_insn (gen_rtx_SET (VOIDmode, dest, cmp));
|
||||
}
|
||||
else if (op_false == CONST0_RTX (mode))
|
||||
{
|
||||
op_true = force_reg (mode, op_true);
|
||||
x = gen_rtx_AND (mode, cmp, op_true);
|
||||
|
@ -6561,6 +6561,29 @@ invoke undefined behavior at runtime. Warnings for out of bound
|
||||
accesses for vector subscription can be enabled with
|
||||
@option{-Warray-bounds}.
|
||||
|
||||
In GNU C vector comparison is supported within standard comparison
|
||||
operators: @code{==, !=, <, <=, >, >=}. Comparison operands can be
|
||||
vector expressions of integer-type or real-type. Comparison between
|
||||
integer-type vectors and real-type vectors are not supported. The
|
||||
result of the comparison is a vector of the same width and number of
|
||||
elements as the comparison operands with a signed integral element
|
||||
type.
|
||||
|
||||
Vectors are compared element-wise producing 0 when comparison is false
|
||||
and -1 (constant of the appropriate type where all bits are set)
|
||||
otherwise. Consider the following example.
|
||||
|
||||
@smallexample
|
||||
typedef int v4si __attribute__ ((vector_size (16)));
|
||||
|
||||
v4si a = @{1,2,3,4@};
|
||||
v4si b = @{3,2,1,4@};
|
||||
v4si c;
|
||||
|
||||
c = a > b; /* The result would be @{0, 0,-1, 0@} */
|
||||
c = a == b; /* The result would be @{0,-1, 0,-1@} */
|
||||
@end smallexample
|
||||
|
||||
You can declare variables and use them in function calls and returns, as
|
||||
well as in assignments and some casts. You can specify a vector type as
|
||||
a return type for a function. Vector types can also be used as function
|
||||
|
11
gcc/expr.c
11
gcc/expr.c
@ -10309,6 +10309,17 @@ do_store_flag (sepops ops, rtx target, enum machine_mode mode)
|
||||
STRIP_NOPS (arg0);
|
||||
STRIP_NOPS (arg1);
|
||||
|
||||
/* For vector typed comparisons emit code to generate the desired
|
||||
all-ones or all-zeros mask. Conveniently use the VEC_COND_EXPR
|
||||
expander for this. */
|
||||
if (TREE_CODE (ops->type) == VECTOR_TYPE)
|
||||
{
|
||||
tree ifexp = build2 (ops->code, ops->type, arg0, arg1);
|
||||
tree if_true = constant_boolean_node (true, ops->type);
|
||||
tree if_false = constant_boolean_node (false, ops->type);
|
||||
return expand_vec_cond_expr (ops->type, ifexp, if_true, if_false, target);
|
||||
}
|
||||
|
||||
/* Get the rtx comparison code to use. We know that EXP is a comparison
|
||||
operation of some type. Some comparisons against 1 and -1 can be
|
||||
converted to comparisons with zero. Do so here so that the tests
|
||||
|
@ -1,3 +1,10 @@
|
||||
2011-09-29 Artjoms Sinkarovs <artyom.shinkaroff@gmail.com>
|
||||
|
||||
* gcc.c-torture/execute/vector-compare-1.c: New testcase.
|
||||
* gcc.c-torture/execute/vector-compare-2.c: Likewise.
|
||||
* gcc.dg/vector-compare-1.c: Likewise.
|
||||
* gcc.dg/vector-compare-2.c: Likewise.
|
||||
|
||||
2011-09-29 David S. Miller <davem@davemloft.net>
|
||||
|
||||
* gcc.target/sparc/array.c: New test.
|
||||
|
123
gcc/testsuite/gcc.c-torture/execute/vector-compare-1.c
Normal file
123
gcc/testsuite/gcc.c-torture/execute/vector-compare-1.c
Normal file
@ -0,0 +1,123 @@
|
||||
#define vector(elcount, type) \
|
||||
__attribute__((vector_size((elcount)*sizeof(type)))) type
|
||||
|
||||
#define check_compare(count, res, i0, i1, op, fmt) \
|
||||
do { \
|
||||
int __i; \
|
||||
for (__i = 0; __i < count; __i ++) { \
|
||||
if ((res)[__i] != ((i0)[__i] op (i1)[__i] ? -1 : 0)) \
|
||||
{ \
|
||||
__builtin_printf ("%i != ((" fmt " " #op " " fmt " ? -1 : 0) ", \
|
||||
(res)[__i], (i0)[__i], (i1)[__i]); \
|
||||
__builtin_abort (); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define test(count, v0, v1, res, fmt); \
|
||||
do { \
|
||||
res = (v0 > v1); \
|
||||
check_compare (count, res, v0, v1, >, fmt); \
|
||||
res = (v0 < v1); \
|
||||
check_compare (count, res, v0, v1, <, fmt); \
|
||||
res = (v0 >= v1); \
|
||||
check_compare (count, res, v0, v1, >=, fmt); \
|
||||
res = (v0 <= v1); \
|
||||
check_compare (count, res, v0, v1, <=, fmt); \
|
||||
res = (v0 == v1); \
|
||||
check_compare (count, res, v0, v1, ==, fmt); \
|
||||
res = (v0 != v1); \
|
||||
check_compare (count, res, v0, v1, !=, fmt); \
|
||||
} while (0)
|
||||
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
#define INT int
|
||||
vector (4, INT) i0;
|
||||
vector (4, INT) i1;
|
||||
vector (4, int) ires;
|
||||
int i;
|
||||
|
||||
i0 = (vector (4, INT)){argc, 1, 2, 10};
|
||||
i1 = (vector (4, INT)){0, 3, 2, (INT)-23};
|
||||
test (4, i0, i1, ires, "%i");
|
||||
#undef INT
|
||||
|
||||
#define INT unsigned int
|
||||
vector (4, int) ures;
|
||||
vector (4, INT) u0;
|
||||
vector (4, INT) u1;
|
||||
|
||||
u0 = (vector (4, INT)){argc, 1, 2, 10};
|
||||
u1 = (vector (4, INT)){0, 3, 2, (INT)-23};
|
||||
test (4, u0, u1, ures, "%u");
|
||||
#undef INT
|
||||
|
||||
|
||||
#define SHORT short
|
||||
vector (8, SHORT) s0;
|
||||
vector (8, SHORT) s1;
|
||||
vector (8, short) sres;
|
||||
|
||||
s0 = (vector (8, SHORT)){argc, 1, 2, 10, 6, 87, (SHORT)-5, 2};
|
||||
s1 = (vector (8, SHORT)){0, 3, 2, (SHORT)-23, 12, 10, (SHORT)-2, 0};
|
||||
test (8, s0, s1, sres, "%i");
|
||||
#undef SHORT
|
||||
|
||||
#define SHORT unsigned short
|
||||
vector (8, SHORT) us0;
|
||||
vector (8, SHORT) us1;
|
||||
vector (8, short) usres;
|
||||
|
||||
us0 = (vector (8, SHORT)){argc, 1, 2, 10, 6, 87, (SHORT)-5, 2};
|
||||
us1 = (vector (8, SHORT)){0, 3, 2, (SHORT)-23, 12, 10, (SHORT)-2, 0};
|
||||
test (8, us0, us1, usres, "%u");
|
||||
#undef SHORT
|
||||
|
||||
#define CHAR signed char
|
||||
vector (16, CHAR) c0;
|
||||
vector (16, CHAR) c1;
|
||||
vector (16, signed char) cres;
|
||||
|
||||
c0 = (vector (16, CHAR)){argc, 1, 2, 10, 6, 87, (CHAR)-5, 2, \
|
||||
argc, 1, 2, 10, 6, 87, (CHAR)-5, 2 };
|
||||
|
||||
c1 = (vector (16, CHAR)){0, 3, 2, (CHAR)-23, 12, 10, (CHAR)-2, 0, \
|
||||
0, 3, 2, (CHAR)-23, 12, 10, (CHAR)-2, 0};
|
||||
test (16, c0, c1, cres, "%i");
|
||||
#undef CHAR
|
||||
|
||||
#define CHAR unsigned char
|
||||
vector (16, CHAR) uc0;
|
||||
vector (16, CHAR) uc1;
|
||||
vector (16, signed char) ucres;
|
||||
|
||||
uc0 = (vector (16, CHAR)){argc, 1, 2, 10, 6, 87, (CHAR)-5, 2, \
|
||||
argc, 1, 2, 10, 6, 87, (CHAR)-5, 2 };
|
||||
|
||||
uc1 = (vector (16, CHAR)){0, 3, 2, (CHAR)-23, 12, 10, (CHAR)-2, 0, \
|
||||
0, 3, 2, (CHAR)-23, 12, 10, (CHAR)-2, 0};
|
||||
test (16, uc0, uc1, ucres, "%u");
|
||||
#undef CHAR
|
||||
/* Float comparison. */
|
||||
vector (4, float) f0;
|
||||
vector (4, float) f1;
|
||||
vector (4, int) ifres;
|
||||
|
||||
f0 = (vector (4, float)){(float)argc, 1., 2., 10.};
|
||||
f1 = (vector (4, float)){0., 3., 2., (float)-23};
|
||||
test (4, f0, f1, ifres, "%f");
|
||||
|
||||
/* Double comparison. */
|
||||
vector (2, double) d0;
|
||||
vector (2, double) d1;
|
||||
vector (2, long long) idres;
|
||||
|
||||
d0 = (vector (2, double)){(double)argc, 10.};
|
||||
d1 = (vector (2, double)){0., (double)-23};
|
||||
test (2, d0, d1, idres, "%f");
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
27
gcc/testsuite/gcc.c-torture/execute/vector-compare-2.c
Normal file
27
gcc/testsuite/gcc.c-torture/execute/vector-compare-2.c
Normal file
@ -0,0 +1,27 @@
|
||||
#define vector(elcount, type) \
|
||||
__attribute__((vector_size((elcount)*sizeof(type)))) type
|
||||
|
||||
/* Check that constant folding in
|
||||
these simple cases works. */
|
||||
vector (4, int)
|
||||
foo (vector (4, int) x)
|
||||
{
|
||||
return (x == x) + (x != x) + (x > x)
|
||||
+ (x < x) + (x >= x) + (x <= x);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
vector (4, int) t = {argc, 2, argc, 42};
|
||||
vector (4, int) r;
|
||||
int i;
|
||||
|
||||
r = foo (t);
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
if (r[i] != -3)
|
||||
__builtin_abort ();
|
||||
|
||||
return 0;
|
||||
}
|
17
gcc/testsuite/gcc.dg/vector-compare-1.c
Normal file
17
gcc/testsuite/gcc.dg/vector-compare-1.c
Normal file
@ -0,0 +1,17 @@
|
||||
/* { dg-do compile } */
|
||||
#define vector(elcount, type) \
|
||||
__attribute__((vector_size((elcount)*sizeof(type)))) type
|
||||
|
||||
void
|
||||
foo (vector (4, int) x, vector (4, float) y)
|
||||
{
|
||||
vector (4, int) p4;
|
||||
vector (4, int) r4;
|
||||
vector (4, unsigned int) q4;
|
||||
vector (8, int) r8;
|
||||
vector (4, float) f4;
|
||||
|
||||
r4 = x > y; /* { dg-error "comparing vectors with different element types" } */
|
||||
r8 = (x != p4); /* { dg-error "incompatible types when assigning to type" } */
|
||||
r8 == r4; /* { dg-error "comparing vectors with different number of elements" } */
|
||||
}
|
26
gcc/testsuite/gcc.dg/vector-compare-2.c
Normal file
26
gcc/testsuite/gcc.dg/vector-compare-2.c
Normal file
@ -0,0 +1,26 @@
|
||||
/* { dg-do compile } */
|
||||
|
||||
/* Test if C_MAYBE_CONST are folded correctly when
|
||||
creating VEC_COND_EXPR. */
|
||||
|
||||
typedef int vec __attribute__((vector_size(16)));
|
||||
|
||||
vec i,j;
|
||||
extern vec a, b, c;
|
||||
|
||||
extern int p, q, z;
|
||||
extern vec foo (int);
|
||||
|
||||
vec
|
||||
foo (int x)
|
||||
{
|
||||
return foo (p ? q :z) > a;
|
||||
}
|
||||
|
||||
vec
|
||||
bar (int x)
|
||||
{
|
||||
return b > foo (p ? q :z);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,10 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "expr.h"
|
||||
#include "optabs.h"
|
||||
|
||||
|
||||
static void expand_vector_operations_1 (gimple_stmt_iterator *);
|
||||
|
||||
|
||||
/* Build a constant of type TYPE, made of VALUE's bits replicated
|
||||
every TYPE_SIZE (INNER_TYPE) bits to fit TYPE's precision. */
|
||||
static tree
|
||||
@ -125,6 +129,31 @@ do_binop (gimple_stmt_iterator *gsi, tree inner_type, tree a, tree b,
|
||||
return gimplify_build2 (gsi, code, inner_type, a, b);
|
||||
}
|
||||
|
||||
|
||||
/* Construct expression (A[BITPOS] code B[BITPOS]) ? -1 : 0
|
||||
|
||||
INNER_TYPE is the type of A and B elements
|
||||
|
||||
returned expression is of signed integer type with the
|
||||
size equal to the size of INNER_TYPE. */
|
||||
static tree
|
||||
do_compare (gimple_stmt_iterator *gsi, tree inner_type, tree a, tree b,
|
||||
tree bitpos, tree bitsize, enum tree_code code)
|
||||
{
|
||||
tree comp_type;
|
||||
|
||||
a = tree_vec_extract (gsi, inner_type, a, bitsize, bitpos);
|
||||
b = tree_vec_extract (gsi, inner_type, b, bitsize, bitpos);
|
||||
|
||||
comp_type = build_nonstandard_integer_type
|
||||
(GET_MODE_BITSIZE (TYPE_MODE (inner_type)), 0);
|
||||
|
||||
return gimplify_build3 (gsi, COND_EXPR, comp_type,
|
||||
fold_build2 (code, boolean_type_node, a, b),
|
||||
build_int_cst (comp_type, -1),
|
||||
build_int_cst (comp_type, 0));
|
||||
}
|
||||
|
||||
/* Expand vector addition to scalars. This does bit twiddling
|
||||
in order to increase parallelism:
|
||||
|
||||
@ -333,6 +362,24 @@ uniform_vector_p (tree vec)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Try to expand vector comparison expression OP0 CODE OP1 by
|
||||
querying optab if the following expression:
|
||||
VEC_COND_EXPR< OP0 CODE OP1, {-1,...}, {0,...}>
|
||||
can be expanded. */
|
||||
static tree
|
||||
expand_vector_comparison (gimple_stmt_iterator *gsi, tree type, tree op0,
|
||||
tree op1, enum tree_code code)
|
||||
{
|
||||
tree t;
|
||||
if (! expand_vec_cond_expr_p (type, TREE_TYPE (op0)))
|
||||
t = expand_vector_piecewise (gsi, do_compare, type,
|
||||
TREE_TYPE (TREE_TYPE (op0)), op0, op1, code);
|
||||
else
|
||||
t = NULL_TREE;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static tree
|
||||
expand_vector_operation (gimple_stmt_iterator *gsi, tree type, tree compute_type,
|
||||
gimple assign, enum tree_code code)
|
||||
@ -376,7 +423,26 @@ expand_vector_operation (gimple_stmt_iterator *gsi, tree type, tree compute_type
|
||||
return expand_vector_parallel (gsi, do_unop, type,
|
||||
gimple_assign_rhs1 (assign),
|
||||
NULL_TREE, code);
|
||||
case EQ_EXPR:
|
||||
case NE_EXPR:
|
||||
case GT_EXPR:
|
||||
case LT_EXPR:
|
||||
case GE_EXPR:
|
||||
case LE_EXPR:
|
||||
case UNEQ_EXPR:
|
||||
case UNGT_EXPR:
|
||||
case UNLT_EXPR:
|
||||
case UNGE_EXPR:
|
||||
case UNLE_EXPR:
|
||||
case LTGT_EXPR:
|
||||
case ORDERED_EXPR:
|
||||
case UNORDERED_EXPR:
|
||||
{
|
||||
tree rhs1 = gimple_assign_rhs1 (assign);
|
||||
tree rhs2 = gimple_assign_rhs2 (assign);
|
||||
|
||||
return expand_vector_comparison (gsi, type, rhs1, rhs2, code);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -450,11 +516,11 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
|
||||
|
||||
code = gimple_assign_rhs_code (stmt);
|
||||
rhs_class = get_gimple_rhs_class (code);
|
||||
lhs = gimple_assign_lhs (stmt);
|
||||
|
||||
if (rhs_class != GIMPLE_UNARY_RHS && rhs_class != GIMPLE_BINARY_RHS)
|
||||
return;
|
||||
|
||||
lhs = gimple_assign_lhs (stmt);
|
||||
rhs1 = gimple_assign_rhs1 (stmt);
|
||||
type = gimple_expr_type (stmt);
|
||||
if (rhs_class == GIMPLE_BINARY_RHS)
|
||||
@ -598,6 +664,11 @@ expand_vector_operations_1 (gimple_stmt_iterator *gsi)
|
||||
|
||||
gcc_assert (code != VEC_LSHIFT_EXPR && code != VEC_RSHIFT_EXPR);
|
||||
new_rhs = expand_vector_operation (gsi, type, compute_type, stmt, code);
|
||||
|
||||
/* Leave expression untouched for later expansion. */
|
||||
if (new_rhs == NULL_TREE)
|
||||
return;
|
||||
|
||||
if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (new_rhs)))
|
||||
new_rhs = gimplify_build1 (gsi, VIEW_CONVERT_EXPR, TREE_TYPE (lhs),
|
||||
new_rhs);
|
||||
|
Loading…
Reference in New Issue
Block a user