#include "coverage.h"
#include "gimple-pretty-print.h"
#include "data-streamer.h"
+#include "tree-streamer.h"
#include "fold-const.h"
#include "calls.h"
#include "varasm.h"
#include "attribs.h"
#include "print-tree.h"
#include "ipa-utils.h"
+#include "tree-ssa-alias-compare.h"
#include "ipa-icf-gimple.h"
#include "fibonacci_heap.h"
#include "ipa-icf.h"
#include "dbgcnt.h"
#include "tree-vector-builder.h"
#include "symtab-thunks.h"
+#include "alias.h"
using namespace ipa_icf_gimple;
/* Semantic function constructor that uses STACK as bitmap memory stack. */
sem_function::sem_function (bitmap_obstack *stack)
-: sem_item (FUNC, stack), m_checker (NULL), m_compared_func (NULL)
+ : sem_item (FUNC, stack), memory_access_types (), m_alias_sets_hash (0),
+ m_checker (NULL), m_compared_func (NULL)
{
bb_sizes.create (0);
bb_sorted.create (0);
}
sem_function::sem_function (cgraph_node *node, bitmap_obstack *stack)
-: sem_item (FUNC, node, stack), m_checker (NULL), m_compared_func (NULL)
+ : sem_item (FUNC, node, stack), memory_access_types (),
+ m_alias_sets_hash (0), m_checker (NULL), m_compared_func (NULL)
{
bb_sizes.create (0);
bb_sorted.create (0);
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)
+ && opt_for_fn (decl, flag_devirtualize)
&& TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
{
if (TREE_CODE (TREE_TYPE (item->decl)) != METHOD_TYPE)
m_checker = new func_checker (decl, m_compared_func->decl,
false,
+ opt_for_fn (m_compared_func->decl,
+ flag_strict_aliasing),
&refs_set,
&m_compared_func->refs_set);
arg1 = DECL_ARGUMENTS (decl);
{
case GIMPLE_SWITCH:
m_checker->hash_operand (gimple_switch_index (as_a <gswitch *> (stmt)),
- hstate, 0);
+ hstate, 0, func_checker::OP_NORMAL);
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)))
- {
- m_checker->hash_operand (gimple_assign_rhs1 (stmt), hstate, 0);
- m_checker->hash_operand (gimple_assign_rhs2 (stmt), hstate, 0);
- if (commutative_ternary_tree_code (gimple_assign_rhs_code (stmt)))
- m_checker->hash_operand (gimple_assign_rhs3 (stmt), hstate, 0);
- m_checker->hash_operand (gimple_assign_lhs (stmt), hstate, 0);
- }
/* fall through */
case GIMPLE_CALL:
case GIMPLE_ASM:
case GIMPLE_COND:
case GIMPLE_GOTO:
case GIMPLE_RETURN:
- /* All these statements are equivalent if their operands are. */
- for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
- m_checker->hash_operand (gimple_op (stmt, i), hstate, 0);
- /* Consider nocf_check attribute in hash as it affects code
- generation. */
- if (code == GIMPLE_CALL
- && flag_cf_protection & CF_BRANCH)
- hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
+ {
+ func_checker::operand_access_type_map map (5);
+ func_checker::classify_operands (stmt, &map);
+
+ /* All these statements are equivalent if their operands are. */
+ for (unsigned i = 0; i < gimple_num_ops (stmt); ++i)
+ {
+ func_checker::operand_access_type
+ access_type = func_checker::get_operand_access_type
+ (&map, gimple_op (stmt, i));
+ m_checker->hash_operand (gimple_op (stmt, i), hstate, 0,
+ access_type);
+ /* For memory accesses when hasing for LTO stremaing record
+ base and ref alias ptr types so we can compare them at WPA
+ time without having to read actual function body. */
+ if (access_type == func_checker::OP_MEMORY
+ && lto_streaming_expected_p ()
+ && flag_strict_aliasing)
+ {
+ ao_ref ref;
+
+ ao_ref_init (&ref, gimple_op (stmt, i));
+ tree t = ao_ref_alias_ptr_type (&ref);
+ if (!variably_modified_type_p (t, NULL_TREE))
+ memory_access_types.safe_push (t);
+ t = ao_ref_base_alias_ptr_type (&ref);
+ if (!variably_modified_type_p (t, NULL_TREE))
+ memory_access_types.safe_push (t);
+ }
+ }
+ /* Consider nocf_check attribute in hash as it affects code
+ generation. */
+ if (code == GIMPLE_CALL
+ && flag_cf_protection & CF_BRANCH)
+ hstate.add_flag (gimple_call_nocf_check_p (as_a <gcall *> (stmt)));
+ }
+ break;
default:
break;
}
tree phi_result1 = gimple_phi_result (phi1);
tree phi_result2 = gimple_phi_result (phi2);
- if (!m_checker->compare_operand (phi_result1, phi_result2))
+ if (!m_checker->compare_operand (phi_result1, phi_result2,
+ func_checker::OP_NORMAL))
return return_false_with_msg ("PHI results are different");
size1 = gimple_phi_num_args (phi1);
t1 = gimple_phi_arg (phi1, i)->def;
t2 = gimple_phi_arg (phi2, i)->def;
- if (!m_checker->compare_operand (t1, t2))
+ if (!m_checker->compare_operand (t1, t2, func_checker::OP_NORMAL))
return return_false ();
e1 = gimple_phi_arg_edge (phi1, i);
streamer_write_uhwi_stream (ob->main_stream, node_ref);
streamer_write_uhwi (ob, (*item)->get_hash ());
+
+ if ((*item)->type == FUNC)
+ {
+ sem_function *fn = static_cast<sem_function *> (*item);
+ streamer_write_uhwi (ob, fn->memory_access_types.length ());
+ for (unsigned i = 0; i < fn->memory_access_types.length (); i++)
+ stream_write_tree (ob, fn->memory_access_types[i], true);
+ }
}
}
cgraph_node *cnode = dyn_cast <cgraph_node *> (node);
sem_function *fn = new sem_function (cnode, &m_bmstack);
+ unsigned count = streamer_read_uhwi (&ib_main);
+ inchash::hash hstate (0);
+ if (flag_incremental_link == INCREMENTAL_LINK_LTO)
+ fn->memory_access_types.reserve_exact (count);
+ for (unsigned i = 0; i < count; i++)
+ {
+ tree type = stream_read_tree (&ib_main, data_in);
+ hstate.add_int (get_deref_alias_set (type));
+ if (flag_incremental_link == INCREMENTAL_LINK_LTO)
+ fn->memory_access_types.quick_push (type);
+ }
+ fn->m_alias_sets_hash = hstate.end ();
fn->set_hash (hash);
m_items.safe_push (fn);
}
build_graph ();
update_hash_by_addr_refs ();
+ update_hash_by_memory_access_type ();
build_hash_based_classes ();
if (dump_file)
= TYPE_METHOD_BASETYPE (TREE_TYPE (m_items[i]->decl));
inchash::hash hstate (m_items[i]->get_hash ());
+ /* Hash ODR types by mangled name if it is defined.
+ If not we know that type is anonymous of free_lang_data
+ was not run and in that case type main variants are
+ unique. */
if (TYPE_NAME (class_type)
- && DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (class_type)))
+ && DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (class_type))
+ && !type_in_anonymous_namespace_p
+ (class_type))
hstate.add_hwi
(IDENTIFIER_HASH_VALUE
(DECL_ASSEMBLER_NAME (TYPE_NAME (class_type))));
+ else
+ {
+ gcc_checking_assert
+ (!in_lto_p
+ || type_in_anonymous_namespace_p (class_type));
+ hstate.add_hwi (TYPE_UID (TYPE_MAIN_VARIANT (class_type)));
+ }
m_items[i]->set_hash (hstate.end ());
}
m_items[i]->set_hash (m_items[i]->global_hash);
}
+void
+sem_item_optimizer::update_hash_by_memory_access_type ()
+{
+ for (unsigned i = 0; i < m_items.length (); i++)
+ {
+ if (m_items[i]->type == FUNC)
+ {
+ sem_function *fn = static_cast<sem_function *> (m_items[i]);
+ inchash::hash hstate (fn->get_hash ());
+ hstate.add_int (fn->m_alias_sets_hash);
+ fn->set_hash (hstate.end ());
+ }
+ }
+}
+
/* Congruence classes are built by hash value. */
void