From f72d05044f9d8a77b0d2c3df68eba4f88824d08b Mon Sep 17 00:00:00 2001 From: mliska Date: Thu, 9 Jul 2015 15:56:34 +0200 Subject: [PATCH 1/2] tree-ssa-tail-merge: replace engine with IPA ICF infrastructure. gcc/ChangeLog: 2015-07-09 Martin Liska * dbgcnt.def: Add new debug counter. * ipa-icf-gimple.c (func_checker::compare_ssa_name): Use newly added state flag. (func_checker::compare_memory_operand): Likewise. (func_checker::compare_cst_or_decl): Handle if we are in tail_merge_mode. (func_checker::reset_preferences): New function. (func_checker::set_comparing_sensitive_rhs): Likewise. (func_checker::stmt_local_def): New function. (func_checker::compare_phi_node): Move from sem_function class. (func_checker::compare_bb_tail_merge): New function. (func_checker::compare_bb): Improve STMT iteration. (func_checker::compare_gimple_call): Return false in case of an UBSAN function. (func_checker::compare_gimple_assign): Likewise. (func_checker::compare_gimple_label): Remove unused flag. (ssa_names_set): New class. (ssa_names_set::build): New function. * ipa-icf-gimple.h (func_checker::gsi_next_nonlocal): New function. (ssa_names_set::contains): New function. (ssa_names_set::add): Likewise. * ipa-icf.c (sem_function::equals_private): Use transformed function. (sem_function::compare_phi_node): Move to func_checker class. (make_pass_ipa_icf): Change namespace. * ipa-icf.h: Add new declarations and rename namespace. * tree-ssa-tail-merge.c (check_edges_correspondence): New function. (find_duplicate): Add usage of IPA ICF gimple infrastructure. (find_clusters_1): Pass new sem_function argument. (find_clusters): Likewise. (tail_merge_optimize): Call IPA ICF comparison machinery. (gvn_uses_equal): Remove. (gimple_equal_p): Likewise. (gsi_advance_bw_nondebug_nonlocal): Likewise. (find_duplicate): Remove unused argument. (make_pass_tail_merge): New function. (pass_tail_merge::execute): Likewise. (equal_ssa_uses): New function. (same_succ_hash): Skip hashing of call arguments. (same_succ_hash): Handle NULL value which can occur. (gimple_operand_equal_value_p): Remove. (same_phi_alternatives): Use newly added function equal_ssa_uses. (same_phi_alternatives_1): Pass a new argument. * passes.def: Add new pass. * tree-pass.h: Likewise. * tree-ssa-pre.c (pass_pre::execute): Remove connection to tail-merge pass. --- gcc/dbgcnt.def | 1 + gcc/ipa-icf-gimple.c | 314 ++++++++++++++++++++++++++++++++++++--- gcc/ipa-icf-gimple.h | 101 +++++++++++-- gcc/ipa-icf.c | 75 +--------- gcc/ipa-icf.h | 24 +-- gcc/passes.def | 1 + gcc/tree-pass.h | 2 +- gcc/tree-ssa-pre.c | 9 -- gcc/tree-ssa-tail-merge.c | 366 +++++++++++++++++++--------------------------- 9 files changed, 556 insertions(+), 337 deletions(-) diff --git a/gcc/dbgcnt.def b/gcc/dbgcnt.def index 95f6b06..79d99d9 100644 --- a/gcc/dbgcnt.def +++ b/gcc/dbgcnt.def @@ -187,6 +187,7 @@ DEBUG_COUNTER (sms_sched_loop) DEBUG_COUNTER (split_for_sched2) DEBUG_COUNTER (store_motion) DEBUG_COUNTER (tail_call) +DEBUG_COUNTER (tail_merge) DEBUG_COUNTER (treepre_insert) DEBUG_COUNTER (tree_sra) DEBUG_COUNTER (vect_loop) diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c index e727693..6413984 100644 --- a/gcc/ipa-icf-gimple.c +++ b/gcc/ipa-icf-gimple.c @@ -54,11 +54,12 @@ along with GCC; see the file COPYING3. If not see #include #include "tree-eh.h" #include "builtins.h" +#include "trans-mem.h" #include "ipa-icf-gimple.h" #include "ipa-icf.h" -namespace ipa_icf_gimple { +namespace icf { /* Initialize internal structures for a given SOURCE_FUNC_DECL and TARGET_FUNC_DECL. Strict polymorphic comparison is processed if @@ -69,14 +70,14 @@ namespace ipa_icf_gimple { func_checker::func_checker (tree source_func_decl, tree target_func_decl, bool compare_polymorphic, - bool ignore_labels, + bool tail_merge_mode, hash_set *ignored_source_nodes, hash_set *ignored_target_nodes) : m_source_func_decl (source_func_decl), m_target_func_decl (target_func_decl), m_ignored_source_nodes (ignored_source_nodes), m_ignored_target_nodes (ignored_target_nodes), m_compare_polymorphic (compare_polymorphic), - m_ignore_labels (ignore_labels) + m_tail_merge_mode (tail_merge_mode) { function *source_func = DECL_STRUCT_FUNCTION (source_func_decl); function *target_func = DECL_STRUCT_FUNCTION (target_func_decl); @@ -102,7 +103,8 @@ func_checker::~func_checker () m_target_ssa_names.release(); } -/* Verifies that trees T1 and T2 are equivalent from perspective of ICF. */ +/* Verifies that trees T1 and T2 are equivalent from + identical code perspective. */ bool func_checker::compare_ssa_name (tree t1, tree t2) @@ -113,6 +115,10 @@ func_checker::compare_ssa_name (tree t1, tree t2) unsigned i1 = SSA_NAME_VERSION (t1); unsigned i2 = SSA_NAME_VERSION (t2); + if (m_comparing_sensitive_rhs && m_tail_merge_mode) + return t1 == t2 || + (m_source_ssa_names[i1] != -1 && m_source_ssa_names[i1] == (int) i2); + if (m_source_ssa_names[i1] == -1) m_source_ssa_names[i1] = i2; else if (m_source_ssa_names[i1] != (int) i2) @@ -237,7 +243,10 @@ func_checker::compatible_polymorphic_types_p (tree t1, tree t2, return true; } -/* Return true if types are compatible from perspective of ICF. */ +/* Return true if types are compatible from identical code perspective. + FIRST_ARGUMENT indicates if the comparison is called for + first parameter of a function. */ + bool func_checker::compatible_types_p (tree t1, tree t2) { @@ -256,7 +265,8 @@ func_checker::compatible_types_p (tree t1, tree t2) return true; } -/* Function compare for equality given memory operands T1 and T2. */ +/* Function compare for equality given memory operands T1 and T2. + If STRICT flag is true, versions must match strictly. */ bool func_checker::compare_memory_operand (tree t1, tree t2) @@ -351,11 +361,18 @@ func_checker::compare_cst_or_decl (tree t1, tree t2) return return_with_debug (ret); } case FUNCTION_DECL: - /* All function decls are in the symbol table and known to match + /* If we are called within an invocation of IPA ICF, + all function decls are in the symbol table and known to match before we start comparing bodies. */ - return true; + return m_tail_merge_mode ? t1 == t2 : true; case VAR_DECL: - return return_with_debug (compare_variable_decl (t1, t2)); + { + /* Be strict in case of tail-merge optimization. */ + if (m_tail_merge_mode) + return t1 == t2; + + return return_with_debug (compare_variable_decl (t1, t2)); + } case FIELD_DECL: { tree offset1 = DECL_FIELD_OFFSET (t1); @@ -371,6 +388,10 @@ func_checker::compare_cst_or_decl (tree t1, tree t2) } case LABEL_DECL: { + /* Be strict in case of tail-merge optimization. */ + if (m_tail_merge_mode) + return t1 == t2; + int *bb1 = m_label_bb_map.get (t1); int *bb2 = m_label_bb_map.get (t2); @@ -380,6 +401,9 @@ func_checker::compare_cst_or_decl (tree t1, tree t2) case RESULT_DECL: case CONST_DECL: { + if (m_tail_merge_mode) + return t1 == t2; + ret = compare_decl (t1, t2); return return_with_debug (ret); } @@ -604,6 +628,21 @@ func_checker::compare_variable_decl (tree t1, tree t2) return return_with_debug (ret); } +/* Reset checker preferences. */ + +void +func_checker::reset_preferences () +{ + m_comparing_sensitive_rhs = false; +} + +/* Set flag that we compare sensitive RHS of a gimple statement. */ + +void +func_checker::set_comparing_sensitive_rhs () +{ + m_comparing_sensitive_rhs = true; +} /* Function visits all gimple labels and creates corresponding mapping between basic blocks and labels. */ @@ -626,6 +665,138 @@ func_checker::parse_labels (sem_bb *bb) } } +/* Return true if gimple STMT is just a local definition in a + basic block. Local definition in this context means that a product + of the statement (transitively) does not escape the basic block. + Used SSA names are contained in SSA_NAMES_SET. */ + +bool +func_checker::stmt_local_def (gimple stmt, ssa_names_set *ssa_names_set) +{ + basic_block bb, def_bb; + imm_use_iterator iter; + use_operand_p use_p; + tree val; + def_operand_p def_p; + + if (gimple_vdef (stmt) != NULL_TREE + || gimple_has_side_effects (stmt) + || gimple_could_trap_p_1 (stmt, false, false) + || gimple_vuse (stmt) != NULL_TREE) + return false; + + def_p = SINGLE_SSA_DEF_OPERAND (stmt, SSA_OP_DEF); + if (def_p == NULL) + return false; + + val = DEF_FROM_PTR (def_p); + if (val == NULL_TREE || TREE_CODE (val) != SSA_NAME) + return false; + + def_bb = gimple_bb (stmt); + + FOR_EACH_IMM_USE_FAST (use_p, iter, val) + { + gimple use_stmt = USE_STMT (use_p); + if (is_gimple_debug (use_stmt)) + continue; + bb = gimple_bb (use_stmt); + if (bb == def_bb) + continue; + + if (gimple_code (use_stmt) != GIMPLE_PHI) + continue; + + return false; + } + + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + if (ssa_names_set && ssa_names_set->contains (gimple_op (stmt, i))) + return false; + + return true; +} + +/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC), + return true if phi nodes are semantically equivalent in these blocks. */ + +bool +func_checker::compare_phi_node (sem_bb *sem_bb1, sem_bb *sem_bb2) +{ + gphi_iterator si1, si2; + gphi *phi1, *phi2; + unsigned size1, size2, i; + tree t1, t2; + edge e1, e2; + + basic_block bb1 = sem_bb1->bb; + basic_block bb2 = sem_bb2->bb; + + gcc_assert (bb1 != NULL); + gcc_assert (bb2 != NULL); + + si2 = gsi_start_phis (bb2); + for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1); + gsi_next (&si1)) + { + gsi_next_nonvirtual_phi (&si1); + gsi_next_nonvirtual_phi (&si2); + + if (gsi_end_p (si1) && gsi_end_p (si2)) + break; + + if (gsi_end_p (si1) || gsi_end_p (si2)) + return return_false (); + + phi1 = si1.phi (); + phi2 = si2.phi (); + + tree phi_result1 = gimple_phi_result (phi1); + tree phi_result2 = gimple_phi_result (phi2); + + if (!compare_operand (phi_result1, phi_result2)) + return return_false_with_msg ("PHI results are different"); + + size1 = gimple_phi_num_args (phi1); + size2 = gimple_phi_num_args (phi2); + + if (size1 != size2) + return return_false (); + + for (i = 0; i < size1; ++i) + { + t1 = gimple_phi_arg (phi1, i)->def; + t2 = gimple_phi_arg (phi2, i)->def; + + if (!compare_operand (t1, t2)) + return return_false (); + + e1 = gimple_phi_arg_edge (phi1, i); + e2 = gimple_phi_arg_edge (phi2, i); + + if (!compare_edge (e1, e2)) + return return_false (); + } + + gsi_next (&si2); + } + + return true; +} + + +bool +func_checker::compare_bb_tail_merge (sem_bb *bb1, sem_bb *bb2) +{ + if (!compare_bb (bb1, bb2)) + return return_false_with_msg ("BB are different"); + + if (!compare_phi_node (bb1, bb2)) + return return_false_with_msg ("PHI nodes are different"); + + return true; +} + /* Basic block equivalence comparison function that returns true if basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond. @@ -642,25 +813,47 @@ func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) gsi1 = gsi_start_bb_nondebug (bb1->bb); gsi2 = gsi_start_bb_nondebug (bb2->bb); - while (!gsi_end_p (gsi1)) + ssa_names_set ssa_names_set1; + ssa_names_set ssa_names_set2; + + ssa_names_set1.build (bb1->bb); + ssa_names_set2.build (bb2->bb); + + while (true) { - if (gsi_end_p (gsi2)) - return return_false (); + if (m_tail_merge_mode) + { + gsi_next_nonlocal (&gsi1, &ssa_names_set1); + gsi_next_nonlocal (&gsi2, &ssa_names_set2); + } + + if (gsi_end_p (gsi1) || gsi_end_p (gsi2)) + break; s1 = gsi_stmt (gsi1); s2 = gsi_stmt (gsi2); + /* What could be better than to this this here is to blacklist the bb + containing the stmt, when encountering the stmt f.i. in + same_succ_hash. */ + if (is_tm_ending (s1) + || is_tm_ending (s2)) + return return_false_with_msg ("TM endings are different"); + int eh1 = lookup_stmt_eh_lp_fn (DECL_STRUCT_FUNCTION (m_source_func_decl), s1); int eh2 = lookup_stmt_eh_lp_fn (DECL_STRUCT_FUNCTION (m_target_func_decl), s2); - if (eh1 != eh2) + if (eh1 != eh2 && !m_tail_merge_mode) return return_false_with_msg ("EH regions are different"); if (gimple_code (s1) != gimple_code (s2)) return return_false_with_msg ("gimple codes are different"); + /* Reset inner state of the checker. */ + reset_preferences (); + switch (gimple_code (s1)) { case GIMPLE_CALL: @@ -723,7 +916,7 @@ func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2) gsi_next_nondebug (&gsi2); } - if (!gsi_end_p (gsi2)) + if (!gsi_end_p (gsi1) || !gsi_end_p (gsi2)) return return_false (); return true; @@ -776,6 +969,8 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2) return return_false_with_msg ("static call chains are different"); /* Checking of argument. */ + set_comparing_sensitive_rhs (); + for (i = 0; i < gimple_call_num_args (s1); ++i) { t1 = gimple_call_arg (s1, i); @@ -785,10 +980,29 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2) return return_false_with_msg ("memory operands are different"); } + /* Reset preferences after we compare all arguments. */ + reset_preferences (); + /* Return value checking. */ t1 = gimple_get_lhs (s1); t2 = gimple_get_lhs (s2); + /* In case of tail merge mode, ignore all UBSAN_* internal functions. */ + if (gimple_call_internal_p (s1)) + switch (gimple_call_internal_fn (s1)) + { + case IFN_UBSAN_NULL: + case IFN_UBSAN_BOUNDS: + case IFN_UBSAN_VPTR: + case IFN_UBSAN_CHECK_ADD: + case IFN_UBSAN_CHECK_SUB: + case IFN_UBSAN_CHECK_MUL: + case IFN_UBSAN_OBJECT_SIZE: + return false; + default: + break; + } + return compare_memory_operand (t1, t2); } @@ -820,6 +1034,10 @@ func_checker::compare_gimple_assign (gimple s1, gimple s2) arg1 = gimple_op (s1, i); arg2 = gimple_op (s2, i); + /* Set indication that we're going to compare RHS of a gimple assign. */ + if (i != 0) + set_comparing_sensitive_rhs (); + if (!compare_memory_operand (arg1, arg2)) return return_false_with_msg ("memory operands are different"); } @@ -869,9 +1087,6 @@ func_checker::compare_tree_ssa_label (tree t1, tree t2) bool func_checker::compare_gimple_label (const glabel *g1, const glabel *g2) { - if (m_ignore_labels) - return true; - tree t1 = gimple_label_label (g1); tree t2 = gimple_label_label (g2); @@ -1037,4 +1252,67 @@ func_checker::compare_gimple_asm (const gasm *g1, const gasm *g2) return true; } -} // ipa_icf_gimple namespace +void +ssa_names_set::build (basic_block bb) +{ + tree var; + ssa_op_iter iter; + + /* Build default set of important SSA names. */ + gimple_stmt_iterator gsi = gsi_start_nondebug_bb (bb); + + /* In the first phase, we iterate all non-local statements and + we extract all SSA names that are used in these statements. */ + while (!gsi_end_p (gsi)) + { + gimple stmt = gsi_stmt (gsi); + if (!func_checker::stmt_local_def (stmt, NULL)) + for (unsigned i = 0; i < gimple_num_ops (stmt); i++) + add (gimple_op (stmt, i)); + + gsi_next_nondebug (&gsi); + } + + gsi = gsi_last_nondebug_bb (bb); + + /* In the second phase, we process reverse run through all statements + and we add all SSA names whose values is based on a SSA name already + belonging to set. The set was created in previous step and + is incrementally expanded. At the end of this phase, we will have + all SSA names that not used just locally. */ + while (!gsi_end_p (gsi)) + { + gimple stmt = gsi_stmt (gsi); + + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + { + tree lhs = gimple_assign_lhs (stmt); + if (contains (lhs)) + { + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE) + add (var); + } + break; + } + case GIMPLE_COND: + case GIMPLE_SWITCH: + case GIMPLE_CALL: + case GIMPLE_RETURN: + case GIMPLE_ASM: + { + FOR_EACH_SSA_TREE_OPERAND (var, stmt, iter, SSA_OP_USE) + add (var); + + break; + } + default: + break; + } + + gsi_prev_nondebug (&gsi); + } +} + +} // icf namespace diff --git a/gcc/ipa-icf-gimple.h b/gcc/ipa-icf-gimple.h index 6a9cbed..3e8db52 100644 --- a/gcc/ipa-icf-gimple.h +++ b/gcc/ipa-icf-gimple.h @@ -106,13 +106,14 @@ return_different_stmts_1 (gimple s1, gimple s2, const char *code, #define return_different_stmts(s1, s2, code) \ return_different_stmts_1 (s1, s2, code, __func__, __LINE__) -namespace ipa_icf_gimple { +namespace icf { /* Basic block struct for semantic equality pass. */ class sem_bb { public: - sem_bb (basic_block bb_, unsigned nondbg_stmt_count_, unsigned edge_count_): + sem_bb (basic_block bb_, unsigned nondbg_stmt_count_ = 0, + unsigned edge_count_ = 0): bb (bb_), nondbg_stmt_count (nondbg_stmt_count_), edge_count (edge_count_) {} /* Basic block the structure belongs to. */ @@ -125,6 +126,9 @@ public: unsigned edge_count; }; +/* Forward declaration. */ +class ssa_names_set; + /* A class aggregating all connections and semantic equivalents for a given pair of semantic function candidates. */ class func_checker @@ -138,7 +142,7 @@ public: of declarations that can be skipped. */ func_checker (tree source_func_decl, tree target_func_decl, bool compare_polymorphic, - bool ignore_labels = false, + bool tail_merge_mode = false, hash_set *ignored_source_nodes = NULL, hash_set *ignored_target_nodes = NULL); @@ -149,11 +153,20 @@ public: mapping between basic blocks and labels. */ void parse_labels (sem_bb *bb); + /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC), + true value is returned if phi nodes are semantically + equivalent in these blocks. */ + bool compare_phi_node (sem_bb *sem_bb1, sem_bb *sem_bb2); + + /* Run tail-merge comparison for basic blocks BB1 and BB2. */ + bool compare_bb_tail_merge (sem_bb *bb1, sem_bb *bb2); + /* Basic block equivalence comparison function that returns true if basic blocks BB1 and BB2 correspond. */ bool compare_bb (sem_bb *bb1, sem_bb *bb2); - /* Verifies that trees T1 and T2 are equivalent from perspective of ICF. */ + /* Verifies that trees T1 and T2 are equivalent from + identical code perspective. */ bool compare_ssa_name (tree t1, tree t2); /* Verification function for edges E1 and E2. */ @@ -219,24 +232,35 @@ public: two trees are semantically equivalent. */ bool compare_tree_list_operand (tree t1, tree t2); - /* Verifies that trees T1 and T2, representing function declarations - are equivalent from perspective of ICF. */ - bool compare_function_decl (tree t1, tree t2); - /* Verifies that trees T1 and T2 do correspond. */ bool compare_variable_decl (tree t1, tree t2); + /* Reset checker preferences. */ + void reset_preferences (); + + /* Set flag that we compare sensitive RHS of a gimple statement. */ + void set_comparing_sensitive_rhs (); + /* Return true if types are compatible for polymorphic call analysis. COMPARE_PTR indicates if polymorphic type comparsion should be done for pointers, too. */ static bool compatible_polymorphic_types_p (tree t1, tree t2, bool compare_ptr); - /* Return true if types are compatible from perspective of ICF. + /* Return true if types are compatible from identical code perspective. FIRST_ARGUMENT indicates if the comparison is called for first parameter of a function. */ static bool compatible_types_p (tree t1, tree t2); + /* Return true if gimple STMT is just a local definition in a + basic block. Local definition in this context means that a product + of the statement (transitively) does not escape the basic block. + Used SSA names are contained in SSA_NAMES_SET. */ + static bool stmt_local_def (gimple stmt, ssa_names_set *ssa_names_set); + + /* Advance the iterator to the next non-local gimple statement. */ + static void gsi_next_nonlocal (gimple_stmt_iterator *i, + ssa_names_set *ssa_names_set); private: /* Vector mapping source SSA names to target ones. */ @@ -271,8 +295,61 @@ private: /* Flag if polymorphic comparison should be executed. */ bool m_compare_polymorphic; - /* Flag if ignore labels in comparison. */ - bool m_ignore_labels; + /* Flag which changes behavior for tree-ssa-tail-merge pass. */ + bool m_tail_merge_mode; + + /* Flag which indicates that we compare a sensitve RHS operand. */ + bool m_comparing_sensitive_rhs; }; -} // ipa_icf_gimple namespace +/* SSA NAMES set. */ +class ssa_names_set +{ +public: + /* Return true if SSA_NAME is in the set. */ + bool contains (tree ssa_name); + + /* Add a new SSA_NAME to the set. */ + void add (tree ssa_name); + + /* Build the set for given basic block BB. In the first phase, we collect + all SSA names that are not used not just in the basic block. After that, + having this set of SSA names, we can efficiently mark all statements + in the basic block that must be compared for equality. The rest can be + just skipped. Very similar operation was processed + in original implementation of tree-ssa-tail merge pass. */ + void build (basic_block bb); + +private: + hash_set m_set; +}; + +inline void +func_checker::gsi_next_nonlocal (gimple_stmt_iterator *i, + ssa_names_set *ssa_names_set) +{ + while (!gsi_end_p (*i) && + (is_gimple_debug (gsi_stmt (*i)) + || func_checker::stmt_local_def (gsi_stmt (*i), ssa_names_set))) + gsi_next (i); +} + +inline bool +ssa_names_set::contains (tree ssa_name) +{ + if (ssa_name == NULL || TREE_CODE (ssa_name) != SSA_NAME) + return false; + + return m_set.contains (ssa_name); +} + +inline void +ssa_names_set::add (tree ssa_name) +{ + if (ssa_name == NULL || TREE_CODE (ssa_name) != SSA_NAME) + return; + + m_set.add (ssa_name); +} + +} // icf namespace diff --git a/gcc/ipa-icf.c b/gcc/ipa-icf.c index 3597b3a1..ca4f1e9 100644 --- a/gcc/ipa-icf.c +++ b/gcc/ipa-icf.c @@ -97,9 +97,7 @@ along with GCC; see the file COPYING3. If not see #include "stor-layout.h" #include "dbgcnt.h" -using namespace ipa_icf_gimple; - -namespace ipa_icf { +namespace icf { /* Initialization and computation of symtab node hash, there data are propagated later on. */ @@ -995,7 +993,8 @@ sem_function::equals_private (sem_item *item) /* Basic block PHI nodes comparison. */ for (unsigned i = 0; i < bb_sorted.length (); i++) - if (!compare_phi_node (bb_sorted[i]->bb, m_compared_func->bb_sorted[i]->bb)) + if (!m_checker->compare_phi_node (bb_sorted[i], + m_compared_func->bb_sorted[i])) return return_false_with_msg ("PHI node comparison returns false"); return result; @@ -1707,70 +1706,6 @@ sem_function::parse (cgraph_node *node, bitmap_obstack *stack) return f; } -/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC), - return true if phi nodes are semantically equivalent in these blocks . */ - -bool -sem_function::compare_phi_node (basic_block bb1, basic_block bb2) -{ - gphi_iterator si1, si2; - gphi *phi1, *phi2; - unsigned size1, size2, i; - tree t1, t2; - edge e1, e2; - - gcc_assert (bb1 != NULL); - gcc_assert (bb2 != NULL); - - si2 = gsi_start_phis (bb2); - for (si1 = gsi_start_phis (bb1); !gsi_end_p (si1); - gsi_next (&si1)) - { - gsi_next_nonvirtual_phi (&si1); - gsi_next_nonvirtual_phi (&si2); - - if (gsi_end_p (si1) && gsi_end_p (si2)) - break; - - if (gsi_end_p (si1) || gsi_end_p (si2)) - return return_false(); - - phi1 = si1.phi (); - phi2 = si2.phi (); - - tree phi_result1 = gimple_phi_result (phi1); - tree phi_result2 = gimple_phi_result (phi2); - - if (!m_checker->compare_operand (phi_result1, phi_result2)) - return return_false_with_msg ("PHI results are different"); - - size1 = gimple_phi_num_args (phi1); - size2 = gimple_phi_num_args (phi2); - - if (size1 != size2) - return return_false (); - - for (i = 0; i < size1; ++i) - { - t1 = gimple_phi_arg (phi1, i)->def; - t2 = gimple_phi_arg (phi2, i)->def; - - if (!m_checker->compare_operand (t1, t2)) - return return_false (); - - e1 = gimple_phi_arg_edge (phi1, i); - e2 = gimple_phi_arg_edge (phi2, i); - - if (!m_checker->compare_edge (e1, e2)) - return return_false (); - } - - gsi_next (&si2); - } - - return true; -} - /* Returns true if tree T can be compared as a handled component. */ bool @@ -3553,10 +3488,10 @@ public: } }; // class pass_ipa_icf -} // ipa_icf namespace +} // icf namespace ipa_opt_pass_d * make_pass_ipa_icf (gcc::context *ctxt) { - return new ipa_icf::pass_ipa_icf (ctxt); + return new icf::pass_ipa_icf (ctxt); } diff --git a/gcc/ipa-icf.h b/gcc/ipa-icf.h index 6428f25..0cc9c98 100644 --- a/gcc/ipa-icf.h +++ b/gcc/ipa-icf.h @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ -namespace ipa_icf { +namespace icf { class sem_item; /* Congruence class encompasses a collection of either functions or @@ -350,19 +350,23 @@ public: unsigned ssa_names_size; /* Array of structures for all basic blocks. */ - vec bb_sorted; + vec bb_sorted; /* Return true if parameter I may be used. */ bool param_used_p (unsigned int i); + /* Set a new function checker. */ + void set_checker (func_checker *checker) + { + if (m_checker) + delete m_checker; + + m_checker = checker; + } + private: /* Calculates hash value based on a BASIC_BLOCK. */ - hashval_t get_bb_hash (const ipa_icf_gimple::sem_bb *basic_block); - - /* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC), - true value is returned if phi nodes are semantically - equivalent in these blocks . */ - bool compare_phi_node (basic_block bb1, basic_block bb2); + hashval_t get_bb_hash (const sem_bb *basic_block); /* Basic blocks dictionary BB_DICT returns true if SOURCE index BB corresponds to TARGET. */ @@ -379,7 +383,7 @@ private: static bool icf_handled_component_p (tree t); /* Function checker stores binding between functions. */ - ipa_icf_gimple::func_checker *m_checker; + func_checker *m_checker; /* COMPARED_FUNC is a function that we compare to. */ sem_function *m_compared_func; @@ -627,4 +631,4 @@ private: bitmap_obstack m_bmstack; }; // class sem_item_optimizer -} // ipa_icf namespace +} // icf namespace diff --git a/gcc/passes.def b/gcc/passes.def index 6b66f8f..c20357d 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -216,6 +216,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_laddress); NEXT_PASS (pass_split_crit_edges); NEXT_PASS (pass_pre); + NEXT_PASS (pass_tail_merge); NEXT_PASS (pass_sink_code); NEXT_PASS (pass_asan); NEXT_PASS (pass_tsan); diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index 7b66a1c..9b8e4a9 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -395,7 +395,7 @@ extern gimple_opt_pass *make_pass_merge_phi (gcc::context *ctxt); extern gimple_opt_pass *make_pass_split_crit_edges (gcc::context *ctxt); extern gimple_opt_pass *make_pass_laddress (gcc::context *ctxt); extern gimple_opt_pass *make_pass_pre (gcc::context *ctxt); -extern unsigned int tail_merge_optimize (unsigned int); +extern gimple_opt_pass *make_pass_tail_merge (gcc::context *ctxt); extern gimple_opt_pass *make_pass_profile (gcc::context *ctxt); extern gimple_opt_pass *make_pass_strip_predict_hints (gcc::context *ctxt); extern gimple_opt_pass *make_pass_lower_complex_O0 (gcc::context *ctxt); diff --git a/gcc/tree-ssa-pre.c b/gcc/tree-ssa-pre.c index 041cb78..8f56881 100644 --- a/gcc/tree-ssa-pre.c +++ b/gcc/tree-ssa-pre.c @@ -4869,15 +4869,6 @@ pass_pre::execute (function *fun) todo |= fini_eliminate (); loop_optimizer_finalize (); - /* TODO: tail_merge_optimize may merge all predecessors of a block, in which - case we can merge the block with the remaining predecessor of the block. - It should either: - - call merge_blocks after each tail merge iteration - - call merge_blocks after all tail merge iterations - - mark TODO_cleanup_cfg when necessary - - share the cfg cleanup with fini_pre. */ - todo |= tail_merge_optimize (todo); - free_scc_vn (); /* Tail merging invalidates the virtual SSA web, together with diff --git a/gcc/tree-ssa-tail-merge.c b/gcc/tree-ssa-tail-merge.c index 88a3032..8c9a1cb 100644 --- a/gcc/tree-ssa-tail-merge.c +++ b/gcc/tree-ssa-tail-merge.c @@ -1,6 +1,7 @@ /* Tail merging for gimple. Copyright (C) 2011-2015 Free Software Foundation, Inc. Contributed by Tom de Vries (tom@codesourcery.com) + and Martin Liska (mliska@suse.cz) This file is part of GCC. @@ -124,35 +125,10 @@ along with GCC; see the file COPYING3. If not see are redirected to enter the other block. Note that this operation might involve introducing phi operations. - For efficient implementation, we would like to value numbers the blocks, and - have a comparison operator that tells us whether the blocks are equal. - Besides being runtime efficient, block value numbering should also abstract - from irrelevant differences in order of operations, much like normal value - numbering abstracts from irrelevant order of operations. - - For the first situation (same_operations, same predecessors), normal value - numbering fits well. We can calculate a block value number based on the - value numbers of the defs and vdefs. - - For the second situation (same operations, same successors), this approach - doesn't work so well. We can illustrate this using the example. The calls - to free use different vdefs: MEMD.3923_16 and MEMD.3923_14, and these will - remain different in value numbering, since they represent different memory - states. So the resulting vdefs of the frees will be different in value - numbering, so the block value numbers will be different. - - The reason why we call the blocks equal is not because they define the same - values, but because uses in the blocks use (possibly different) defs in the - same way. To be able to detect this efficiently, we need to do some kind of - reverse value numbering, meaning number the uses rather than the defs, and - calculate a block value number based on the value number of the uses. - Ideally, a block comparison operator will also indicate which phis are needed - to merge the blocks. - - For the moment, we don't do block value numbering, but we do insn-by-insn - matching, using scc value numbers to match operations with results, and - structural comparison otherwise, while ignoring vop mismatches. - + Basic blocks are compared insn-by-insn by ICF infrastructure which was + originally used for IPA-ICF. Even though the pass is function base, + BB equality is subset of comparisons that are performed and can be reused + for this pass. IMPLEMENTATION @@ -198,6 +174,7 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "stor-layout.h" #include "trans-mem.h" +#include "inchash.h" #include "tm_p.h" #include "cfganal.h" #include "cfgcleanup.h" @@ -209,11 +186,19 @@ along with GCC; see the file COPYING3. If not see #include "tree-into-ssa.h" #include "params.h" #include "gimple-pretty-print.h" -#include "tree-ssa-sccvn.h" #include "tree-dump.h" #include "cfgloop.h" #include "tree-pass.h" #include "trans-mem.h" +#include "ipa-ref.h" +#include "lto-streamer.h" +#include "cgraph.h" +#include +#include "ipa-icf-gimple.h" +#include "ipa-icf.h" +#include "dbgcnt.h" + +using namespace icf; /* Describes a group of bbs with the same successors. The successor bbs are cached in succs, and the successor edge flags are cached in succ_flags. @@ -360,24 +345,29 @@ gsi_advance_fw_nondebug_nonlocal (gimple_stmt_iterator *gsi) } } -/* VAL1 and VAL2 are either: - - uses in BB1 and BB2, or - - phi alternatives for BB1 and BB2. - Return true if the uses have the same gvn value. */ +/* Return true if either VAL1 and VAL2 are constant values and are equal, + or if both values are SSA names. In this case we conditionally return true + and ICF machinery will verify that these SSA names are really equivalent + in basic blocks we compare. */ static bool -gvn_uses_equal (tree val1, tree val2) +equal_ssa_uses (tree val1, tree val2, + auto_vec > *ssa_phi_pairs) { gcc_checking_assert (val1 != NULL_TREE && val2 != NULL_TREE); if (val1 == val2) return true; - if (vn_valueize (val1) != vn_valueize (val2)) + if (CONSTANT_CLASS_P (val1) && CONSTANT_CLASS_P (val2)) + return operand_equal_p (val1, val2, OEP_ONLY_CONST); + else if (TREE_CODE (val1) == SSA_NAME && TREE_CODE (val2) == SSA_NAME) + { + ssa_phi_pairs->safe_push (std::make_pair (val1, val2)); + return true; + } + else return false; - - return ((TREE_CODE (val1) == SSA_NAME || CONSTANT_CLASS_P (val1)) - && (TREE_CODE (val2) == SSA_NAME || CONSTANT_CLASS_P (val2))); } /* Prints E to FILE. */ @@ -454,7 +444,6 @@ same_succ_hash (const_same_succ e) basic_block bb = BASIC_BLOCK_FOR_FN (cfun, first); int size = 0; gimple stmt; - tree arg; unsigned int s; bitmap_iterator bs; @@ -480,12 +469,6 @@ same_succ_hash (const_same_succ e) if (gimple_call_chain (stmt)) inchash::add_expr (gimple_call_chain (stmt), hstate); } - for (i = 0; i < gimple_call_num_args (stmt); i++) - { - arg = gimple_call_arg (stmt, i); - arg = vn_valueize (arg); - inchash::add_expr (arg, hstate); - } } hstate.add_int (size); @@ -818,6 +801,10 @@ static void same_succ_flush_bb (basic_block bb) { same_succ same = BB_SAME_SUCC (bb); + + if (same == NULL) + return; + BB_SAME_SUCC (bb) = NULL; if (bitmap_single_bit_set_p (same->bbs)) same_succ_htab->remove_elt_with_hash (same, same->hashval); @@ -1083,201 +1070,94 @@ set_cluster (basic_block bb1, basic_block bb2) gcc_unreachable (); } -/* Return true if gimple operands T1 and T2 have the same value. */ - -static bool -gimple_operand_equal_value_p (tree t1, tree t2) -{ - if (t1 == t2) - return true; - - if (t1 == NULL_TREE - || t2 == NULL_TREE) - return false; - - if (operand_equal_p (t1, t2, 0)) - return true; - - return gvn_uses_equal (t1, t2); -} - -/* Return true if gimple statements S1 and S2 are equal. Gimple_bb (s1) and - gimple_bb (s2) are members of SAME_SUCC. */ +/* Make sure that all edges coming from basic block BB1 and BB2 have + the same destination index, and make sure that true/false edge values + are equal. */ static bool -gimple_equal_p (same_succ same_succ, gimple s1, gimple s2) +check_edges_correspondence (basic_block bb1, basic_block bb2) { - unsigned int i; - tree lhs1, lhs2; - basic_block bb1 = gimple_bb (s1), bb2 = gimple_bb (s2); - tree t1, t2; - bool inv_cond; - enum tree_code code1, code2; - - if (gimple_code (s1) != gimple_code (s2)) - return false; + edge e1, e2; + edge_iterator ei2 = ei_start (bb2->succs); - switch (gimple_code (s1)) + for (edge_iterator ei1 = ei_start (bb1->succs); ei_cond (ei1, &e1); + ei_next (&ei1)) { - case GIMPLE_CALL: - if (!gimple_call_same_target_p (s1, s2)) - return false; - - t1 = gimple_call_chain (s1); - t2 = gimple_call_chain (s2); - if (!gimple_operand_equal_value_p (t1, t2)) - return false; - - if (gimple_call_num_args (s1) != gimple_call_num_args (s2)) - return false; - - for (i = 0; i < gimple_call_num_args (s1); ++i) - { - t1 = gimple_call_arg (s1, i); - t2 = gimple_call_arg (s2, i); - if (!gimple_operand_equal_value_p (t1, t2)) - return false; - } - - lhs1 = gimple_get_lhs (s1); - lhs2 = gimple_get_lhs (s2); - if (lhs1 == NULL_TREE && lhs2 == NULL_TREE) - return true; - if (lhs1 == NULL_TREE || lhs2 == NULL_TREE) - return false; - if (TREE_CODE (lhs1) == SSA_NAME && TREE_CODE (lhs2) == SSA_NAME) - return vn_valueize (lhs1) == vn_valueize (lhs2); - return operand_equal_p (lhs1, lhs2, 0); - - case GIMPLE_ASSIGN: - lhs1 = gimple_get_lhs (s1); - lhs2 = gimple_get_lhs (s2); - if (TREE_CODE (lhs1) != SSA_NAME - && TREE_CODE (lhs2) != SSA_NAME) - return (operand_equal_p (lhs1, lhs2, 0) - && gimple_operand_equal_value_p (gimple_assign_rhs1 (s1), - gimple_assign_rhs1 (s2))); - else if (TREE_CODE (lhs1) == SSA_NAME - && TREE_CODE (lhs2) == SSA_NAME) - return operand_equal_p (gimple_assign_rhs1 (s1), - gimple_assign_rhs1 (s2), 0); - return false; - - case GIMPLE_COND: - t1 = gimple_cond_lhs (s1); - t2 = gimple_cond_lhs (s2); - if (!gimple_operand_equal_value_p (t1, t2)) - return false; + ei_cond (ei2, &e2); - t1 = gimple_cond_rhs (s1); - t2 = gimple_cond_rhs (s2); - if (!gimple_operand_equal_value_p (t1, t2)) + if (e1->dest->index != e2->dest->index + || (e1->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)) + != (e2->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))) return false; - code1 = gimple_expr_code (s1); - code2 = gimple_expr_code (s2); - inv_cond = (bitmap_bit_p (same_succ->inverse, bb1->index) - != bitmap_bit_p (same_succ->inverse, bb2->index)); - if (inv_cond) - { - bool honor_nans = HONOR_NANS (t1); - code2 = invert_tree_comparison (code2, honor_nans); - } - return code1 == code2; - - default: - return false; + ei_next (&ei2); } -} - -/* Let GSI skip backwards over local defs. Return the earliest vuse in VUSE. - Return true in VUSE_ESCAPED if the vuse influenced a SSA_OP_DEF of one of the - processed statements. */ - -static void -gsi_advance_bw_nondebug_nonlocal (gimple_stmt_iterator *gsi, tree *vuse, - bool *vuse_escaped) -{ - gimple stmt; - tree lvuse; - - while (true) - { - if (gsi_end_p (*gsi)) - return; - stmt = gsi_stmt (*gsi); - lvuse = gimple_vuse (stmt); - if (lvuse != NULL_TREE) - { - *vuse = lvuse; - if (!ZERO_SSA_OPERANDS (stmt, SSA_OP_DEF)) - *vuse_escaped = true; - } - - if (!stmt_local_def (stmt)) - return; - gsi_prev_nondebug (gsi); - } + return true; } /* Determines whether BB1 and BB2 (members of same_succ) are duplicates. If so, clusters them. */ static void -find_duplicate (same_succ same_succ, basic_block bb1, basic_block bb2) +find_duplicate (basic_block bb1, basic_block bb2, sem_function &f, + auto_vec > &ssa_phi_pairs) { - gimple_stmt_iterator gsi1 = gsi_last_nondebug_bb (bb1); - gimple_stmt_iterator gsi2 = gsi_last_nondebug_bb (bb2); - tree vuse1 = NULL_TREE, vuse2 = NULL_TREE; - bool vuse_escaped = false; + sem_bb sem_bb1 = sem_bb (bb1); + sem_bb sem_bb2 = sem_bb (bb2); + + func_checker *checker = new func_checker (f.decl, f.decl, true, true); + f.set_checker (checker); + bool icf_result = checker->compare_bb_tail_merge (&sem_bb1, &sem_bb2); - gsi_advance_bw_nondebug_nonlocal (&gsi1, &vuse1, &vuse_escaped); - gsi_advance_bw_nondebug_nonlocal (&gsi2, &vuse2, &vuse_escaped); + if (!icf_result || !check_edges_correspondence (bb1, bb2)) + return; - while (!gsi_end_p (gsi1) && !gsi_end_p (gsi2)) + for (unsigned i = 0; i < ssa_phi_pairs.length (); i++) { - gimple stmt1 = gsi_stmt (gsi1); - gimple stmt2 = gsi_stmt (gsi2); - - /* What could be better than this here is to blacklist the bb - containing the stmt, when encountering the stmt f.i. in - same_succ_hash. */ - if (is_tm_ending (stmt1) - || is_tm_ending (stmt2)) - return; + std::pair v = ssa_phi_pairs[i]; - if (!gimple_equal_p (same_succ, stmt1, stmt2)) - return; + /* If one of definitions does not belong the function we consider + for equation, SSA names must be a same pointer. */ + gimple def1 = SSA_NAME_DEF_STMT (v.first); + gimple def2 = SSA_NAME_DEF_STMT (v.second); - gsi_prev_nondebug (&gsi1); - gsi_prev_nondebug (&gsi2); - gsi_advance_bw_nondebug_nonlocal (&gsi1, &vuse1, &vuse_escaped); - gsi_advance_bw_nondebug_nonlocal (&gsi2, &vuse2, &vuse_escaped); - } + basic_block bbdef1 = gimple_bb (def1); + basic_block bbdef2 = gimple_bb (def2); - if (!(gsi_end_p (gsi1) && gsi_end_p (gsi2))) - return; + if (bb1 != bbdef1 || bb2 != bbdef2) + if (v.first != v.second) + return; - /* If the incoming vuses are not the same, and the vuse escaped into an - SSA_OP_DEF, then merging the 2 blocks will change the value of the def, - which potentially means the semantics of one of the blocks will be changed. - TODO: make this check more precise. */ - if (vuse_escaped && vuse1 != vuse2) - return; + if (!checker->compare_ssa_name (v.first, v.second)) + return; + } if (dump_file) + { fprintf (dump_file, "find_duplicates: duplicate of \n", bb1->index, bb2->index); - set_cluster (bb1, bb2); + } + + if (dbg_cnt (tail_merge)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + dump_bb (dump_file, bb1, 0, TDF_DETAILS); + dump_bb (dump_file, bb2, 0, TDF_DETAILS); + } + + set_cluster (bb1, bb2); + } } /* Returns whether for all phis in DEST the phi alternatives for E1 and E2 are equal. */ static bool -same_phi_alternatives_1 (basic_block dest, edge e1, edge e2) +same_phi_alternatives_1 (basic_block dest, edge e1, edge e2, + auto_vec > *ssa_phi_pairs) { int n1 = e1->dest_idx, n2 = e2->dest_idx; gphi_iterator gsi; @@ -1294,7 +1174,8 @@ same_phi_alternatives_1 (basic_block dest, edge e1, edge e2) if (operand_equal_for_phi_arg_p (val1, val2)) continue; - if (gvn_uses_equal (val1, val2)) + + if (equal_ssa_uses (val1, val2, ssa_phi_pairs)) continue; return false; @@ -1307,7 +1188,8 @@ same_phi_alternatives_1 (basic_block dest, edge e1, edge e2) phi alternatives for BB1 and BB2 are equal. */ static bool -same_phi_alternatives (same_succ same_succ, basic_block bb1, basic_block bb2) +same_phi_alternatives (same_succ same_succ, basic_block bb1, basic_block bb2, + auto_vec > *ssa_phi_pairs) { unsigned int s; bitmap_iterator bs; @@ -1325,7 +1207,7 @@ same_phi_alternatives (same_succ same_succ, basic_block bb1, basic_block bb2) /* For all phis in bb, the phi alternatives for e1 and e2 need to have the same value. */ - if (!same_phi_alternatives_1 (succ, e1, e2)) + if (!same_phi_alternatives_1 (succ, e1, e2, ssa_phi_pairs)) return false; } @@ -1392,7 +1274,7 @@ deps_ok_for_redirect (basic_block bb1, basic_block bb2) /* Within SAME_SUCC->bbs, find clusters of bbs which can be merged. */ static void -find_clusters_1 (same_succ same_succ) +find_clusters_1 (same_succ same_succ, sem_function &f) { basic_block bb1, bb2; unsigned int i, j; @@ -1431,10 +1313,12 @@ find_clusters_1 (same_succ same_succ) if (!deps_ok_for_redirect (bb1, bb2)) continue; - if (!(same_phi_alternatives (same_succ, bb1, bb2))) + auto_vec > ssa_phi_pairs; + + if (!(same_phi_alternatives (same_succ, bb1, bb2, &ssa_phi_pairs))) continue; - find_duplicate (same_succ, bb1, bb2); + find_duplicate (bb1, bb2, f, ssa_phi_pairs); } } } @@ -1442,7 +1326,7 @@ find_clusters_1 (same_succ same_succ) /* Find clusters of bbs which can be merged. */ static void -find_clusters (void) +find_clusters (sem_function &f) { same_succ same; @@ -1455,7 +1339,7 @@ find_clusters (void) fprintf (dump_file, "processing worklist entry\n"); same_succ_print (dump_file, same); } - find_clusters_1 (same); + find_clusters_1 (same, f); } } @@ -1637,9 +1521,10 @@ update_debug_stmts (void) /* Runs tail merge optimization. */ -unsigned int -tail_merge_optimize (unsigned int todo) +static unsigned int +tail_merge_optimize () { + unsigned todo = 0; int nr_bbs_removed_total = 0; int nr_bbs_removed; bool loop_entered = false; @@ -1660,6 +1545,10 @@ tail_merge_optimize (unsigned int todo) } init_worklist (); + bitmap_obstack b; + bitmap_obstack_initialize (&b); + cgraph_node *node = cgraph_node::get (cfun->decl); + while (!worklist.is_empty ()) { if (!loop_entered) @@ -1675,7 +1564,8 @@ tail_merge_optimize (unsigned int todo) if (dump_file && (dump_flags & TDF_DETAILS)) fprintf (dump_file, "worklist iteration #%d\n", iteration_nr); - find_clusters (); + sem_function f (node, 0, &b); + find_clusters (f); gcc_assert (worklist.is_empty ()); if (all_clusters.is_empty ()) break; @@ -1726,3 +1616,45 @@ tail_merge_optimize (unsigned int todo) return todo; } + +namespace { + +const pass_data pass_data_tail_merge = +{ + GIMPLE_PASS, /* type */ + "tail-merge", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_TREE_TAIL_MERGE, /* tv_id */ + ( PROP_cfg | PROP_ssa ), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + TODO_update_ssa_only_virtuals, /* todo_flags_finish */ +}; + +class pass_tail_merge : public gimple_opt_pass +{ +public: + pass_tail_merge (gcc::context *ctxt) + : gimple_opt_pass (pass_data_tail_merge, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) { return flag_tree_tail_merge != 0; } + virtual unsigned int execute (function *); + +}; // class pass_tail_merge + +} // anon namespace + +unsigned int +pass_tail_merge::execute (function *) +{ + return tail_merge_optimize (); +} + +gimple_opt_pass * +make_pass_tail_merge (gcc::context *ctxt) +{ + return new pass_tail_merge (ctxt); +} -- 2.4.6