/* Interprocedural analyses.
- Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ Copyright (C) 2005-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "alias.h"
+#include "symtab.h"
+#include "options.h"
#include "tree.h"
+#include "fold-const.h"
#include "predict.h"
-#include "vec.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "machmode.h"
#include "tm.h"
#include "hard-reg-set.h"
-#include "input.h"
#include "function.h"
#include "dominance.h"
#include "cfg.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "stor-layout.h"
#include "print-tree.h"
#include "gimple-walk.h"
#include "langhooks.h"
#include "target.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-prop.h"
#include "bitmap.h"
#include "gimple-ssa.h"
#include "tree-pass.h"
#include "tree-inline.h"
#include "ipa-inline.h"
-#include "flags.h"
#include "diagnostic.h"
#include "gimple-pretty-print.h"
#include "lto-streamer.h"
#include "dbgcnt.h"
#include "domwalk.h"
#include "builtins.h"
-#include "calls.h"
/* Intermediate information that we get from alias analysis about a particular
parameter in a particular basic_block. When a parameter or the memory it
unsigned int aa_walked;
};
-/* Vector where the parameter infos are actually stored. */
-vec<ipa_node_params> ipa_node_params_vector;
+/* Function summary where the parameter infos are actually stored. */
+ipa_node_params_t *ipa_node_params_sum = NULL;
/* Vector of IPA-CP transformation data for each clone. */
vec<ipcp_transformation_summary, va_gc> *ipcp_transformations;
/* Vector where the parameter infos are actually stored. */
/* Holders of ipa cgraph hooks: */
static struct cgraph_edge_hook_list *edge_removal_hook_holder;
-static struct cgraph_node_hook_list *node_removal_hook_holder;
static struct cgraph_2edge_hook_list *edge_duplication_hook_holder;
-static struct cgraph_2node_hook_list *node_duplication_hook_holder;
static struct cgraph_node_hook_list *function_insertion_hook_holder;
/* Description of a reference to an IPA constant. */
/* Allocation pool for reference descriptions. */
-static alloc_pool ipa_refdesc_pool;
+static pool_allocator<ipa_cst_ref_desc> ipa_refdesc_pool
+ ("IPA-PROP ref descriptions", 32);
/* Return true if DECL_FUNCTION_SPECIFIC_OPTIMIZATION of the decl associated
with NODE should prevent us from analyzing it for the purposes of IPA-CP. */
else if (type == IPA_JF_ANCESTOR)
{
fprintf (f, "ANCESTOR: ");
- fprintf (f, "%d, offset "HOST_WIDE_INT_PRINT_DEC,
+ fprintf (f, "%d, offset " HOST_WIDE_INT_PRINT_DEC,
jump_func->value.ancestor.formal_id,
jump_func->value.ancestor.offset);
if (jump_func->value.ancestor.agg_preserved)
continue;
fprintf (f, " callsite %s/%i -> %s/%i : \n",
- xstrdup (node->name ()), node->order,
- xstrdup (cs->callee->name ()),
+ xstrdup_for_dump (node->name ()), node->order,
+ xstrdup_for_dump (cs->callee->name ()),
cs->callee->order);
ipa_print_node_jump_functions_for_edge (f, cs);
}
&& TREE_CODE (TREE_OPERAND (constant, 0)) == FUNCTION_DECL)
{
struct ipa_cst_ref_desc *rdesc;
- if (!ipa_refdesc_pool)
- ipa_refdesc_pool = create_alloc_pool ("IPA-PROP ref descriptions",
- sizeof (struct ipa_cst_ref_desc), 32);
- rdesc = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+ rdesc = ipa_refdesc_pool.allocate ();
rdesc->cs = cs;
rdesc->next_duplicate = NULL;
rdesc->refcount = 1;
/* Walk the inline stack and watch out for ctors/dtors. */
for (tree block = gimple_block (call); block && TREE_CODE (block) == BLOCK;
block = BLOCK_SUPERCONTEXT (block))
- if (BLOCK_ABSTRACT_ORIGIN (block)
- && TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) == FUNCTION_DECL)
- {
- tree fn = BLOCK_ABSTRACT_ORIGIN (block);
-
- if (flags_from_decl_or_type (fn) & (ECF_PURE | ECF_CONST))
- continue;
- if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
- && (DECL_CXX_CONSTRUCTOR_P (fn)
- || DECL_CXX_DESTRUCTOR_P (fn)))
- return true;
- }
+ if (inlined_polymorphic_ctor_dtor_block_p (block, false))
+ return true;
return false;
}
}
unsigned align;
if (get_pointer_alignment_1 (arg, &align, &hwi_bitpos)
- && align > BITS_PER_UNIT)
+ && align % BITS_PER_UNIT == 0
+ && hwi_bitpos % BITS_PER_UNIT == 0)
{
jfunc->alignment.known = true;
- jfunc->alignment.align = align;
+ jfunc->alignment.align = align / BITS_PER_UNIT;
jfunc->alignment.misalign = hwi_bitpos / BITS_PER_UNIT;
}
else
target = canonicalize_constructor_val (target, NULL);
if (!target || TREE_CODE (target) != FUNCTION_DECL)
{
- if (ie->indirect_info->member_ptr)
- /* Member pointer call that goes through a VMT lookup. */
- return NULL;
+ /* Member pointer call that goes through a VMT lookup. */
+ if (ie->indirect_info->member_ptr
+ /* Or if target is not an invariant expression and we do not
+ know if it will evaulate to function at runtime.
+ This can happen when folding through &VAR, where &VAR
+ is IP invariant, but VAR itself is not.
+
+ TODO: Revisit this when GCC 5 is branched. It seems that
+ member_ptr check is not needed and that we may try to fold
+ the expression and see if VAR is readonly. */
+ || !is_gimple_ip_invariant (target))
+ {
+ if (dump_enabled_p ())
+ {
+ location_t loc = gimple_location_safe (ie->call_stmt);
+ dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc,
+ "discovered direct call non-invariant "
+ "%s/%i\n",
+ ie->caller->name (), ie->caller->order);
+ }
+ return NULL;
+ }
+
if (dump_enabled_p ())
{
if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a known target "
"(%s/%i -> %s/%i) but can not refer to it. Giving up.\n",
- xstrdup (ie->caller->name ()),
+ xstrdup_for_dump (ie->caller->name ()),
ie->caller->order,
- xstrdup (ie->callee->name ()),
+ xstrdup_for_dump (ie->callee->name ()),
ie->callee->order);
return NULL;
}
if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
"(%s/%i -> %s/%i) but the call is already speculated to %s/%i. Giving up.\n",
- xstrdup (ie->caller->name ()),
+ xstrdup_for_dump (ie->caller->name ()),
ie->caller->order,
- xstrdup (callee->name ()),
+ xstrdup_for_dump (callee->name ()),
callee->order,
- xstrdup (e2->callee->name ()),
+ xstrdup_for_dump (e2->callee->name ()),
e2->callee->order);
}
else
if (dump_file)
fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
"(%s/%i -> %s/%i) this agree with previous speculation.\n",
- xstrdup (ie->caller->name ()),
+ xstrdup_for_dump (ie->caller->name ()),
ie->caller->order,
- xstrdup (callee->name ()),
+ xstrdup_for_dump (callee->name ()),
callee->order);
}
return NULL;
"(%s/%i -> %s/%i), for stmt ",
ie->indirect_info->polymorphic ? "a virtual" : "an indirect",
speculative ? "speculative" : "known",
- xstrdup (ie->caller->name ()),
+ xstrdup_for_dump (ie->caller->name ()),
ie->caller->order,
- xstrdup (callee->name ()),
+ xstrdup_for_dump (callee->name ()),
callee->order);
if (ie->call_stmt)
print_gimple_stmt (dump_file, ie->call_stmt, 2, TDF_SLIM);
ie->caller->name (), callee->name ());
}
if (!speculative)
- ie = ie->make_direct (callee);
+ {
+ struct cgraph_edge *orig = ie;
+ ie = ie->make_direct (callee);
+ /* If we resolved speculative edge the cost is already up to date
+ for direct call (adjusted by inline_edge_duplication_hook). */
+ if (ie == orig)
+ {
+ es = inline_edge_summary (ie);
+ es->call_stmt_size -= (eni_size_weights.indirect_call_cost
+ - eni_size_weights.call_cost);
+ es->call_stmt_time -= (eni_time_weights.indirect_call_cost
+ - eni_time_weights.call_cost);
+ }
+ }
else
{
if (!callee->can_be_discarded_p ())
if (alias)
callee = alias;
}
+ /* make_speculative will update ie's cost to direct call cost. */
ie = ie->make_speculative
(callee, ie->count * 8 / 10, ie->frequency * 8 / 10);
}
- es = inline_edge_summary (ie);
- es->call_stmt_size -= (eni_size_weights.indirect_call_cost
- - eni_size_weights.call_cost);
- es->call_stmt_time -= (eni_time_weights.indirect_call_cost
- - eni_time_weights.call_cost);
return ie;
}
to_del->remove_reference ();
if (dump_file)
fprintf (dump_file, "ipa-prop: Removed a reference from %s/%i to %s.\n",
- xstrdup (origin->caller->name ()),
- origin->caller->order, xstrdup (symbol->name ()));
+ xstrdup_for_dump (origin->caller->name ()),
+ origin->caller->order, xstrdup_for_dump (symbol->name ()));
return true;
}
|| !possible_polymorphic_call_target_p
(ie, cgraph_node::get (t)))
{
- /* Do not speculate builtin_unreachable, it is stpid! */
+ /* Do not speculate builtin_unreachable, it is stupid! */
if (!ie->indirect_info->vptr_changed)
target = ipa_impossible_devirt_target (ie, target);
}
ctx, &final);
if (final && targets.length () <= 1)
{
+ speculative = false;
if (targets.length () == 1)
target = targets[0]->decl;
else
struct cgraph_indirect_call_info *ici = ie->indirect_info;
struct ipa_jump_func *jfunc;
int param_index;
+ cgraph_node *spec_target = NULL;
next_ie = ie->next_callee;
param_index = ici->param_index;
jfunc = ipa_get_ith_jump_func (top, param_index);
+ if (ie->speculative)
+ {
+ struct cgraph_edge *de;
+ struct ipa_ref *ref;
+ ie->speculative_call_info (de, ie, ref);
+ spec_target = de->callee;
+ }
+
if (!opt_for_fn (node->decl, flag_indirect_inlining))
new_direct_edge = NULL;
else if (ici->polymorphic)
new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
new_root_info);
/* If speculation was removed, then we need to do nothing. */
- if (new_direct_edge && new_direct_edge != ie)
+ if (new_direct_edge && new_direct_edge != ie
+ && new_direct_edge->callee == spec_target)
{
new_direct_edge->indirect_inlining_edge = 1;
top = IPA_EDGE_REF (cs);
res = true;
+ if (!new_direct_edge->speculative)
+ continue;
}
else if (new_direct_edge)
{
res = true;
}
top = IPA_EDGE_REF (cs);
+ /* If speculative edge was introduced we still need to update
+ call info of the indirect edge. */
+ if (!new_direct_edge->speculative)
+ continue;
}
- else if (jfunc->type == IPA_JF_PASS_THROUGH
- && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
+ if (jfunc->type == IPA_JF_PASS_THROUGH
+ && ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
{
- if ((ici->agg_contents
- && !ipa_get_jf_pass_through_agg_preserved (jfunc))
- || (ici->polymorphic
- && !ipa_get_jf_pass_through_type_preserved (jfunc)))
+ if (ici->agg_contents
+ && !ipa_get_jf_pass_through_agg_preserved (jfunc)
+ && !ici->polymorphic)
ici->param_index = -1;
else
- ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
+ {
+ ici->param_index = ipa_get_jf_pass_through_formal_id (jfunc);
+ if (ici->polymorphic
+ && !ipa_get_jf_pass_through_type_preserved (jfunc))
+ ici->vptr_changed = true;
+ }
}
else if (jfunc->type == IPA_JF_ANCESTOR)
{
- if ((ici->agg_contents
- && !ipa_get_jf_ancestor_agg_preserved (jfunc))
- || (ici->polymorphic
- && !ipa_get_jf_ancestor_type_preserved (jfunc)))
+ if (ici->agg_contents
+ && !ipa_get_jf_ancestor_agg_preserved (jfunc)
+ && !ici->polymorphic)
ici->param_index = -1;
else
{
ici->param_index = ipa_get_jf_ancestor_formal_id (jfunc);
ici->offset += ipa_get_jf_ancestor_offset (jfunc);
+ if (ici->polymorphic
+ && !ipa_get_jf_ancestor_type_preserved (jfunc))
+ ici->vptr_changed = true;
}
}
else
if (dump_file)
fprintf (dump_file, "ipa-prop: Removing cloning-created "
"reference from %s/%i to %s/%i.\n",
- xstrdup (new_root->name ()),
+ xstrdup_for_dump (new_root->name ()),
new_root->order,
- xstrdup (n->name ()), n->order);
+ xstrdup_for_dump (n->name ()), n->order);
ref->remove_reference ();
}
}
fprintf (dump_file, "ipa-prop: Removing "
"cloning-created reference "
"from %s/%i to %s/%i.\n",
- xstrdup (clone->name ()),
+ xstrdup_for_dump (clone->name ()),
clone->order,
- xstrdup (n->name ()),
+ xstrdup_for_dump (n->name ()),
n->order);
ref->remove_reference ();
}
bool changed;
/* Do nothing if the preparation phase has not been carried out yet
(i.e. during early inlining). */
- if (!ipa_node_params_vector.exists ())
+ if (!ipa_node_params_sum)
return false;
gcc_assert (ipa_edge_args_vector);
/* Frees all dynamically allocated structures that the param info points
to. */
-void
-ipa_free_node_params_substructures (struct ipa_node_params *info)
+ipa_node_params::~ipa_node_params ()
{
- info->descriptors.release ();
- free (info->lattices);
+ descriptors.release ();
+ free (lattices);
/* Lattice values and their sources are deallocated with their alocation
pool. */
- info->known_csts.release ();
- info->known_contexts.release ();
- memset (info, 0, sizeof (*info));
+ known_contexts.release ();
+
+ lattices = NULL;
+ ipcp_orig_node = NULL;
+ analysis_done = 0;
+ node_enqueued = 0;
+ do_clone_for_all_contexts = 0;
+ is_all_contexts_clone = 0;
+ node_dead = 0;
}
/* Free all ipa_node_params structures. */
void
ipa_free_all_node_params (void)
{
- int i;
- struct ipa_node_params *info;
-
- FOR_EACH_VEC_ELT (ipa_node_params_vector, i, info)
- ipa_free_node_params_substructures (info);
-
- ipa_node_params_vector.release ();
+ delete ipa_node_params_sum;
+ ipa_node_params_sum = NULL;
}
/* Grow ipcp_transformations if necessary. */
ipa_free_edge_args_substructures (IPA_EDGE_REF (cs));
}
-/* Hook that is called by cgraph.c when a node is removed. */
-
-static void
-ipa_node_removal_hook (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
-{
- /* During IPA-CP updating we can be called on not-yet analyze clones. */
- if (ipa_node_params_vector.length () > (unsigned)node->uid)
- ipa_free_node_params_substructures (IPA_NODE_REF (node));
- if (vec_safe_length (ipcp_transformations) > (unsigned)node->uid)
- {
- (*ipcp_transformations)[(unsigned)node->uid].agg_values = NULL;
- (*ipcp_transformations)[(unsigned)node->uid].alignments = NULL;
- }
-}
-
/* Hook that is called by cgraph.c when an edge is duplicated. */
static void
ipa_edge_duplication_hook (struct cgraph_edge *src, struct cgraph_edge *dst,
- __attribute__((unused)) void *data)
+ void *)
{
struct ipa_edge_args *old_args, *new_args;
unsigned int i;
gcc_checking_assert (ref);
dst->caller->clone_reference (ref, ref->stmt);
- gcc_checking_assert (ipa_refdesc_pool);
- struct ipa_cst_ref_desc *dst_rdesc
- = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+ struct ipa_cst_ref_desc *dst_rdesc = ipa_refdesc_pool.allocate ();
dst_rdesc->cs = dst;
dst_rdesc->refcount = src_rdesc->refcount;
dst_rdesc->next_duplicate = NULL;
}
else if (src_rdesc->cs == src)
{
- struct ipa_cst_ref_desc *dst_rdesc;
- gcc_checking_assert (ipa_refdesc_pool);
- dst_rdesc
- = (struct ipa_cst_ref_desc *) pool_alloc (ipa_refdesc_pool);
+ struct ipa_cst_ref_desc *dst_rdesc = ipa_refdesc_pool.allocate ();
dst_rdesc->cs = dst;
dst_rdesc->refcount = src_rdesc->refcount;
dst_rdesc->next_duplicate = src_rdesc->next_duplicate;
}
}
-/* Hook that is called by cgraph.c when a node is duplicated. */
+/* Analyze newly added function into callgraph. */
static void
-ipa_node_duplication_hook (struct cgraph_node *src, struct cgraph_node *dst,
- ATTRIBUTE_UNUSED void *data)
+ipa_add_new_function (cgraph_node *node, void *data ATTRIBUTE_UNUSED)
{
- struct ipa_node_params *old_info, *new_info;
- struct ipa_agg_replacement_value *old_av, *new_av;
+ if (node->has_gimple_body_p ())
+ ipa_analyze_node (node);
+}
- ipa_check_create_node_params ();
- old_info = IPA_NODE_REF (src);
- new_info = IPA_NODE_REF (dst);
+/* Hook that is called by summary when a node is duplicated. */
+
+void
+ipa_node_params_t::duplicate(cgraph_node *src, cgraph_node *dst,
+ ipa_node_params *old_info,
+ ipa_node_params *new_info)
+{
+ ipa_agg_replacement_value *old_av, *new_av;
new_info->descriptors = old_info->descriptors.copy ();
new_info->lattices = NULL;
}
}
-
-/* Analyze newly added function into callgraph. */
-
-static void
-ipa_add_new_function (struct cgraph_node *node, void *data ATTRIBUTE_UNUSED)
-{
- if (node->has_gimple_body_p ())
- ipa_analyze_node (node);
-}
-
/* Register our cgraph hooks if they are not already there. */
void
ipa_register_cgraph_hooks (void)
{
+ ipa_check_create_node_params ();
+
if (!edge_removal_hook_holder)
edge_removal_hook_holder =
symtab->add_edge_removal_hook (&ipa_edge_removal_hook, NULL);
- if (!node_removal_hook_holder)
- node_removal_hook_holder =
- symtab->add_cgraph_removal_hook (&ipa_node_removal_hook, NULL);
if (!edge_duplication_hook_holder)
edge_duplication_hook_holder =
symtab->add_edge_duplication_hook (&ipa_edge_duplication_hook, NULL);
- if (!node_duplication_hook_holder)
- node_duplication_hook_holder =
- symtab->add_cgraph_duplication_hook (&ipa_node_duplication_hook, NULL);
function_insertion_hook_holder =
symtab->add_cgraph_insertion_hook (&ipa_add_new_function, NULL);
}
{
symtab->remove_edge_removal_hook (edge_removal_hook_holder);
edge_removal_hook_holder = NULL;
- symtab->remove_cgraph_removal_hook (node_removal_hook_holder);
- node_removal_hook_holder = NULL;
symtab->remove_edge_duplication_hook (edge_duplication_hook_holder);
edge_duplication_hook_holder = NULL;
- symtab->remove_cgraph_duplication_hook (node_duplication_hook_holder);
- node_duplication_hook_holder = NULL;
symtab->remove_cgraph_insertion_hook (function_insertion_hook_holder);
function_insertion_hook_holder = NULL;
}
{
ipa_free_all_edge_args ();
ipa_free_all_node_params ();
- free_alloc_pool (ipcp_sources_pool);
- free_alloc_pool (ipcp_cst_values_pool);
- free_alloc_pool (ipcp_poly_ctx_values_pool);
- free_alloc_pool (ipcp_agg_lattice_pool);
+ ipcp_sources_pool.release ();
+ ipcp_cst_values_pool.release ();
+ ipcp_poly_ctx_values_pool.release ();
+ ipcp_agg_lattice_pool.release ();
ipa_unregister_cgraph_hooks ();
- if (ipa_refdesc_pool)
- free_alloc_pool (ipa_refdesc_pool);
+ ipa_refdesc_pool.release ();
}
}
ipa_free_all_edge_args ();
ipa_free_all_node_params ();
ipa_unregister_cgraph_hooks ();
- if (ipcp_sources_pool)
- free_alloc_pool (ipcp_sources_pool);
- if (ipcp_cst_values_pool)
- free_alloc_pool (ipcp_cst_values_pool);
- if (ipcp_poly_ctx_values_pool)
- free_alloc_pool (ipcp_poly_ctx_values_pool);
- if (ipcp_agg_lattice_pool)
- free_alloc_pool (ipcp_agg_lattice_pool);
- if (ipa_refdesc_pool)
- free_alloc_pool (ipa_refdesc_pool);
+ ipcp_sources_pool.release ();
+ ipcp_cst_values_pool.release ();
+ ipcp_poly_ctx_values_pool.release ();
+ ipcp_agg_lattice_pool.release ();
+ ipa_refdesc_pool.release ();
}
/* Print ipa_tree_map data structures of all functions in the
lto_symtab_encoder_iterator lsei;
lto_symtab_encoder_t encoder;
-
- if (!ipa_node_params_vector.exists ())
+ if (!ipa_node_params_sum)
return;
ob = create_output_block (LTO_section_jump_functions);
unsigned int count;
lto_input_block ib_main ((const char *) data + main_offset,
- header->main_size);
+ header->main_size, file_data->mode_table);
data_in =
lto_data_in_create (file_data, (const char *) data + string_offset,
unsigned int count;
lto_input_block ib_main ((const char *) data + main_offset,
- header->main_size);
+ header->main_size, file_data->mode_table);
data_in = lto_data_in_create (file_data, (const char *) data + string_offset,
header->string_size, vNULL);