This hooks the machinery into fold_stmt (but not allowed to traverse SSA use-def chains by default) and provides a new overload to provide a valueization hook. With the patch tree-ssa-forwprop.c makes use of that new overload, first folding all statements while keeping a simple const-and-copy lattice.
Once all "toplevel" patterns are in place (patterns with a single operation) we can remove the dispatch to GENERIC folding from fold_stmt. A question is whether to really do as the patch does and simplify all statements or whether I should restrict this to stmts that were previously covered by its manual patterns (which further patches will transition to match-and-simplify patterns). As for the GENERIC part, here is what the simplification routine looks like for /* fold_negate_exprs convert - (~A) to A + 1. */ (simplify (negate (bit_not @0)) (if (INTEGRAL_TYPE_P (type)) (plus @0 { build_int_cst (TREE_TYPE (@0), 1); } ))) static bool gimple_simplify (code_helper *res_code, tree *res_ops, gimple_seq *seq, tree (*valueize)(tree), code_helper code, tree type, tree op0) { switch (code.get_rep()) { ... case NEGATE_EXPR: { switch (TREE_CODE (op0)) { case SSA_NAME: { gimple def_stmt = SSA_NAME_DEF_STMT (op0); if (is_gimple_assign (def_stmt)) switch (gimple_assign_rhs_code (def_stmt)) { case BIT_NOT_EXPR: { tree o20 = gimple_assign_rhs1 (def_stmt); if ((o20 = do_valueize (valueize, o20)) != 0) { { /* #line 136 "/space/rguenther/src/svn/match-and-simplify/gcc/match.pd" */ tree captures[2] ATTRIBUTE_UNUSED = {}; captures[0] = o20; /* #line 135 "/space/rguenther/src/svn/match-and-simplify/gcc/match.pd" */ if (INTEGRAL_TYPE_P (type)) { if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "Applying pattern match.pd:136, %s:%d\n", __FILE__, __LINE__); *res_code = PLUS_EXPR; res_ops[0] = captures[0]; res_ops[1] = build_int_cst (TREE_TYPE (captures[0]), 1); gimple_resimplify2 (seq, res_code, type, res_ops, valueize); return true; } } } break; } ... As you can see we use gimple_resimplify2 for re-simplification and code-generation and the toplevel expression of the result is kept in pieces. gimple_resimplify2 in turn ends up calling gimple_simplify again and fold to get the "real" constant folding cases. Boostrap and regtest is pending on x86_64-unknown-linux-gnu. Ok for trunk? Thanks, Richard. 2014-10-15 Richard Biener <rguent...@suse.de> * gimple-fold.h (fold_stmt): New overload with valueization hook. (no_follow_ssa_edges): Declare. * gimple-fold.c: Include tree-eh.h and gimple-match.h. (fold_stmt_1): Add valueization hook parameter. Dispatch to gimple_simplify after canonicalization. (no_follow_ssa_edges): New function. (fold_stmt): New overload and adjust. (fold_stmt_inplace): Adjust. * tree-ssa-forwprop.c: Include tree-cfgcleanup.h and tree-into-ssa.h. (lattice): New global. (fwprop_ssa_val): New function. (pass_forwprop::execute): Do a first pass over all stmts in dominator order simplifying all statements while keeping a simple const-and-copy lattice up-to-date. Index: gcc/gimple-fold.h =================================================================== *** gcc/gimple-fold.h.orig 2014-10-15 13:01:41.548100887 +0200 --- gcc/gimple-fold.h 2014-10-15 13:57:05.627872029 +0200 *************** extern tree canonicalize_constructor_val *** 26,36 **** --- 26,38 ---- 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)); Index: gcc/gimple-fold.c =================================================================== *** gcc/gimple-fold.c.orig 2014-10-15 13:02:08.158099055 +0200 --- gcc/gimple-fold.c 2014-10-15 13:56:20.710875121 +0200 *************** along with GCC; see the file COPYING3. *** 55,60 **** --- 55,63 ---- #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. *************** maybe_canonicalize_mem_ref_addr (tree *t *** 2811,2817 **** distinguishes both cases. */ static bool ! fold_stmt_1 (gimple_stmt_iterator *gsi, bool inplace) { bool changed = false; gimple stmt = gsi_stmt (*gsi); --- 2814,2820 ---- 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, *** 2889,2894 **** --- 2892,3017 ---- default:; } + /* Dispatch to pattern-based folding. */ + /* ??? Change "inplace" semantics to allow replacing a stmt if + no further stmts need to be inserted (basically disallow + creating of new SSA names). */ + 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 (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. */ + /* ??? Not sure how we want to deal with combining + from possibly throwing statements. Trivial + simplifications may lead to DCEing an internal + throw. But we probably still want to simplify + things to a constant for example? Similar to + abnormals we could discard the simplification + result if we ever push a could-throw stmt to + the sequence. */ + && (!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) + goto fail; + gimple_cond_set_condition (stmt, NE_EXPR, res, + build_zero_cst (TREE_TYPE (res))); + } + else + goto fail; + 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); + changed = true; + fail: + ; + } + else if (is_gimple_assign (stmt) + && rcode.is_tree_code ()) + { + if ((!inplace + || gimple_num_ops (stmt) <= get_gimple_rhs_num_ops (rcode)) + /* Play safe and do not allow abnormals to be mentioned in + newly created statements. */ + && !((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])))) + { + 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); + changed = 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); + changed = true; + } + else + gcc_unreachable (); + } + } + } + + stmt = gsi_stmt (*gsi); + /* Fold the main computation performed by the statement. */ switch (gimple_code (stmt)) { *************** fold_stmt_1 (gimple_stmt_iterator *gsi, *** 3028,3033 **** --- 3151,3164 ---- 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, *** 3038,3044 **** bool fold_stmt (gimple_stmt_iterator *gsi) { ! return fold_stmt_1 (gsi, false); } /* Perform the minimal folding on statement *GSI. Only operations like --- 3169,3181 ---- 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 *** 3053,3059 **** 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; } --- 3190,3196 ---- 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/tree-ssa-forwprop.c =================================================================== *** gcc/tree-ssa-forwprop.c.orig 2014-10-14 15:49:39.836355545 +0200 --- gcc/tree-ssa-forwprop.c 2014-10-15 13:56:20.725875120 +0200 *************** along with GCC; see the file COPYING3. *** 54,59 **** --- 54,61 ---- #include "tree-ssa-propagate.h" #include "tree-ssa-dom.h" #include "builtins.h" + #include "tree-cfgcleanup.h" + #include "tree-into-ssa.h" /* This pass propagates the RHS of assignment statements into use sites of the LHS of the assignment. It's basically a specialized *************** simplify_mult (gimple_stmt_iterator *gsi *** 3586,3591 **** --- 3588,3613 ---- return false; } + + + static vec<tree> lattice; + + /* Primitive "lattice" function for gimple_simplify to discard + matches on names whose definition contains abnormal SSA names. */ + + static tree + fwprop_ssa_val (tree name) + { + if (TREE_CODE (name) == SSA_NAME + && SSA_NAME_VERSION (name) < lattice.length ()) + { + tree val = lattice[SSA_NAME_VERSION (name)]; + if (val) + return val; + } + return name; + } + /* Main entry point for the forward propagation and statement combine optimizer. */ *************** pass_forwprop::execute (function *fun) *** 3626,3631 **** --- 3648,3708 ---- cfg_changed = false; + /* Combine stmts with the stmts defining their operands. Do that + in an order that guarantees visiting SSA defs before SSA uses. */ + lattice.create (num_ssa_names); + lattice.quick_grow_cleared (num_ssa_names); + int *postorder = XNEWVEC (int, n_basic_blocks_for_fn (fun)); + int postorder_num = inverted_post_order_compute (postorder); + for (int i = 0; i < postorder_num; ++i) + { + bb = BASIC_BLOCK_FOR_FN (fun, postorder[i]); + /* ??? Maybe want to handle degenerate PHIs to populate + the lattice more optimistically. */ + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple stmt = gsi_stmt (gsi); + gimple orig_stmt = stmt; + + if (fold_stmt (&gsi, fwprop_ssa_val)) + { + stmt = gsi_stmt (gsi); + if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt) + && gimple_purge_dead_eh_edges (bb)) + cfg_changed = true; + update_stmt (stmt); + } + + /* Fill up the lattice. */ + if (gimple_assign_single_p (stmt)) + { + tree lhs = gimple_assign_lhs (stmt); + tree rhs = gimple_assign_rhs1 (stmt); + if (TREE_CODE (lhs) == SSA_NAME) + { + if (TREE_CODE (rhs) == SSA_NAME) + lattice[SSA_NAME_VERSION (lhs)] = fwprop_ssa_val (rhs); + else if (is_gimple_min_invariant (rhs)) + lattice[SSA_NAME_VERSION (lhs)] = rhs; + else + lattice[SSA_NAME_VERSION (lhs)] = lhs; + } + } + } + } + free (postorder); + lattice.release (); + + /* ??? Code below doesn't expect non-renamed VOPs and the above + doesn't keep virtual operand form up-to-date. */ + if (cfg_changed) + { + cleanup_tree_cfg (); + cfg_changed = false; + } + update_ssa (TODO_update_ssa_only_virtuals); + FOR_EACH_BB_FN (bb, fun) { gimple_stmt_iterator gsi;