From e0ee10ed5af17d90ea7621d4270a50284ad76c45 Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Fri, 24 Oct 2014 11:00:08 +0000 Subject: [PATCH] genmatch.c (expr::gen_transform): Use fold_buildN_loc and build_call_expr_loc. 2014-10-24 Richard Biener * genmatch.c (expr::gen_transform): Use fold_buildN_loc and build_call_expr_loc. (dt_simplify::gen): Drop non_lvalue for GIMPLE, use non_lvalue_loc to build it for GENERIC. (decision_tree::gen_generic): Add location argument to generic_simplify prototype. (capture_info): New class. (capture_info::capture_info): New constructor. (capture_info::walk_match): New method. (capture_info::walk_result): New method. (capture_info::walk_c_expr): New method. (dt_simplify::gen): Handle preserving side-effects for GENERIC code generation. (decision_tree::gen_generic): Do not reject operands with TREE_SIDE_EFFECTS. * generic-match.h: New file. * generic-match-head.c: Include generic-match.h, not gimple-match.h. * match.pd: Add some constant folding patterns from fold-const.c. * fold-const.c: Include generic-match.h. (fold_unary_loc): Dispatch to generic_simplify. (fold_ternary_loc): Likewise. (fold_binary_loc): Likewise. Remove patterns now implemented by generic_simplify. * gimple-fold.c (replace_stmt_with_simplification): New function. (fold_stmt_1): Add valueize parameter, dispatch to gimple_simplify. (no_follow_ssa_edges): New function. (fold_stmt): New overload with valueization hook. Use no_follow_ssa_edges for the overload without hook. (fold_stmt_inplace): Likewise. * gimple-fold.h (no_follow_ssa_edges): Declare. From-SVN: r216631 --- gcc/ChangeLog | 33 +++++ gcc/fold-const.c | 63 ++------- gcc/generic-match-head.c | 2 +- gcc/generic-match.h | 33 +++++ gcc/genmatch.c | 295 ++++++++++++++++++++++++++++++++++----- gcc/gimple-fold.c | 156 ++++++++++++++++++++- gcc/gimple-fold.h | 2 + gcc/match.pd | 60 ++++++++ 8 files changed, 552 insertions(+), 92 deletions(-) create mode 100644 gcc/generic-match.h diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 73884885687..b72f4cb663d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,36 @@ +2014-10-24 Richard Biener + + * genmatch.c (expr::gen_transform): Use fold_buildN_loc + and build_call_expr_loc. + (dt_simplify::gen): Drop non_lvalue for GIMPLE, use + non_lvalue_loc to build it for GENERIC. + (decision_tree::gen_generic): Add location argument to + generic_simplify prototype. + (capture_info): New class. + (capture_info::capture_info): New constructor. + (capture_info::walk_match): New method. + (capture_info::walk_result): New method. + (capture_info::walk_c_expr): New method. + (dt_simplify::gen): Handle preserving side-effects for + GENERIC code generation. + (decision_tree::gen_generic): Do not reject operands + with TREE_SIDE_EFFECTS. + * generic-match.h: New file. + * generic-match-head.c: Include generic-match.h, not gimple-match.h. + * match.pd: Add some constant folding patterns from fold-const.c. + * fold-const.c: Include generic-match.h. + (fold_unary_loc): Dispatch to generic_simplify. + (fold_ternary_loc): Likewise. + (fold_binary_loc): Likewise. Remove patterns now implemented + by generic_simplify. + * gimple-fold.c (replace_stmt_with_simplification): New function. + (fold_stmt_1): Add valueize parameter, dispatch to gimple_simplify. + (no_follow_ssa_edges): New function. + (fold_stmt): New overload with valueization hook. Use + no_follow_ssa_edges for the overload without hook. + (fold_stmt_inplace): Likewise. + * gimple-fold.h (no_follow_ssa_edges): Declare. + 2014-10-24 Felix Yang Jiji Jiang diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 40317f3ace9..8c4971cd470 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -70,6 +70,7 @@ along with GCC; see the file COPYING3. If not see #include "hash-table.h" /* Required for ENABLE_FOLD_CHECKING. */ #include "builtins.h" #include "cgraph.h" +#include "generic-match.h" /* Nonzero if we are folding constants inside an initializer; zero otherwise. */ @@ -7564,6 +7565,10 @@ fold_unary_loc (location_t loc, enum tree_code code, tree type, tree op0) gcc_assert (IS_EXPR_CODE_CLASS (kind) && TREE_CODE_LENGTH (code) == 1); + tem = generic_simplify (loc, code, type, op0); + if (tem) + return tem; + arg0 = op0; if (arg0) { @@ -9913,6 +9918,10 @@ fold_binary_loc (location_t loc, && tree_swap_operands_p (arg0, arg1, true)) return fold_build2_loc (loc, swap_tree_comparison (code), type, op1, op0); + tem = generic_simplify (loc, code, type, op0, op1); + if (tem) + return tem; + /* ARG0 is the first operand of EXPR, and ARG1 is the second operand. First check for cases where an arithmetic operation is applied to a @@ -10035,10 +10044,6 @@ fold_binary_loc (location_t loc, if (integer_zerop (arg0)) return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg1)); - /* PTR +p 0 -> PTR */ - if (integer_zerop (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); - /* INT +p INT -> (PTR)(INT + INT). Stripping types allows for this. */ if (INTEGRAL_TYPE_P (TREE_TYPE (arg1)) && INTEGRAL_TYPE_P (TREE_TYPE (arg0))) @@ -10159,9 +10164,6 @@ fold_binary_loc (location_t loc, if (! FLOAT_TYPE_P (type)) { - if (integer_zerop (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); - /* If we are adding two BIT_AND_EXPR's, both of which are and'ing with a constant, and the two constants have no bits in common, we should treat this as a BIT_IOR_EXPR since this may produce more @@ -10648,8 +10650,6 @@ fold_binary_loc (location_t loc, { if (integer_zerop (arg0)) return negate_expr (fold_convert_loc (loc, type, arg1)); - if (integer_zerop (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); /* Fold A - (A & B) into ~B & A. */ if (!TREE_SIDE_EFFECTS (arg0) @@ -10742,16 +10742,6 @@ fold_binary_loc (location_t loc, } } - /* Fold &x - &x. This can happen from &x.foo - &x. - This is unsafe for certain floats even in non-IEEE formats. - In IEEE, it is unsafe because it does wrong for NaNs. - Also note that operand_equal_p is always false if an operand - is volatile. */ - - if ((!FLOAT_TYPE_P (type) || !HONOR_NANS (TYPE_MODE (type))) - && operand_equal_p (arg0, arg1, 0)) - return omit_one_operand_loc (loc, type, build_zero_cst (type), arg0); - /* A - B -> A + (-B) if B is easily negatable. */ if (negate_expr_p (arg1) && ((FLOAT_TYPE_P (type) @@ -10829,10 +10819,6 @@ fold_binary_loc (location_t loc, if (! FLOAT_TYPE_P (type)) { - if (integer_zerop (arg1)) - return omit_one_operand_loc (loc, type, arg1, arg0); - if (integer_onep (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); /* Transform x * -1 into -x. Make sure to do the negation on the original operand with conversions not stripped because we can only strip non-sign-changing conversions. */ @@ -11129,10 +11115,6 @@ fold_binary_loc (location_t loc, case BIT_IOR_EXPR: bit_ior: - if (integer_all_onesp (arg1)) - return omit_one_operand_loc (loc, type, arg1, arg0); - if (integer_zerop (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); if (operand_equal_p (arg0, arg1, 0)) return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); @@ -11271,12 +11253,8 @@ fold_binary_loc (location_t loc, goto bit_rotate; case BIT_XOR_EXPR: - if (integer_zerop (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); if (integer_all_onesp (arg1)) return fold_build1_loc (loc, BIT_NOT_EXPR, type, op0); - if (operand_equal_p (arg0, arg1, 0)) - return omit_one_operand_loc (loc, type, integer_zero_node, arg0); /* ~X ^ X is -1. */ if (TREE_CODE (arg0) == BIT_NOT_EXPR @@ -11434,8 +11412,6 @@ fold_binary_loc (location_t loc, case BIT_AND_EXPR: if (integer_all_onesp (arg1)) return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); - if (integer_zerop (arg1)) - return omit_one_operand_loc (loc, type, arg1, arg0); if (operand_equal_p (arg0, arg1, 0)) return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); @@ -12186,8 +12162,6 @@ fold_binary_loc (location_t loc, case ROUND_DIV_EXPR: case CEIL_DIV_EXPR: case EXACT_DIV_EXPR: - if (integer_onep (arg1)) - return non_lvalue_loc (loc, fold_convert_loc (loc, type, arg0)); if (integer_zerop (arg1)) return NULL_TREE; /* X / -1 is -X. */ @@ -12257,21 +12231,6 @@ fold_binary_loc (location_t loc, case FLOOR_MOD_EXPR: case ROUND_MOD_EXPR: case TRUNC_MOD_EXPR: - /* X % 1 is always zero, but be sure to preserve any side - effects in X. */ - if (integer_onep (arg1)) - return omit_one_operand_loc (loc, type, integer_zero_node, arg0); - - /* X % 0, return X % 0 unchanged so that we can get the - proper warnings and errors. */ - if (integer_zerop (arg1)) - return NULL_TREE; - - /* 0 % X is always zero, but be sure to preserve any side - effects in X. Place this after checking for X == 0. */ - if (integer_zerop (arg0)) - return omit_one_operand_loc (loc, type, integer_zero_node, arg1); - /* X % -1 is zero. */ if (!TYPE_UNSIGNED (type) && TREE_CODE (arg1) == INTEGER_CST @@ -13804,6 +13763,10 @@ fold_ternary_loc (location_t loc, enum tree_code code, tree type, && tree_swap_operands_p (op0, op1, true)) return fold_build3_loc (loc, code, type, op1, op0, op2); + tem = generic_simplify (loc, code, type, op0, op1, op2); + if (tem) + return tem; + /* Strip any conversions that don't change the mode. This is safe for every expression, except for a comparison expression because its signedness is derived from its operands. So, in the latter diff --git a/gcc/generic-match-head.c b/gcc/generic-match-head.c index 245f3ede456..0ed7e1d0c99 100644 --- a/gcc/generic-match-head.c +++ b/gcc/generic-match-head.c @@ -43,6 +43,6 @@ along with GCC; see the file COPYING3. If not see #include "tree-phinodes.h" #include "ssa-iterators.h" #include "dumpfile.h" -#include "gimple-match.h" +#include "generic-match.h" diff --git a/gcc/generic-match.h b/gcc/generic-match.h new file mode 100644 index 00000000000..180886068a9 --- /dev/null +++ b/gcc/generic-match.h @@ -0,0 +1,33 @@ +/* Generic simplify definitions. + + Copyright (C) 2011-2014 Free Software Foundation, Inc. + Contributed by Richard Guenther + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_GENERIC_MATCH_H +#define GCC_GENERIC_MATCH_H + +/* Note the following functions are supposed to be only used from + fold_unary_loc, fold_binary_loc and fold_ternary_loc respectively. + They are not considered a public API. */ + +tree generic_simplify (location_t, enum tree_code, tree, tree); +tree generic_simplify (location_t, enum tree_code, tree, tree, tree); +tree generic_simplify (location_t, enum tree_code, tree, tree, tree, tree); + +#endif /* GCC_GENERIC_MATCH_H */ diff --git a/gcc/genmatch.c b/gcc/genmatch.c index 2def7fa58cd..ebdb7d35859 100644 --- a/gcc/genmatch.c +++ b/gcc/genmatch.c @@ -1374,10 +1374,11 @@ expr::gen_transform (FILE *f, const char *dest, bool gimple, int depth, else { if (operation->kind == id_base::CODE) - fprintf (f, " res = fold_build%d (%s, %s", + fprintf (f, " res = fold_build%d_loc (loc, %s, %s", ops.length(), operation->id, type); else - fprintf (f, " res = build_call_expr (builtin_decl_implicit (%s), %d", + fprintf (f, " res = build_call_expr_loc (loc, " + "builtin_decl_implicit (%s), %d", operation->id, ops.length()); for (unsigned i = 0; i < ops.length (); ++i) fprintf (f, ", ops%d[%u]", depth, i); @@ -1860,6 +1861,167 @@ dt_operand::gen (FILE *f, bool gimple) fprintf (f, "}\n"); } + +/* For GENERIC we have to take care of wrapping multiple-used + expressions with side-effects in save_expr and preserve side-effects + of expressions with omit_one_operand. Analyze captures in + match, result and with expressions and perform early-outs + on the outermost match expression operands for cases we cannot + handle. */ + +struct capture_info +{ + capture_info (simplify *s); + void walk_match (operand *o, unsigned toplevel_arg, bool); + void walk_result (operand *o, bool); + void walk_c_expr (c_expr *); + + struct cinfo + { + bool expr_p; + bool cse_p; + bool force_no_side_effects_p; + unsigned long toplevel_msk; + int result_use_count; + }; + + auto_vec info; + unsigned long force_no_side_effects; +}; + +/* Analyze captures in S. */ + +capture_info::capture_info (simplify *s) +{ + expr *e; + if (!s->result + || ((e = dyn_cast (s->result)) + && is_a (e->operation))) + { + force_no_side_effects = -1; + return; + } + + force_no_side_effects = 0; + info.safe_grow_cleared (s->capture_max + 1); + e = as_a (s->match); + for (unsigned i = 0; i < e->ops.length (); ++i) + walk_match (e->ops[i], i, false); + + walk_result (s->result, false); + + for (unsigned i = 0; i < s->ifexpr_vec.length (); ++i) + if (s->ifexpr_vec[i].is_with) + walk_c_expr (as_a (s->ifexpr_vec[i].cexpr)); +} + +/* Analyze captures in the match expression piece O. */ + +void +capture_info::walk_match (operand *o, unsigned toplevel_arg, bool conditional_p) +{ + if (capture *c = dyn_cast (o)) + { + info[c->where].toplevel_msk |= 1 << toplevel_arg; + info[c->where].force_no_side_effects_p |= conditional_p; + /* Mark expr (non-leaf) captures and recurse. */ + if (c->what + && is_a (c->what)) + { + info[c->where].expr_p = true; + walk_match (c->what, toplevel_arg, conditional_p); + } + } + else if (expr *e = dyn_cast (o)) + { + for (unsigned i = 0; i < e->ops.length (); ++i) + { + bool cond_p = conditional_p; + if (i == 0 + && *e->operation == COND_EXPR) + cond_p = true; + else if (*e->operation == TRUTH_ANDIF_EXPR + || *e->operation == TRUTH_ORIF_EXPR) + cond_p = true; + walk_match (e->ops[i], toplevel_arg, cond_p); + } + } + else if (is_a (o)) + { + /* Mark non-captured leafs toplevel arg for checking. */ + force_no_side_effects |= 1 << toplevel_arg; + } + else + gcc_unreachable (); +} + +/* Analyze captures in the result expression piece O. */ + +void +capture_info::walk_result (operand *o, bool conditional_p) +{ + if (capture *c = dyn_cast (o)) + { + info[c->where].result_use_count++; + /* If we substitute an expression capture we don't know + which captures this will end up using (well, we don't + compute that). Force the uses to be side-effect free + which means forcing the toplevels that reach the + expression side-effect free. */ + if (info[c->where].expr_p) + force_no_side_effects |= info[c->where].toplevel_msk; + /* Mark CSE capture capture uses as forced to have + no side-effects. */ + if (c->what + && is_a (c->what)) + { + info[c->where].cse_p = true; + walk_result (c->what, true); + } + } + else if (expr *e = dyn_cast (o)) + { + for (unsigned i = 0; i < e->ops.length (); ++i) + { + bool cond_p = conditional_p; + if (i == 0 + && *e->operation == COND_EXPR) + cond_p = true; + else if (*e->operation == TRUTH_ANDIF_EXPR + || *e->operation == TRUTH_ORIF_EXPR) + cond_p = true; + walk_result (e->ops[i], cond_p); + } + } + else if (c_expr *e = dyn_cast (o)) + walk_c_expr (e); + else + gcc_unreachable (); +} + +/* Look for captures in the C expr E. */ + +void +capture_info::walk_c_expr (c_expr *e) +{ + /* Give up for C exprs mentioning captures. */ + for (unsigned i = 0; i < e->code.length (); ++i) + if (e->code[i].type == CPP_ATSIGN + && (e->code[i+1].type == CPP_NUMBER + || e->code[i+1].type == CPP_NAME) + && !(e->code[i+1].flags & PREV_WHITE)) + { + const cpp_token *n = &e->code[i+1]; + const char *id; + if (n->type == CPP_NUMBER) + id = (const char *)n->val.str.text; + else + id = (const char *)CPP_HASHNODE (n->val.node.node)->ident.str; + info[(*e->capture_ids)[id]].force_no_side_effects_p = true; + } +} + + /* Generate code for the '(if ...)', '(with ..)' and actual transform step of a '(simplify ...)' or '(match ...)'. This handles everything that is not part of the decision tree (simplify->match). */ @@ -1928,21 +2090,54 @@ dt_simplify::gen (FILE *f, bool gimple) n_braces++; } + /* Analyze captures and perform early-outs on the incoming arguments + that cover cases we cannot handle. */ + capture_info cinfo (s); + expr *e; + if (!gimple + && s->result + && !((e = dyn_cast (s->result)) + && is_a (e->operation))) + { + for (unsigned i = 0; i < as_a (s->match)->ops.length (); ++i) + if (cinfo.force_no_side_effects & (1 << i)) + fprintf (f, "if (TREE_SIDE_EFFECTS (op%d)) return NULL_TREE;\n", i); + for (int i = 0; i <= s->capture_max; ++i) + if (cinfo.info[i].cse_p) + ; + else if (cinfo.info[i].force_no_side_effects_p + && (cinfo.info[i].toplevel_msk + & cinfo.force_no_side_effects) == 0) + fprintf (f, "if (TREE_SIDE_EFFECTS (captures[%d])) " + "return NULL_TREE;\n", i); + else if ((cinfo.info[i].toplevel_msk + & cinfo.force_no_side_effects) != 0) + /* Mark capture as having no side-effects if we had to verify + that via forced toplevel operand checks. */ + cinfo.info[i].force_no_side_effects_p = true; + } + fprintf (f, "if (dump_file && (dump_flags & TDF_DETAILS)) " "fprintf (dump_file, \"Applying pattern "); output_line_directive (f, s->result_location, true); fprintf (f, ", %%s:%%d\\n\", __FILE__, __LINE__);\n"); - if (!s->result) + operand *result = s->result; + if (!result) { /* If there is no result then this is a predicate implementation. */ fprintf (f, "return true;\n"); } else if (gimple) { - if (s->result->type == operand::OP_EXPR) + /* For GIMPLE simply drop NON_LVALUE_EXPR (which only appears + in outermost position). */ + if (result->type == operand::OP_EXPR + && *as_a (result)->operation == NON_LVALUE_EXPR) + result = as_a (result)->ops[0]; + if (result->type == operand::OP_EXPR) { - expr *e = as_a (s->result); + expr *e = as_a (result); bool is_predicate = is_a (e->operation); if (!is_predicate) fprintf (f, "*res_code = %s;\n", e->operation->id); @@ -1964,10 +2159,10 @@ dt_simplify::gen (FILE *f, bool gimple) fprintf (f, "gimple_resimplify%d (seq, res_code, type, " "res_ops, valueize);\n", e->ops.length ()); } - else if (s->result->type == operand::OP_CAPTURE - || s->result->type == operand::OP_C_EXPR) + else if (result->type == operand::OP_CAPTURE + || result->type == operand::OP_C_EXPR) { - s->result->gen_transform (f, "res_ops[0]", true, 1, "type", indexes); + result->gen_transform (f, "res_ops[0]", true, 1, "type", indexes); fprintf (f, "*res_code = TREE_CODE (res_ops[0]);\n"); } else @@ -1976,10 +2171,22 @@ dt_simplify::gen (FILE *f, bool gimple) } else /* GENERIC */ { - if (s->result->type == operand::OP_EXPR) + bool is_predicate = false; + if (result->type == operand::OP_EXPR) { - expr *e = as_a (s->result); - bool is_predicate = is_a (e->operation); + expr *e = as_a (result); + is_predicate = is_a (e->operation); + /* Search for captures used multiple times in the result expression + and dependent on TREE_SIDE_EFFECTS emit a SAVE_EXPR. */ + if (!is_predicate) + for (int i = 0; i < s->capture_max + 1; ++i) + { + if (!cinfo.info[i].force_no_side_effects_p + && cinfo.info[i].result_use_count > 1) + fprintf (f, " if (TREE_SIDE_EFFECTS (captures[%d]))\n" + " captures[%d] = save_expr (captures[%d]);\n", + i, i, i); + } for (unsigned j = 0; j < e->ops.length (); ++j) { char dest[32]; @@ -1997,32 +2204,56 @@ dt_simplify::gen (FILE *f, bool gimple) ? NULL : "TREE_TYPE (res_op0)"); e->ops[j]->gen_transform (f, dest, false, 1, optype, indexes); } - if (is_a (e->operation)) + if (is_predicate) fprintf (f, "return true;\n"); else { - /* Re-fold the toplevel result. */ - if (e->operation->kind == id_base::CODE) - fprintf (f, " return fold_build%d (%s, type", - e->ops.length (), e->operation->id); + fprintf (f, " tree res;\n"); + /* Re-fold the toplevel result. Use non_lvalue to + build NON_LVALUE_EXPRs so they get properly + ignored when in GIMPLE form. */ + if (*e->operation == NON_LVALUE_EXPR) + fprintf (f, " res = non_lvalue_loc (loc, res_op0);\n"); else - fprintf (f, " return build_call_expr " - "(builtin_decl_implicit (%s), %d", - e->operation->id, e->ops.length()); - for (unsigned j = 0; j < e->ops.length (); ++j) - fprintf (f, ", res_op%d", j); - fprintf (f, ");\n"); + { + if (e->operation->kind == id_base::CODE) + fprintf (f, " res = fold_build%d_loc (loc, %s, type", + e->ops.length (), e->operation->id); + else + fprintf (f, " res = build_call_expr_loc " + "(loc, builtin_decl_implicit (%s), %d", + e->operation->id, e->ops.length()); + for (unsigned j = 0; j < e->ops.length (); ++j) + fprintf (f, ", res_op%d", j); + fprintf (f, ");\n"); + } } } - else if (s->result->type == operand::OP_CAPTURE - || s->result->type == operand::OP_C_EXPR) + else if (result->type == operand::OP_CAPTURE + || result->type == operand::OP_C_EXPR) + { fprintf (f, " tree res;\n"); s->result->gen_transform (f, " res", false, 1, "type", indexes); - fprintf (f, " return res;\n"); } else gcc_unreachable (); + if (!is_predicate) + { + /* Search for captures not used in the result expression and dependent + on TREE_SIDE_EFFECTS emit omit_one_operand. */ + for (int i = 0; i < s->capture_max + 1; ++i) + { + if (!cinfo.info[i].force_no_side_effects_p + && !cinfo.info[i].expr_p + && cinfo.info[i].result_use_count == 0) + fprintf (f, " if (TREE_SIDE_EFFECTS (captures[%d]))\n" + " res = build2_loc (loc, COMPOUND_EXPR, type," + " fold_ignored_result (captures[%d]), res);\n", + i, i); + } + fprintf (f, " return res;\n"); + } } for (unsigned i = 0; i < n_braces; ++i) @@ -2086,25 +2317,13 @@ decision_tree::gen_generic (FILE *f) for (unsigned n = 1; n <= 3; ++n) { fprintf (f, "\ntree\n" - "generic_simplify (enum tree_code code, " + "generic_simplify (location_t loc, enum tree_code code, " "tree type ATTRIBUTE_UNUSED"); for (unsigned i = 0; i < n; ++i) fprintf (f, ", tree op%d", i); fprintf (f, ")\n"); fprintf (f, "{\n"); - /* ??? For now reject all simplifications on operands with - side-effects as we are not prepared to properly wrap - omitted parts with omit_one_operand and friends. In - principle we can do that automagically for a subset of - transforms (and only reject the remaining cases). - This fixes for example gcc.c-torture/execute/20050131-1.c. */ - fprintf (f, "if ((op0 && TREE_SIDE_EFFECTS (op0))"); - for (unsigned i = 1; i < n; ++i) - fprintf (f, "|| (op%d && TREE_SIDE_EFFECTS (op%d))", i, i); - fprintf (f, ")\n" - " return NULL_TREE;\n"); - fprintf (f, "switch (code)\n" "{\n"); for (unsigned i = 0; i < root->kids.length (); i++) diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 5b47cbc37bb..b2627197b44 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -61,6 +61,8 @@ along with GCC; see the file COPYING3. If not see #include "dbgcnt.h" #include "builtins.h" #include "output.h" +#include "tree-eh.h" +#include "gimple-match.h" /* Return true when DECL can be referenced from current unit. FROM_DECL (if non-null) specify constructor of variable DECL was taken from. @@ -2792,6 +2794,121 @@ gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace) return changed; } + +/* Worker for fold_stmt_1 dispatch to pattern based folding with + gimple_simplify. + + Replaces *GSI with the simplification result in RCODE and OPS + and the associated statements in *SEQ. Does the replacement + according to INPLACE and returns true if the operation succeeded. */ + +static bool +replace_stmt_with_simplification (gimple_stmt_iterator *gsi, + code_helper rcode, tree *ops, + gimple_seq *seq, bool inplace) +{ + gimple stmt = gsi_stmt (*gsi); + + /* Play safe and do not allow abnormals to be mentioned in + newly created statements. See also maybe_push_res_to_seq. */ + 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]))) + return false; + + if (gimple_code (stmt) == GIMPLE_COND) + { + gcc_assert (rcode.is_tree_code ()); + if (TREE_CODE_CLASS ((enum tree_code)rcode) == tcc_comparison + /* GIMPLE_CONDs condition may not throw. */ + && (!flag_exceptions + || !cfun->can_throw_non_call_exceptions + || !operation_could_trap_p (rcode, + FLOAT_TYPE_P (TREE_TYPE (ops[0])), + false, NULL_TREE))) + gimple_cond_set_condition (stmt, rcode, ops[0], ops[1]); + else if (rcode == SSA_NAME) + gimple_cond_set_condition (stmt, NE_EXPR, ops[0], + build_zero_cst (TREE_TYPE (ops[0]))); + else if (rcode == INTEGER_CST) + { + if (integer_zerop (ops[0])) + gimple_cond_make_false (stmt); + else + gimple_cond_make_true (stmt); + } + else if (!inplace) + { + tree res = maybe_push_res_to_seq (rcode, boolean_type_node, + ops, seq); + if (!res) + return false; + gimple_cond_set_condition (stmt, NE_EXPR, res, + build_zero_cst (TREE_TYPE (res))); + } + else + return false; + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "gimple_simplified to "); + if (!gimple_seq_empty_p (*seq)) + print_gimple_seq (dump_file, *seq, 0, TDF_SLIM); + print_gimple_stmt (dump_file, gsi_stmt (*gsi), + 0, TDF_SLIM); + } + gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT); + return true; + } + else if (is_gimple_assign (stmt) + && rcode.is_tree_code ()) + { + if (!inplace + || gimple_num_ops (stmt) <= get_gimple_rhs_num_ops (rcode)) + { + maybe_build_generic_op (rcode, + TREE_TYPE (gimple_assign_lhs (stmt)), + &ops[0], ops[1], ops[2]); + gimple_assign_set_rhs_with_ops_1 (gsi, rcode, + ops[0], ops[1], ops[2]); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "gimple_simplified to "); + if (!gimple_seq_empty_p (*seq)) + print_gimple_seq (dump_file, *seq, 0, TDF_SLIM); + print_gimple_stmt (dump_file, gsi_stmt (*gsi), + 0, TDF_SLIM); + } + gsi_insert_seq_before (gsi, *seq, GSI_SAME_STMT); + return true; + } + } + else if (!inplace) + { + if (gimple_has_lhs (stmt)) + { + tree lhs = gimple_get_lhs (stmt); + maybe_push_res_to_seq (rcode, TREE_TYPE (lhs), + ops, seq, lhs); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "gimple_simplified to "); + print_gimple_seq (dump_file, *seq, 0, TDF_SLIM); + } + gsi_replace_with_seq_vops (gsi, *seq); + return true; + } + else + gcc_unreachable (); + } + + return false; +} + /* Canonicalize MEM_REFs invariant address operand after propagation. */ static bool @@ -2878,7 +2995,7 @@ maybe_canonicalize_mem_ref_addr (tree *t) distinguishes both cases. */ static bool -fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) +fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree)) { bool changed = false; gimple stmt = gsi_stmt (*gsi); @@ -2956,6 +3073,25 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) default:; } + /* Dispatch to pattern-based folding. */ + if (!inplace + || is_gimple_assign (stmt) + || gimple_code (stmt) == GIMPLE_COND) + { + gimple_seq seq = NULL; + code_helper rcode; + tree ops[3] = {}; + if (gimple_simplify (stmt, &rcode, ops, inplace ? NULL : &seq, valueize)) + { + if (replace_stmt_with_simplification (gsi, rcode, ops, &seq, inplace)) + changed = true; + else + gimple_seq_discard (seq); + } + } + + stmt = gsi_stmt (*gsi); + /* Fold the main computation performed by the statement. */ switch (gimple_code (stmt)) { @@ -3095,6 +3231,14 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) return changed; } +/* Valueziation callback that ends up not following SSA edges. */ + +tree +no_follow_ssa_edges (tree) +{ + return NULL_TREE; +} + /* Fold the statement pointed to by GSI. In some cases, this function may replace the whole statement with a new one. Returns true iff folding makes any changes. @@ -3105,7 +3249,13 @@ fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) bool fold_stmt (gimple_stmt_iterator *gsi) { - return fold_stmt_1 (gsi, false); + return fold_stmt_1 (gsi, false, no_follow_ssa_edges); +} + +bool +fold_stmt (gimple_stmt_iterator *gsi, tree (*valueize) (tree)) +{ + return fold_stmt_1 (gsi, false, valueize); } /* Perform the minimal folding on statement *GSI. Only operations like @@ -3120,7 +3270,7 @@ bool fold_stmt_inplace (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); - bool changed = fold_stmt_1 (gsi, true); + bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges); gcc_assert (gsi_stmt (*gsi) == stmt); return changed; } diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 93814cad0f1..39c53ff3916 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -26,11 +26,13 @@ extern tree canonicalize_constructor_val (tree, tree); extern tree get_symbol_constant_value (tree); extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); extern bool fold_stmt (gimple_stmt_iterator *); +extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree)); extern bool fold_stmt_inplace (gimple_stmt_iterator *); extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, enum tree_code, tree, tree); extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree, enum tree_code, tree, tree); +extern tree no_follow_ssa_edges (tree); extern tree gimple_fold_stmt_to_constant_1 (gimple, tree (*) (tree)); extern tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree)); extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree)); diff --git a/gcc/match.pd b/gcc/match.pd index 097dfcd2dfa..aa2afc267d7 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -28,3 +28,63 @@ along with GCC; see the file COPYING3. If not see integer_onep integer_zerop integer_all_onesp real_zerop real_onep CONSTANT_CLASS_P) + + +/* Simplifications of operations with one constant operand and + simplifications to constants. */ + +(for op (plus pointer_plus minus bit_ior bit_xor) + (simplify + (op @0 integer_zerop) + (non_lvalue @0))) + +/* Simplify x - x. + This is unsafe for certain floats even in non-IEEE formats. + In IEEE, it is unsafe because it does wrong for NaNs. + Also note that operand_equal_p is always false if an operand + is volatile. */ +(simplify + (minus @0 @0) + (if (!FLOAT_TYPE_P (type) || !HONOR_NANS (TYPE_MODE (type))) + { build_zero_cst (type); })) + +(simplify + (mult @0 integer_zerop@1) + @1) + +/* Make sure to preserve divisions by zero. This is the reason why + we don't simplify x / x to 1 or 0 / x to 0. */ +(for op (mult trunc_div ceil_div floor_div round_div exact_div) + (simplify + (op @0 integer_onep) + (non_lvalue @0))) + +/* Same applies to modulo operations, but fold is inconsistent here + and simplifies 0 % x to 0, only preserving literal 0 % 0. */ +(for op (ceil_mod floor_mod round_mod trunc_mod) + /* 0 % X is always zero. */ + (simplify + (trunc_mod integer_zerop@0 @1) + /* But not for 0 % 0 so that we can get the proper warnings and errors. */ + (if (!integer_zerop (@1)) + @0)) + /* X % 1 is always zero. */ + (simplify + (trunc_mod @0 integer_onep) + { build_zero_cst (type); })) + +/* x | ~0 -> ~0 */ +(simplify + (bit_ior @0 integer_all_onesp@1) + @1) + +/* x & 0 -> 0 */ +(simplify + (bit_and @0 integer_zerop@1) + @1) + +/* x ^ x -> 0 */ +(simplify + (bit_xor @0 @0) + { build_zero_cst (type); }) + -- 2.30.2