diff options
author | Richard Sandiford <richard.sandiford@linaro.org> | 2018-07-12 13:01:17 +0000 |
---|---|---|
committer | Richard Sandiford <rsandifo@gcc.gnu.org> | 2018-07-12 13:01:17 +0000 |
commit | 6a86928d9882c17b7526d657a38cb314fa0aaba6 (patch) | |
tree | 57cdf4000ddf7bace5fd55b3cfdb32a5457aa332 /gcc/gimple-match-head.c | |
parent | d5cbbf873956db1c4eed15a88f935700e7d6012a (diff) | |
download | gcc-6a86928d9882c17b7526d657a38cb314fa0aaba6.zip gcc-6a86928d9882c17b7526d657a38cb314fa0aaba6.tar.gz gcc-6a86928d9882c17b7526d657a38cb314fa0aaba6.tar.bz2 |
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.c | 161 |
1 files changed, 160 insertions, 1 deletions
diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c index c5a1923..e165a77 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: |