> diff --git a/gcc/cgraph.h b/gcc/cgraph.h > index fb41b01..2de98b4 100644 > --- a/gcc/cgraph.h > +++ b/gcc/cgraph.h > @@ -172,6 +172,12 @@ public: > /* Dump referring in list to FILE. */ > void dump_referring (FILE *); > > + /* Get number of references for this node. */ > + inline unsigned get_references_count (void) > + { > + return ref_list.references ? ref_list.references->length () : 0; > + }
Probably better called num_references() (like we have num_edge in basic-block.h) > @@ -8068,6 +8069,19 @@ it may significantly increase code size > (see @option{--param ipcp-unit-growth=@var{value}}). > This flag is enabled by default at @option{-O3}. > > +@item -fipa-icf > +@opindex fipa-icf > +Perform Identical Code Folding for functions and read-only variables. > +The optimization reduces code size and may disturb unwind stacks by replacing > +a function by equivalent one with a different name. The optimization works > +more effectively with link time optimization enabled. > + > +Nevertheless the behavior is similar to Gold Linker ICF optimization, GCC ICF > +works on different levels and thus the optimizations are not same - there are > +equivalences that are found only by GCC and equivalences found only by Gold. > + > +This flag is enabled by default at @option{-O2}. ... and -Os? > + 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); > + } No need for {...} if there are no local vars. > +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; Comparing aliases is bit more complicated than just handling weakrefs. I have patch for symtab_node::equivalent_address_p somewhre in queue. lets just drop the fancy stuff for the moment and compare f1&&f2 for equivalence. > + ret = compare_decl (t1, t2); Why functions are not compared with compare_decl while variables are? > + > + 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 (t1 == NULL || t2 == NULL) > + { > + if (!compare_operand (t1, t2)) > + return return_false(); I think the comment above is out of date. compare_operand should do the right job for indirect calls. > + > + if (cn1 && cn2 && cn1->weakref && cn2->weakref > + && cn1->alias_target == cn2->alias_target) > + return true; Lets consistently drop the weakrefs handling and add full alias handling incrementally. > + > + /* Checking function arguments. */ attributes > + tree decl1 = DECL_ATTRIBUTES (decl); > + tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl); You can still do this as part of the wap_comparison, right? > + > + m_checker = new func_checker (decl, m_compared_func->decl, > + compare_polymorphic_p (), > + false, > + &refs_set, > + &m_compared_func->refs_set); > + while (decl1) > + { > + if (decl2 == NULL) > + return return_false (); > + > + if (get_attribute_name (decl1) != get_attribute_name (decl2)) > + return return_false (); > + > + tree attr_value1 = TREE_VALUE (decl1); > + tree attr_value2 = TREE_VALUE (decl2); > + > + if (attr_value1 && attr_value2) > + { > + bool ret = m_checker->compare_operand (TREE_VALUE (attr_value1), > + TREE_VALUE (attr_value2)); > + if (!ret) > + return return_false_with_msg ("attribute values are different"); > + } > + else if (!attr_value1 && !attr_value2) > + {} > + else > + return return_false (); > + > + decl1 = TREE_CHAIN (decl1); > + decl2 = TREE_CHAIN (decl2); > + } > + > + if (decl1 != decl2) > + return return_false(); > + > + > + for (arg1 = DECL_ARGUMENTS (decl), > + arg2 = DECL_ARGUMENTS (m_compared_func->decl); > + arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2)) > + if (!m_checker->compare_decl (arg1, arg2)) > + return return_false (); > + > + /* Fill-up label dictionary. */ > + for (unsigned i = 0; i < bb_sorted.length (); ++i) > + { > + m_checker->parse_labels (bb_sorted[i]); > + m_checker->parse_labels (m_compared_func->bb_sorted[i]); > + } > + > + /* Checking all basic blocks. */ > + for (unsigned i = 0; i < bb_sorted.length (); ++i) > + if(!m_checker->compare_bb (bb_sorted[i], m_compared_func->bb_sorted[i])) > + return return_false(); > + > + dump_message ("All BBs are equal\n"); > + > + /* Basic block edges check. */ > + for (unsigned i = 0; i < bb_sorted.length (); ++i) > + { > + bb_dict = XNEWVEC (int, bb_sorted.length () + 2); > + memset (bb_dict, -1, (bb_sorted.length () + 2) * sizeof (int)); > + > + bb1 = bb_sorted[i]->bb; > + bb2 = m_compared_func->bb_sorted[i]->bb; > + > + ei2 = ei_start (bb2->preds); > + > + for (ei1 = ei_start (bb1->preds); ei_cond (ei1, &e1); ei_next (&ei1)) > + { > + ei_cond (ei2, &e2); > + > + if (e1->flags != e2->flags) > + return return_false_with_msg ("flags comparison returns false"); > + > + if (!bb_dict_test (bb_dict, e1->src->index, e2->src->index)) > + return return_false_with_msg ("edge comparison returns false"); > + > + if (!bb_dict_test (bb_dict, e1->dest->index, e2->dest->index)) > + return return_false_with_msg ("BB comparison returns false"); > + > + if (!m_checker->compare_edge (e1, e2)) > + return return_false_with_msg ("edge comparison returns false"); > + > + ei_next (&ei2); > + } > + } > + > + /* 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)) > + return return_false_with_msg ("PHI node comparison returns false"); > + > + return result; > +} The rest of patch seems fine. I think we went across enough of iteraitons, the patch is OK with changes above and lets handle rest incrementally. Thanks! Honza