mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-27 05:44:15 +08:00
SVE intrinsics: Refactor const_binop to allow constant folding of intrinsics.
This patch sets the stage for constant folding of binary operations for SVE intrinsics: In fold-const.cc, the code for folding vector constants was moved from const_binop to a new function vector_const_binop. This function takes a function pointer as argument specifying how to fold the vector elements. The intention is to call vector_const_binop from the backend with an aarch64-specific callback function. The code in const_binop for folding operations where the first operand is a vector constant and the second argument is an integer constant was also moved into vector_const_binop to to allow folding of binary SVE intrinsics where the second operand is an integer (_n). To allow calling poly_int_binop from the backend, the latter was made public. The patch was bootstrapped and regtested on aarch64-linux-gnu, no regression. OK for mainline? Signed-off-by: Jennifer Schmitz <jschmitz@nvidia.com> gcc/ * fold-const.h: Declare vector_const_binop. * fold-const.cc (const_binop): Remove cases for vector constants. (vector_const_binop): New function that folds vector constants element-wise. (int_const_binop): Remove call to wide_int_binop. (poly_int_binop): Add call to wide_int_binop.
This commit is contained in:
parent
7c9394e84c
commit
87217bea3a
@ -1236,13 +1236,24 @@ can_min_p (const_tree arg1, const_tree arg2, poly_wide_int &res)
|
||||
produce a new constant in RES. Return FALSE if we don't know how
|
||||
to evaluate CODE at compile-time. */
|
||||
|
||||
static bool
|
||||
bool
|
||||
poly_int_binop (poly_wide_int &res, enum tree_code code,
|
||||
const_tree arg1, const_tree arg2,
|
||||
signop sign, wi::overflow_type *overflow)
|
||||
{
|
||||
gcc_assert (NUM_POLY_INT_COEFFS != 1);
|
||||
gcc_assert (poly_int_tree_p (arg1) && poly_int_tree_p (arg2));
|
||||
|
||||
if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg2) == INTEGER_CST)
|
||||
{
|
||||
wide_int warg1 = wi::to_wide (arg1), wi_res;
|
||||
wide_int warg2 = wi::to_wide (arg2, TYPE_PRECISION (TREE_TYPE (arg1)));
|
||||
if (!wide_int_binop (wi_res, code, warg1, warg2, sign, overflow))
|
||||
return NULL_TREE;
|
||||
res = wi_res;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case PLUS_EXPR:
|
||||
@ -1304,17 +1315,9 @@ int_const_binop (enum tree_code code, const_tree arg1, const_tree arg2,
|
||||
signop sign = TYPE_SIGN (type);
|
||||
wi::overflow_type overflow = wi::OVF_NONE;
|
||||
|
||||
if (TREE_CODE (arg1) == INTEGER_CST && TREE_CODE (arg2) == INTEGER_CST)
|
||||
{
|
||||
wide_int warg1 = wi::to_wide (arg1), res;
|
||||
wide_int warg2 = wi::to_wide (arg2, TYPE_PRECISION (type));
|
||||
if (!wide_int_binop (res, code, warg1, warg2, sign, &overflow))
|
||||
return NULL_TREE;
|
||||
poly_res = res;
|
||||
}
|
||||
else if (!poly_int_tree_p (arg1)
|
||||
|| !poly_int_tree_p (arg2)
|
||||
|| !poly_int_binop (poly_res, code, arg1, arg2, sign, &overflow))
|
||||
if (!poly_int_tree_p (arg1)
|
||||
|| !poly_int_tree_p (arg2)
|
||||
|| !poly_int_binop (poly_res, code, arg1, arg2, sign, &overflow))
|
||||
return NULL_TREE;
|
||||
return force_fit_type (type, poly_res, overflowable,
|
||||
(((sign == SIGNED || overflowable == -1)
|
||||
@ -1365,6 +1368,90 @@ simplify_const_binop (tree_code code, tree op, tree other_op,
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* If ARG1 and ARG2 are constants, and if performing CODE on them would
|
||||
be an elementwise vector operation, try to fold the operation to a
|
||||
constant vector, using ELT_CONST_BINOP to fold each element. Return
|
||||
the folded value on success, otherwise return null. */
|
||||
tree
|
||||
vector_const_binop (tree_code code, tree arg1, tree arg2,
|
||||
tree (*elt_const_binop) (enum tree_code, tree, tree))
|
||||
{
|
||||
if (TREE_CODE (arg1) == VECTOR_CST && TREE_CODE (arg2) == VECTOR_CST
|
||||
&& known_eq (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg1)),
|
||||
TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg2))))
|
||||
{
|
||||
tree type = TREE_TYPE (arg1);
|
||||
bool step_ok_p;
|
||||
if (VECTOR_CST_STEPPED_P (arg1)
|
||||
&& VECTOR_CST_STEPPED_P (arg2))
|
||||
/* We can operate directly on the encoding if:
|
||||
|
||||
a3 - a2 == a2 - a1 && b3 - b2 == b2 - b1
|
||||
implies
|
||||
(a3 op b3) - (a2 op b2) == (a2 op b2) - (a1 op b1)
|
||||
|
||||
Addition and subtraction are the supported operators
|
||||
for which this is true. */
|
||||
step_ok_p = (code == PLUS_EXPR || code == MINUS_EXPR);
|
||||
else if (VECTOR_CST_STEPPED_P (arg1))
|
||||
/* We can operate directly on stepped encodings if:
|
||||
|
||||
a3 - a2 == a2 - a1
|
||||
implies:
|
||||
(a3 op c) - (a2 op c) == (a2 op c) - (a1 op c)
|
||||
|
||||
which is true if (x -> x op c) distributes over addition. */
|
||||
step_ok_p = distributes_over_addition_p (code, 1);
|
||||
else
|
||||
/* Similarly in reverse. */
|
||||
step_ok_p = distributes_over_addition_p (code, 2);
|
||||
tree_vector_builder elts;
|
||||
if (!elts.new_binary_operation (type, arg1, arg2, step_ok_p))
|
||||
return NULL_TREE;
|
||||
unsigned int count = elts.encoded_nelts ();
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
tree elem1 = VECTOR_CST_ELT (arg1, i);
|
||||
tree elem2 = VECTOR_CST_ELT (arg2, i);
|
||||
|
||||
tree elt = elt_const_binop (code, elem1, elem2);
|
||||
|
||||
/* It is possible that const_binop cannot handle the given
|
||||
code and return NULL_TREE */
|
||||
if (elt == NULL_TREE)
|
||||
return NULL_TREE;
|
||||
elts.quick_push (elt);
|
||||
}
|
||||
|
||||
return elts.build ();
|
||||
}
|
||||
|
||||
if (TREE_CODE (arg1) == VECTOR_CST
|
||||
&& TREE_CODE (arg2) == INTEGER_CST)
|
||||
{
|
||||
tree type = TREE_TYPE (arg1);
|
||||
bool step_ok_p = distributes_over_addition_p (code, 1);
|
||||
tree_vector_builder elts;
|
||||
if (!elts.new_unary_operation (type, arg1, step_ok_p))
|
||||
return NULL_TREE;
|
||||
unsigned int count = elts.encoded_nelts ();
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
tree elem1 = VECTOR_CST_ELT (arg1, i);
|
||||
|
||||
tree elt = elt_const_binop (code, elem1, arg2);
|
||||
|
||||
/* It is possible that const_binop cannot handle the given
|
||||
code and return NULL_TREE. */
|
||||
if (elt == NULL_TREE)
|
||||
return NULL_TREE;
|
||||
elts.quick_push (elt);
|
||||
}
|
||||
|
||||
return elts.build ();
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Combine two constants ARG1 and ARG2 under operation CODE to produce a new
|
||||
constant. We assume ARG1 and ARG2 have the same data type, or at least
|
||||
@ -1677,83 +1764,7 @@ const_binop (enum tree_code code, tree arg1, tree arg2)
|
||||
&& (simplified = simplify_const_binop (code, arg2, arg1, 1)))
|
||||
return simplified;
|
||||
|
||||
if (TREE_CODE (arg1) == VECTOR_CST
|
||||
&& TREE_CODE (arg2) == VECTOR_CST
|
||||
&& known_eq (TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg1)),
|
||||
TYPE_VECTOR_SUBPARTS (TREE_TYPE (arg2))))
|
||||
{
|
||||
tree type = TREE_TYPE (arg1);
|
||||
bool step_ok_p;
|
||||
if (VECTOR_CST_STEPPED_P (arg1)
|
||||
&& VECTOR_CST_STEPPED_P (arg2))
|
||||
/* We can operate directly on the encoding if:
|
||||
|
||||
a3 - a2 == a2 - a1 && b3 - b2 == b2 - b1
|
||||
implies
|
||||
(a3 op b3) - (a2 op b2) == (a2 op b2) - (a1 op b1)
|
||||
|
||||
Addition and subtraction are the supported operators
|
||||
for which this is true. */
|
||||
step_ok_p = (code == PLUS_EXPR || code == MINUS_EXPR);
|
||||
else if (VECTOR_CST_STEPPED_P (arg1))
|
||||
/* We can operate directly on stepped encodings if:
|
||||
|
||||
a3 - a2 == a2 - a1
|
||||
implies:
|
||||
(a3 op c) - (a2 op c) == (a2 op c) - (a1 op c)
|
||||
|
||||
which is true if (x -> x op c) distributes over addition. */
|
||||
step_ok_p = distributes_over_addition_p (code, 1);
|
||||
else
|
||||
/* Similarly in reverse. */
|
||||
step_ok_p = distributes_over_addition_p (code, 2);
|
||||
tree_vector_builder elts;
|
||||
if (!elts.new_binary_operation (type, arg1, arg2, step_ok_p))
|
||||
return NULL_TREE;
|
||||
unsigned int count = elts.encoded_nelts ();
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
tree elem1 = VECTOR_CST_ELT (arg1, i);
|
||||
tree elem2 = VECTOR_CST_ELT (arg2, i);
|
||||
|
||||
tree elt = const_binop (code, elem1, elem2);
|
||||
|
||||
/* It is possible that const_binop cannot handle the given
|
||||
code and return NULL_TREE */
|
||||
if (elt == NULL_TREE)
|
||||
return NULL_TREE;
|
||||
elts.quick_push (elt);
|
||||
}
|
||||
|
||||
return elts.build ();
|
||||
}
|
||||
|
||||
/* Shifts allow a scalar offset for a vector. */
|
||||
if (TREE_CODE (arg1) == VECTOR_CST
|
||||
&& TREE_CODE (arg2) == INTEGER_CST)
|
||||
{
|
||||
tree type = TREE_TYPE (arg1);
|
||||
bool step_ok_p = distributes_over_addition_p (code, 1);
|
||||
tree_vector_builder elts;
|
||||
if (!elts.new_unary_operation (type, arg1, step_ok_p))
|
||||
return NULL_TREE;
|
||||
unsigned int count = elts.encoded_nelts ();
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
{
|
||||
tree elem1 = VECTOR_CST_ELT (arg1, i);
|
||||
|
||||
tree elt = const_binop (code, elem1, arg2);
|
||||
|
||||
/* It is possible that const_binop cannot handle the given
|
||||
code and return NULL_TREE. */
|
||||
if (elt == NULL_TREE)
|
||||
return NULL_TREE;
|
||||
elts.quick_push (elt);
|
||||
}
|
||||
|
||||
return elts.build ();
|
||||
}
|
||||
return NULL_TREE;
|
||||
return vector_const_binop (code, arg1, arg2, const_binop);
|
||||
}
|
||||
|
||||
/* Overload that adds a TYPE parameter to be able to dispatch
|
||||
|
@ -126,6 +126,9 @@ extern tree fold_vec_perm (tree, tree, tree, const vec_perm_indices &);
|
||||
extern bool wide_int_binop (wide_int &res, enum tree_code,
|
||||
const wide_int &arg1, const wide_int &arg2,
|
||||
signop, wi::overflow_type *);
|
||||
extern bool poly_int_binop (poly_wide_int &res, enum tree_code,
|
||||
const_tree, const_tree, signop,
|
||||
wi::overflow_type *);
|
||||
extern tree int_const_binop (enum tree_code, const_tree, const_tree, int = 1);
|
||||
#define build_fold_addr_expr(T)\
|
||||
build_fold_addr_expr_loc (UNKNOWN_LOCATION, (T))
|
||||
@ -218,6 +221,8 @@ extern bool simple_condition_p (tree);
|
||||
extern tree exact_inverse (tree, tree);
|
||||
extern bool expr_not_equal_to (tree t, const wide_int &);
|
||||
extern tree const_unop (enum tree_code, tree, tree);
|
||||
extern tree vector_const_binop (enum tree_code, tree, tree,
|
||||
tree (*) (enum tree_code, tree, tree));
|
||||
extern tree const_binop (enum tree_code, tree, tree, tree);
|
||||
extern bool negate_mathfn_p (combined_fn);
|
||||
extern const char *getbyterep (tree, unsigned HOST_WIDE_INT *);
|
||||
|
Loading…
Reference in New Issue
Block a user