Index: gcc/tree-ssa-tail-merge.c =================================================================== --- /dev/null (new file) +++ gcc/tree-ssa-tail-merge.c (revision 0) @@ -0,0 +1,732 @@ +/* Tail merging for gimple. + Copyright (C) 2011 Free Software Foundation, Inc. + Contributed by Tom de Vries (tom@codesourcery.com) + +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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "tm_p.h" +#include "basic-block.h" +#include "output.h" +#include "flags.h" +#include "function.h" +#include "tree-flow.h" +#include "timevar.h" +#include "tree-pass.h" +#include "splay-tree.h" +#include "bitmap.h" +#include "tree-ssa-alias.h" + +static bitmap altered_bbs; + +/* Returns true if S contains (I1, I2). */ + +static bool +int_int_splay_lookup (splay_tree s, unsigned int i1, unsigned int i2) +{ + splay_tree_node node; + + if (s == NULL) + return false; + + node = splay_tree_lookup (s, i1); + return node && node->value == i2; +} + +/* Attempts to insert (I1, I2) into *S. Returns true if successful. + Allocates *S if necessary. */ + +static bool +int_int_splay_insert (splay_tree *s, unsigned int i1 , unsigned int i2) +{ + if (*s != NULL) + { + /* Check for existing element, which would otherwise be silently + overwritten by splay_tree_insert. */ + if (splay_tree_lookup (*s, i1)) + return false; + } + else + *s = splay_tree_new (splay_tree_compare_ints, 0, 0); + + splay_tree_insert (*s, i1, i2); + return true; +} + +/* Returns 0 if (NODE->value, NODE->key) is an element of S. Otherwise, + returns 1. */ + +static int +int_int_splay_node_contained_in (splay_tree_node node, void *s) +{ + splay_tree_node snode = splay_tree_lookup ((splay_tree)s, node->key); + return (!snode || node->value != snode->value) ? 1 : 0; +} + +/* Returns true if all elements of S1 are also in S2. */ + +static bool +int_int_splay_contained_in (splay_tree s1, splay_tree s2) +{ + if (s1 == NULL) + return true; + if (s2 == NULL) + return false; + return splay_tree_foreach (s1, int_int_splay_node_contained_in, s2) == 0; +} + +typedef splay_tree equiv_t; + +/* Returns true if EQUIV contains (SSA_NAME_VERSION (VAL1), + SSA_NAME_VERSION (VAL2)). */ + +static bool +equiv_lookup (equiv_t equiv, tree val1, tree val2) +{ + if (val1 == NULL_TREE || val2 == NULL_TREE + || TREE_CODE (val1) != SSA_NAME || TREE_CODE (val2) != SSA_NAME) + return false; + + return int_int_splay_lookup (equiv, SSA_NAME_VERSION (val1), + SSA_NAME_VERSION (val2)); +} + +/* Attempts to insert (SSA_NAME_VERSION (VAL1), SSA_NAME_VERSION (VAL2)) into + EQUIV, provided they are defined BB1 and BB2. Returns true if successful. + Allocates *EQUIV if necessary. */ + +static bool +equiv_insert (equiv_t *equiv, tree val1, tree val2, + basic_block bb1, basic_block bb2) +{ + if (val1 == NULL_TREE || val2 == NULL_TREE + || TREE_CODE (val1) != SSA_NAME || TREE_CODE (val2) != SSA_NAME + || gimple_bb (SSA_NAME_DEF_STMT (val1)) != bb1 + || gimple_bb (SSA_NAME_DEF_STMT (val2)) != bb2) + return false; + + return int_int_splay_insert (equiv, SSA_NAME_VERSION (val1), + SSA_NAME_VERSION (val2)); +} + +/* Returns true if all elements of S1 are also in S2. */ + +static bool +equiv_contained_in (equiv_t s1, equiv_t s2) +{ + return int_int_splay_contained_in (s1, s2); +} + +/* Init equiv_t *S. */ + +static void +equiv_init (equiv_t *s) +{ + *s = NULL; +} + +/* Delete equiv_t *S and reinit. */ + +static void +equiv_delete (equiv_t *s) +{ + if (!*s) + return; + + splay_tree_delete (*s); + *s = NULL; +} + +/* Check whether S1 and S2 are equal, considering the fields in + gimple_statement_base. Ignores fields uid, location, bb, and block, and the + pass-local flags visited and plf. */ + +static bool +gimple_base_equal_p (gimple s1, gimple s2) +{ + if (gimple_code (s1) != gimple_code (s2)) + return false; + + if (gimple_no_warning_p (s1) != gimple_no_warning_p (s2)) + return false; + + if (is_gimple_assign (s1) + && (gimple_assign_nontemporal_move_p (s1) + != gimple_assign_nontemporal_move_p (s2))) + return false; + + if (gimple_modified_p (s1) || gimple_modified_p (s2)) + return false; + + if (gimple_has_volatile_ops (s1) != gimple_has_volatile_ops (s2)) + return false; + + if (s1->gsbase.subcode != s2->gsbase.subcode) + return false; + + if (gimple_num_ops (s1) != gimple_num_ops (s2)) + return false; + + return true; +} + +/* Return true if p1 and p2 can be considered equal. */ + +static bool +pt_solution_equal_p (struct pt_solution *p1, struct pt_solution *p2) +{ + if (p1->anything != p2->anything + || p1->nonlocal != p2->nonlocal + || p1->escaped != p2->escaped + || p1->ipa_escaped != p2->ipa_escaped + || p1->null != p2->null + || p1->vars_contains_global != p2->vars_contains_global + || p1->vars_contains_restrict != p2->vars_contains_restrict) + return false; + + if ((p1->vars == NULL) != (p2->vars == NULL)) + return false; + + if (p1->vars == NULL) + return true; + + return bitmap_equal_p (p1->vars, p2->vars); +} + +/* Return true if gimple statements S1 and S2 are equal. At entry, EQUIV + contains pairs of local defs that can be considered equivalent. If S1 and S2 + are equal, at exit EQUAL contains the defs and vdefs of S1 and S2. If found, + return vop state at bb entry in PHI_USE1 for BB1 and PHI_USE2 for BB2. */ + +static bool +gimple_equal_p (equiv_t *equiv, gimple s1, gimple s2, + tree *phi_vuse1, tree *phi_vuse2) +{ + unsigned int i; + enum gimple_statement_structure_enum gss; + tree lhs1, lhs2; + basic_block bb1 = gimple_bb (s1), bb2 = gimple_bb (s2); + + /* Handle omp gimples conservatively. */ + if (is_gimple_omp (s1) || is_gimple_omp (s2)) + return false; + + if (!gimple_base_equal_p (s1, s2)) + return false; + + gss = gimple_statement_structure (s1); + switch (gss) + { + case GSS_CALL: + if (!pt_solution_equal_p (gimple_call_use_set (s1), + gimple_call_use_set (s2)) + || !pt_solution_equal_p (gimple_call_clobber_set (s1), + gimple_call_clobber_set (s2)) + || !gimple_call_same_target_p (s1, s2)) + return false; + /* Falthru. */ + + case GSS_WITH_MEM_OPS_BASE: + case GSS_WITH_MEM_OPS: + { + tree vdef1 = gimple_vdef (s1), vdef2 = gimple_vdef (s2); + tree vuse1 = gimple_vuse (s1), vuse2 = gimple_vuse (s2); + if (vuse1 == NULL_TREE || vuse2 == NULL_TREE) + { + if (vuse1 != vuse2) + return false; + } + else if (*phi_vuse1 == NULL_TREE) + { + *phi_vuse1 = vuse1; + *phi_vuse2 = vuse2; + } + else if (vuse1 != vuse2 && !(vuse1 == *phi_vuse1 && vuse2 == *phi_vuse2) + &&!equiv_lookup (*equiv, vuse1, vuse2)) + return false; + + if (vdef1 == NULL_TREE || vdef2 == NULL_TREE) + { + if (vdef1 != vdef2) + return false; + } + else if (!equiv_insert (equiv, vdef1, vdef2, bb1, bb2)) + return false; + } + /* Falthru. */ + + case GSS_WITH_OPS: + /* Ignore gimple_def_ops and gimple_use_ops. They are duplicates of + gimple_vdef, gimple_vuse and gimple_ops, which are checked + elsewhere. */ + /* Falthru. */ + + case GSS_BASE: + break; + + default: + return false; + } + + /* Find lhs. */ + lhs1 = gimple_get_lhs (s1); + lhs2 = gimple_get_lhs (s2); + + /* Handle ops. */ + for (i = 0; i < gimple_num_ops (s1); ++i) + { + tree t1 = gimple_op (s1, i); + tree t2 = gimple_op (s2, i); + if (t1 == NULL_TREE && t2 == NULL_TREE) + continue; + if (t1 == NULL_TREE || t2 == NULL_TREE) + return false; + /* Skip lhs. */ + if (lhs1 == t1 && i == 0) + continue; + if (!operand_equal_p (t1, t2, 0) && !equiv_lookup (*equiv, t1, t2)) + return false; + } + + /* Handle lhs. */ + if (lhs1 != lhs2 && !equiv_insert (equiv, lhs1, lhs2, bb1, bb2)) + return false; + + return true; +} + +/* Return true if BB1 and BB2 contain the same non-debug gimple statements, and + if the def pairs in PHI_EQUIV are found to be equivalent defs in BB1 and + BB2. Return vop state at bb entry in PHI_USE1 for BB1 and PHI_USE2 for + BB2. */ + +static bool +bb_gimple_equal_p (equiv_t phi_equiv, basic_block bb1, basic_block bb2, + tree *phi_vuse1, tree *phi_vuse2) +{ + gimple_stmt_iterator gsi1 = gsi_start_nondebug_bb (bb1); + gimple_stmt_iterator gsi2 = gsi_start_nondebug_bb (bb2); + bool end1, end2; + equiv_t equiv; + bool equal = true; + + end1 = gsi_end_p (gsi1); + end2 = gsi_end_p (gsi2); + + /* Don't handle empty blocks, these are handled elsewhere in the cleanup. */ + if (end1 || end2) + return false; + + /* TODO: handle blocks with phi-nodes. We'll have find corresponding + phi-nodes in bb1 and bb2, with the same alternatives for the same + preds. */ + if (phi_nodes (bb1) != NULL || phi_nodes (bb2) != NULL) + return false; + + equiv_init (&equiv); + while (true) + { + if (end1 && end2) + break; + if (end1 || end2 + || !gimple_equal_p (&equiv, gsi_stmt (gsi1), gsi_stmt (gsi2), + phi_vuse1, phi_vuse2)) + { + equal = false; + break; + } + + gsi_next_nondebug (&gsi1); + gsi_next_nondebug (&gsi2); + end1 = gsi_end_p (gsi1); + end2 = gsi_end_p (gsi2); + } + + /* equiv now contains all bb1,bb2 def pairs which are equivalent. + Check if the phi alternatives are indeed equivalent. */ + equal = equal && equiv_contained_in (phi_equiv, equiv); + + equiv_delete (&equiv); + + return equal; +} + +/* Resets all debug statements in BBUSE that have uses that are not + dominated by their defs. */ + +static void +update_debug_stmts (basic_block bbuse) +{ + use_operand_p use_p; + ssa_op_iter oi; + basic_block bbdef; + gimple stmt, def_stmt; + gimple_stmt_iterator gsi; + tree name; + + for (gsi = gsi_start_bb (bbuse); !gsi_end_p (gsi); gsi_next (&gsi)) + { + stmt = gsi_stmt (gsi); + if (!is_gimple_debug (stmt)) + continue; + gcc_assert (gimple_debug_bind_p (stmt)); + + FOR_EACH_PHI_OR_STMT_USE (use_p, stmt, oi, SSA_OP_USE) + { + name = USE_FROM_PTR (use_p); + gcc_assert (TREE_CODE (name) == SSA_NAME); + + def_stmt = SSA_NAME_DEF_STMT (name); + gcc_assert (def_stmt != NULL); + + bbdef = gimple_bb (def_stmt); + if (bbdef == NULL || bbuse == bbdef + || dominated_by_p (CDI_DOMINATORS, bbuse, bbdef)) + continue; + + gimple_debug_bind_reset_value (stmt); + update_stmt (stmt); + } + } +} + +/* Create a vop phi in BB2, with VUSE1 arguments for all the REDIRECTED_EDGES, + and VUSE2 for the other edges. Then use the phi instead of VUSE2 in BB2. */ + +static void +update_vuses (tree vuse1, tree vuse2, basic_block bb2, + VEC (edge,heap) *redirected_edges) +{ + gimple stmt, phi = NULL; + tree lhs, vuse, vdef; + unsigned int i; + gimple def_stmt1, def_stmt2; + gimple_stmt_iterator gsi; + source_location locus1, locus2; + + if (vuse1 == NULL_TREE && vuse2 == NULL_TREE) + return; + gcc_assert (vuse1 != NULL_TREE && vuse2 != NULL_TREE); + + def_stmt1 = SSA_NAME_DEF_STMT (vuse1); + locus1 = gimple_location (def_stmt1); + def_stmt2 = SSA_NAME_DEF_STMT (vuse2); + locus2 = gimple_location (def_stmt2); + + /* This is not triggered yet, since we bail out if bb2 has more than + 1 predecessor. */ + gcc_assert (gimple_bb (def_stmt2) != bb2); + + /* No need to create a phi with 2 equal arguments. */ + if (vuse1 == vuse2) + return; + + /* Create a phi, first with default argument vuse2 for all preds. */ + lhs = make_ssa_name (SSA_NAME_VAR (vuse2), NULL); + phi = create_phi_node (lhs, bb2); + SSA_NAME_DEF_STMT (lhs) = phi; + for (i = 0; i < EDGE_COUNT (bb2->preds); ++i) + add_phi_arg (phi, vuse2, EDGE_PRED (bb2, i), locus2); + + /* Now overwrite the arguments associated with the redirected edges with + vuse1. */ + for (i = 0; i < EDGE_COUNT (redirected_edges); ++i) + { + edge e = VEC_index (edge, redirected_edges, i); + gcc_assert (PHI_ARG_DEF_FROM_EDGE (phi, e)); + SET_PHI_ARG_DEF (phi, e->dest_idx, vuse1); + gimple_phi_arg_set_location (phi, e->dest_idx, locus1); + } + + /* Replace uses of vuse2 with uses of the phi. */ + for (gsi = gsi_start_bb (bb2); !gsi_end_p (gsi); gsi_next (&gsi)) + { + stmt = gsi_stmt (gsi); + vuse = gimple_vuse (stmt); + vdef = gimple_vdef (stmt); + if (vuse != NULL_TREE) + { + gcc_assert (vuse == vuse2); + gimple_set_vuse (stmt, lhs); + update_stmt (stmt); + } + if (vdef != NULL_TREE) + break; + } +} + +/* E1 and E2 have a common dest. Detect if E1->src and E2->src are duplicates, + and if so, redirect the predecessor edges of E1->src to E2->src and remove + E1->src. Returns true if any changes were made. */ + +static bool +cleanup_duplicate_preds_1 (equiv_t phi_equiv, edge e1, edge e2) +{ + edge pred_edge; + basic_block bb1, bb2, pred; + basic_block bb_dom = NULL, bb2_dom = NULL; + unsigned int i; + basic_block bb = e1->dest; + tree phi_vuse1 = NULL_TREE, phi_vuse2 = NULL_TREE; + VEC (edge,heap) *redirected_edges; + gcc_assert (bb == e2->dest); + + if (e1->flags != e2->flags) + return false; + + bb1 = e1->src; + bb2 = e2->src; + + /* TODO: We could allow multiple successor edges here, as long as bb1 and bb2 + have the same successors. */ + if (EDGE_COUNT (bb1->succs) != 1 || EDGE_COUNT (bb2->succs) != 1) + return false; + + if (!bb_gimple_equal_p (phi_equiv, bb1, bb2, &phi_vuse1, &phi_vuse2)) + return false; + + if (dump_file) + fprintf (dump_file, "cleanup_duplicate_preds: " + "cleaning up , duplicate of \n", bb1->index, + bb2->index); + + /* Calculate the changes to be made to the dominator info. + Calculate bb2_dom. */ + bb2_dom = nearest_common_dominator (CDI_DOMINATORS, bb2, bb1); + if (bb2_dom == bb1 || bb2_dom == bb2) + bb2_dom = get_immediate_dominator (CDI_DOMINATORS, bb2_dom); + + /* Calculate bb_dom. */ + bb_dom = get_immediate_dominator (CDI_DOMINATORS, bb); + if (bb == bb2) + bb_dom = bb2_dom; + else if (bb_dom == bb1 || bb_dom == bb2) + bb_dom = bb2; + else + { + /* Count the predecessors of bb (other than bb1 or bb2), not dominated + by bb. If there are none, merging bb1 and bb2 will mean that bb2 + dominates bb. */ + int not_dominated = 0; + for (i = 0; i < EDGE_COUNT (bb->preds); ++i) + { + pred_edge = EDGE_PRED (bb, i); + pred = pred_edge->src; + if (pred == bb1 || pred == bb2) + continue; + if (dominated_by_p (CDI_DOMINATORS, pred, bb)) + continue; + not_dominated++; + } + if (not_dominated == 0) + bb_dom = bb2; + } + + redirected_edges = VEC_alloc (edge, heap, 10); + + /* Redirect the incoming edges of bb1 to bb2. */ + for (i = EDGE_COUNT (bb1->preds); i > 0 ; --i) + { + pred_edge = EDGE_PRED (bb1, i - 1); + pred = pred_edge->src; + pred_edge = redirect_edge_and_branch (pred_edge, bb2); + gcc_assert (pred_edge != NULL); + VEC_safe_push (edge, heap, redirected_edges, pred_edge); + } + + /* The set of predecessors has changed for both bb and bb2. */ + bitmap_set_bit (altered_bbs, bb->index); + bitmap_set_bit (altered_bbs, bb2->index); + + /* bb1 has no incoming edges anymore, and has become unreachable. */ + delete_basic_block (bb1); + bitmap_clear_bit (altered_bbs, bb1->index); + + /* Update dominator info. Note: update order is relevant. */ + set_immediate_dominator (CDI_DOMINATORS, bb2, bb2_dom); + if (bb != bb2) + set_immediate_dominator (CDI_DOMINATORS, bb, bb_dom); + + /* Reset invalidated debug statements. */ + update_debug_stmts (bb2); + + /* Insert vop phi, and update vuses. */ + update_vuses (phi_vuse1, phi_vuse2, bb2, redirected_edges); + + VEC_free (edge, heap, redirected_edges); + + return true; +} + +/* Returns whether for all phis in E1->dest the phi alternatives for E1 and + E2 are either: + - equal, or + - defined locally in E1->src and E2->src. + In the latter case, register the alternatives in *PHI_EQUIV. */ + +static bool +same_or_local_phi_alternatives (equiv_t *phi_equiv, edge e1, edge e2) +{ + int n1 = e1->dest_idx; + int n2 = e2->dest_idx; + gimple_stmt_iterator gsi; + basic_block dest = e1->dest; + gcc_assert (dest == e2->dest); + + for (gsi = gsi_start_phis (dest); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple phi = gsi_stmt (gsi); + tree val1 = gimple_phi_arg_def (phi, n1); + tree val2 = gimple_phi_arg_def (phi, n2); + + gcc_assert (val1 != NULL_TREE); + gcc_assert (val2 != NULL_TREE); + + if (operand_equal_for_phi_arg_p (val1, val2)) + continue; + + if (equiv_insert (phi_equiv, val1, val2, e1->src, e2->src)) + continue; + + /* TODO: handle case that val1 and val2 are vops which are not locally + defined. */ + return false; + } + + return true; +} + +/* Detect duplicate predecessor blocks of BB and clean them up. Return true if + any changes were made. */ + +static bool +cleanup_duplicate_preds (basic_block bb) +{ + edge e1, e2, e1_swapped, e2_swapped; + unsigned int i, j, n; + equiv_t phi_equiv; + bool changed; + + n = EDGE_COUNT (bb->preds); + + for (i = 0; i < n; ++i) + { + e1 = EDGE_PRED (bb, i); + if (e1->flags & EDGE_COMPLEX) + continue; + for (j = i + 1; j < n; ++j) + { + e2 = EDGE_PRED (bb, j); + if (e2->flags & EDGE_COMPLEX) + continue; + + /* Block e1->src might be deleted. If bb and e1->src are the same + block, delete e2->src instead, by swapping e1 and e2. */ + e1_swapped = (bb == e1->src) ? e2: e1; + e2_swapped = (bb == e1->src) ? e1: e2; + + /* For all phis in bb, the phi alternatives for e1 and e2 need to have + the same value. */ + equiv_init (&phi_equiv); + if (same_or_local_phi_alternatives (&phi_equiv, e1_swapped, + e2_swapped)) + /* Collapse e1->src and e2->src if they are duplicates. */ + changed = cleanup_duplicate_preds_1 (phi_equiv, e1_swapped, + e2_swapped); + else + changed = false; + + equiv_delete (&phi_equiv); + + if (changed) + return true; + } + } + + return false; +} + +/* Runs tail merge optimization. */ + +static unsigned int +tail_merge_optimize (void) +{ + basic_block bb; + unsigned i, n; + + calculate_dominance_info (CDI_DOMINATORS); + + /* Initialize worklist. */ + n = last_basic_block - NUM_FIXED_BLOCKS; + altered_bbs = BITMAP_ALLOC (NULL); + bitmap_set_range (altered_bbs, NUM_FIXED_BLOCKS, n); + + /* Now process the altered blocks, as long as any are available. */ + while (!bitmap_empty_p (altered_bbs)) + { + i = bitmap_first_set_bit (altered_bbs); + bitmap_clear_bit (altered_bbs, i); + if (i < NUM_FIXED_BLOCKS) + continue; + + bb = BASIC_BLOCK (i); + if (!bb) + continue; + + cleanup_duplicate_preds (bb); + } + + BITMAP_FREE (altered_bbs); + +#ifdef ENABLE_CHECKING + verify_dominators (CDI_DOMINATORS); +#endif + + return 0; +} + +/* Returns true if tail merge pass should be run. */ + +static bool +gate_tail_merge (void) +{ + return optimize >= 2; +} + +struct gimple_opt_pass pass_tail_merge = +{ + { + GIMPLE_PASS, + "tailmerge", /* name */ + gate_tail_merge, /* gate */ + tail_merge_optimize, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_TREE_CFG, /* tv_id */ + PROP_ssa | PROP_cfg, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_verify_ssa | TODO_verify_stmts + | TODO_cleanup_cfg | TODO_dump_func /* todo_flags_finish */ + } +}; Index: gcc/tree-pass.h =================================================================== --- gcc/tree-pass.h (revision 173734) +++ gcc/tree-pass.h (working copy) @@ -446,6 +446,7 @@ extern struct gimple_opt_pass pass_trace extern struct gimple_opt_pass pass_warn_unused_result; extern struct gimple_opt_pass pass_split_functions; extern struct gimple_opt_pass pass_feedback_split_functions; +extern struct gimple_opt_pass pass_tail_merge; /* IPA Passes */ extern struct simple_ipa_opt_pass pass_ipa_lower_emutls; Index: gcc/Makefile.in =================================================================== --- gcc/Makefile.in (revision 173734) +++ gcc/Makefile.in (working copy) @@ -1441,6 +1441,7 @@ OBJS-common = \ tree-ssa-sccvn.o \ tree-ssa-sink.o \ tree-ssa-structalias.o \ + tree-ssa-tail-merge.o \ tree-ssa-ter.o \ tree-ssa-threadedge.o \ tree-ssa-threadupdate.o \ @@ -2395,6 +2396,13 @@ stor-layout.o : stor-layout.c $(CONFIG_H $(TREE_H) $(PARAMS_H) $(FLAGS_H) $(FUNCTION_H) $(EXPR_H) output.h $(RTL_H) \ $(GGC_H) $(TM_P_H) $(TARGET_H) langhooks.h $(REGS_H) gt-stor-layout.h \ $(DIAGNOSTIC_CORE_H) $(CGRAPH_H) $(TREE_INLINE_H) $(TREE_DUMP_H) $(GIMPLE_H) +tree-ssa-tail-merge.o: tree-ssa-tail-merge.c \ + $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(BITMAP_H) \ + $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) output.h \ + $(TREE_H) $(TREE_FLOW_H) $(TREE_INLINE_H) \ + $(GIMPLE_H) $(FUNCTION_H) \ + $(TREE_PASS_H) $(TIMEVAR_H) $(SPLAY_TREE_H) \ + $(CGRAPH_H) tree-ssa-structalias.o: tree-ssa-structalias.c \ $(SYSTEM_H) $(CONFIG_H) coretypes.h $(TM_H) $(GGC_H) $(OBSTACK_H) $(BITMAP_H) \ $(FLAGS_H) $(TM_P_H) $(BASIC_BLOCK_H) output.h \ Index: gcc/passes.c =================================================================== --- gcc/passes.c (revision 173734) +++ gcc/passes.c (working copy) @@ -767,6 +767,7 @@ init_optimization_passes (void) NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_merge_phi); NEXT_PASS (pass_cd_dce); + NEXT_PASS (pass_tail_merge); NEXT_PASS (pass_early_ipa_sra); NEXT_PASS (pass_tail_recursion); NEXT_PASS (pass_convert_switch);