summaryrefslogtreecommitdiff
path: root/gcc/gimple-match-head.c
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@linaro.org>2018-07-12 13:01:17 +0000
committerRichard Sandiford <rsandifo@gcc.gnu.org>2018-07-12 13:01:17 +0000
commit6a86928d9882c17b7526d657a38cb314fa0aaba6 (patch)
tree57cdf4000ddf7bace5fd55b3cfdb32a5457aa332 /gcc/gimple-match-head.c
parentd5cbbf873956db1c4eed15a88f935700e7d6012a (diff)
Extend tree code folds to IFN_COND_*
This patch adds match.pd support for applying normal folds to their IFN_COND_* forms. E.g. the rule: (plus @0 (negate @1)) -> (minus @0 @1) also allows the fold: (IFN_COND_ADD @0 @1 (negate @2) @3) -> (IFN_COND_SUB @0 @1 @2 @3) Actually doing this by direct matches in gimple-match.c would probably lead to combinatorial explosion, so instead, the patch makes gimple_match_op carry a condition under which the operation happens ("cond"), and the value to use when the condition is false ("else_value"). Thus in the example above we'd do the following (a) convert: cond:NULL_TREE (IFN_COND_ADD @0 @1 @4 @3) else_value:NULL_TREE to: cond:@0 (plus @1 @4) else_value:@3 (b) apply gimple_resimplify to (plus @1 @4) (c) reintroduce cond and else_value when constructing the result. Nested operations inherit the condition of the outer operation (so that we don't introduce extra faults) but have a null else_value. If we try to build such an operation, the target gets to choose what else_value it can handle efficiently: obvious choices include one of the operands or a zero constant. (The alternative would be to have some representation for an undefined value, but that seems a bit invasive, and isn't likely to be useful here.) I've made the condition a mandatory part of the gimple_match_op constructor so that it doesn't accidentally get dropped. 2018-07-12 Richard Sandiford <richard.sandiford@linaro.org> gcc/ * target.def (preferred_else_value): New target hook. * doc/tm.texi.in (TARGET_PREFERRED_ELSE_VALUE): New hook. * doc/tm.texi: Regenerate. * targhooks.h (default_preferred_else_value): Declare. * targhooks.c (default_preferred_else_value): New function. * internal-fn.h (conditional_internal_fn_code): Declare. * internal-fn.c (FOR_EACH_CODE_MAPPING): New macro. (get_conditional_internal_fn): Use it. (conditional_internal_fn_code): New function. * gimple-match.h (gimple_match_cond): New struct. (gimple_match_op): Add a cond member function. (gimple_match_op::gimple_match_op): Update all forms to take a gimple_match_cond. * genmatch.c (expr::gen_transform): Use the same condition as res_op for the suboperation, but don't specify a particular else_value. * tree-ssa-sccvn.c (vn_nary_simplify, vn_reference_lookup_3) (visit_nary_op, visit_reference_op_load): Pass gimple_match_cond::UNCOND to the gimple_match_op constructor. * gimple-match-head.c: Include tree-eh.h (convert_conditional_op): New function. (maybe_resimplify_conditional_op): Likewise. (gimple_resimplify1): Call maybe_resimplify_conditional_op. (gimple_resimplify2): Likewise. (gimple_resimplify3): Likewise. (gimple_resimplify4): Likewise. (maybe_push_res_to_seq): Return null for conditional operations. (try_conditional_simplification): New function. (gimple_simplify): Call it. Pass conditions to the gimple_match_op constructor. * match.pd: Fold VEC_COND_EXPRs of an IFN_COND_* call to a new IFN_COND_* call. * config/aarch64/aarch64.c (aarch64_preferred_else_value): New function. (TARGET_PREFERRED_ELSE_VALUE): Redefine. gcc/testsuite/ * gcc.dg/vect/vect-cond-arith-2.c: New test. * gcc.target/aarch64/sve/loop_add_6.c: Likewise. From-SVN: r262586
Diffstat (limited to 'gcc/gimple-match-head.c')
-rw-r--r--gcc/gimple-match-head.c161
1 files changed, 160 insertions, 1 deletions
diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c
index c5a19239798..e165a77132b 100644
--- a/gcc/gimple-match-head.c
+++ b/gcc/gimple-match-head.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "case-cfn-macros.h"
#include "gimplify.h"
#include "optabs-tree.h"
+#include "tree-eh.h"
/* Forward declarations of the private auto-generated matchers.
@@ -68,6 +69,95 @@ constant_for_folding (tree t)
&& TREE_CODE (TREE_OPERAND (t, 0)) == STRING_CST));
}
+/* Try to convert conditional operation ORIG_OP into an IFN_COND_*
+ operation. Return true on success, storing the new operation in NEW_OP. */
+
+static bool
+convert_conditional_op (gimple_match_op *orig_op,
+ gimple_match_op *new_op)
+{
+ internal_fn ifn;
+ if (orig_op->code.is_tree_code ())
+ ifn = get_conditional_internal_fn ((tree_code) orig_op->code);
+ else
+ return false;
+ if (ifn == IFN_LAST)
+ return false;
+ unsigned int num_ops = orig_op->num_ops;
+ new_op->set_op (as_combined_fn (ifn), orig_op->type, num_ops + 2);
+ new_op->ops[0] = orig_op->cond.cond;
+ for (unsigned int i = 0; i < num_ops; ++i)
+ new_op->ops[i + 1] = orig_op->ops[i];
+ tree else_value = orig_op->cond.else_value;
+ if (!else_value)
+ else_value = targetm.preferred_else_value (ifn, orig_op->type,
+ num_ops, orig_op->ops);
+ new_op->ops[num_ops + 1] = else_value;
+ return true;
+}
+
+/* RES_OP is the result of a simplification. If it is conditional,
+ try to replace it with the equivalent UNCOND form, such as an
+ IFN_COND_* call or a VEC_COND_EXPR. Also try to resimplify the
+ result of the replacement if appropriate, adding any new statements to
+ SEQ and using VALUEIZE as the valueization function. Return true if
+ this resimplification occurred and resulted in at least one change. */
+
+static bool
+maybe_resimplify_conditional_op (gimple_seq *seq, gimple_match_op *res_op,
+ tree (*valueize) (tree))
+{
+ if (!res_op->cond.cond)
+ return false;
+
+ if (!res_op->cond.else_value
+ && res_op->code.is_tree_code ())
+ {
+ /* The "else" value doesn't matter. If the "then" value is a
+ gimple value, just use it unconditionally. This isn't a
+ simplification in itself, since there was no operation to
+ build in the first place. */
+ if (gimple_simplified_result_is_gimple_val (res_op))
+ {
+ res_op->cond.cond = NULL_TREE;
+ return false;
+ }
+
+ /* Likewise if the operation would not trap. */
+ bool honor_trapv = (INTEGRAL_TYPE_P (res_op->type)
+ && TYPE_OVERFLOW_TRAPS (res_op->type));
+ if (!operation_could_trap_p ((tree_code) res_op->code,
+ FLOAT_TYPE_P (res_op->type),
+ honor_trapv, res_op->op_or_null (1)))
+ {
+ res_op->cond.cond = NULL_TREE;
+ return false;
+ }
+ }
+
+ /* If the "then" value is a gimple value and the "else" value matters,
+ create a VEC_COND_EXPR between them, then see if it can be further
+ simplified. */
+ gimple_match_op new_op;
+ if (res_op->cond.else_value
+ && VECTOR_TYPE_P (res_op->type)
+ && gimple_simplified_result_is_gimple_val (res_op))
+ {
+ new_op.set_op (VEC_COND_EXPR, res_op->type,
+ res_op->cond.cond, res_op->ops[0],
+ res_op->cond.else_value);
+ *res_op = new_op;
+ return gimple_resimplify3 (seq, res_op, valueize);
+ }
+
+ /* Otherwise try rewriting the operation as an IFN_COND_* call.
+ Again, this isn't a simplification in itself, since it's what
+ RES_OP already described. */
+ if (convert_conditional_op (res_op, &new_op))
+ *res_op = new_op;
+
+ return false;
+}
/* Helper that matches and simplifies the toplevel result from
a gimple_simplify run (where we don't want to build
@@ -93,6 +183,7 @@ gimple_resimplify1 (gimple_seq *seq, gimple_match_op *res_op,
if (TREE_OVERFLOW_P (tem))
tem = drop_tree_overflow (tem);
res_op->set_value (tem);
+ maybe_resimplify_conditional_op (seq, res_op, valueize);
return true;
}
}
@@ -122,6 +213,9 @@ gimple_resimplify1 (gimple_seq *seq, gimple_match_op *res_op,
}
--depth;
+ if (maybe_resimplify_conditional_op (seq, res_op, valueize))
+ return true;
+
return false;
}
@@ -151,6 +245,7 @@ gimple_resimplify2 (gimple_seq *seq, gimple_match_op *res_op,
if (TREE_OVERFLOW_P (tem))
tem = drop_tree_overflow (tem);
res_op->set_value (tem);
+ maybe_resimplify_conditional_op (seq, res_op, valueize);
return true;
}
}
@@ -190,6 +285,9 @@ gimple_resimplify2 (gimple_seq *seq, gimple_match_op *res_op,
}
--depth;
+ if (maybe_resimplify_conditional_op (seq, res_op, valueize))
+ return true;
+
return canonicalized;
}
@@ -221,6 +319,7 @@ gimple_resimplify3 (gimple_seq *seq, gimple_match_op *res_op,
if (TREE_OVERFLOW_P (tem))
tem = drop_tree_overflow (tem);
res_op->set_value (tem);
+ maybe_resimplify_conditional_op (seq, res_op, valueize);
return true;
}
}
@@ -257,6 +356,9 @@ gimple_resimplify3 (gimple_seq *seq, gimple_match_op *res_op,
}
--depth;
+ if (maybe_resimplify_conditional_op (seq, res_op, valueize))
+ return true;
+
return canonicalized;
}
@@ -295,6 +397,9 @@ gimple_resimplify4 (gimple_seq *seq, gimple_match_op *res_op,
}
--depth;
+ if (maybe_resimplify_conditional_op (seq, res_op, valueize))
+ return true;
+
return false;
}
@@ -353,6 +458,12 @@ maybe_push_res_to_seq (gimple_match_op *res_op, gimple_seq *seq, tree res)
tree *ops = res_op->ops;
unsigned num_ops = res_op->num_ops;
+ /* The caller should have converted conditional operations into an UNCOND
+ form and resimplified as appropriate. The conditional form only
+ survives this far if that conversion failed. */
+ if (res_op->cond.cond)
+ return NULL_TREE;
+
if (res_op->code.is_tree_code ())
{
if (!res
@@ -614,6 +725,50 @@ do_valueize (tree op, tree (*valueize)(tree), bool &valueized)
return op;
}
+/* If RES_OP is a call to a conditional internal function, try simplifying
+ the associated unconditional operation and using the result to build
+ a new conditional operation. For example, if RES_OP is:
+
+ IFN_COND_ADD (COND, A, B, ELSE)
+
+ try simplifying (plus A B) and using the result to build a replacement
+ for the whole IFN_COND_ADD.
+
+ Return true if this approach led to a simplification, otherwise leave
+ RES_OP unchanged (and so suitable for other simplifications). When
+ returning true, add any new statements to SEQ and use VALUEIZE as the
+ valueization function.
+
+ RES_OP is known to be a call to IFN. */
+
+static bool
+try_conditional_simplification (internal_fn ifn, gimple_match_op *res_op,
+ gimple_seq *seq, tree (*valueize) (tree))
+{
+ tree_code code = conditional_internal_fn_code (ifn);
+ if (code == ERROR_MARK)
+ return false;
+
+ unsigned int num_ops = res_op->num_ops;
+ gimple_match_op cond_op (gimple_match_cond (res_op->ops[0],
+ res_op->ops[num_ops - 1]),
+ code, res_op->type, num_ops - 2);
+ for (unsigned int i = 1; i < num_ops - 1; ++i)
+ cond_op.ops[i - 1] = res_op->ops[i];
+ switch (num_ops - 2)
+ {
+ case 2:
+ if (!gimple_resimplify2 (seq, &cond_op, valueize))
+ return false;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ *res_op = cond_op;
+ maybe_resimplify_conditional_op (seq, res_op, valueize);
+ return true;
+}
+
/* The main STMT based simplification entry. It is used by the fold_stmt
and the fold_stmt_to_constant APIs. */
@@ -699,7 +854,7 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
tree rhs = TREE_OPERAND (rhs1, 1);
lhs = do_valueize (lhs, top_valueize, valueized);
rhs = do_valueize (rhs, top_valueize, valueized);
- gimple_match_op res_op2 (TREE_CODE (rhs1),
+ gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1),
TREE_TYPE (rhs1), lhs, rhs);
if ((gimple_resimplify2 (seq, &res_op2, valueize)
|| valueized)
@@ -770,6 +925,10 @@ gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq,
tree arg = gimple_call_arg (stmt, i);
res_op->ops[i] = do_valueize (arg, top_valueize, valueized);
}
+ if (internal_fn_p (cfn)
+ && try_conditional_simplification (as_internal_fn (cfn),
+ res_op, seq, valueize))
+ return true;
switch (num_args)
{
case 1: