summaryrefslogtreecommitdiff
path: root/gcc/ipa-icf-gimple.c
diff options
context:
space:
mode:
authormarxin <marxin@138bc75d-0d04-0410-961f-82ee72b054a4>2014-10-16 10:47:55 +0000
committermarxin <marxin@138bc75d-0d04-0410-961f-82ee72b054a4>2014-10-16 10:47:55 +0000
commit52200d03c231f0bddbd4bbc5cd3608c6a1dd4598 (patch)
treea924d04dfecfeecfc3dbb2cd245d5482ed23f090 /gcc/ipa-icf-gimple.c
parentfc8021097ec7542658f7b035604de66387c3d50e (diff)
downloadgcc-52200d03c231f0bddbd4bbc5cd3608c6a1dd4598.tar.gz
IPA ICF pass, part 3/5
* Makefile.in: New object files included. * cgraph.c (cgraph_node::dump): New cgraph_node flag icf_merged is printed. (verify_edge_corresponds_to_fndecl): More sensitive verification of nodes that are merged by IPA ICF. * cgraph.h (cgraph_node::num_references): New function. * cgraphunit.c (cgraph_node::expand_thunk): White space fixed. * common.opt: New options ipa-icf, ipa-icf-functions and ipa-icf-variables introduced. * doc/invoke.texi: Documentation of new options introduced. * ipa-icf-gimple.c: New file. * ipa-icf-gimple.h: New file. * ipa-icf.c: New file. * ipa-icf.h: New file. * lto-cgraph.c (lto_output_node): Streaming of icf_merged flag added. (input_overwrite_node): Likewise. * lto-section-in.c: New icf section added. * lto-streamer.h (enum lto_section_type): Likewise. * opts.c (common_handle_option): New option added. * passes.def: New pass included. * timevar.def: Time variable for IPA ICF added. * tree-pass.h: New IPA ICF pass entry point added. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@216305 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-icf-gimple.c')
-rw-r--r--gcc/ipa-icf-gimple.c897
1 files changed, 897 insertions, 0 deletions
diff --git a/gcc/ipa-icf-gimple.c b/gcc/ipa-icf-gimple.c
new file mode 100644
index 00000000000..792a3e402bf
--- /dev/null
+++ b/gcc/ipa-icf-gimple.c
@@ -0,0 +1,897 @@
+/* Interprocedural Identical Code Folding pass
+ Copyright (C) 2014 Free Software Foundation, Inc.
+
+ Contributed by Jan Hubicka <hubicka@ucw.cz> and Martin Liska <mliska@suse.cz>
+
+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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "expr.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "tree-dfa.h"
+#include "tree-pass.h"
+#include "gimple-pretty-print.h"
+#include "cfgloop.h"
+#include "except.h"
+#include "data-streamer.h"
+#include "ipa-utils.h"
+#include <list>
+#include "tree-ssanames.h"
+#include "tree-eh.h"
+
+#include "ipa-icf-gimple.h"
+#include "ipa-icf.h"
+
+namespace ipa_icf_gimple {
+
+/* Initialize internal structures for a given SOURCE_FUNC_DECL and
+ TARGET_FUNC_DECL. Strict polymorphic comparison is processed if
+ an option COMPARE_POLYMORPHIC is true. For special cases, one can
+ set IGNORE_LABELS to skip label comparison.
+ Similarly, IGNORE_SOURCE_DECLS and IGNORE_TARGET_DECLS are sets
+ of declarations that can be skipped. */
+
+func_checker::func_checker (tree source_func_decl, tree target_func_decl,
+ bool compare_polymorphic,
+ bool ignore_labels,
+ hash_set<symtab_node *> *ignored_source_nodes,
+ hash_set<symtab_node *> *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)
+{
+ function *source_func = DECL_STRUCT_FUNCTION (source_func_decl);
+ function *target_func = DECL_STRUCT_FUNCTION (target_func_decl);
+
+ unsigned ssa_source = SSANAMES (source_func)->length ();
+ unsigned ssa_target = SSANAMES (target_func)->length ();
+
+ m_source_ssa_names.create (ssa_source);
+ m_target_ssa_names.create (ssa_target);
+
+ for (unsigned i = 0; i < ssa_source; i++)
+ m_source_ssa_names.safe_push (-1);
+
+ for (unsigned i = 0; i < ssa_target; i++)
+ m_target_ssa_names.safe_push (-1);
+}
+
+/* Memory release routine. */
+
+func_checker::~func_checker ()
+{
+ m_source_ssa_names.release();
+ m_target_ssa_names.release();
+}
+
+/* Verifies that trees T1 and T2 are equivalent from perspective of ICF. */
+
+bool
+func_checker::compare_ssa_name (tree t1, tree t2)
+{
+ unsigned i1 = SSA_NAME_VERSION (t1);
+ unsigned i2 = SSA_NAME_VERSION (t2);
+
+ if (m_source_ssa_names[i1] == -1)
+ m_source_ssa_names[i1] = i2;
+ else if (m_source_ssa_names[i1] != (int) i2)
+ return false;
+
+ if(m_target_ssa_names[i2] == -1)
+ m_target_ssa_names[i2] = i1;
+ else if (m_target_ssa_names[i2] != (int) i1)
+ return false;
+
+ return true;
+}
+
+/* Verification function for edges E1 and E2. */
+
+bool
+func_checker::compare_edge (edge e1, edge e2)
+{
+ if (e1->flags != e2->flags)
+ return false;
+
+ bool existed_p;
+
+ edge &slot = m_edge_map.get_or_insert (e1, &existed_p);
+ if (existed_p)
+ return return_with_debug (slot == e2);
+ else
+ slot = e2;
+
+ /* TODO: filter edge probabilities for profile feedback match. */
+
+ return true;
+}
+
+/* Verification function for declaration trees T1 and T2 that
+ come from functions FUNC1 and FUNC2. */
+
+bool
+func_checker::compare_decl (tree t1, tree t2)
+{
+ if (!auto_var_in_fn_p (t1, m_source_func_decl)
+ || !auto_var_in_fn_p (t2, m_target_func_decl))
+ return return_with_debug (t1 == t2);
+
+ tree_code t = TREE_CODE (t1);
+ if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+ && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
+ return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
+
+ if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+ m_compare_polymorphic))
+ return return_false ();
+
+ bool existed_p;
+
+ tree &slot = m_decl_map.get_or_insert (t1, &existed_p);
+ if (existed_p)
+ return return_with_debug (slot == t2);
+ else
+ slot = t2;
+
+ return true;
+}
+
+/* Return true if types are compatible from perspective of ICF. */
+bool func_checker::compatible_types_p (tree t1, tree t2,
+ bool compare_polymorphic,
+ bool first_argument)
+{
+ if (TREE_CODE (t1) != TREE_CODE (t2))
+ return return_false_with_msg ("different tree types");
+
+ if (!types_compatible_p (t1, t2))
+ return return_false_with_msg ("types are not compatible");
+
+ if (get_alias_set (t1) != get_alias_set (t2))
+ return return_false_with_msg ("alias sets are different");
+
+ /* We call contains_polymorphic_type_p with this pointer type. */
+ if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
+ {
+ t1 = TREE_TYPE (t1);
+ t2 = TREE_TYPE (t2);
+ }
+
+ if (compare_polymorphic)
+ if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
+ {
+ if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
+ return return_false_with_msg ("one type is not polymorphic");
+
+ if (!types_must_be_same_for_odr (t1, t2))
+ return return_false_with_msg ("types are not same for ODR");
+ }
+
+ return true;
+}
+
+/* Function responsible for comparison of handled components T1 and T2.
+ If these components, from functions FUNC1 and FUNC2, are equal, true
+ is returned. */
+
+bool
+func_checker::compare_operand (tree t1, tree t2)
+{
+ tree base1, base2, x1, x2, y1, y2, z1, z2;
+ HOST_WIDE_INT offset1 = 0, offset2 = 0;
+ bool ret;
+
+ if (!t1 && !t2)
+ return true;
+ else if (!t1 || !t2)
+ return false;
+
+ tree tt1 = TREE_TYPE (t1);
+ tree tt2 = TREE_TYPE (t2);
+
+ if (!func_checker::compatible_types_p (tt1, tt2))
+ return false;
+
+ base1 = get_addr_base_and_unit_offset (t1, &offset1);
+ base2 = get_addr_base_and_unit_offset (t2, &offset2);
+
+ if (base1 && base2)
+ {
+ if (offset1 != offset2)
+ return return_false_with_msg ("base offsets are different");
+
+ t1 = base1;
+ t2 = base2;
+ }
+
+ if (TREE_CODE (t1) != TREE_CODE (t2))
+ return return_false ();
+
+ switch (TREE_CODE (t1))
+ {
+ case CONSTRUCTOR:
+ {
+ unsigned length1 = vec_safe_length (CONSTRUCTOR_ELTS (t1));
+ unsigned length2 = vec_safe_length (CONSTRUCTOR_ELTS (t2));
+
+ if (length1 != length2)
+ return return_false ();
+
+ for (unsigned i = 0; i < length1; i++)
+ if (!compare_operand (CONSTRUCTOR_ELT (t1, i)->value,
+ CONSTRUCTOR_ELT (t2, i)->value))
+ return return_false();
+
+ return true;
+ }
+ case ARRAY_REF:
+ case ARRAY_RANGE_REF:
+ x1 = TREE_OPERAND (t1, 0);
+ x2 = TREE_OPERAND (t2, 0);
+ y1 = TREE_OPERAND (t1, 1);
+ y2 = TREE_OPERAND (t2, 1);
+
+ if (!compare_operand (array_ref_low_bound (t1),
+ array_ref_low_bound (t2)))
+ return return_false_with_msg ("");
+ if (!compare_operand (array_ref_element_size (t1),
+ array_ref_element_size (t2)))
+ return return_false_with_msg ("");
+ if (!compare_operand (x1, x2))
+ return return_false_with_msg ("");
+ return compare_operand (y1, y2);
+ case MEM_REF:
+ {
+ x1 = TREE_OPERAND (t1, 0);
+ x2 = TREE_OPERAND (t2, 0);
+ y1 = TREE_OPERAND (t1, 1);
+ y2 = TREE_OPERAND (t2, 1);
+
+ /* See if operand is an memory access (the test originate from
+ gimple_load_p).
+
+ In this case the alias set of the function being replaced must
+ be subset of the alias set of the other function. At the moment
+ we seek for equivalency classes, so simply require inclussion in
+ both directions. */
+
+ if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
+ return return_false ();
+
+ if (!compare_operand (x1, x2))
+ return return_false_with_msg ("");
+
+ if (get_alias_set (TREE_TYPE (y1)) != get_alias_set (TREE_TYPE (y2)))
+ return return_false_with_msg ("alias set for MEM_REF offsets are different");
+
+ ao_ref r1, r2;
+ ao_ref_init (&r1, t1);
+ ao_ref_init (&r2, t2);
+ if (ao_ref_alias_set (&r1) != ao_ref_alias_set (&r2)
+ || ao_ref_base_alias_set (&r1) != ao_ref_base_alias_set (&r2))
+ return return_false_with_msg ("ao alias sets are different");
+
+ /* Type of the offset on MEM_REF does not matter. */
+ return wi::to_offset (y1) == wi::to_offset (y2);
+ }
+ case COMPONENT_REF:
+ {
+ x1 = TREE_OPERAND (t1, 0);
+ x2 = TREE_OPERAND (t2, 0);
+ y1 = TREE_OPERAND (t1, 1);
+ y2 = TREE_OPERAND (t2, 1);
+
+ ret = compare_operand (x1, x2)
+ && compare_operand (y1, y2);
+
+ return return_with_debug (ret);
+ }
+ /* Virtual table call. */
+ case OBJ_TYPE_REF:
+ {
+ x1 = TREE_OPERAND (t1, 0);
+ x2 = TREE_OPERAND (t2, 0);
+ y1 = TREE_OPERAND (t1, 1);
+ y2 = TREE_OPERAND (t2, 1);
+ z1 = TREE_OPERAND (t1, 2);
+ z2 = TREE_OPERAND (t2, 2);
+
+ ret = compare_operand (x1, x2)
+ && compare_operand (y1, y2)
+ && compare_operand (z1, z2);
+
+ return return_with_debug (ret);
+ }
+ case ADDR_EXPR:
+ {
+ x1 = TREE_OPERAND (t1, 0);
+ x2 = TREE_OPERAND (t2, 0);
+
+ ret = compare_operand (x1, x2);
+ return return_with_debug (ret);
+ }
+ case SSA_NAME:
+ {
+ ret = compare_ssa_name (t1, t2);
+
+ if (!ret)
+ return return_with_debug (ret);
+
+ if (SSA_NAME_IS_DEFAULT_DEF (t1))
+ {
+ tree b1 = SSA_NAME_VAR (t1);
+ tree b2 = SSA_NAME_VAR (t2);
+
+ if (b1 == NULL && b2 == NULL)
+ return true;
+
+ if (b1 == NULL || b2 == NULL || TREE_CODE (b1) != TREE_CODE (b2))
+ return return_false ();
+
+ switch (TREE_CODE (b1))
+ {
+ case VAR_DECL:
+ return return_with_debug (compare_variable_decl (t1, t2));
+ case PARM_DECL:
+ case RESULT_DECL:
+ ret = compare_decl (b1, b2);
+ return return_with_debug (ret);
+ default:
+ return return_false_with_msg ("Unknown TREE code reached");
+ }
+ }
+ else
+ return true;
+ }
+ case INTEGER_CST:
+ {
+ ret = compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2))
+ && wi::to_offset (t1) == wi::to_offset (t2);
+
+ return return_with_debug (ret);
+ }
+ case COMPLEX_CST:
+ case VECTOR_CST:
+ case STRING_CST:
+ case REAL_CST:
+ {
+ ret = operand_equal_p (t1, t2, OEP_ONLY_CONST);
+ return return_with_debug (ret);
+ }
+ case FUNCTION_DECL:
+ {
+ ret = compare_function_decl (t1, t2);
+ return return_with_debug (ret);
+ }
+ case VAR_DECL:
+ return return_with_debug (compare_variable_decl (t1, t2));
+ case FIELD_DECL:
+ {
+ tree offset1 = DECL_FIELD_OFFSET (t1);
+ tree offset2 = DECL_FIELD_OFFSET (t2);
+
+ tree bit_offset1 = DECL_FIELD_BIT_OFFSET (t1);
+ tree bit_offset2 = DECL_FIELD_BIT_OFFSET (t2);
+
+ ret = compare_operand (offset1, offset2)
+ && compare_operand (bit_offset1, bit_offset2);
+
+ return return_with_debug (ret);
+ }
+ case LABEL_DECL:
+ {
+ int *bb1 = m_label_bb_map.get (t1);
+ int *bb2 = m_label_bb_map.get (t2);
+
+ return return_with_debug (*bb1 == *bb2);
+ }
+ case PARM_DECL:
+ case RESULT_DECL:
+ case CONST_DECL:
+ case BIT_FIELD_REF:
+ {
+ ret = compare_decl (t1, t2);
+ return return_with_debug (ret);
+ }
+ default:
+ return return_false_with_msg ("Unknown TREE code reached");
+ }
+}
+
+/* Compares two tree list operands T1 and T2 and returns true if these
+ two trees are semantically equivalent. */
+
+bool
+func_checker::compare_tree_list_operand (tree t1, tree t2)
+{
+ gcc_assert (TREE_CODE (t1) == TREE_LIST);
+ gcc_assert (TREE_CODE (t2) == TREE_LIST);
+
+ for (; t1; t1 = TREE_CHAIN (t1))
+ {
+ if (!t2)
+ return false;
+
+ if (!compare_operand (TREE_VALUE (t1), TREE_VALUE (t2)))
+ return return_false ();
+
+ t2 = TREE_CHAIN (t2);
+ }
+
+ if (t2)
+ return return_false ();
+
+ return true;
+}
+
+/* Verifies that trees T1 and T2, representing function declarations
+ are equivalent from perspective of ICF. */
+
+bool
+func_checker::compare_function_decl (tree t1, tree t2)
+{
+ bool ret = false;
+
+ if (t1 == t2)
+ return true;
+
+ symtab_node *n1 = symtab_node::get (t1);
+ symtab_node *n2 = symtab_node::get (t2);
+
+ if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+ {
+ ret = m_ignored_source_nodes->contains (n1)
+ && m_ignored_target_nodes->contains (n2);
+
+ if (ret)
+ return true;
+ }
+
+ /* If function decl is WEAKREF, we compare targets. */
+ cgraph_node *f1 = cgraph_node::get (t1);
+ cgraph_node *f2 = cgraph_node::get (t2);
+
+ if(f1 && f2 && f1->weakref && f2->weakref)
+ ret = f1->alias_target == f2->alias_target;
+
+ return ret;
+}
+
+/* Verifies that trees T1 and T2 do correspond. */
+
+bool
+func_checker::compare_variable_decl (tree t1, tree t2)
+{
+ bool ret = false;
+
+ if (t1 == t2)
+ return true;
+
+ if (TREE_CODE (t1) == VAR_DECL && (DECL_EXTERNAL (t1) || TREE_STATIC (t1)))
+ {
+ symtab_node *n1 = symtab_node::get (t1);
+ symtab_node *n2 = symtab_node::get (t2);
+
+ if (m_ignored_source_nodes != NULL && m_ignored_target_nodes != NULL)
+ {
+ ret = m_ignored_source_nodes->contains (n1)
+ && m_ignored_target_nodes->contains (n2);
+
+ if (ret)
+ return true;
+ }
+ }
+ ret = compare_decl (t1, t2);
+
+ return return_with_debug (ret);
+}
+
+void
+func_checker::parse_labels (sem_bb *bb)
+{
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb->bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+
+ if (gimple_code (stmt) == GIMPLE_LABEL)
+ {
+ tree t = gimple_label_label (stmt);
+ gcc_assert (TREE_CODE (t) == LABEL_DECL);
+
+ m_label_bb_map.put (t, bb->bb->index);
+ }
+ }
+}
+
+/* Basic block equivalence comparison function that returns true if
+ basic blocks BB1 and BB2 (from functions FUNC1 and FUNC2) correspond.
+
+ In general, a collection of equivalence dictionaries is built for types
+ like SSA names, declarations (VAR_DECL, PARM_DECL, ..). This infrastructure
+ is utilized by every statement-by-stament comparison function. */
+
+bool
+func_checker::compare_bb (sem_bb *bb1, sem_bb *bb2)
+{
+ unsigned i;
+ gimple_stmt_iterator gsi1, gsi2;
+ gimple s1, s2;
+
+ if (bb1->nondbg_stmt_count != bb2->nondbg_stmt_count
+ || bb1->edge_count != bb2->edge_count)
+ return return_false ();
+
+ gsi1 = gsi_start_bb (bb1->bb);
+ gsi2 = gsi_start_bb (bb2->bb);
+
+ for (i = 0; i < bb1->nondbg_stmt_count; i++)
+ {
+ if (is_gimple_debug (gsi_stmt (gsi1)))
+ gsi_next_nondebug (&gsi1);
+
+ if (is_gimple_debug (gsi_stmt (gsi2)))
+ gsi_next_nondebug (&gsi2);
+
+ s1 = gsi_stmt (gsi1);
+ s2 = gsi_stmt (gsi2);
+
+ 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)
+ 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");
+
+ switch (gimple_code (s1))
+ {
+ case GIMPLE_CALL:
+ if (!compare_gimple_call (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_CALL");
+ break;
+ case GIMPLE_ASSIGN:
+ if (!compare_gimple_assign (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_ASSIGN");
+ break;
+ case GIMPLE_COND:
+ if (!compare_gimple_cond (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_COND");
+ break;
+ case GIMPLE_SWITCH:
+ if (!compare_gimple_switch (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_SWITCH");
+ break;
+ case GIMPLE_DEBUG:
+ case GIMPLE_EH_DISPATCH:
+ break;
+ case GIMPLE_RESX:
+ if (!compare_gimple_resx (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_RESX");
+ break;
+ case GIMPLE_LABEL:
+ if (!compare_gimple_label (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_LABEL");
+ break;
+ case GIMPLE_RETURN:
+ if (!compare_gimple_return (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_RETURN");
+ break;
+ case GIMPLE_GOTO:
+ if (!compare_gimple_goto (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_GOTO");
+ break;
+ case GIMPLE_ASM:
+ if (!compare_gimple_asm (s1, s2))
+ return return_different_stmts (s1, s2, "GIMPLE_ASM");
+ break;
+ case GIMPLE_PREDICT:
+ case GIMPLE_NOP:
+ return true;
+ default:
+ return return_false_with_msg ("Unknown GIMPLE code reached");
+ }
+
+ gsi_next (&gsi1);
+ gsi_next (&gsi2);
+ }
+
+ return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ call statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_call (gimple s1, gimple s2)
+{
+ unsigned i;
+ tree t1, t2;
+
+ if (gimple_call_num_args (s1) != gimple_call_num_args (s2))
+ return false;
+
+ t1 = gimple_call_fndecl (s1);
+ t2 = gimple_call_fndecl (s2);
+
+ /* Function pointer variables are not supported yet. */
+ if (!compare_operand (t1, t2))
+ return return_false();
+
+ /* Checking of argument. */
+ for (i = 0; i < gimple_call_num_args (s1); ++i)
+ {
+ t1 = gimple_call_arg (s1, i);
+ t2 = gimple_call_arg (s2, i);
+
+ if (!compare_operand (t1, t2))
+ return false;
+ }
+
+ /* Return value checking. */
+ t1 = gimple_get_lhs (s1);
+ t2 = gimple_get_lhs (s2);
+
+ return compare_operand (t1, t2);
+}
+
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ assignment statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_assign (gimple s1, gimple s2)
+{
+ tree arg1, arg2;
+ tree_code code1, code2;
+ unsigned i;
+
+ code1 = gimple_expr_code (s1);
+ code2 = gimple_expr_code (s2);
+
+ if (code1 != code2)
+ return false;
+
+ code1 = gimple_assign_rhs_code (s1);
+ code2 = gimple_assign_rhs_code (s2);
+
+ if (code1 != code2)
+ return false;
+
+ for (i = 0; i < gimple_num_ops (s1); i++)
+ {
+ arg1 = gimple_op (s1, i);
+ arg2 = gimple_op (s2, i);
+
+ if (!compare_operand (arg1, arg2))
+ return false;
+ }
+
+
+ return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ condition statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_cond (gimple s1, gimple s2)
+{
+ tree t1, t2;
+ tree_code code1, code2;
+
+ code1 = gimple_expr_code (s1);
+ code2 = gimple_expr_code (s2);
+
+ if (code1 != code2)
+ return false;
+
+ t1 = gimple_cond_lhs (s1);
+ t2 = gimple_cond_lhs (s2);
+
+ if (!compare_operand (t1, t2))
+ return false;
+
+ t1 = gimple_cond_rhs (s1);
+ t2 = gimple_cond_rhs (s2);
+
+ return compare_operand (t1, t2);
+}
+
+/* Verifies that tree labels T1 and T2 correspond in FUNC1 and FUNC2. */
+
+bool
+func_checker::compare_tree_ssa_label (tree t1, tree t2)
+{
+ return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ label statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_label (gimple g1, gimple g2)
+{
+ if (m_ignore_labels)
+ return true;
+
+ tree t1 = gimple_label_label (g1);
+ tree t2 = gimple_label_label (g2);
+
+ if (FORCED_LABEL (t1) || FORCED_LABEL (t2))
+ return return_false_with_msg ("FORCED_LABEL");
+
+ return compare_tree_ssa_label (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ switch statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_switch (gimple g1, gimple g2)
+{
+ unsigned lsize1, lsize2, i;
+
+ lsize1 = gimple_switch_num_labels (g1);
+ lsize2 = gimple_switch_num_labels (g2);
+
+ if (lsize1 != lsize2)
+ return false;
+
+ tree t1 = gimple_switch_index (g1);
+ tree t2 = gimple_switch_index (g2);
+
+ if (!compare_operand (t1, t2))
+ return false;
+
+ for (i = 0; i < lsize1; i++)
+ {
+ tree label1 = gimple_switch_label (g1, i);
+ tree label2 = gimple_switch_label (g2, i);
+
+ if (TREE_CODE (label1) == CASE_LABEL_EXPR
+ && TREE_CODE (label2) == CASE_LABEL_EXPR)
+ {
+ label1 = CASE_LABEL (label1);
+ label2 = CASE_LABEL (label2);
+
+ if (!compare_operand (label1, label2))
+ return return_false_with_msg ("switch label_exprs are different");
+ }
+ else if (!tree_int_cst_equal (label1, label2))
+ return return_false_with_msg ("switch labels are different");
+ }
+
+ return true;
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ return statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_return (gimple g1, gimple g2)
+{
+ tree t1, t2;
+
+ t1 = gimple_return_retval (g1);
+ t2 = gimple_return_retval (g2);
+
+ /* Void return type. */
+ if (t1 == NULL && t2 == NULL)
+ return true;
+ else
+ return compare_operand (t1, t2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ goto statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_goto (gimple g1, gimple g2)
+{
+ tree dest1, dest2;
+
+ dest1 = gimple_goto_dest (g1);
+ dest2 = gimple_goto_dest (g2);
+
+ if (TREE_CODE (dest1) != TREE_CODE (dest2) || TREE_CODE (dest1) != SSA_NAME)
+ return false;
+
+ return compare_operand (dest1, dest2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that
+ resx statements are semantically equivalent. */
+
+bool
+func_checker::compare_gimple_resx (gimple g1, gimple g2)
+{
+ return gimple_resx_region (g1) == gimple_resx_region (g2);
+}
+
+/* Verifies for given GIMPLEs S1 and S2 that ASM statements are equivalent.
+ For the beginning, the pass only supports equality for
+ '__asm__ __volatile__ ("", "", "", "memory")'. */
+
+bool
+func_checker::compare_gimple_asm (gimple g1, gimple g2)
+{
+ if (gimple_asm_volatile_p (g1) != gimple_asm_volatile_p (g2))
+ return false;
+
+ if (gimple_asm_ninputs (g1) != gimple_asm_ninputs (g2))
+ return false;
+
+ if (gimple_asm_noutputs (g1) != gimple_asm_noutputs (g2))
+ return false;
+
+ /* We do not suppport goto ASM statement comparison. */
+ if (gimple_asm_nlabels (g1) || gimple_asm_nlabels (g2))
+ return false;
+
+ if (gimple_asm_nclobbers (g1) != gimple_asm_nclobbers (g2))
+ return false;
+
+ for (unsigned i = 0; i < gimple_asm_ninputs (g1); i++)
+ {
+ tree input1 = gimple_asm_input_op (g1, i);
+ tree input2 = gimple_asm_input_op (g2, i);
+
+ if (!compare_tree_list_operand (input1, input2))
+ return return_false_with_msg ("ASM input is different");
+ }
+
+ for (unsigned i = 0; i < gimple_asm_noutputs (g1); i++)
+ {
+ tree output1 = gimple_asm_output_op (g1, i);
+ tree output2 = gimple_asm_output_op (g2, i);
+
+ if (!compare_tree_list_operand (output1, output2))
+ return return_false_with_msg ("ASM output is different");
+ }
+
+ for (unsigned i = 0; i < gimple_asm_nclobbers (g1); i++)
+ {
+ tree clobber1 = gimple_asm_clobber_op (g1, i);
+ tree clobber2 = gimple_asm_clobber_op (g2, i);
+
+ if (!operand_equal_p (TREE_VALUE (clobber1), TREE_VALUE (clobber2),
+ OEP_ONLY_CONST))
+ return return_false_with_msg ("ASM clobber is different");
+ }
+
+ return true;
+}
+
+} // ipa_icf_gimple namespace