This combines the already posted 3/n (first simple patterns), 4/n (hook into fold-const.c) and not yet posted 5/n (hook into fold_stmt). Over the first posting this also contains recent improvements to the generator from the branch regarding to TREE_SIDE_EFFECTS and NON_LVALUE_EXPR handling.
The hook into fold_stmt leaves all existing calls in doing what fold_stmt does currently (the match-and-simplify machinery will not follow SSA edges). It adds the ability to enable that though via an overload taking a valueization hook as argument (this is how tree-ssa-forwprop.c will exercise it). Bootstrapped and tested on x86_64-unknown-linux-gnu. Thanks, Richard. 2014-10-24 Richard Biener <rguent...@suse.de> * 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. Index: gcc/generic-match.h =================================================================== *** /dev/null 1970-01-01 00:00:00.000000000 +0000 --- gcc/generic-match.h 2014-10-23 15:45:28.322836040 +0200 *************** *** 0 **** --- 1,33 ---- + /* Generic simplify definitions. + + Copyright (C) 2011-2014 Free Software Foundation, Inc. + Contributed by Richard Guenther <rguent...@suse.de> + + 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 + <http://www.gnu.org/licenses/>. */ + + #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 */ Index: gcc/generic-match-head.c =================================================================== *** gcc/generic-match-head.c.orig 2014-10-23 15:45:26.935836135 +0200 --- gcc/generic-match-head.c 2014-10-23 15:45:28.322836040 +0200 *************** along with GCC; see the file COPYING3. *** 43,48 **** #include "tree-phinodes.h" #include "ssa-iterators.h" #include "dumpfile.h" ! #include "gimple-match.h" --- 43,48 ---- #include "tree-phinodes.h" #include "ssa-iterators.h" #include "dumpfile.h" ! #include "generic-match.h" Index: gcc/fold-const.c =================================================================== *** gcc/fold-const.c.orig 2014-10-23 15:44:38.601839463 +0200 --- gcc/fold-const.c 2014-10-23 15:45:51.976834411 +0200 *************** along with GCC; see the file COPYING3. *** 70,75 **** --- 70,76 ---- #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. */ *************** fold_unary_loc (location_t loc, enum tre *** 7564,7569 **** --- 7565,7574 ---- 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) { *************** fold_binary_loc (location_t loc, *** 9913,9918 **** --- 9918,9927 ---- && 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 *************** fold_binary_loc (location_t loc, *** 10035,10044 **** 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))) --- 10044,10049 ---- *************** fold_binary_loc (location_t loc, *** 10159,10167 **** 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 --- 10164,10169 ---- *************** fold_binary_loc (location_t loc, *** 10647,10654 **** { 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) --- 10649,10654 ---- *************** fold_binary_loc (location_t loc, *** 10741,10756 **** } } - /* 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) --- 10741,10746 ---- *************** fold_binary_loc (location_t loc, *** 10828,10837 **** 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. */ --- 10818,10823 ---- *************** fold_binary_loc (location_t loc, *** 11128,11137 **** 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)); --- 11114,11119 ---- *************** fold_binary_loc (location_t loc, *** 11270,11281 **** 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 --- 11252,11259 ---- *************** fold_binary_loc (location_t loc, *** 11433,11440 **** 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)); --- 11411,11416 ---- *************** fold_binary_loc (location_t loc, *** 12185,12192 **** 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. */ --- 12161,12166 ---- *************** fold_binary_loc (location_t loc, *** 12256,12276 **** 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 --- 12230,12235 ---- *************** fold_ternary_loc (location_t loc, enum t *** 13803,13808 **** --- 13762,13771 ---- && 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 Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c.orig 2014-10-23 15:45:26.949836134 +0200 --- gcc/gimple-fold.c 2014-10-23 15:45:28.341836039 +0200 *************** along with GCC; see the file COPYING3. *** 61,66 **** --- 61,68 ---- #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. *************** gimple_fold_call (gimple_stmt_iterator * *** 2792,2797 **** --- 2794,2914 ---- 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 *************** maybe_canonicalize_mem_ref_addr (tree *t *** 2878,2884 **** distinguishes both cases. */ static bool ! fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) { bool changed = false; gimple stmt = gsi_stmt (*gsi); --- 2995,3001 ---- distinguishes both cases. */ static bool ! fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace, tree (*valueize) (tree)) { bool changed = false; gimple stmt = gsi_stmt (*gsi); *************** fold_stmt_1 (gimple_stmt_iterator *gsi, *** 2956,2961 **** --- 3073,3097 ---- 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)) { *************** fold_stmt_1 (gimple_stmt_iterator *gsi, *** 3095,3100 **** --- 3231,3244 ---- 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. *************** fold_stmt_1 (gimple_stmt_iterator *gsi, *** 3105,3111 **** bool fold_stmt (gimple_stmt_iterator *gsi) { ! return fold_stmt_1 (gsi, false); } /* Perform the minimal folding on statement *GSI. Only operations like --- 3249,3261 ---- bool fold_stmt (gimple_stmt_iterator *gsi) { ! 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 *************** bool *** 3120,3126 **** fold_stmt_inplace (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); ! bool changed = fold_stmt_1 (gsi, true); gcc_assert (gsi_stmt (*gsi) == stmt); return changed; } --- 3270,3276 ---- fold_stmt_inplace (gimple_stmt_iterator *gsi) { gimple stmt = gsi_stmt (*gsi); ! bool changed = fold_stmt_1 (gsi, true, no_follow_ssa_edges); gcc_assert (gsi_stmt (*gsi) == stmt); return changed; } Index: gcc/match.pd =================================================================== *** gcc/match.pd.orig 2014-10-23 15:45:26.949836134 +0200 --- gcc/match.pd 2014-10-23 15:45:28.341836039 +0200 *************** along with GCC; see the file COPYING3. *** 28,30 **** --- 28,90 ---- 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); }) + Index: gcc/gimple-fold.h =================================================================== *** gcc/gimple-fold.h.orig 2014-10-23 15:45:26.949836134 +0200 --- gcc/gimple-fold.h 2014-10-23 15:45:28.342836038 +0200 *************** extern tree maybe_fold_and_comparisons ( *** 31,36 **** --- 31,37 ---- 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)); Index: gcc/genmatch.c =================================================================== --- gcc/genmatch.c (svn+ssh://rgue...@gcc.gnu.org/svn/gcc/trunk/gcc/genmatch.c) (revision 216542) +++ gcc/genmatch.c (.../gcc/genmatch.c) (working copy) @@ -1374,10 +1374,11 @@ expr::gen_transform (FILE *f, const char 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<cinfo> 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 <expr *> (s->result)) + && is_a <predicate_id *> (e->operation))) + { + force_no_side_effects = -1; + return; + } + + force_no_side_effects = 0; + info.safe_grow_cleared (s->capture_max + 1); + e = as_a <expr *> (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 <c_expr *>(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 <capture *> (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 <expr *> (c->what)) + { + info[c->where].expr_p = true; + walk_match (c->what, toplevel_arg, conditional_p); + } + } + else if (expr *e = dyn_cast <expr *> (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 <predicate *> (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 <capture *> (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 <expr *> (c->what)) + { + info[c->where].cse_p = true; + walk_result (c->what, true); + } + } + else if (expr *e = dyn_cast <expr *> (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 <c_expr *> (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 <expr *> (s->result)) + && is_a <predicate_id *> (e->operation))) + { + for (unsigned i = 0; i < as_a <expr *> (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 <expr *> (result)->operation == NON_LVALUE_EXPR) + result = as_a <expr *> (result)->ops[0]; + if (result->type == operand::OP_EXPR) { - expr *e = as_a <expr *> (s->result); + expr *e = as_a <expr *> (result); bool is_predicate = is_a <predicate_id *> (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 <expr *> (s->result); - bool is_predicate = is_a <predicate_id *> (e->operation); + expr *e = as_a <expr *> (result); + is_predicate = is_a <predicate_id *> (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 <predicate_id *> (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++)