#include "system.h"
#include <list>
#include "coretypes.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "options.h"
-#include "wide-int.h"
-#include "inchash.h"
#include "tree.h"
#include "fold-const.h"
#include "predict.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
-#include "hashtab.h"
#include "rtl.h"
#include "flags.h"
-#include "statistics.h"
-#include "real.h"
-#include "fixed-value.h"
#include "insn-config.h"
#include "expmed.h"
#include "dojump.h"
#include "tree-dfa.h"
#include "tree-pass.h"
#include "gimple-pretty-print.h"
-#include "hash-map.h"
-#include "plugin-api.h"
-#include "ipa-ref.h"
#include "cgraph.h"
#include "alloc-pool.h"
#include "symbol-summary.h"
#include "ipa-inline.h"
#include "cfgloop.h"
#include "except.h"
-#include "hash-table.h"
#include "coverage.h"
#include "attribs.h"
#include "print-tree.h"
#include "ipa-icf-gimple.h"
#include "ipa-icf.h"
#include "stor-layout.h"
+#include "dbgcnt.h"
using namespace ipa_icf_gimple;
namespace ipa_icf {
+/* Initialization and computation of symtab node hash, there data
+ are propagated later on. */
+
+static sem_item_optimizer *optimizer = NULL;
+
/* Constructor. */
symbol_compare_collection::symbol_compare_collection (symtab_node *node)
if (is_a <varpool_node *> (node) && DECL_VIRTUAL_P (node->decl))
return;
- for (unsigned i = 0; i < node->num_references (); i++)
+ for (unsigned i = 0; node->iterate_reference (i, ref); i++)
{
- ref = node->iterate_reference (i, ref);
if (ref->address_matters_p ())
m_references.safe_push (ref->referred);
if (dump_file)
{
fprintf (dump_file, "[%s] %s (%u) (tree:%p)\n", type == FUNC ? "func" : "var",
- name(), node->order, (void *) node->decl);
+ node->name(), node->order, (void *) node->decl);
fprintf (dump_file, " hash: %u\n", get_hash ());
fprintf (dump_file, " references: ");
for (unsigned i = 0; i < refs.length (); i++)
- fprintf (dump_file, "%s%s ", refs[i]->name (),
+ fprintf (dump_file, "%s%s ", refs[i]->node->name (),
i < refs.length() - 1 ? "," : "");
fprintf (dump_file, "\n");
sem_function::sem_function (bitmap_obstack *stack): sem_item (FUNC, stack),
m_checker (NULL), m_compared_func (NULL)
{
- arg_types.create (0);
bb_sizes.create (0);
bb_sorted.create (0);
}
sem_item (FUNC, node, hash, stack),
m_checker (NULL), m_compared_func (NULL)
{
- arg_types.create (0);
bb_sizes.create (0);
bb_sorted.create (0);
}
for (unsigned i = 0; i < bb_sorted.length (); i++)
delete (bb_sorted[i]);
- arg_types.release ();
bb_sizes.release ();
bb_sorted.release ();
}
for (unsigned i = 0; i < bb_sizes.length (); i++)
hstate.add_int (bb_sizes[i]);
+
+ /* Add common features of declaration itself. */
+ if (DECL_FUNCTION_SPECIFIC_TARGET (decl))
+ hstate.add_wide_int
+ (cl_target_option_hash
+ (TREE_TARGET_OPTION (DECL_FUNCTION_SPECIFIC_TARGET (decl))));
+ if (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))
+ (cl_optimization_hash
+ (TREE_OPTIMIZATION (DECL_FUNCTION_SPECIFIC_OPTIMIZATION (decl))));
+ hstate.add_flag (DECL_CXX_CONSTRUCTOR_P (decl));
+ hstate.add_flag (DECL_CXX_DESTRUCTOR_P (decl));
+
hash = hstate.end ();
}
return hash;
}
+/* Return ture if A1 and A2 represent equivalent function attribute lists.
+ Based on comp_type_attributes. */
+
+bool
+sem_item::compare_attributes (const_tree a1, const_tree a2)
+{
+ const_tree a;
+ if (a1 == a2)
+ return true;
+ for (a = a1; a != NULL_TREE; a = TREE_CHAIN (a))
+ {
+ const struct attribute_spec *as;
+ const_tree attr;
+
+ as = lookup_attribute_spec (get_attribute_name (a));
+ /* TODO: We can introduce as->affects_decl_identity
+ and as->affects_decl_reference_identity if attribute mismatch
+ gets a common reason to give up on merging. It may not be worth
+ the effort.
+ For example returns_nonnull affects only references, while
+ optimize attribute can be ignored because it is already lowered
+ into flags representation and compared separately. */
+ if (!as)
+ continue;
+
+ attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
+ if (!attr || !attribute_value_equal (a, attr))
+ break;
+ }
+ if (!a)
+ {
+ for (a = a2; a != NULL_TREE; a = TREE_CHAIN (a))
+ {
+ const struct attribute_spec *as;
+
+ as = lookup_attribute_spec (get_attribute_name (a));
+ if (!as)
+ continue;
+
+ if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
+ break;
+ /* We don't need to compare trees again, as we did this
+ already in first loop. */
+ }
+ if (!a)
+ return true;
+ }
+ /* TODO: As in comp_type_attributes we may want to introduce target hook. */
+ return false;
+}
+
+/* Compare properties of symbols N1 and N2 that does not affect semantics of
+ symbol itself but affects semantics of its references from USED_BY (which
+ may be NULL if it is unknown). If comparsion is false, symbols
+ can still be merged but any symbols referring them can't.
+
+ If ADDRESS is true, do extra checking needed for IPA_REF_ADDR.
+
+ TODO: We can also split attributes to those that determine codegen of
+ a function body/variable constructor itself and those that are used when
+ referring to it. */
+
+bool
+sem_item::compare_referenced_symbol_properties (symtab_node *used_by,
+ symtab_node *n1,
+ symtab_node *n2,
+ bool address)
+{
+ if (is_a <cgraph_node *> (n1))
+ {
+ /* Inline properties matters: we do now want to merge uses of inline
+ function to uses of normal function because inline hint would be lost.
+ We however can merge inline function to noinline because the alias
+ will keep its DECL_DECLARED_INLINE flag.
+
+ Also ignore inline flag when optimizing for size or when function
+ is known to not be inlinable.
+
+ TODO: the optimize_size checks can also be assumed to be true if
+ unit has no !optimize_size functions. */
+
+ if ((!used_by || address || !is_a <cgraph_node *> (used_by)
+ || !opt_for_fn (used_by->decl, optimize_size))
+ && !opt_for_fn (n1->decl, optimize_size)
+ && n1->get_availability () > AVAIL_INTERPOSABLE
+ && (!DECL_UNINLINABLE (n1->decl) || !DECL_UNINLINABLE (n2->decl)))
+ {
+ if (DECL_DISREGARD_INLINE_LIMITS (n1->decl)
+ != DECL_DISREGARD_INLINE_LIMITS (n2->decl))
+ return return_false_with_msg
+ ("DECL_DISREGARD_INLINE_LIMITS are different");
+
+ if (DECL_DECLARED_INLINE_P (n1->decl)
+ != DECL_DECLARED_INLINE_P (n2->decl))
+ return return_false_with_msg ("inline attributes are different");
+ }
+
+ if (DECL_IS_OPERATOR_NEW (n1->decl)
+ != DECL_IS_OPERATOR_NEW (n2->decl))
+ return return_false_with_msg ("operator new flags are different");
+ }
+
+ /* Merging two definitions with a reference to equivalent vtables, but
+ belonging to a different type may result in ipa-polymorphic-call analysis
+ giving a wrong answer about the dynamic type of instance. */
+ if (is_a <varpool_node *> (n1))
+ {
+ if ((DECL_VIRTUAL_P (n1->decl) || DECL_VIRTUAL_P (n2->decl))
+ && (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl)
+ || !types_must_be_same_for_odr (DECL_CONTEXT (n1->decl),
+ DECL_CONTEXT (n2->decl)))
+ && (!used_by || !is_a <cgraph_node *> (used_by) || address
+ || opt_for_fn (used_by->decl, flag_devirtualize)))
+ return return_false_with_msg
+ ("references to virtual tables can not be merged");
+
+ if (address && DECL_ALIGN (n1->decl) != DECL_ALIGN (n2->decl))
+ return return_false_with_msg ("alignment mismatch");
+
+ /* For functions we compare attributes in equals_wpa, because we do
+ not know what attributes may cause codegen differences, but for
+ variables just compare attributes for references - the codegen
+ for constructors is affected only by those attributes that we lower
+ to explicit representation (such as DECL_ALIGN or DECL_SECTION). */
+ if (!compare_attributes (DECL_ATTRIBUTES (n1->decl),
+ DECL_ATTRIBUTES (n2->decl)))
+ return return_false_with_msg ("different var decl attributes");
+ if (comp_type_attributes (TREE_TYPE (n1->decl),
+ TREE_TYPE (n2->decl)) != 1)
+ return return_false_with_msg ("different var type attributes");
+ }
+
+ /* When matching virtual tables, be sure to also match information
+ relevant for polymorphic call analysis. */
+ if (used_by && is_a <varpool_node *> (used_by)
+ && DECL_VIRTUAL_P (used_by->decl))
+ {
+ if (DECL_VIRTUAL_P (n1->decl) != DECL_VIRTUAL_P (n2->decl))
+ return return_false_with_msg ("virtual flag mismatch");
+ if (DECL_VIRTUAL_P (n1->decl) && is_a <cgraph_node *> (n1)
+ && (DECL_FINAL_P (n1->decl) != DECL_FINAL_P (n2->decl)))
+ return return_false_with_msg ("final flag mismatch");
+ }
+ return true;
+}
+
+/* Hash properties that are compared by compare_referenced_symbol_properties. */
+
+void
+sem_item::hash_referenced_symbol_properties (symtab_node *ref,
+ inchash::hash &hstate,
+ bool address)
+{
+ if (is_a <cgraph_node *> (ref))
+ {
+ if ((type != FUNC || address || !opt_for_fn (decl, optimize_size))
+ && !opt_for_fn (ref->decl, optimize_size)
+ && !DECL_UNINLINABLE (ref->decl))
+ {
+ hstate.add_flag (DECL_DISREGARD_INLINE_LIMITS (ref->decl));
+ hstate.add_flag (DECL_DECLARED_INLINE_P (ref->decl));
+ }
+ hstate.add_flag (DECL_IS_OPERATOR_NEW (ref->decl));
+ }
+ else if (is_a <varpool_node *> (ref))
+ {
+ hstate.add_flag (DECL_VIRTUAL_P (ref->decl));
+ if (address)
+ hstate.add_int (DECL_ALIGN (ref->decl));
+ }
+}
+
+
/* For a given symbol table nodes N1 and N2, we check that FUNCTION_DECLs
point to a same function. Comparison can be skipped if IGNORED_NODES
contains these nodes. ADDRESS indicate if address is taken. */
bool
-sem_item::compare_cgraph_references (
+sem_item::compare_symbol_references (
hash_map <symtab_node *, sem_item *> &ignored_nodes,
symtab_node *n1, symtab_node *n2, bool address)
{
enum availability avail1, avail2;
+ if (n1 == n2)
+ return true;
+
+ /* Never match variable and function. */
+ if (is_a <varpool_node *> (n1) != is_a <varpool_node *> (n2))
+ return false;
+
+ if (!compare_referenced_symbol_properties (node, n1, n2, address))
+ return false;
if (address && n1->equal_address_to (n2) == 1)
return true;
if (!address && n1->semantically_equivalent_p (n2))
return true;
}
+/* Return true if parameter I may be used. */
+
+bool
+sem_function::param_used_p (unsigned int i)
+{
+ if (ipa_node_params_sum == NULL)
+ return false;
+
+ struct ipa_node_params *parms_info = IPA_NODE_REF (get_node ());
+
+ if (parms_info->descriptors.is_empty ()
+ || parms_info->descriptors.length () <= i)
+ return true;
+
+ return ipa_is_param_used (IPA_NODE_REF (get_node ()), i);
+}
+
+/* Perform additional check needed to match types function parameters that are
+ used. Unlike for normal decls it matters if type is TYPE_RESTRICT and we
+ make an assumption that REFERENCE_TYPE parameters are always non-NULL. */
+
+bool
+sem_function::compatible_parm_types_p (tree parm1, tree parm2)
+{
+ /* Be sure that parameters are TBAA compatible. */
+ if (!func_checker::compatible_types_p (parm1, parm2))
+ return return_false_with_msg ("parameter type is not compatible");
+
+ if (POINTER_TYPE_P (parm1)
+ && (TYPE_RESTRICT (parm1) != TYPE_RESTRICT (parm2)))
+ return return_false_with_msg ("argument restrict flag mismatch");
+
+ /* nonnull_arg_p implies non-zero range to REFERENCE types. */
+ if (POINTER_TYPE_P (parm1)
+ && TREE_CODE (parm1) != TREE_CODE (parm2)
+ && opt_for_fn (decl, flag_delete_null_pointer_checks))
+ return return_false_with_msg ("pointer wrt reference mismatch");
+
+ return true;
+}
+
/* Fast equality function based on knowledge known in WPA. */
bool
hash_map <symtab_node *, sem_item *> &ignored_nodes)
{
gcc_assert (item->type == FUNC);
+ cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+ cgraph_node *cnode2 = dyn_cast <cgraph_node *> (item->node);
m_compared_func = static_cast<sem_function *> (item);
- if (arg_types.length () != m_compared_func->arg_types.length ())
- return return_false_with_msg ("different number of arguments");
+ if (cnode->thunk.thunk_p != cnode2->thunk.thunk_p)
+ return return_false_with_msg ("thunk_p mismatch");
+
+ if (cnode->thunk.thunk_p)
+ {
+ if (cnode->thunk.fixed_offset != cnode2->thunk.fixed_offset)
+ return return_false_with_msg ("thunk fixed_offset mismatch");
+ if (cnode->thunk.virtual_value != cnode2->thunk.virtual_value)
+ return return_false_with_msg ("thunk virtual_value mismatch");
+ if (cnode->thunk.this_adjusting != cnode2->thunk.this_adjusting)
+ return return_false_with_msg ("thunk this_adjusting mismatch");
+ if (cnode->thunk.virtual_offset_p != cnode2->thunk.virtual_offset_p)
+ return return_false_with_msg ("thunk virtual_offset_p mismatch");
+ if (cnode->thunk.add_pointer_bounds_args
+ != cnode2->thunk.add_pointer_bounds_args)
+ return return_false_with_msg ("thunk add_pointer_bounds_args mismatch");
+ }
+
+ /* Compare special function DECL attributes. */
+ if (DECL_FUNCTION_PERSONALITY (decl)
+ != DECL_FUNCTION_PERSONALITY (item->decl))
+ return return_false_with_msg ("function personalities are different");
+
+ if (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl)
+ != DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (item->decl))
+ return return_false_with_msg ("intrument function entry exit "
+ "attributes are different");
+
+ if (DECL_NO_LIMIT_STACK (decl) != DECL_NO_LIMIT_STACK (item->decl))
+ return return_false_with_msg ("no stack limit attributes are different");
+
+ if (DECL_CXX_CONSTRUCTOR_P (decl) != DECL_CXX_CONSTRUCTOR_P (item->decl))
+ return return_false_with_msg ("DECL_CXX_CONSTRUCTOR mismatch");
+
+ if (DECL_CXX_DESTRUCTOR_P (decl) != DECL_CXX_DESTRUCTOR_P (item->decl))
+ return return_false_with_msg ("DECL_CXX_DESTRUCTOR mismatch");
+
+ /* TODO: pure/const flags mostly matters only for references, except for
+ the fact that codegen takes LOOPING flag as a hint that loops are
+ finite. We may arrange the code to always pick leader that has least
+ specified flags and then this can go into comparing symbol properties. */
+ if (flags_from_decl_or_type (decl) != flags_from_decl_or_type (item->decl))
+ return return_false_with_msg ("decl_or_type flags are different");
+
+ /* Do not match polymorphic constructors of different types. They calls
+ type memory location for ipa-polymorphic-call and we do not want
+ it to get confused by wrong type. */
+ if (DECL_CXX_CONSTRUCTOR_P (decl)
+ && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+ {
+ if (TREE_CODE (TREE_TYPE (item->decl)) != METHOD_TYPE)
+ return return_false_with_msg ("DECL_CXX_CONSTURCTOR type mismatch");
+ else if (!func_checker::compatible_polymorphic_types_p
+ (TYPE_METHOD_BASETYPE (TREE_TYPE (decl)),
+ TYPE_METHOD_BASETYPE (TREE_TYPE (item->decl)), false))
+ return return_false_with_msg ("ctor polymorphic type mismatch");
+ }
+
+ /* Checking function TARGET and OPTIMIZATION flags. */
+ cl_target_option *tar1 = target_opts_for_fn (decl);
+ cl_target_option *tar2 = target_opts_for_fn (item->decl);
+
+ if (tar1 != tar2 && !cl_target_option_eq (tar1, tar2))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "target flags difference");
+ cl_target_option_print_diff (dump_file, 2, tar1, tar2);
+ }
+
+ return return_false_with_msg ("Target flags are different");
+ }
+
+ cl_optimization *opt1 = opts_for_fn (decl);
+ cl_optimization *opt2 = opts_for_fn (item->decl);
+
+ if (opt1 != opt2 && memcmp (opt1, opt2, sizeof(cl_optimization)))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "optimization flags difference");
+ cl_optimization_print_diff (dump_file, 2, opt1, opt2);
+ }
+
+ return return_false_with_msg ("optimization flags are different");
+ }
+
+ /* Result type checking. */
+ if (!func_checker::compatible_types_p
+ (TREE_TYPE (TREE_TYPE (decl)),
+ TREE_TYPE (TREE_TYPE (m_compared_func->decl))))
+ return return_false_with_msg ("result types are different");
/* Checking types of arguments. */
- for (unsigned i = 0; i < arg_types.length (); i++)
+ tree list1 = TYPE_ARG_TYPES (TREE_TYPE (decl)),
+ list2 = TYPE_ARG_TYPES (TREE_TYPE (m_compared_func->decl));
+ for (unsigned i = 0; list1 && list2;
+ list1 = TREE_CHAIN (list1), list2 = TREE_CHAIN (list2), i++)
{
+ tree parm1 = TREE_VALUE (list1);
+ tree parm2 = TREE_VALUE (list2);
+
/* This guard is here for function pointer with attributes (pr59927.c). */
- if (!arg_types[i] || !m_compared_func->arg_types[i])
+ if (!parm1 || !parm2)
return return_false_with_msg ("NULL argument type");
- /* Polymorphic comparison is executed just for non-leaf functions. */
- bool is_not_leaf = get_node ()->callees != NULL
- || get_node ()->indirect_calls != NULL;
+ /* Verify that types are compatible to ensure that both functions
+ have same calling conventions. */
+ if (!types_compatible_p (parm1, parm2))
+ return return_false_with_msg ("parameter types are not compatible");
+
+ if (!param_used_p (i))
+ continue;
- if (!func_checker::compatible_types_p (arg_types[i],
- m_compared_func->arg_types[i],
- is_not_leaf, i == 0))
- return return_false_with_msg ("argument type is different");
+ /* Perform additional checks for used parameters. */
+ if (!compatible_parm_types_p (parm1, parm2))
+ return false;
}
- /* Result type checking. */
- if (!func_checker::compatible_types_p (result_type,
- m_compared_func->result_type))
- return return_false_with_msg ("result types are different");
+ if (list1 || list2)
+ return return_false_with_msg ("Mismatched number of parameters");
if (node->num_references () != item->node->num_references ())
return return_false_with_msg ("different number of references");
+ /* Checking function attributes.
+ This is quadratic in number of attributes */
+ if (comp_type_attributes (TREE_TYPE (decl),
+ TREE_TYPE (item->decl)) != 1)
+ return return_false_with_msg ("different type attributes");
+ if (!compare_attributes (DECL_ATTRIBUTES (decl),
+ DECL_ATTRIBUTES (item->decl)))
+ return return_false_with_msg ("different decl attributes");
+
+ /* The type of THIS pointer type memory location for
+ ipa-polymorphic-call-analysis. */
+ if (opt_for_fn (decl, flag_devirtualize)
+ && (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
+ || TREE_CODE (TREE_TYPE (item->decl)) == METHOD_TYPE)
+ && param_used_p (0)
+ && compare_polymorphic_p ())
+ {
+ if (TREE_CODE (TREE_TYPE (decl)) != TREE_CODE (TREE_TYPE (item->decl)))
+ return return_false_with_msg ("METHOD_TYPE and FUNCTION_TYPE mismatch");
+ if (!func_checker::compatible_polymorphic_types_p
+ (TYPE_METHOD_BASETYPE (TREE_TYPE (decl)),
+ TYPE_METHOD_BASETYPE (TREE_TYPE (item->decl)), false))
+ return return_false_with_msg ("THIS pointer ODR type mismatch");
+ }
+
ipa_ref *ref = NULL, *ref2 = NULL;
for (unsigned i = 0; node->iterate_reference (i, ref); i++)
{
item->node->iterate_reference (i, ref2);
- if (!compare_cgraph_references (ignored_nodes, ref->referred,
+ if (ref->use != ref2->use)
+ return return_false_with_msg ("reference use mismatch");
+
+ if (!compare_symbol_references (ignored_nodes, ref->referred,
ref2->referred,
ref->address_matters_p ()))
return false;
while (e1 && e2)
{
- if (!compare_cgraph_references (ignored_nodes, e1->callee,
+ if (!compare_symbol_references (ignored_nodes, e1->callee,
e2->callee, false))
return false;
+ if (!compare_edge_flags (e1, e2))
+ return false;
e1 = e1->next_callee;
e2 = e2->next_callee;
}
if (e1 || e2)
- return return_false_with_msg ("different number of edges");
+ return return_false_with_msg ("different number of calls");
+
+ e1 = dyn_cast <cgraph_node *> (node)->indirect_calls;
+ e2 = dyn_cast <cgraph_node *> (item->node)->indirect_calls;
+
+ while (e1 && e2)
+ {
+ if (!compare_edge_flags (e1, e2))
+ return false;
+
+ e1 = e1->next_callee;
+ e2 = e2->next_callee;
+ }
+
+ if (e1 || e2)
+ return return_false_with_msg ("different number of indirect calls");
return true;
}
+/* Update hash by address sensitive references. We iterate over all
+ sensitive references (address_matters_p) and we hash ultime alias
+ target of these nodes, which can improve a semantic item hash.
+
+ Also hash in referenced symbols properties. This can be done at any time
+ (as the properties should not change), but it is convenient to do it here
+ while we walk the references anyway. */
+
+void
+sem_item::update_hash_by_addr_refs (hash_map <symtab_node *,
+ sem_item *> &m_symtab_node_map)
+{
+ ipa_ref* ref;
+ inchash::hash hstate (hash);
+
+ for (unsigned i = 0; node->iterate_reference (i, ref); i++)
+ {
+ hstate.add_int (ref->use);
+ hash_referenced_symbol_properties (ref->referred, hstate,
+ ref->use == IPA_REF_ADDR);
+ if (ref->address_matters_p () || !m_symtab_node_map.get (ref->referred))
+ hstate.add_int (ref->referred->ultimate_alias_target ()->order);
+ }
+
+ if (is_a <cgraph_node *> (node))
+ {
+ for (cgraph_edge *e = dyn_cast <cgraph_node *> (node)->callers; e;
+ e = e->next_caller)
+ {
+ sem_item **result = m_symtab_node_map.get (e->callee);
+ hash_referenced_symbol_properties (e->callee, hstate, false);
+ if (!result)
+ hstate.add_int (e->callee->ultimate_alias_target ()->order);
+ }
+ }
+
+ hash = hstate.end ();
+}
+
+/* Update hash by computed local hash values taken from different
+ semantic items.
+ TODO: stronger SCC based hashing would be desirable here. */
+
+void
+sem_item::update_hash_by_local_refs (hash_map <symtab_node *,
+ sem_item *> &m_symtab_node_map)
+{
+ ipa_ref* ref;
+ inchash::hash state (hash);
+
+ for (unsigned j = 0; node->iterate_reference (j, ref); j++)
+ {
+ sem_item **result = m_symtab_node_map.get (ref->referring);
+ if (result)
+ state.merge_hash ((*result)->hash);
+ }
+
+ if (type == FUNC)
+ {
+ for (cgraph_edge *e = dyn_cast <cgraph_node *> (node)->callees; e;
+ e = e->next_callee)
+ {
+ sem_item **result = m_symtab_node_map.get (e->caller);
+ if (result)
+ state.merge_hash ((*result)->hash);
+ }
+ }
+
+ global_hash = state.end ();
+}
+
/* Returns true if the item equals to ITEM given as argument. */
bool
sem_function::equals (sem_item *item,
- hash_map <symtab_node *, sem_item *> &ignored_nodes)
+ hash_map <symtab_node *, sem_item *> &)
{
gcc_assert (item->type == FUNC);
- bool eq = equals_private (item, ignored_nodes);
+ bool eq = equals_private (item);
if (m_checker != NULL)
{
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"Equals called for:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
- name(), item->name (), node->order, item->node->order, asm_name (),
- item->asm_name (), eq ? "true" : "false");
+ xstrdup_for_dump (node->name()),
+ xstrdup_for_dump (item->node->name ()),
+ node->order,
+ item->node->order,
+ xstrdup_for_dump (node->asm_name ()),
+ xstrdup_for_dump (item->node->asm_name ()),
+ eq ? "true" : "false");
return eq;
}
/* Processes function equality comparison. */
bool
-sem_function::equals_private (sem_item *item,
- hash_map <symtab_node *, sem_item *> &ignored_nodes)
+sem_function::equals_private (sem_item *item)
{
if (item->type != FUNC)
return false;
|| cfg_checksum != m_compared_func->cfg_checksum)
return return_false ();
- if (!equals_wpa (item, ignored_nodes))
- return false;
-
- /* Checking function TARGET and OPTIMIZATION flags. */
- cl_target_option *tar1 = target_opts_for_fn (decl);
- cl_target_option *tar2 = target_opts_for_fn (m_compared_func->decl);
-
- if (tar1 != NULL && tar2 != NULL)
- {
- if (!cl_target_option_eq (tar1, tar2))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "target flags difference");
- cl_target_option_print_diff (dump_file, 2, tar1, tar2);
- }
-
- return return_false_with_msg ("Target flags are different");
- }
- }
- else if (tar1 != NULL || tar2 != NULL)
- return return_false_with_msg ("Target flags are different");
-
- cl_optimization *opt1 = opts_for_fn (decl);
- cl_optimization *opt2 = opts_for_fn (m_compared_func->decl);
-
- if (opt1 != NULL && opt2 != NULL)
- {
- if (memcmp (opt1, opt2, sizeof(cl_optimization)))
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "optimization flags difference");
- cl_optimization_print_diff (dump_file, 2, opt1, opt2);
- }
-
- return return_false_with_msg ("optimization flags are different");
- }
- }
- else if (opt1 != NULL || opt2 != NULL)
- return return_false_with_msg ("optimization flags are different");
-
- /* Checking function arguments. */
- tree decl1 = DECL_ATTRIBUTES (decl);
- tree decl2 = DECL_ATTRIBUTES (m_compared_func->decl);
-
m_checker = new func_checker (decl, m_compared_func->decl,
compare_polymorphic_p (),
false,
&refs_set,
&m_compared_func->refs_set);
- while (decl1)
+ arg1 = DECL_ARGUMENTS (decl);
+ arg2 = DECL_ARGUMENTS (m_compared_func->decl);
+ for (unsigned i = 0;
+ arg1 && arg2; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2), i++)
{
- 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 (!types_compatible_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
+ return return_false_with_msg ("argument types are not compatible");
+ if (!param_used_p (i))
+ continue;
+ /* Perform additional checks for used parameters. */
+ if (!compatible_parm_types_p (TREE_TYPE (arg1), TREE_TYPE (arg2)))
+ return false;
+ if (!m_checker->compare_decl (arg1, arg2))
+ return return_false ();
}
+ if (arg1 || arg2)
+ return return_false_with_msg ("Mismatched number of arguments");
- 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 ();
+ if (!dyn_cast <cgraph_node *> (node)->has_gimple_body_p ())
+ return true;
/* Fill-up label dictionary. */
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");
- /* Compare special function DECL attributes. */
- if (DECL_FUNCTION_PERSONALITY (decl) != DECL_FUNCTION_PERSONALITY (item->decl))
- return return_false_with_msg ("function personalities are different");
-
- if (DECL_DISREGARD_INLINE_LIMITS (decl) != DECL_DISREGARD_INLINE_LIMITS (item->decl))
- return return_false_with_msg ("DECL_DISREGARD_INLINE_LIMITS are different");
-
- if (DECL_DECLARED_INLINE_P (decl) != DECL_DECLARED_INLINE_P (item->decl))
- return return_false_with_msg ("inline attributes are different");
-
- if (DECL_IS_OPERATOR_NEW (decl) != DECL_IS_OPERATOR_NEW (item->decl))
- return return_false_with_msg ("operator new flags are different");
-
- if (DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (decl)
- != DECL_NO_INSTRUMENT_FUNCTION_ENTRY_EXIT (item->decl))
- return return_false_with_msg ("intrument function entry exit "
- "attributes are different");
-
- if (DECL_NO_LIMIT_STACK (decl) != DECL_NO_LIMIT_STACK (item->decl))
- return return_false_with_msg ("no stack limit attributes are different");
-
- if (flags_from_decl_or_type (decl) != flags_from_decl_or_type (item->decl))
- return return_false_with_msg ("decl_or_type flags are different");
-
return result;
}
{
int nredirected = 0;
ipa_ref *ref;
+ cgraph_edge *e = n->callers;
- while (n->callers)
+ while (e)
{
- cgraph_edge *e = n->callers;
- e->redirect_callee (to);
- nredirected++;
+ /* Redirecting thunks to interposable symbols or symbols in other sections
+ may not be supported by target output code. Play safe for now and
+ punt on redirection. */
+ if (!e->caller->thunk.thunk_p)
+ {
+ struct cgraph_edge *nexte = e->next_caller;
+ e->redirect_callee (to);
+ e = nexte;
+ nredirected++;
+ }
+ else
+ e = e->next_callee;
}
for (unsigned i = 0; n->iterate_direct_aliases (i, ref);)
{
{
nredirected += redirect_all_callers (n_alias, to);
if (n_alias->can_remove_if_no_direct_calls_p ()
+ && !n_alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+ NULL, true)
&& !n_alias->has_aliases_p ())
n_alias->remove ();
}
bool original_address_matters = original->address_matters_p ();
bool alias_address_matters = alias->address_matters_p ();
+ if (DECL_EXTERNAL (alias->decl))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Not unifying; alias is external.\n\n");
+ return false;
+ }
+
if (DECL_NO_INLINE_WARNING_P (original->decl)
!= DECL_NO_INLINE_WARNING_P (alias->decl))
{
{
/* First see if we can produce wrapper. */
+ /* Symbol properties that matter for references must be preserved.
+ TODO: We can produce wrapper, but we need to produce alias of ORIGINAL
+ with proper properties. */
+ if (!sem_item::compare_referenced_symbol_properties (NULL, original, alias,
+ alias->address_taken))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Wrapper cannot be created because referenced symbol "
+ "properties mismatch\n");
+ }
/* Do not turn function in one comdat group into wrapper to another
comdat group. Other compiler producing the body of the
another comdat group may make opossite decision and with unfortunate
linker choices this may close a loop. */
- if (DECL_COMDAT_GROUP (original->decl) && DECL_COMDAT_GROUP (alias->decl)
- && (DECL_COMDAT_GROUP (alias->decl)
- != DECL_COMDAT_GROUP (original->decl)))
+ else if (DECL_COMDAT_GROUP (original->decl)
+ && DECL_COMDAT_GROUP (alias->decl)
+ && (DECL_COMDAT_GROUP (alias->decl)
+ != DECL_COMDAT_GROUP (original->decl)))
{
if (dump_file)
fprintf (dump_file,
= alias->get_availability () > AVAIL_INTERPOSABLE
&& original->get_availability () > AVAIL_INTERPOSABLE
&& !alias->instrumented_version;
+ /* TODO: We can redirect, but we need to produce alias of ORIGINAL
+ with proper properties. */
+ if (!sem_item::compare_referenced_symbol_properties (NULL, original, alias,
+ alias->address_taken))
+ redirect_callers = false;
if (!redirect_callers && !create_wrapper)
{
return false;
}
if (!create_wrapper
+ && !alias->call_for_symbol_and_aliases (cgraph_node::has_thunk_p,
+ NULL, true)
&& !alias->can_remove_if_no_direct_calls_p ())
{
if (dump_file)
if (dump_file)
fprintf (dump_file, "Unified; Wrapper has been created.\n\n");
}
- gcc_assert (alias->icf_merged || remove);
+
+ /* It's possible that redirection can hit thunks that block
+ redirection opportunities. */
+ gcc_assert (alias->icf_merged || remove || redirect_callers);
original->icf_merged = true;
/* Inform the inliner about cross-module merging. */
arg_count = count_formal_params (fndecl);
edge_count = n_edges_for_fn (func);
- cfg_checksum = coverage_compute_cfg_checksum (func);
-
- inchash::hash hstate;
-
- basic_block bb;
- FOR_EACH_BB_FN (bb, func)
- {
- unsigned nondbg_stmt_count = 0;
+ cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
+ if (!cnode->thunk.thunk_p)
+ {
+ cfg_checksum = coverage_compute_cfg_checksum (func);
- edge e;
- for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e);
- ei_next (&ei))
- cfg_checksum = iterative_hash_host_wide_int (e->flags,
- cfg_checksum);
+ inchash::hash hstate;
- for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
- gsi_next (&gsi))
+ basic_block bb;
+ FOR_EACH_BB_FN (bb, func)
{
- gimple stmt = gsi_stmt (gsi);
+ unsigned nondbg_stmt_count = 0;
- if (gimple_code (stmt) != GIMPLE_DEBUG
- && gimple_code (stmt) != GIMPLE_PREDICT)
+ edge e;
+ for (edge_iterator ei = ei_start (bb->preds); ei_cond (ei, &e);
+ ei_next (&ei))
+ cfg_checksum = iterative_hash_host_wide_int (e->flags,
+ cfg_checksum);
+
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
{
- hash_stmt (stmt, hstate);
- nondbg_stmt_count++;
- }
- }
+ gimple stmt = gsi_stmt (gsi);
- gcode_hash = hstate.end ();
- bb_sizes.safe_push (nondbg_stmt_count);
+ if (gimple_code (stmt) != GIMPLE_DEBUG
+ && gimple_code (stmt) != GIMPLE_PREDICT)
+ {
+ hash_stmt (stmt, hstate);
+ nondbg_stmt_count++;
+ }
+ }
- /* Inserting basic block to hash table. */
- sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
- EDGE_COUNT (bb->preds)
- + EDGE_COUNT (bb->succs));
+ gcode_hash = hstate.end ();
+ bb_sizes.safe_push (nondbg_stmt_count);
- bb_sorted.safe_push (semantic_bb);
- }
+ /* Inserting basic block to hash table. */
+ sem_bb *semantic_bb = new sem_bb (bb, nondbg_stmt_count,
+ EDGE_COUNT (bb->preds)
+ + EDGE_COUNT (bb->succs));
- parse_tree_args ();
+ bb_sorted.safe_push (semantic_bb);
+ }
+ }
+ else
+ {
+ cfg_checksum = 0;
+ inchash::hash hstate;
+ hstate.add_wide_int (cnode->thunk.fixed_offset);
+ hstate.add_wide_int (cnode->thunk.virtual_value);
+ hstate.add_flag (cnode->thunk.this_adjusting);
+ hstate.add_flag (cnode->thunk.virtual_offset_p);
+ hstate.add_flag (cnode->thunk.add_pointer_bounds_args);
+ gcode_hash = hstate.end ();
+ }
}
/* Accumulate to HSTATE a hash of expression EXP.
}
}
+/* Accumulate to HSTATE a hash of type t.
+ TYpes that may end up being compatible after LTO type merging needs to have
+ the same hash. */
+
+void
+sem_item::add_type (const_tree type, inchash::hash &hstate)
+{
+ if (type == NULL_TREE)
+ {
+ hstate.merge_hash (0);
+ return;
+ }
+
+ type = TYPE_MAIN_VARIANT (type);
+ if (TYPE_CANONICAL (type))
+ type = TYPE_CANONICAL (type);
+
+ if (!AGGREGATE_TYPE_P (type))
+ hstate.add_int (TYPE_MODE (type));
+
+ if (TREE_CODE (type) == COMPLEX_TYPE)
+ {
+ hstate.add_int (COMPLEX_TYPE);
+ sem_item::add_type (TREE_TYPE (type), hstate);
+ }
+ else if (INTEGRAL_TYPE_P (type))
+ {
+ hstate.add_int (INTEGER_TYPE);
+ hstate.add_flag (TYPE_UNSIGNED (type));
+ hstate.add_int (TYPE_PRECISION (type));
+ }
+ else if (VECTOR_TYPE_P (type))
+ {
+ hstate.add_int (VECTOR_TYPE);
+ hstate.add_int (TYPE_PRECISION (type));
+ sem_item::add_type (TREE_TYPE (type), hstate);
+ }
+ else if (TREE_CODE (type) == ARRAY_TYPE)
+ {
+ hstate.add_int (ARRAY_TYPE);
+ /* Do not hash size, so complete and incomplete types can match. */
+ sem_item::add_type (TREE_TYPE (type), hstate);
+ }
+ else if (RECORD_OR_UNION_TYPE_P (type))
+ {
+ hashval_t *val = optimizer->m_type_hash_cache.get (type);
+
+ if (!val)
+ {
+ inchash::hash hstate2;
+ unsigned nf;
+ tree f;
+ hashval_t hash;
+
+ hstate2.add_int (RECORD_TYPE);
+ gcc_assert (COMPLETE_TYPE_P (type));
+
+ for (f = TYPE_FIELDS (type), nf = 0; f; f = TREE_CHAIN (f))
+ if (TREE_CODE (f) == FIELD_DECL)
+ {
+ add_type (TREE_TYPE (f), hstate2);
+ nf++;
+ }
+
+ hstate2.add_int (nf);
+ hash = hstate2.end ();
+ hstate.add_wide_int (hash);
+ optimizer->m_type_hash_cache.put (type, hash);
+ }
+ else
+ hstate.add_wide_int (*val);
+ }
+}
+
/* Improve accumulated hash for HSTATE based on a gimple statement STMT. */
void
switch (code)
{
+ case GIMPLE_SWITCH:
+ add_expr (gimple_switch_index (as_a <gswitch *> (stmt)), hstate);
+ break;
case GIMPLE_ASSIGN:
+ hstate.add_int (gimple_assign_rhs_code (stmt));
if (commutative_tree_code (gimple_assign_rhs_code (stmt))
|| commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
{
inchash::hash one, two;
add_expr (gimple_assign_rhs1 (stmt), one);
+ add_type (TREE_TYPE (gimple_assign_rhs1 (stmt)), one);
add_expr (gimple_assign_rhs2 (stmt), two);
hstate.add_commutative (one, two);
+ if (commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
+ {
+ add_expr (gimple_assign_rhs3 (stmt), hstate);
+ add_type (TREE_TYPE (gimple_assign_rhs3 (stmt)), hstate);
+ }
add_expr (gimple_assign_lhs (stmt), hstate);
+ add_type (TREE_TYPE (gimple_assign_lhs (stmt)), two);
break;
}
/* ... fall through ... */
case GIMPLE_RETURN:
/* All these statements are equivalent if their operands are. */
for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
- add_expr (gimple_op (stmt, i), hstate);
+ {
+ add_expr (gimple_op (stmt, i), hstate);
+ if (gimple_op (stmt, i))
+ add_type (TREE_TYPE (gimple_op (stmt, i)), hstate);
+ }
default:
break;
}
bool
sem_function::compare_polymorphic_p (void)
{
- return get_node ()->callees != NULL
- || get_node ()->indirect_calls != NULL
- || m_compared_func->get_node ()->callees != NULL
- || m_compared_func->get_node ()->indirect_calls != NULL;
+ struct cgraph_edge *e;
+
+ if (!opt_for_fn (get_node ()->decl, flag_devirtualize))
+ return false;
+ if (get_node ()->indirect_calls != NULL)
+ return true;
+ /* TODO: We can do simple propagation determining what calls may lead to
+ a polymorphic call. */
+ for (e = get_node ()->callees; e; e = e->next_callee)
+ if (e->callee->definition
+ && opt_for_fn (e->callee->decl, flag_devirtualize))
+ return true;
+ return false;
}
/* For a given call graph NODE, the function constructs new
tree fndecl = node->decl;
function *func = DECL_STRUCT_FUNCTION (fndecl);
- /* TODO: add support for thunks. */
-
- if (!func || !node->has_gimple_body_p ())
+ if (!func || (!node->has_gimple_body_p () && !node->thunk.thunk_p))
return NULL;
if (lookup_attribute_by_prefix ("omp ", DECL_ATTRIBUTES (node->decl)) != NULL)
return f;
}
-/* Parses function arguments and result type. */
-
-void
-sem_function::parse_tree_args (void)
-{
- tree result;
-
- if (arg_types.exists ())
- arg_types.release ();
-
- arg_types.create (4);
- tree fnargs = DECL_ARGUMENTS (decl);
-
- for (tree parm = fnargs; parm; parm = DECL_CHAIN (parm))
- arg_types.safe_push (DECL_ARG_TYPE (parm));
-
- /* Function result type. */
- result = DECL_RESULT (decl);
- result_type = result ? TREE_TYPE (result) : NULL;
-
- /* During WPA, we can get arguments by following method. */
- if (!fnargs)
- {
- tree type = TYPE_ARG_TYPES (TREE_TYPE (decl));
- for (tree parm = type; parm; parm = TREE_CHAIN (parm))
- arg_types.safe_push (TYPE_CANONICAL (TREE_VALUE (parm)));
-
- result_type = TREE_TYPE (TREE_TYPE (decl));
- }
-}
-
/* For given basic blocks BB1 and BB2 (from functions FUNC1 and FUNC),
return true if phi nodes are semantically equivalent in these blocks . */
{
tree_code tc = TREE_CODE (t);
- return ((handled_component_p (t))
- || tc == ADDR_EXPR || tc == MEM_REF || tc == REALPART_EXPR
- || tc == IMAGPART_EXPR || tc == OBJ_TYPE_REF);
+ return (handled_component_p (t)
+ || tc == ADDR_EXPR || tc == MEM_REF || tc == OBJ_TYPE_REF);
}
/* Basic blocks dictionary BB_DICT returns true if SOURCE index BB
return (*bb_dict)[source] == target;
}
-/* Iterates all tree types in T1 and T2 and returns true if all types
- are compatible. If COMPARE_POLYMORPHIC is set to true,
- more strict comparison is executed. */
-
-bool
-sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
-{
- tree tv1, tv2;
- tree_code tc1, tc2;
-
- if (!t1 && !t2)
- return true;
-
- while (t1 != NULL && t2 != NULL)
- {
- tv1 = TREE_VALUE (t1);
- tv2 = TREE_VALUE (t2);
-
- tc1 = TREE_CODE (tv1);
- tc2 = TREE_CODE (tv2);
-
- if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
- {}
- else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
- return false;
- else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
- return false;
-
- t1 = TREE_CHAIN (t1);
- t2 = TREE_CHAIN (t2);
- }
-
- return !(t1 || t2);
-}
-
/* Semantic variable constructor that uses STACK as bitmap memory stack. */
if (DECL_TLS_MODEL (decl) || DECL_TLS_MODEL (item->decl))
return return_false_with_msg ("TLS model");
- if (DECL_ALIGN (decl) != DECL_ALIGN (item->decl))
- return return_false_with_msg ("alignment mismatch");
+ /* DECL_ALIGN is safe to merge, because we will always chose the largest
+ alignment out of all aliases. */
if (DECL_VIRTUAL_P (decl) != DECL_VIRTUAL_P (item->decl))
return return_false_with_msg ("Virtual flag mismatch");
{
item->node->iterate_reference (i, ref2);
- if (!compare_cgraph_references (ignored_nodes,
+ if (ref->use != ref2->use)
+ return return_false_with_msg ("reference use mismatch");
+
+ if (!compare_symbol_references (ignored_nodes,
ref->referred, ref2->referred,
ref->address_matters_p ()))
return false;
/* Returns true if the item equals to ITEM given as argument. */
-/* Returns true if the item equals to ITEM given as argument. */
-
bool
sem_variable::equals (sem_item *item,
hash_map <symtab_node *, sem_item *> &)
if (DECL_INITIAL (item->decl) == error_mark_node && in_lto_p)
dyn_cast <varpool_node *>(item->node)->get_constructor ();
+ /* As seen in PR ipa/65303 we have to compare variables types. */
+ if (!func_checker::compatible_types_p (TREE_TYPE (decl),
+ TREE_TYPE (item->decl)))
+ return return_false_with_msg ("variables types are different");
+
ret = sem_variable::equals (DECL_INITIAL (decl),
DECL_INITIAL (item->node->decl));
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file,
"Equals called for vars:%s:%s (%u:%u) (%s:%s) with result: %s\n\n",
- name(), item->name (), node->order, item->node->order, asm_name (),
- item->asm_name (), ret ? "true" : "false");
+ xstrdup_for_dump (node->name()),
+ xstrdup_for_dump (item->node->name ()),
+ node->order, item->node->order,
+ xstrdup_for_dump (node->asm_name ()),
+ xstrdup_for_dump (item->node->asm_name ()), ret ? "true" : "false");
return ret;
}
tree y1 = TREE_OPERAND (t1, 1);
tree y2 = TREE_OPERAND (t2, 1);
- if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
- true))
+ if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
return return_false ();
/* Type of the offset on MEM_REF does not matter. */
tree y1 = TREE_OPERAND (t1, 1);
tree y2 = TREE_OPERAND (t2, 1);
- if (!sem_variable::equals (x1, x2) && sem_variable::equals (y1, y2))
+ if (!sem_variable::equals (x1, x2) || !sem_variable::equals (y1, y2))
return false;
if (!sem_variable::equals (array_ref_low_bound (t1),
array_ref_low_bound (t2)))
CASE_CONVERT:
case VIEW_CONVERT_EXPR:
- if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
- true))
+ if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
return return_false ();
return sem_variable::equals (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
case ERROR_MARK:
sem_variable *
sem_variable::parse (varpool_node *node, bitmap_obstack *stack)
{
- if (TREE_THIS_VOLATILE (node->decl) || DECL_HARD_REGISTER (node->decl))
+ if (TREE_THIS_VOLATILE (node->decl) || DECL_HARD_REGISTER (node->decl)
+ || node->alias)
return NULL;
sem_variable *v = new sem_variable (node, 0, stack);
sem_variable::get_hash (void)
{
if (hash)
-
return hash;
+
/* All WPA streamed in symbols should have their hashes computed at compile
time. At this point, the constructor may not be in memory at all.
DECL_INITIAL (decl) would be error_mark_node in that case. */
return false;
}
+ if (DECL_EXTERNAL (alias_item->decl))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Not unifying; alias is external.\n\n");
+ return false;
+ }
+
sem_variable *alias_var = static_cast<sem_variable *> (alias_item);
varpool_node *original = get_node ();
fprintf (file, "\n\n");
}
-/* Iterates though a constructor and identifies tree references
- we are interested in semantic function equality. */
-
-void
-sem_variable::parse_tree_refs (tree t)
-{
- switch (TREE_CODE (t))
- {
- case CONSTRUCTOR:
- {
- unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
-
- for (unsigned i = 0; i < length; i++)
- parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
-
- break;
- }
- case NOP_EXPR:
- case ADDR_EXPR:
- {
- tree op = TREE_OPERAND (t, 0);
- parse_tree_refs (op);
- break;
- }
- case FUNCTION_DECL:
- {
- tree_refs.safe_push (t);
- break;
- }
- default:
- break;
- }
-}
-
unsigned int sem_item_optimizer::class_id = 0;
sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
gcc_assert (node->definition);
if (dump_file)
- fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n", node->asm_name (),
- (void *) node->decl, node->order);
+ fprintf (dump_file, "Symbol added:%s (tree: %p, uid:%u)\n",
+ node->asm_name (), (void *) node->decl, node->order);
if (is_a<cgraph_node *> (node))
{
{
cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
- bool no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
- if (no_body_function || !opt_for_fn (item->decl, flag_ipa_icf_functions)
- || DECL_CXX_CONSTRUCTOR_P (item->decl)
- || DECL_CXX_DESTRUCTOR_P (item->decl))
+ if (in_lto_p && (cnode->alias || cnode->body_removed))
remove_item (item);
else
filtered.safe_push (item);
filter_removed_items ();
unregister_hooks ();
+ build_graph ();
+ update_hash_by_addr_refs ();
build_hash_based_classes ();
if (dump_file)
for (unsigned int i = 0; i < m_items.length(); i++)
m_items[i]->init_wpa ();
- build_graph ();
-
subdivide_classes_by_equality (true);
if (dump_file)
m_symtab_node_map.put (cnode, f);
if (dump_file)
- fprintf (dump_file, "Parsed function:%s\n", f->asm_name ());
+ fprintf (dump_file, "Parsed function:%s\n", f->node->asm_name ());
if (dump_file && (dump_flags & TDF_DETAILS))
f->dump_to_file (dump_file);
item->cls = cls;
}
+/* For each semantic item, append hash values of references. */
+
+void
+sem_item_optimizer::update_hash_by_addr_refs ()
+{
+ /* First, append to hash sensitive references and class type if it need to
+ be matched for ODR. */
+ for (unsigned i = 0; i < m_items.length (); i++)
+ {
+ m_items[i]->update_hash_by_addr_refs (m_symtab_node_map);
+ if (m_items[i]->type == FUNC)
+ {
+ if (TREE_CODE (TREE_TYPE (m_items[i]->decl)) == METHOD_TYPE
+ && contains_polymorphic_type_p
+ (TYPE_METHOD_BASETYPE (TREE_TYPE (m_items[i]->decl)))
+ && (DECL_CXX_CONSTRUCTOR_P (m_items[i]->decl)
+ || (static_cast<sem_function *> (m_items[i])->param_used_p (0)
+ && static_cast<sem_function *> (m_items[i])
+ ->compare_polymorphic_p ())))
+ {
+ tree class_type
+ = TYPE_METHOD_BASETYPE (TREE_TYPE (m_items[i]->decl));
+ inchash::hash hstate (m_items[i]->hash);
+
+ if (TYPE_NAME (class_type)
+ && DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (class_type)))
+ hstate.add_wide_int
+ (IDENTIFIER_HASH_VALUE
+ (DECL_ASSEMBLER_NAME (TYPE_NAME (class_type))));
+
+ m_items[i]->hash = hstate.end ();
+ }
+ }
+ }
+
+ /* Once all symbols have enhanced hash value, we can append
+ hash values of symbols that are seen by IPA ICF and are
+ references by a semantic item. Newly computed values
+ are saved to global_hash member variable. */
+ for (unsigned i = 0; i < m_items.length (); i++)
+ m_items[i]->update_hash_by_local_refs (m_symtab_node_map);
+
+ /* Global hash value replace current hash values. */
+ for (unsigned i = 0; i < m_items.length (); i++)
+ m_items[i]->hash = m_items[i]->global_hash;
+}
+
/* Congruence classes are built by hash value. */
void
{
sem_item *item = m_items[i];
- congruence_class_group *group = get_group_by_hash (item->get_hash (),
+ congruence_class_group *group = get_group_by_hash (item->hash,
item->type);
if (!group->classes.length ())
unsigned
sem_item_optimizer::subdivide_classes_by_sensitive_refs ()
{
+ typedef hash_map <symbol_compare_hash, vec <sem_item *> > subdivide_hash_map;
+
unsigned newly_created_classes = 0;
for (hash_table <congruence_class_group_hash>::iterator it = m_classes.begin ();
if (c->members.length() > 1)
{
- hash_map <symbol_compare_collection *, vec <sem_item *>,
- symbol_compare_hashmap_traits> split_map;
+ subdivide_hash_map split_map;
for (unsigned j = 0; j < c->members.length (); j++)
{
symbol_compare_collection *collection = new symbol_compare_collection (source_node->node);
- vec <sem_item *> *slot = &split_map.get_or_insert (collection);
+ bool existed;
+ vec <sem_item *> *slot = &split_map.get_or_insert (collection,
+ &existed);
gcc_checking_assert (slot);
slot->safe_push (source_node);
+
+ if (existed)
+ delete collection;
}
/* If the map contains more than one key, we have to split the map
{
bool first_class = true;
- hash_map <symbol_compare_collection *, vec <sem_item *>,
- symbol_compare_hashmap_traits>::iterator it2 = split_map.begin ();
- for (; it2 != split_map.end (); ++it2)
+ for (subdivide_hash_map::iterator it2 = split_map.begin ();
+ it2 != split_map.end (); ++it2)
{
congruence_class *new_cls;
new_cls = new congruence_class (class_id++);
}
}
}
+
+ /* Release memory. */
+ for (subdivide_hash_map::iterator it2 = split_map.begin ();
+ it2 != split_map.end (); ++it2)
+ {
+ delete (*it2).first;
+ (*it2).second.release ();
+ }
}
}
if (dump_file)
{
fprintf (dump_file, "Semantic equality hit:%s->%s\n",
- source->name (), alias->name ());
+ xstrdup_for_dump (source->node->name ()),
+ xstrdup_for_dump (alias->node->name ()));
fprintf (dump_file, "Assembler symbol names:%s->%s\n",
- source->asm_name (), alias->asm_name ());
+ xstrdup_for_dump (source->node->asm_name ()),
+ xstrdup_for_dump (alias->node->asm_name ()));
}
if (lookup_attribute ("no_icf", DECL_ATTRIBUTES (alias->decl)))
alias->dump_to_file (dump_file);
}
- if (source->merge (alias))
- merged_p = true;
+ if (dbg_cnt (merged_ipa_icf))
+ merged_p |= source->merge (alias);
}
}
FPUTS_SPACES (file, indent + 2, "");
for (unsigned i = 0; i < members.length (); i++)
- fprintf (file, "%s(%p/%u) ", members[i]->asm_name (), (void *) members[i]->decl,
+ fprintf (file, "%s(%p/%u) ", members[i]->node->asm_name (),
+ (void *) members[i]->decl,
members[i]->node->order);
fprintf (file, "\n");
return false;
}
-/* Initialization and computation of symtab node hash, there data
- are propagated later on. */
-
-static sem_item_optimizer *optimizer = NULL;
-
/* Generate pass summary for IPA ICF pass. */
static void