X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fgimple-match-head.c;h=bbbc0f2c28b15c941b53f4fb684b77bcd782cc35;hb=2ee43ae6d6973d21cddea8964db58f7abe67df51;hp=17bd992614422f4e3d09ba0d9ba3e02a7bf183c2;hpb=a0c012fda997b1c2c918752f88abec680274ffe8;p=gcc.git diff --git a/gcc/gimple-match-head.c b/gcc/gimple-match-head.c index 17bd9926144..bbbc0f2c28b 100644 --- a/gcc/gimple-match-head.c +++ b/gcc/gimple-match-head.c @@ -1,5 +1,5 @@ /* Preamble and helpers for the autogenerated gimple-match.c file. - Copyright (C) 2014-2015 Free Software Foundation, Inc. + Copyright (C) 2014-2019 Free Software Foundation, Inc. This file is part of GCC. @@ -21,48 +21,43 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "backend.h" +#include "target.h" +#include "rtl.h" #include "tree.h" #include "gimple.h" -#include "rtl.h" #include "ssa.h" -#include "alias.h" -#include "options.h" +#include "cgraph.h" #include "fold-const.h" +#include "fold-const-call.h" #include "stor-layout.h" -#include "flags.h" -#include "internal-fn.h" #include "gimple-fold.h" -#include "gimple-iterator.h" -#include "insn-config.h" -#include "expmed.h" -#include "dojump.h" -#include "explow.h" #include "calls.h" -#include "emit-rtl.h" -#include "varasm.h" -#include "stmt.h" -#include "expr.h" #include "tree-dfa.h" #include "builtins.h" -#include "dumpfile.h" -#include "target.h" -#include "cgraph.h" #include "gimple-match.h" +#include "tree-pass.h" +#include "internal-fn.h" +#include "case-cfn-macros.h" +#include "gimplify.h" +#include "optabs-tree.h" +#include "tree-eh.h" /* Forward declarations of the private auto-generated matchers. They expect valueized operands in canonical order and do not perform simplification of all-constant operands. */ -static bool gimple_simplify (code_helper *, tree *, - gimple_seq *, tree (*)(tree), +static bool gimple_simplify (gimple_match_op *, gimple_seq *, tree (*)(tree), code_helper, tree, tree); -static bool gimple_simplify (code_helper *, tree *, - gimple_seq *, tree (*)(tree), +static bool gimple_simplify (gimple_match_op *, gimple_seq *, tree (*)(tree), code_helper, tree, tree, tree); -static bool gimple_simplify (code_helper *, tree *, - gimple_seq *, tree (*)(tree), +static bool gimple_simplify (gimple_match_op *, gimple_seq *, tree (*)(tree), code_helper, tree, tree, tree, tree); +static bool gimple_simplify (gimple_match_op *, gimple_seq *, tree (*)(tree), + code_helper, tree, tree, tree, tree, tree); +static bool gimple_simplify (gimple_match_op *, gimple_seq *, tree (*)(tree), + code_helper, tree, tree, tree, tree, tree, tree); +const unsigned int gimple_match_op::MAX_NUM_OPS; /* Return whether T is a constant that we'll dispatch to fold to evaluate fully constant expressions. */ @@ -76,59 +71,157 @@ 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 + { + combined_fn cfn = orig_op->code; + if (!internal_fn_p (cfn)) + return false; + ifn = get_conditional_internal_fn (as_internal_fn (cfn)); + } + 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 a stmt in case it's used in in-place folding). Replaces - *RES_CODE and *RES_OPS with a simplified and/or canonicalized - result and returns whether any change was made. */ + RES_OP with a simplified and/or canonicalized result and + returns whether any change was made. */ -static bool -gimple_resimplify1 (gimple_seq *seq, - code_helper *res_code, tree type, tree *res_ops, +bool +gimple_resimplify1 (gimple_seq *seq, gimple_match_op *res_op, tree (*valueize)(tree)) { - if (constant_for_folding (res_ops[0])) + if (constant_for_folding (res_op->ops[0])) { tree tem = NULL_TREE; - if (res_code->is_tree_code ()) - tem = const_unop (*res_code, type, res_ops[0]); + if (res_op->code.is_tree_code ()) + tem = const_unop (res_op->code, res_op->type, res_op->ops[0]); else - { - tree decl = builtin_decl_implicit (*res_code); - if (decl) - { - tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 1, false); - if (tem) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (tem); - tem = fold_convert (type, tem); - } - } - } + tem = fold_const_call (combined_fn (res_op->code), res_op->type, + res_op->ops[0]); if (tem != NULL_TREE && CONSTANT_CLASS_P (tem)) { - res_ops[0] = tem; - res_ops[1] = NULL_TREE; - res_ops[2] = NULL_TREE; - *res_code = TREE_CODE (res_ops[0]); + 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; } } - code_helper res_code2; - tree res_ops2[3] = {}; - if (gimple_simplify (&res_code2, res_ops2, seq, valueize, - *res_code, type, res_ops[0])) + /* Limit recursion, there are cases like PR80887 and others, for + example when value-numbering presents us with unfolded expressions + that we are really not prepared to handle without eventual + oscillation like ((_50 + 0) + 8) where _50 gets mapped to _50 + itself as available expression. */ + static unsigned depth; + if (depth > 10) + { + if (dump_file && (dump_flags & TDF_FOLDING)) + fprintf (dump_file, "Aborting expression simplification due to " + "deep recursion\n"); + return false; + } + + ++depth; + gimple_match_op res_op2 (*res_op); + if (gimple_simplify (&res_op2, seq, valueize, + res_op->code, res_op->type, res_op->ops[0])) { - *res_code = res_code2; - res_ops[0] = res_ops2[0]; - res_ops[1] = res_ops2[1]; - res_ops[2] = res_ops2[2]; + --depth; + *res_op = res_op2; return true; } + --depth; + + if (maybe_resimplify_conditional_op (seq, res_op, valueize)) + return true; return false; } @@ -136,68 +229,71 @@ gimple_resimplify1 (gimple_seq *seq, /* Helper that matches and simplifies the toplevel result from a gimple_simplify run (where we don't want to build a stmt in case it's used in in-place folding). Replaces - *RES_CODE and *RES_OPS with a simplified and/or canonicalized - result and returns whether any change was made. */ + RES_OP with a simplified and/or canonicalized result and + returns whether any change was made. */ -static bool -gimple_resimplify2 (gimple_seq *seq, - code_helper *res_code, tree type, tree *res_ops, +bool +gimple_resimplify2 (gimple_seq *seq, gimple_match_op *res_op, tree (*valueize)(tree)) { - if (constant_for_folding (res_ops[0]) && constant_for_folding (res_ops[1])) + if (constant_for_folding (res_op->ops[0]) + && constant_for_folding (res_op->ops[1])) { tree tem = NULL_TREE; - if (res_code->is_tree_code ()) - tem = const_binop (*res_code, type, res_ops[0], res_ops[1]); + if (res_op->code.is_tree_code ()) + tem = const_binop (res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1]); else - { - tree decl = builtin_decl_implicit (*res_code); - if (decl) - { - tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 2, false); - if (tem) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (tem); - tem = fold_convert (type, tem); - } - } - } + tem = fold_const_call (combined_fn (res_op->code), res_op->type, + res_op->ops[0], res_op->ops[1]); if (tem != NULL_TREE && CONSTANT_CLASS_P (tem)) { - res_ops[0] = tem; - res_ops[1] = NULL_TREE; - res_ops[2] = NULL_TREE; - *res_code = TREE_CODE (res_ops[0]); + 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; } } /* Canonicalize operand order. */ bool canonicalized = false; - if (res_code->is_tree_code () - && (TREE_CODE_CLASS ((enum tree_code) *res_code) == tcc_comparison - || commutative_tree_code (*res_code)) - && tree_swap_operands_p (res_ops[0], res_ops[1], false)) - { - std::swap (res_ops[0], res_ops[1]); - if (TREE_CODE_CLASS ((enum tree_code) *res_code) == tcc_comparison) - *res_code = swap_tree_comparison (*res_code); + if (res_op->code.is_tree_code () + && (TREE_CODE_CLASS ((enum tree_code) res_op->code) == tcc_comparison + || commutative_tree_code (res_op->code)) + && tree_swap_operands_p (res_op->ops[0], res_op->ops[1])) + { + std::swap (res_op->ops[0], res_op->ops[1]); + if (TREE_CODE_CLASS ((enum tree_code) res_op->code) == tcc_comparison) + res_op->code = swap_tree_comparison (res_op->code); canonicalized = true; } - code_helper res_code2; - tree res_ops2[3] = {}; - if (gimple_simplify (&res_code2, res_ops2, seq, valueize, - *res_code, type, res_ops[0], res_ops[1])) + /* Limit recursion, see gimple_resimplify1. */ + static unsigned depth; + if (depth > 10) + { + if (dump_file && (dump_flags & TDF_FOLDING)) + fprintf (dump_file, "Aborting expression simplification due to " + "deep recursion\n"); + return false; + } + + ++depth; + gimple_match_op res_op2 (*res_op); + if (gimple_simplify (&res_op2, seq, valueize, + res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1])) { - *res_code = res_code2; - res_ops[0] = res_ops2[0]; - res_ops[1] = res_ops2[1]; - res_ops[2] = res_ops2[2]; + --depth; + *res_op = res_op2; return true; } + --depth; + + if (maybe_resimplify_conditional_op (seq, res_op, valueize)) + return true; return canonicalized; } @@ -205,154 +301,293 @@ gimple_resimplify2 (gimple_seq *seq, /* Helper that matches and simplifies the toplevel result from a gimple_simplify run (where we don't want to build a stmt in case it's used in in-place folding). Replaces - *RES_CODE and *RES_OPS with a simplified and/or canonicalized - result and returns whether any change was made. */ + RES_OP with a simplified and/or canonicalized result and + returns whether any change was made. */ -static bool -gimple_resimplify3 (gimple_seq *seq, - code_helper *res_code, tree type, tree *res_ops, +bool +gimple_resimplify3 (gimple_seq *seq, gimple_match_op *res_op, tree (*valueize)(tree)) { - if (constant_for_folding (res_ops[0]) && constant_for_folding (res_ops[1]) - && constant_for_folding (res_ops[2])) + if (constant_for_folding (res_op->ops[0]) + && constant_for_folding (res_op->ops[1]) + && constant_for_folding (res_op->ops[2])) { tree tem = NULL_TREE; - if (res_code->is_tree_code ()) - tem = fold_ternary/*_to_constant*/ (*res_code, type, res_ops[0], - res_ops[1], res_ops[2]); + if (res_op->code.is_tree_code ()) + tem = fold_ternary/*_to_constant*/ (res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1], + res_op->ops[2]); else - { - tree decl = builtin_decl_implicit (*res_code); - if (decl) - { - tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 3, false); - if (tem) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (tem); - tem = fold_convert (type, tem); - } - } - } + tem = fold_const_call (combined_fn (res_op->code), res_op->type, + res_op->ops[0], res_op->ops[1], res_op->ops[2]); if (tem != NULL_TREE && CONSTANT_CLASS_P (tem)) { - res_ops[0] = tem; - res_ops[1] = NULL_TREE; - res_ops[2] = NULL_TREE; - *res_code = TREE_CODE (res_ops[0]); + 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; } } /* Canonicalize operand order. */ bool canonicalized = false; - if (res_code->is_tree_code () - && commutative_ternary_tree_code (*res_code) - && tree_swap_operands_p (res_ops[0], res_ops[1], false)) + if (res_op->code.is_tree_code () + && commutative_ternary_tree_code (res_op->code) + && tree_swap_operands_p (res_op->ops[0], res_op->ops[1])) { - std::swap (res_ops[0], res_ops[1]); + std::swap (res_op->ops[0], res_op->ops[1]); canonicalized = true; } - code_helper res_code2; - tree res_ops2[3] = {}; - if (gimple_simplify (&res_code2, res_ops2, seq, valueize, - *res_code, type, - res_ops[0], res_ops[1], res_ops[2])) + /* Limit recursion, see gimple_resimplify1. */ + static unsigned depth; + if (depth > 10) { - *res_code = res_code2; - res_ops[0] = res_ops2[0]; - res_ops[1] = res_ops2[1]; - res_ops[2] = res_ops2[2]; + if (dump_file && (dump_flags & TDF_FOLDING)) + fprintf (dump_file, "Aborting expression simplification due to " + "deep recursion\n"); + return false; + } + + ++depth; + gimple_match_op res_op2 (*res_op); + if (gimple_simplify (&res_op2, seq, valueize, + res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1], res_op->ops[2])) + { + --depth; + *res_op = res_op2; return true; } + --depth; + + if (maybe_resimplify_conditional_op (seq, res_op, valueize)) + return true; return canonicalized; } +/* Helper that matches and simplifies the toplevel result from + a gimple_simplify run (where we don't want to build + a stmt in case it's used in in-place folding). Replaces + RES_OP with a simplified and/or canonicalized result and + returns whether any change was made. */ + +bool +gimple_resimplify4 (gimple_seq *seq, gimple_match_op *res_op, + tree (*valueize)(tree)) +{ + /* No constant folding is defined for four-operand functions. */ + + /* Limit recursion, see gimple_resimplify1. */ + static unsigned depth; + if (depth > 10) + { + if (dump_file && (dump_flags & TDF_FOLDING)) + fprintf (dump_file, "Aborting expression simplification due to " + "deep recursion\n"); + return false; + } + + ++depth; + gimple_match_op res_op2 (*res_op); + if (gimple_simplify (&res_op2, seq, valueize, + res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1], res_op->ops[2], + res_op->ops[3])) + { + --depth; + *res_op = res_op2; + return true; + } + --depth; + + if (maybe_resimplify_conditional_op (seq, res_op, valueize)) + return true; + + return false; +} + +/* Helper that matches and simplifies the toplevel result from + a gimple_simplify run (where we don't want to build + a stmt in case it's used in in-place folding). Replaces + RES_OP with a simplified and/or canonicalized result and + returns whether any change was made. */ -/* If in GIMPLE expressions with CODE go as single-rhs build - a GENERIC tree for that expression into *OP0. */ +bool +gimple_resimplify5 (gimple_seq *seq, gimple_match_op *res_op, + tree (*valueize)(tree)) +{ + /* No constant folding is defined for five-operand functions. */ + + gimple_match_op res_op2 (*res_op); + if (gimple_simplify (&res_op2, seq, valueize, + res_op->code, res_op->type, + res_op->ops[0], res_op->ops[1], res_op->ops[2], + res_op->ops[3], res_op->ops[4])) + { + *res_op = res_op2; + return true; + } + + if (maybe_resimplify_conditional_op (seq, res_op, valueize)) + return true; + + return false; +} + +/* If in GIMPLE the operation described by RES_OP should be single-rhs, + build a GENERIC tree for that expression and update RES_OP accordingly. */ void -maybe_build_generic_op (enum tree_code code, tree type, - tree *op0, tree op1, tree op2) +maybe_build_generic_op (gimple_match_op *res_op) { + tree_code code = (tree_code) res_op->code; + tree val; switch (code) { case REALPART_EXPR: case IMAGPART_EXPR: case VIEW_CONVERT_EXPR: - *op0 = build1 (code, type, *op0); + val = build1 (code, res_op->type, res_op->ops[0]); + res_op->set_value (val); break; case BIT_FIELD_REF: - *op0 = build3 (code, type, *op0, op1, op2); + val = build3 (code, res_op->type, res_op->ops[0], res_op->ops[1], + res_op->ops[2]); + REF_REVERSE_STORAGE_ORDER (val) = res_op->reverse; + res_op->set_value (val); break; default:; } } -/* Push the exploded expression described by RCODE, TYPE and OPS - as a statement to SEQ if necessary and return a gimple value - denoting the value of the expression. If RES is not NULL - then the result will be always RES and even gimple values are - pushed to SEQ. */ +tree (*mprts_hook) (gimple_match_op *); + +/* Try to build RES_OP, which is known to be a call to FN. Return null + if the target doesn't support the function. */ + +static gcall * +build_call_internal (internal_fn fn, gimple_match_op *res_op) +{ + if (direct_internal_fn_p (fn)) + { + tree_pair types = direct_internal_fn_types (fn, res_op->type, + res_op->ops); + if (!direct_internal_fn_supported_p (fn, types, OPTIMIZE_FOR_BOTH)) + return NULL; + } + return gimple_build_call_internal (fn, res_op->num_ops, + res_op->op_or_null (0), + res_op->op_or_null (1), + res_op->op_or_null (2), + res_op->op_or_null (3), + res_op->op_or_null (4)); +} + +/* Push the exploded expression described by RES_OP as a statement to + SEQ if necessary and return a gimple value denoting the value of the + expression. If RES is not NULL then the result will be always RES + and even gimple values are pushed to SEQ. */ tree -maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops, - gimple_seq *seq, tree res) +maybe_push_res_to_seq (gimple_match_op *res_op, gimple_seq *seq, tree res) { - if (rcode.is_tree_code ()) + 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 - && (TREE_CODE_LENGTH ((tree_code) rcode) == 0 - || ((tree_code) rcode) == ADDR_EXPR) - && is_gimple_val (ops[0])) + && gimple_simplified_result_is_gimple_val (res_op)) return ops[0]; - if (!seq) - return NULL_TREE; - /* Play safe and do not allow abnormals to be mentioned in - newly created statements. */ - if ((TREE_CODE (ops[0]) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[0])) - || (ops[1] - && TREE_CODE (ops[1]) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[1])) - || (ops[2] - && TREE_CODE (ops[2]) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[2]))) + if (mprts_hook) + { + tree tem = mprts_hook (res_op); + if (tem) + return tem; + } + } + + if (!seq) + return NULL_TREE; + + /* Play safe and do not allow abnormals to be mentioned in + newly created statements. */ + for (unsigned int i = 0; i < num_ops; ++i) + if (TREE_CODE (ops[i]) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[i])) + return NULL_TREE; + + if (num_ops > 0 && COMPARISON_CLASS_P (ops[0])) + for (unsigned int i = 0; i < 2; ++i) + if (TREE_CODE (TREE_OPERAND (ops[0], i)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (ops[0], i))) return NULL_TREE; + + if (res_op->code.is_tree_code ()) + { if (!res) - res = make_ssa_name (type); - maybe_build_generic_op (rcode, type, &ops[0], ops[1], ops[2]); - gimple new_stmt = gimple_build_assign (res, rcode, - ops[0], ops[1], ops[2]); + { + if (gimple_in_ssa_p (cfun)) + res = make_ssa_name (res_op->type); + else + res = create_tmp_reg (res_op->type); + } + maybe_build_generic_op (res_op); + gimple *new_stmt = gimple_build_assign (res, res_op->code, + res_op->op_or_null (0), + res_op->op_or_null (1), + res_op->op_or_null (2)); gimple_seq_add_stmt_without_update (seq, new_stmt); return res; } else { - if (!seq) - return NULL_TREE; - tree decl = builtin_decl_implicit (rcode); - if (!decl) - return NULL_TREE; - /* Play safe and do not allow abnormals to be mentioned in - newly created statements. */ - unsigned nargs; - for (nargs = 0; nargs < 3; ++nargs) + gcc_assert (num_ops != 0); + combined_fn fn = res_op->code; + gcall *new_stmt = NULL; + if (internal_fn_p (fn)) { - if (!ops[nargs]) - break; - if (TREE_CODE (ops[nargs]) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ops[nargs])) + /* Generate the given function if we can. */ + internal_fn ifn = as_internal_fn (fn); + new_stmt = build_call_internal (ifn, res_op); + if (!new_stmt) return NULL_TREE; } - gcc_assert (nargs != 0); + else + { + /* Find the function we want to call. */ + tree decl = builtin_decl_implicit (as_builtin_fn (fn)); + if (!decl) + return NULL; + + /* We can't and should not emit calls to non-const functions. */ + if (!(flags_from_decl_or_type (decl) & ECF_CONST)) + return NULL; + + new_stmt = gimple_build_call (decl, num_ops, + res_op->op_or_null (0), + res_op->op_or_null (1), + res_op->op_or_null (2), + res_op->op_or_null (3), + res_op->op_or_null (4)); + } if (!res) - res = make_ssa_name (type); - gimple new_stmt = gimple_build_call (decl, nargs, ops[0], ops[1], ops[2]); + { + if (gimple_in_ssa_p (cfun)) + res = make_ssa_name (res_op->type); + else + res = create_tmp_reg (res_op->type); + } gimple_call_set_lhs (new_stmt, res); gimple_seq_add_stmt_without_update (seq, new_stmt); return res; @@ -384,12 +619,10 @@ gimple_simplify (enum tree_code code, tree type, return res; } - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - code, type, op0)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, code, type, op0)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } /* Binary ops. */ @@ -411,19 +644,17 @@ gimple_simplify (enum tree_code code, tree type, generation. */ if ((commutative_tree_code (code) || TREE_CODE_CLASS (code) == tcc_comparison) - && tree_swap_operands_p (op0, op1, false)) + && tree_swap_operands_p (op0, op1)) { std::swap (op0, op1); if (TREE_CODE_CLASS (code) == tcc_comparison) code = swap_tree_comparison (code); } - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - code, type, op0, op1)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, code, type, op0, op1)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } /* Ternary ops. */ @@ -445,89 +676,60 @@ gimple_simplify (enum tree_code code, tree type, /* Canonicalize operand order both for matching and fallback stmt generation. */ if (commutative_ternary_tree_code (code) - && tree_swap_operands_p (op0, op1, false)) + && tree_swap_operands_p (op0, op1)) std::swap (op0, op1); - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - code, type, op0, op1, op2)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, code, type, op0, op1, op2)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } -/* Builtin function with one argument. */ +/* Builtin or internal function with one argument. */ tree -gimple_simplify (enum built_in_function fn, tree type, +gimple_simplify (combined_fn fn, tree type, tree arg0, gimple_seq *seq, tree (*valueize)(tree)) { if (constant_for_folding (arg0)) { - tree decl = builtin_decl_implicit (fn); - if (decl) - { - tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, &arg0, 1, false); - if (res) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (res); - res = fold_convert (type, res); - if (CONSTANT_CLASS_P (res)) - return res; - } - } + tree res = fold_const_call (fn, type, arg0); + if (res && CONSTANT_CLASS_P (res)) + return res; } - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - fn, type, arg0)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, fn, type, arg0)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } -/* Builtin function with two arguments. */ +/* Builtin or internal function with two arguments. */ tree -gimple_simplify (enum built_in_function fn, tree type, +gimple_simplify (combined_fn fn, tree type, tree arg0, tree arg1, gimple_seq *seq, tree (*valueize)(tree)) { if (constant_for_folding (arg0) && constant_for_folding (arg1)) { - tree decl = builtin_decl_implicit (fn); - if (decl) - { - tree args[2]; - args[0] = arg0; - args[1] = arg1; - tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, args, 2, false); - if (res) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (res); - res = fold_convert (type, res); - if (CONSTANT_CLASS_P (res)) - return res; - } - } + tree res = fold_const_call (fn, type, arg0, arg1); + if (res && CONSTANT_CLASS_P (res)) + return res; } - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - fn, type, arg0, arg1)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, fn, type, arg0, arg1)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } -/* Builtin function with three arguments. */ +/* Builtin or internal function with three arguments. */ tree -gimple_simplify (enum built_in_function fn, tree type, +gimple_simplify (combined_fn fn, tree type, tree arg0, tree arg1, tree arg2, gimple_seq *seq, tree (*valueize)(tree)) { @@ -535,31 +737,15 @@ gimple_simplify (enum built_in_function fn, tree type, && constant_for_folding (arg1) && constant_for_folding (arg2)) { - tree decl = builtin_decl_implicit (fn); - if (decl) - { - tree args[3]; - args[0] = arg0; - args[1] = arg1; - args[2] = arg2; - tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, args, 3, false); - if (res) - { - /* fold_builtin_n wraps the result inside a NOP_EXPR. */ - STRIP_NOPS (res); - res = fold_convert (type, res); - if (CONSTANT_CLASS_P (res)) - return res; - } - } + tree res = fold_const_call (fn, type, arg0, arg1, arg2); + if (res && CONSTANT_CLASS_P (res)) + return res; } - code_helper rcode; - tree ops[3] = {}; - if (!gimple_simplify (&rcode, ops, seq, valueize, - fn, type, arg0, arg1, arg2)) + gimple_match_op res_op; + if (!gimple_simplify (&res_op, seq, valueize, fn, type, arg0, arg1, arg2)) return NULL_TREE; - return maybe_push_res_to_seq (rcode, type, ops, seq); + return maybe_push_res_to_seq (&res_op, seq); } /* Helper for gimple_simplify valueizing OP using VALUEIZE and setting @@ -580,13 +766,67 @@ 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)) +{ + code_helper op; + tree_code code = conditional_internal_fn_code (ifn); + if (code != ERROR_MARK) + op = code; + else + { + ifn = get_unconditional_internal_fn (ifn); + if (ifn == IFN_LAST) + return false; + op = as_combined_fn (ifn); + } + + 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]), + op, 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; + case 3: + if (!gimple_resimplify3 (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. */ bool -gimple_simplify (gimple stmt, - code_helper *rcode, tree *ops, - gimple_seq *seq, +gimple_simplify (gimple *stmt, gimple_match_op *res_op, gimple_seq *seq, tree (*valueize)(tree), tree (*top_valueize)(tree)) { switch (gimple_code (stmt)) @@ -605,9 +845,8 @@ gimple_simplify (gimple stmt, tree op0 = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0); bool valueized = false; op0 = do_valueize (op0, top_valueize, valueized); - *rcode = code; - ops[0] = op0; - return (gimple_resimplify1 (seq, rcode, type, ops, valueize) + res_op->set_op (code, type, op0); + return (gimple_resimplify1 (seq, res_op, valueize) || valueized); } else if (code == BIT_FIELD_REF) @@ -616,11 +855,13 @@ gimple_simplify (gimple stmt, tree op0 = TREE_OPERAND (rhs1, 0); bool valueized = false; op0 = do_valueize (op0, top_valueize, valueized); - *rcode = code; - ops[0] = op0; - ops[1] = TREE_OPERAND (rhs1, 1); - ops[2] = TREE_OPERAND (rhs1, 2); - return (gimple_resimplify3 (seq, rcode, type, ops, valueize) + res_op->set_op (code, type, op0, + TREE_OPERAND (rhs1, 1), + TREE_OPERAND (rhs1, 2), + REF_REVERSE_STORAGE_ORDER (rhs1)); + if (res_op->reverse) + return valueized; + return (gimple_resimplify3 (seq, res_op, valueize) || valueized); } else if (code == SSA_NAME @@ -630,8 +871,7 @@ gimple_simplify (gimple stmt, tree valueized = top_valueize (op0); if (!valueized || op0 == valueized) return false; - ops[0] = valueized; - *rcode = TREE_CODE (op0); + res_op->set_op (TREE_CODE (op0), type, valueized); return true; } break; @@ -640,9 +880,8 @@ gimple_simplify (gimple stmt, tree rhs1 = gimple_assign_rhs1 (stmt); bool valueized = false; rhs1 = do_valueize (rhs1, top_valueize, valueized); - *rcode = code; - ops[0] = rhs1; - return (gimple_resimplify1 (seq, rcode, type, ops, valueize) + res_op->set_op (code, type, rhs1); + return (gimple_resimplify1 (seq, res_op, valueize) || valueized); } case GIMPLE_BINARY_RHS: @@ -652,10 +891,8 @@ gimple_simplify (gimple stmt, bool valueized = false; rhs1 = do_valueize (rhs1, top_valueize, valueized); rhs2 = do_valueize (rhs2, top_valueize, valueized); - *rcode = code; - ops[0] = rhs1; - ops[1] = rhs2; - return (gimple_resimplify2 (seq, rcode, type, ops, valueize) + res_op->set_op (code, type, rhs1, rhs2); + return (gimple_resimplify2 (seq, res_op, valueize) || valueized); } case GIMPLE_TERNARY_RHS: @@ -673,23 +910,21 @@ gimple_simplify (gimple stmt, tree rhs = TREE_OPERAND (rhs1, 1); lhs = do_valueize (lhs, top_valueize, valueized); rhs = do_valueize (rhs, top_valueize, valueized); - code_helper rcode2 = TREE_CODE (rhs1); - tree ops2[3] = {}; - ops2[0] = lhs; - ops2[1] = rhs; - if ((gimple_resimplify2 (seq, &rcode2, TREE_TYPE (rhs1), - ops2, valueize) + gimple_match_op res_op2 (res_op->cond, TREE_CODE (rhs1), + TREE_TYPE (rhs1), lhs, rhs); + if ((gimple_resimplify2 (seq, &res_op2, valueize) || valueized) - && rcode2.is_tree_code ()) + && res_op2.code.is_tree_code ()) { valueized = true; - if (TREE_CODE_CLASS ((enum tree_code)rcode2) + if (TREE_CODE_CLASS ((enum tree_code) res_op2.code) == tcc_comparison) - rhs1 = build2 (rcode2, TREE_TYPE (rhs1), - ops2[0], ops2[1]); - else if (rcode2 == SSA_NAME - || rcode2 == INTEGER_CST) - rhs1 = ops2[0]; + rhs1 = build2 (res_op2.code, TREE_TYPE (rhs1), + res_op2.ops[0], res_op2.ops[1]); + else if (res_op2.code == SSA_NAME + || res_op2.code == INTEGER_CST + || res_op2.code == VECTOR_CST) + rhs1 = res_op2.ops[0]; else valueized = false; } @@ -700,11 +935,8 @@ gimple_simplify (gimple stmt, rhs1 = do_valueize (rhs1, top_valueize, valueized); rhs2 = do_valueize (rhs2, top_valueize, valueized); rhs3 = do_valueize (rhs3, top_valueize, valueized); - *rcode = code; - ops[0] = rhs1; - ops[1] = rhs2; - ops[2] = rhs3; - return (gimple_resimplify3 (seq, rcode, type, ops, valueize) + res_op->set_op (code, type, rhs1, rhs2, rhs3); + return (gimple_resimplify3 (seq, res_op, valueize) || valueized); } default: @@ -717,41 +949,58 @@ gimple_simplify (gimple stmt, /* ??? This way we can't simplify calls with side-effects. */ if (gimple_call_lhs (stmt) != NULL_TREE && gimple_call_num_args (stmt) >= 1 - && gimple_call_num_args (stmt) <= 3) + && gimple_call_num_args (stmt) <= 5) { - tree fn = gimple_call_fn (stmt); - /* ??? Internal function support missing. */ - if (!fn) - return false; bool valueized = false; - fn = do_valueize (fn, top_valueize, valueized); - if (TREE_CODE (fn) != ADDR_EXPR - || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) - return false; - - tree decl = TREE_OPERAND (fn, 0); - if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL - || !builtin_decl_implicit (DECL_FUNCTION_CODE (decl)) - || !gimple_builtin_call_types_compatible_p (stmt, decl)) - return false; - - tree type = TREE_TYPE (gimple_call_lhs (stmt)); - *rcode = DECL_FUNCTION_CODE (decl); - for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i) + combined_fn cfn; + if (gimple_call_internal_p (stmt)) + cfn = as_combined_fn (gimple_call_internal_fn (stmt)); + else + { + tree fn = gimple_call_fn (stmt); + if (!fn) + return false; + + fn = do_valueize (fn, top_valueize, valueized); + if (TREE_CODE (fn) != ADDR_EXPR + || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) + return false; + + tree decl = TREE_OPERAND (fn, 0); + if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL + || !gimple_builtin_call_types_compatible_p (stmt, decl)) + return false; + + cfn = as_combined_fn (DECL_FUNCTION_CODE (decl)); + } + + unsigned int num_args = gimple_call_num_args (stmt); + res_op->set_op (cfn, TREE_TYPE (gimple_call_lhs (stmt)), num_args); + for (unsigned i = 0; i < num_args; ++i) { tree arg = gimple_call_arg (stmt, i); - ops[i] = do_valueize (arg, top_valueize, valueized); + res_op->ops[i] = do_valueize (arg, top_valueize, valueized); } - switch (gimple_call_num_args (stmt)) + if (internal_fn_p (cfn) + && try_conditional_simplification (as_internal_fn (cfn), + res_op, seq, valueize)) + return true; + switch (num_args) { case 1: - return (gimple_resimplify1 (seq, rcode, type, ops, valueize) + return (gimple_resimplify1 (seq, res_op, valueize) || valueized); case 2: - return (gimple_resimplify2 (seq, rcode, type, ops, valueize) + return (gimple_resimplify2 (seq, res_op, valueize) || valueized); case 3: - return (gimple_resimplify3 (seq, rcode, type, ops, valueize) + return (gimple_resimplify3 (seq, res_op, valueize) + || valueized); + case 4: + return (gimple_resimplify4 (seq, res_op, valueize) + || valueized); + case 5: + return (gimple_resimplify5 (seq, res_op, valueize) || valueized); default: gcc_unreachable (); @@ -766,11 +1015,8 @@ gimple_simplify (gimple stmt, bool valueized = false; lhs = do_valueize (lhs, top_valueize, valueized); rhs = do_valueize (rhs, top_valueize, valueized); - *rcode = gimple_cond_code (stmt); - ops[0] = lhs; - ops[1] = rhs; - return (gimple_resimplify2 (seq, rcode, - boolean_type_node, ops, valueize) + res_op->set_op (gimple_cond_code (stmt), boolean_type_node, lhs, rhs); + return (gimple_resimplify2 (seq, res_op, valueize) || valueized); } @@ -788,10 +1034,25 @@ inline tree do_valueize (tree (*valueize)(tree), tree op) { if (valueize && TREE_CODE (op) == SSA_NAME) - return valueize (op); + { + tree tem = valueize (op); + if (tem) + return tem; + } return op; } +/* Helper for the autogenerated code, get at the definition of NAME when + VALUEIZE allows that. */ + +inline gimple * +get_def (tree (*valueize)(tree), tree name) +{ + if (valueize && ! valueize (name)) + return NULL; + return SSA_NAME_DEF_STMT (name); +} + /* Routine to determine if the types T1 and T2 are effectively the same for GIMPLE. If T1 or T2 is not a type, the test applies to their TREE_TYPE. */ @@ -816,3 +1077,113 @@ single_use (tree t) { return TREE_CODE (t) != SSA_NAME || has_zero_uses (t) || has_single_use (t); } + +/* Return true if math operations should be canonicalized, + e.g. sqrt(sqrt(x)) -> pow(x, 0.25). */ + +static inline bool +canonicalize_math_p () +{ + return !cfun || (cfun->curr_properties & PROP_gimple_opt_math) == 0; +} + +/* Return true if math operations that are beneficial only after + vectorization should be canonicalized. */ + +static inline bool +canonicalize_math_after_vectorization_p () +{ + return !cfun || (cfun->curr_properties & PROP_gimple_lvec) != 0; +} + +/* Return true if pow(cst, x) should be optimized into exp(log(cst) * x). + As a workaround for SPEC CPU2017 628.pop2_s, don't do it if arg0 + is an exact integer, arg1 = phi_res +/- cst1 and phi_res = PHI + where cst2 +/- cst1 is an exact integer, because then pow (arg0, arg1) + will likely be exact, while exp (log (arg0) * arg1) might be not. + Also don't do it if arg1 is phi_res above and cst2 is an exact integer. */ + +static bool +optimize_pow_to_exp (tree arg0, tree arg1) +{ + gcc_assert (TREE_CODE (arg0) == REAL_CST); + if (!real_isinteger (TREE_REAL_CST_PTR (arg0), TYPE_MODE (TREE_TYPE (arg0)))) + return true; + + if (TREE_CODE (arg1) != SSA_NAME) + return true; + + gimple *def = SSA_NAME_DEF_STMT (arg1); + gphi *phi = dyn_cast (def); + tree cst1 = NULL_TREE; + enum tree_code code = ERROR_MARK; + if (!phi) + { + if (!is_gimple_assign (def)) + return true; + code = gimple_assign_rhs_code (def); + switch (code) + { + case PLUS_EXPR: + case MINUS_EXPR: + break; + default: + return true; + } + if (TREE_CODE (gimple_assign_rhs1 (def)) != SSA_NAME + || TREE_CODE (gimple_assign_rhs2 (def)) != REAL_CST) + return true; + + cst1 = gimple_assign_rhs2 (def); + + phi = dyn_cast (SSA_NAME_DEF_STMT (gimple_assign_rhs1 (def))); + if (!phi) + return true; + } + + tree cst2 = NULL_TREE; + int n = gimple_phi_num_args (phi); + for (int i = 0; i < n; i++) + { + tree arg = PHI_ARG_DEF (phi, i); + if (TREE_CODE (arg) != REAL_CST) + continue; + else if (cst2 == NULL_TREE) + cst2 = arg; + else if (!operand_equal_p (cst2, arg, 0)) + return true; + } + + if (cst1 && cst2) + cst2 = const_binop (code, TREE_TYPE (cst2), cst2, cst1); + if (cst2 + && TREE_CODE (cst2) == REAL_CST + && real_isinteger (TREE_REAL_CST_PTR (cst2), + TYPE_MODE (TREE_TYPE (cst2)))) + return false; + return true; +} + +/* Return true if a division INNER_DIV / DIVISOR where INNER_DIV + is another division can be optimized. Don't optimize if INNER_DIV + is used in a TRUNC_MOD_EXPR with DIVISOR as second operand. */ + +static bool +optimize_successive_divisions_p (tree divisor, tree inner_div) +{ + if (!gimple_in_ssa_p (cfun)) + return false; + + imm_use_iterator imm_iter; + use_operand_p use_p; + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, inner_div) + { + gimple *use_stmt = USE_STMT (use_p); + if (!is_gimple_assign (use_stmt) + || gimple_assign_rhs_code (use_stmt) != TRUNC_MOD_EXPR + || !operand_equal_p (gimple_assign_rhs2 (use_stmt), divisor, 0)) + continue; + return false; + } + return true; +}