/* Callgraph handling code.
- Copyright (C) 2003-2015 Free Software Foundation, Inc.
+ Copyright (C) 2003-2018 Free Software Foundation, Inc.
Contributed by Jan Hubicka
This file is part of GCC.
/* This file contains basic routines manipulating call graph
- The call-graph is a data structure designed for intra-procedural optimization.
- It represents a multi-graph where nodes are functions and edges are call sites. */
+ The call-graph is a data structure designed for inter-procedural
+ optimization. It represents a multi-graph where nodes are functions
+ (symbols within symbol table) and edges are call sites. */
#include "config.h"
#include "system.h"
#include "value-prof.h"
#include "ipa-utils.h"
#include "symbol-summary.h"
+#include "tree-vrp.h"
#include "ipa-prop.h"
-#include "ipa-inline.h"
+#include "ipa-fnsummary.h"
#include "cfgloop.h"
#include "gimple-pretty-print.h"
#include "tree-dfa.h"
#include "params.h"
#include "tree-chkp.h"
#include "context.h"
+#include "gimplify.h"
+#include "stringpool.h"
+#include "attribs.h"
/* FIXME: Only for PROP_loops, but cgraph shouldn't have to know about this. */
#include "tree-pass.h"
return version_info_node;
}
-/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
- DECL is a duplicate declaration. */
-void
-cgraph_node::delete_function_version (tree decl)
+/* Remove the cgraph_function_version_info node given by DECL_V. */
+static void
+delete_function_version (cgraph_function_version_info *decl_v)
{
- cgraph_node *decl_node = cgraph_node::get (decl);
- cgraph_function_version_info *decl_v = NULL;
-
- if (decl_node == NULL)
- return;
-
- decl_v = decl_node->function_version ();
-
if (decl_v == NULL)
return;
if (decl_v->prev != NULL)
- decl_v->prev->next = decl_v->next;
+ decl_v->prev->next = decl_v->next;
if (decl_v->next != NULL)
decl_v->next->prev = decl_v->prev;
if (cgraph_fnver_htab != NULL)
cgraph_fnver_htab->remove_elt (decl_v);
+}
+
+/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
+ DECL is a duplicate declaration. */
+void
+cgraph_node::delete_function_version_by_decl (tree decl)
+{
+ cgraph_node *decl_node = cgraph_node::get (decl);
+
+ if (decl_node == NULL)
+ return;
+
+ delete_function_version (decl_node->function_version ());
decl_node->remove ();
}
{
if (!dump_file)
dump_file = dump_begin (TDI_cgraph, NULL);
+
+ if (!ipa_clones_dump_file)
+ ipa_clones_dump_file = dump_begin (TDI_clones, NULL);
}
/* Allocate new callgraph node and insert it into basic data structures. */
node->decl = decl;
+ node->count = profile_count::uninitialized ();
+
if ((flag_openacc || flag_openmp)
&& lookup_attribute ("omp declare target", DECL_ATTRIBUTES (decl)))
{
node->decl->decl_with_vis.symtab_node = node;
if (dump_file)
fprintf (dump_file, "Introduced new external node "
- "(%s/%i) and turned into root of the clone tree.\n",
- node->name (), node->order);
+ "(%s) and turned into root of the clone tree.\n",
+ node->dump_name ());
}
else if (dump_file)
fprintf (dump_file, "Introduced new external node "
- "(%s/%i).\n", node->name (), node->order);
+ "(%s).\n", node->dump_name ());
return node;
}
alias_node->definition = true;
alias_node->alias = true;
if (lookup_attribute ("weakref", DECL_ATTRIBUTES (alias)) != NULL)
- alias_node->weakref = true;
+ alias_node->transparent_alias = alias_node->weakref = true;
return alias_node;
}
cgraph_node::create_same_body_alias (tree alias, tree decl)
{
cgraph_node *n;
-#ifndef ASM_OUTPUT_DEF
+
/* If aliases aren't supported by the assembler, fail. */
- return NULL;
-#endif
+ if (!TARGET_SUPPORTS_ALIASES)
+ return NULL;
+
/* Langhooks can create same body aliases of symbols not defined.
Those are useless. Drop them on the floor. */
if (symtab->global_info_ready)
/* Add thunk alias into callgraph. The alias declaration is ALIAS and it
aliases DECL with an adjustments made into the first parameter.
- See comments in thunk_adjust for detail on the parameters. */
+ See comments in struct cgraph_thunk_info for detail on the parameters. */
cgraph_node *
cgraph_node::create_thunk (tree alias, tree, bool this_adjusting,
node->reset ();
else
node = cgraph_node::create (alias);
- gcc_checking_assert (!virtual_offset
- || wi::eq_p (virtual_offset, virtual_value));
+
+ /* Make sure that if VIRTUAL_OFFSET is in sync with VIRTUAL_VALUE. */
+ gcc_checking_assert (virtual_offset
+ ? virtual_value == wi::to_wide (virtual_offset)
+ : virtual_value == 0);
+
node->thunk.fixed_offset = fixed_offset;
- node->thunk.this_adjusting = this_adjusting;
node->thunk.virtual_value = virtual_value;
- node->thunk.virtual_offset_p = virtual_offset != NULL;
node->thunk.alias = real_alias;
+ node->thunk.this_adjusting = this_adjusting;
+ node->thunk.virtual_offset_p = virtual_offset != NULL;
node->thunk.thunk_p = true;
node->definition = true;
cgraph_edge *
symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
- gcall *call_stmt, gcov_type count, int freq,
+ gcall *call_stmt, profile_count count,
bool indir_unknown_callee)
{
cgraph_edge *edge;
edge->lto_stmt_uid = 0;
edge->count = count;
- gcc_assert (count >= 0);
- edge->frequency = freq;
- gcc_assert (freq >= 0);
- gcc_assert (freq <= CGRAPH_FREQ_MAX);
edge->call_stmt = call_stmt;
push_cfun (DECL_STRUCT_FUNCTION (caller->decl));
&& callee && callee->decl
&& !gimple_check_call_matching_types (call_stmt, callee->decl,
false))
- edge->call_stmt_cannot_inline_p = true;
+ {
+ edge->inline_failed = CIF_MISMATCHED_ARGUMENTS;
+ edge->call_stmt_cannot_inline_p = true;
+ }
else
- edge->call_stmt_cannot_inline_p = false;
+ {
+ edge->inline_failed = CIF_FUNCTION_NOT_CONSIDERED;
+ edge->call_stmt_cannot_inline_p = false;
+ }
edge->indirect_info = NULL;
edge->indirect_inlining_edge = 0;
cgraph_edge *
cgraph_node::create_edge (cgraph_node *callee,
- gcall *call_stmt, gcov_type count, int freq)
+ gcall *call_stmt, profile_count count)
{
cgraph_edge *edge = symtab->create_edge (this, callee, call_stmt, count,
- freq, false);
+ false);
initialize_inline_failed (edge);
cgraph_edge *
cgraph_node::create_indirect_edge (gcall *call_stmt, int ecf_flags,
- gcov_type count, int freq,
+ profile_count count,
bool compute_indirect_info)
{
cgraph_edge *edge = symtab->create_edge (this, NULL, call_stmt,
- count, freq, true);
+ count, true);
tree target;
initialize_inline_failed (edge);
Return direct edge created. */
cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, gcov_type direct_count,
- int direct_frequency)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
{
cgraph_node *n = caller;
ipa_ref *ref = NULL;
cgraph_edge *e2;
if (dump_file)
- {
- fprintf (dump_file, "Indirect call -> speculative call"
- " %s/%i => %s/%i\n",
- xstrdup_for_dump (n->name ()), n->order,
- xstrdup_for_dump (n2->name ()), n2->order);
- }
+ fprintf (dump_file, "Indirect call -> speculative call %s => %s\n",
+ n->dump_name (), n2->dump_name ());
speculative = true;
- e2 = n->create_edge (n2, call_stmt, direct_count, direct_frequency);
+ e2 = n->create_edge (n2, call_stmt, direct_count);
initialize_inline_failed (e2);
e2->speculative = true;
if (TREE_NOTHROW (n2->decl))
e2->lto_stmt_uid = lto_stmt_uid;
e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
count -= e2->count;
- frequency -= e2->frequency;
symtab->call_edge_duplication_hooks (this, e2);
ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
ref->lto_stmt_uid = lto_stmt_uid;
{
if (callee_decl)
{
- fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
+ fprintf (dump_file, "Speculative indirect call %s => %s has "
"turned out to have contradicting known target ",
- xstrdup_for_dump (edge->caller->name ()),
- edge->caller->order,
- xstrdup_for_dump (e2->callee->name ()),
- e2->callee->order);
- print_generic_expr (dump_file, callee_decl, 0);
+ edge->caller->dump_name (),
+ e2->callee->dump_name ());
+ print_generic_expr (dump_file, callee_decl);
fprintf (dump_file, "\n");
}
else
{
- fprintf (dump_file, "Removing speculative call %s/%i => %s/%i\n",
- xstrdup_for_dump (edge->caller->name ()),
- edge->caller->order,
- xstrdup_for_dump (e2->callee->name ()),
- e2->callee->order);
+ fprintf (dump_file, "Removing speculative call %s => %s\n",
+ edge->caller->dump_name (),
+ e2->callee->dump_name ());
}
}
}
in the functions inlined through it. */
}
edge->count += e2->count;
- edge->frequency += e2->frequency;
- if (edge->frequency > CGRAPH_FREQ_MAX)
- edge->frequency = CGRAPH_FREQ_MAX;
edge->speculative = false;
e2->speculative = false;
ref->remove_reference ();
/* Insert to callers list of the new callee. */
edge->set_callee (callee);
- if (call_stmt)
- call_stmt_cannot_inline_p
- = !gimple_check_call_matching_types (call_stmt, callee->decl,
- false);
+ if (call_stmt
+ && !gimple_check_call_matching_types (call_stmt, callee->decl, false))
+ {
+ call_stmt_cannot_inline_p = true;
+ inline_failed = CIF_MISMATCHED_ARGUMENTS;
+ }
/* We need to re-determine the inlining status of the edge. */
initialize_inline_failed (edge);
cgraph_edge *e = this;
tree decl = gimple_call_fndecl (e->call_stmt);
- tree lhs = gimple_call_lhs (e->call_stmt);
gcall *new_stmt;
gimple_stmt_iterator gsi;
bool skip_bounds = false;
if (decl)
e = e->resolve_speculation (decl);
/* If types do not match, speculation was likely wrong.
- The direct edge was posisbly redirected to the clone with a different
+ The direct edge was possibly redirected to the clone with a different
signature. We did not update the call statement yet, so compare it
with the reference that still points to the proper type. */
else if (!gimple_check_call_matching_types (e->call_stmt,
true))
{
if (dump_file)
- fprintf (dump_file, "Not expanding speculative call of %s/%i -> %s/%i\n"
+ fprintf (dump_file, "Not expanding speculative call of %s -> %s\n"
"Type mismatch.\n",
- xstrdup_for_dump (e->caller->name ()),
- e->caller->order,
- xstrdup_for_dump (e->callee->name ()),
- e->callee->order);
+ e->caller->dump_name (),
+ e->callee->dump_name ());
e = e->resolve_speculation ();
/* We are producing the final function body and will throw away the
callgraph edges really soon. Reset the counts/frequencies to
keep verifier happy in the case of roundoff errors. */
e->count = gimple_bb (e->call_stmt)->count;
- e->frequency = compute_call_stmt_bb_frequency
- (e->caller->decl, gimple_bb (e->call_stmt));
}
/* Expand speculation into GIMPLE code. */
else
{
if (dump_file)
- fprintf (dump_file,
- "Expanding speculative call of %s/%i -> %s/%i count:"
- "%" PRId64"\n",
- xstrdup_for_dump (e->caller->name ()),
- e->caller->order,
- xstrdup_for_dump (e->callee->name ()),
- e->callee->order,
- (int64_t)e->count);
+ {
+ fprintf (dump_file,
+ "Expanding speculative call of %s -> %s count: ",
+ e->caller->dump_name (),
+ e->callee->dump_name ());
+ e->count.dump (dump_file);
+ fprintf (dump_file, "\n");
+ }
gcc_assert (e2->speculative);
push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
+
+ profile_probability prob = e->count.probability_in (e->count
+ + e2->count);
+ if (!prob.initialized_p ())
+ prob = profile_probability::even ();
new_stmt = gimple_ic (e->call_stmt,
dyn_cast<cgraph_node *> (ref->referred),
- e->count || e2->count
- ? RDIV (e->count * REG_BR_PROB_BASE,
- e->count + e2->count)
- : e->frequency || e2->frequency
- ? RDIV (e->frequency * REG_BR_PROB_BASE,
- e->frequency + e2->frequency)
- : REG_BR_PROB_BASE / 2,
- e->count, e->count + e2->count);
+ prob);
e->speculative = false;
e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
false);
+ e->count = gimple_bb (e->call_stmt)->count;
/* Fix edges for BUILT_IN_CHKP_BNDRET calls attached to the
processed call stmt. */
gcall *ibndret = chkp_retbnd_call_by_val (iresult);
struct cgraph_edge *iedge
= e2->caller->cgraph_node::get_edge (ibndret);
- struct cgraph_edge *dedge;
if (dbndret)
- {
- dedge = iedge->caller->create_edge (iedge->callee,
- dbndret, e->count,
- e->frequency);
- dedge->frequency = compute_call_stmt_bb_frequency
- (dedge->caller->decl, gimple_bb (dedge->call_stmt));
- }
- iedge->frequency = compute_call_stmt_bb_frequency
- (iedge->caller->decl, gimple_bb (iedge->call_stmt));
+ iedge->caller->create_edge (iedge->callee, dbndret, e->count);
}
- e->frequency = compute_call_stmt_bb_frequency
- (e->caller->decl, gimple_bb (e->call_stmt));
- e2->frequency = compute_call_stmt_bb_frequency
- (e2->caller->decl, gimple_bb (e2->call_stmt));
e2->speculative = false;
+ e2->count = gimple_bb (e2->call_stmt)->count;
ref->speculative = false;
ref->stmt = NULL;
/* Indirect edges are not both in the call site hash.
if (symtab->dump_file)
{
- fprintf (symtab->dump_file, "updating call of %s/%i -> %s/%i: ",
- xstrdup_for_dump (e->caller->name ()), e->caller->order,
- xstrdup_for_dump (e->callee->name ()), e->callee->order);
+ fprintf (symtab->dump_file, "updating call of %s -> %s: ",
+ e->caller->dump_name (), e->callee->dump_name ());
print_gimple_stmt (symtab->dump_file, e->call_stmt, 0, dump_flags);
if (e->callee->clone.combined_args_to_skip)
{
if (skip_bounds)
new_stmt = chkp_copy_call_skip_bounds (new_stmt);
+ tree old_fntype = gimple_call_fntype (e->call_stmt);
gimple_call_set_fndecl (new_stmt, e->callee->decl);
- gimple_call_set_fntype (new_stmt, gimple_call_fntype (e->call_stmt));
+ cgraph_node *origin = e->callee;
+ while (origin->clone_of)
+ origin = origin->clone_of;
+
+ if ((origin->former_clone_of
+ && old_fntype == TREE_TYPE (origin->former_clone_of))
+ || old_fntype == TREE_TYPE (origin->decl))
+ gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
+ else
+ {
+ bitmap skip = e->callee->clone.combined_args_to_skip;
+ tree t = cgraph_build_function_type_skip_args (old_fntype, skip,
+ false);
+ gimple_call_set_fntype (new_stmt, t);
+ }
if (gimple_vdef (new_stmt)
&& TREE_CODE (gimple_vdef (new_stmt)) == SSA_NAME)
SSA_NAME_DEF_STMT (gimple_vdef (new_stmt)) = new_stmt;
gsi = gsi_for_stmt (e->call_stmt);
+
+ /* For optimized away parameters, add on the caller side
+ before the call
+ DEBUG D#X => parm_Y(D)
+ stmts and associate D#X with parm in decl_debug_args_lookup
+ vector to say for debug info that if parameter parm had been passed,
+ it would have value parm_Y(D). */
+ if (e->callee->clone.combined_args_to_skip && MAY_HAVE_DEBUG_BIND_STMTS)
+ {
+ vec<tree, va_gc> **debug_args
+ = decl_debug_args_lookup (e->callee->decl);
+ tree old_decl = gimple_call_fndecl (e->call_stmt);
+ if (debug_args && old_decl)
+ {
+ tree parm;
+ unsigned i = 0, num;
+ unsigned len = vec_safe_length (*debug_args);
+ unsigned nargs = gimple_call_num_args (e->call_stmt);
+ for (parm = DECL_ARGUMENTS (old_decl), num = 0;
+ parm && num < nargs;
+ parm = DECL_CHAIN (parm), num++)
+ if (bitmap_bit_p (e->callee->clone.combined_args_to_skip, num)
+ && is_gimple_reg (parm))
+ {
+ unsigned last = i;
+
+ while (i < len && (**debug_args)[i] != DECL_ORIGIN (parm))
+ i += 2;
+ if (i >= len)
+ {
+ i = 0;
+ while (i < last
+ && (**debug_args)[i] != DECL_ORIGIN (parm))
+ i += 2;
+ if (i >= last)
+ continue;
+ }
+ tree ddecl = (**debug_args)[i + 1];
+ tree arg = gimple_call_arg (e->call_stmt, num);
+ if (!useless_type_conversion_p (TREE_TYPE (ddecl),
+ TREE_TYPE (arg)))
+ {
+ tree rhs1;
+ if (!fold_convertible_p (TREE_TYPE (ddecl), arg))
+ continue;
+ if (TREE_CODE (arg) == SSA_NAME
+ && gimple_assign_cast_p (SSA_NAME_DEF_STMT (arg))
+ && (rhs1
+ = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (arg)))
+ && useless_type_conversion_p (TREE_TYPE (ddecl),
+ TREE_TYPE (rhs1)))
+ arg = rhs1;
+ else
+ arg = fold_convert (TREE_TYPE (ddecl), arg);
+ }
+
+ gimple *def_temp
+ = gimple_build_debug_bind (ddecl, unshare_expr (arg),
+ e->call_stmt);
+ gsi_insert_before (&gsi, def_temp, GSI_SAME_STMT);
+ }
+ }
+ }
+
gsi_replace (&gsi, new_stmt, false);
/* We need to defer cleaning EH info on the new statement to
fixup-cfg. We may not have dominator information at this point
update_stmt_fn (DECL_STRUCT_FUNCTION (e->caller->decl), new_stmt);
}
+ /* If changing the call to __cxa_pure_virtual or similar noreturn function,
+ adjust gimple_call_fntype too. */
+ if (gimple_call_noreturn_p (new_stmt)
+ && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (e->callee->decl)))
+ && TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl))
+ && (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl)))
+ == void_type_node))
+ gimple_call_set_fntype (new_stmt, TREE_TYPE (e->callee->decl));
+
/* If the call becomes noreturn, remove the LHS if possible. */
+ tree lhs = gimple_call_lhs (new_stmt);
if (lhs
- && (gimple_call_flags (new_stmt) & ECF_NORETURN)
- && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST)
+ && gimple_call_noreturn_p (new_stmt)
+ && (VOID_TYPE_P (TREE_TYPE (gimple_call_fntype (new_stmt)))
+ || should_remove_lhs_p (lhs)))
{
if (TREE_CODE (lhs) == SSA_NAME)
{
{
cgraph_edge *e = node->get_edge (old_stmt);
cgraph_edge *ne = NULL;
- gcov_type count;
- int frequency;
+ profile_count count;
if (e)
{
since function has changed, so inline plan and other information
attached to edge is invalid. */
count = e->count;
- frequency = e->frequency;
if (e->indirect_unknown_callee || e->inline_failed)
e->remove ();
else
/* We are seeing new direct call; compute profile info based on BB. */
basic_block bb = gimple_bb (new_stmt);
count = bb->count;
- frequency = compute_call_stmt_bb_frequency (current_function_decl,
- bb);
}
if (new_call)
{
ne = node->create_edge (cgraph_node::get_create (new_call),
- as_a <gcall *> (new_stmt), count,
- frequency);
+ as_a <gcall *> (new_stmt), count);
gcc_assert (ne->inline_failed);
}
}
if (fn)
{
if (fn->cfg
- || fn->gimple_df)
+ && loops_for_fn (fn))
{
- if (fn->cfg
- && loops_for_fn (fn))
- {
- fn->curr_properties &= ~PROP_loops;
- loop_optimizer_finalize (fn);
- }
- if (fn->gimple_df)
- {
- delete_tree_ssa (fn);
- delete_tree_cfg_annotations (fn);
- fn->eh = NULL;
- }
- if (fn->cfg)
- {
- gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
- gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
- clear_edges (fn);
- fn->cfg = NULL;
- }
- if (fn->value_histograms)
- free_histograms (fn);
+ fn->curr_properties &= ~PROP_loops;
+ loop_optimizer_finalize (fn);
+ }
+ if (fn->gimple_df)
+ {
+ delete_tree_ssa (fn);
+ fn->eh = NULL;
}
+ if (fn->cfg)
+ {
+ gcc_assert (!dom_info_available_p (fn, CDI_DOMINATORS));
+ gcc_assert (!dom_info_available_p (fn, CDI_POST_DOMINATORS));
+ delete_tree_cfg_annotations (fn);
+ clear_edges (fn);
+ fn->cfg = NULL;
+ }
+ if (fn->value_histograms)
+ free_histograms (fn);
gimple_set_body (decl, NULL);
/* Struct function hangs a lot of data that would leak if we didn't
removed all pointers to it. */
cgraph_node *n;
int uid = this->uid;
+ if (symtab->ipa_clones_dump_file && symtab->cloned_nodes.contains (this))
+ fprintf (symtab->ipa_clones_dump_file,
+ "Callgraph removal;%s;%d;%s;%d;%d\n", asm_name (), order,
+ DECL_SOURCE_FILE (decl), DECL_SOURCE_LINE (decl),
+ DECL_SOURCE_COLUMN (decl));
+
symtab->call_cgraph_removal_hooks (this);
remove_callers ();
remove_callees ();
ipa_transforms_to_apply.release ();
+ delete_function_version (function_version ());
/* Incremental inlining access removed nodes stored in the postorder list.
*/
cgraph_node *node = get (decl);
if (!node)
return NULL;
- node = node->ultimate_alias_target ();
- if (node->decl != current_function_decl
- && !TREE_ASM_WRITTEN (node->decl))
+ enum availability avail;
+ node = node->ultimate_alias_target (&avail);
+ if (decl != current_function_decl
+ && (avail < AVAIL_AVAILABLE
+ || (node->decl != current_function_decl
+ && !TREE_ASM_WRITTEN (node->decl))))
return NULL;
- /* Allocate if it doesnt exist. */
- if (node->ultimate_alias_target ()->rtl == NULL)
- node->ultimate_alias_target ()->rtl = ggc_cleared_alloc<cgraph_rtl_info> ();
- return node->ultimate_alias_target ()->rtl;
+ /* Allocate if it doesn't exist. */
+ if (node->rtl == NULL)
+ node->rtl = ggc_cleared_alloc<cgraph_rtl_info> ();
+ return node->rtl;
}
/* Return a string describing the failure REASON. */
fprintf (f, "(speculative) ");
if (!inline_failed)
fprintf (f, "(inlined) ");
+ if (call_stmt_cannot_inline_p)
+ fprintf (f, "(call_stmt_cannot_inline_p) ");
if (indirect_inlining_edge)
fprintf (f, "(indirect_inlining) ");
- if (count)
- fprintf (f, "(%" PRId64"x) ", (int64_t)count);
- if (frequency)
- fprintf (f, "(%.2f per call) ", frequency / (double)CGRAPH_FREQ_BASE);
+ if (count.initialized_p ())
+ {
+ fprintf (f, "(");
+ count.dump (f);
+ fprintf (f, ",");
+ fprintf (f, "%.2f per call) ", sreal_frequency ().to_double ());
+ }
if (can_throw_external)
fprintf (f, "(can throw external) ");
}
dump_base (f);
if (global.inlined_to)
- fprintf (f, " Function %s/%i is inline copy in %s/%i\n",
- xstrdup_for_dump (name ()),
- order,
- xstrdup_for_dump (global.inlined_to->name ()),
- global.inlined_to->order);
+ fprintf (f, " Function %s is inline copy in %s\n",
+ dump_name (),
+ global.inlined_to->dump_name ());
if (clone_of)
- fprintf (f, " Clone of %s/%i\n",
- clone_of->asm_name (),
- clone_of->order);
+ fprintf (f, " Clone of %s\n", clone_of->dump_asm_name ());
if (symtab->function_flags_ready)
fprintf (f, " Availability: %s\n",
cgraph_availability_names [get_availability ()]);
fprintf (f, " Profile id: %i\n",
profile_id);
fprintf (f, " First run: %i\n", tp_first_run);
+ cgraph_function_version_info *vi = function_version ();
+ if (vi != NULL)
+ {
+ fprintf (f, " Version info: ");
+ if (vi->prev != NULL)
+ {
+ fprintf (f, "prev: ");
+ fprintf (f, "%s ", vi->prev->this_node->dump_asm_name ());
+ }
+ if (vi->next != NULL)
+ {
+ fprintf (f, "next: ");
+ fprintf (f, "%s ", vi->next->this_node->dump_asm_name ());
+ }
+ if (vi->dispatcher_resolver != NULL_TREE)
+ fprintf (f, "dispatcher: %s",
+ lang_hooks.decl_printable_name (vi->dispatcher_resolver, 2));
+
+ fprintf (f, "\n");
+ }
fprintf (f, " Function flags:");
- if (count)
- fprintf (f, " executed %" PRId64"x",
- (int64_t)count);
+ if (count.initialized_p ())
+ {
+ fprintf (f, " count: ");
+ count.dump (f);
+ }
if (origin)
fprintf (f, " nested in: %s", origin->asm_name ());
if (gimple_has_body_p (decl))
fprintf (f, " only_called_at_exit");
if (tm_clone)
fprintf (f, " tm_clone");
+ if (calls_comdat_local)
+ fprintf (f, " calls_comdat_local");
if (icf_merged)
fprintf (f, " icf_merged");
+ if (merged_comdat)
+ fprintf (f, " merged_comdat");
+ if (split_part)
+ fprintf (f, " split_part");
+ if (indirect_call_target)
+ fprintf (f, " indirect_call_target");
if (nonfreeing_fn)
fprintf (f, " nonfreeing_fn");
if (DECL_STATIC_CONSTRUCTOR (decl))
fprintf (f, " Called by: ");
+ profile_count sum = profile_count::zero ();
for (edge = callers; edge; edge = edge->next_caller)
{
- fprintf (f, "%s/%i ", edge->caller->asm_name (),
- edge->caller->order);
+ fprintf (f, "%s ", edge->caller->dump_name ());
edge->dump_edge_flags (f);
+ if (edge->count.initialized_p ())
+ sum += edge->count.ipa ();
}
fprintf (f, "\n Calls: ");
for (edge = callees; edge; edge = edge->next_callee)
{
- fprintf (f, "%s/%i ", edge->callee->asm_name (),
- edge->callee->order);
+ fprintf (f, "%s ", edge->callee->dump_name ());
edge->dump_edge_flags (f);
}
fprintf (f, "\n");
+ if (count.ipa ().initialized_p ())
+ {
+ bool ok = true;
+ bool min = false;
+ ipa_ref *ref;
+
+ FOR_EACH_ALIAS (this, ref)
+ if (dyn_cast <cgraph_node *> (ref->referring)->count.initialized_p ())
+ sum += dyn_cast <cgraph_node *> (ref->referring)->count.ipa ();
+
+ if (global.inlined_to
+ || (symtab->state < EXPANSION
+ && ultimate_alias_target () == this && only_called_directly_p ()))
+ ok = !count.ipa ().differs_from_p (sum);
+ else if (count.ipa () > profile_count::from_gcov_type (100)
+ && count.ipa () < sum.apply_scale (99, 100))
+ ok = false, min = true;
+ if (!ok)
+ {
+ fprintf (f, " Invalid sum of caller counts ");
+ sum.dump (f);
+ if (min)
+ fprintf (f, ", should be at most ");
+ else
+ fprintf (f, ", should be ");
+ count.ipa ().dump (f);
+ fprintf (f, "\n");
+ }
+ }
+
for (edge = indirect_calls; edge; edge = edge->next_callee)
{
if (edge->indirect_info->polymorphic)
/* Return function availability. See cgraph.h for description of individual
return values. */
enum availability
-cgraph_node::get_availability (void)
+cgraph_node::get_availability (symtab_node *ref)
{
+ if (ref)
+ {
+ cgraph_node *cref = dyn_cast <cgraph_node *> (ref);
+ if (cref)
+ ref = cref->global.inlined_to;
+ }
enum availability avail;
if (!analyzed)
avail = AVAIL_NOT_AVAILABLE;
else if (local.local)
avail = AVAIL_LOCAL;
- else if (alias && weakref)
- ultimate_alias_target (&avail);
- else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
+ else if (global.inlined_to)
+ avail = AVAIL_AVAILABLE;
+ else if (transparent_alias)
+ ultimate_alias_target (&avail, ref);
+ else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))
+ || lookup_attribute ("noipa", DECL_ATTRIBUTES (decl)))
avail = AVAIL_INTERPOSABLE;
else if (!externally_visible)
avail = AVAIL_AVAILABLE;
+ /* If this is a reference from symbol itself and there are no aliases, we
+ may be sure that the symbol was not interposed by something else because
+ the symbol itself would be unreachable otherwise.
+
+ Also comdat groups are always resolved in groups. */
+ else if ((this == ref && !has_aliases_p ())
+ || (ref && get_comdat_group ()
+ && get_comdat_group () == ref->get_comdat_group ()))
+ avail = AVAIL_AVAILABLE;
/* Inline functions are safe to be analyzed even if their symbol can
be overwritten at runtime. It is not meaningful to enforce any sane
- behaviour on replacing inline function by different body. */
+ behavior on replacing inline function by different body. */
else if (DECL_DECLARED_INLINE_P (decl))
avail = AVAIL_AVAILABLE;
care at least of two notable extensions - the COMDAT functions
used to share template instantiations in C++ (this is symmetric
to code cp_cannot_inline_tree_fn and probably shall be shared and
- the inlinability hooks completely eliminated).
-
- ??? Does the C++ one definition rule allow us to always return
- AVAIL_AVAILABLE here? That would be good reason to preserve this
- bit. */
+ the inlinability hooks completely eliminated). */
else if (decl_replaceable_p (decl) && !DECL_EXTERNAL (decl))
avail = AVAIL_INTERPOSABLE;
}
/* Call callback on cgraph_node, thunks and aliases associated to cgraph_node.
- When INCLUDE_OVERWRITABLE is false, overwritable aliases and thunks are
+ When INCLUDE_OVERWRITABLE is false, overwritable symbols are
skipped. When EXCLUDE_VIRTUAL_THUNKS is true, virtual thunks are
skipped. */
bool
{
cgraph_edge *e;
ipa_ref *ref;
+ enum availability avail = AVAIL_AVAILABLE;
- if (callback (this, data))
- return true;
+ if (include_overwritable
+ || (avail = get_availability ()) > AVAIL_INTERPOSABLE)
+ {
+ if (callback (this, data))
+ return true;
+ }
FOR_EACH_ALIAS (this, ref)
{
cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
exclude_virtual_thunks))
return true;
}
+ if (avail <= AVAIL_INTERPOSABLE)
+ return false;
for (e = callers; e; e = e->next_caller)
if (e->caller->thunk.thunk_p
&& (include_overwritable
node->forced_by_abi = false;
node->local.local = true;
node->set_section (NULL);
- node->unique_name = (node->resolution == LDPR_PREVAILING_DEF_IRONLY
- || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP);
+ node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
+ || node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
+ && !flag_incremental_link);
node->resolution = LDPR_PREVAILING_DEF_IRONLY;
gcc_assert (node->get_availability () == AVAIL_LOCAL);
}
/* Worker to set nothrow flag. */
-static bool
-cgraph_set_nothrow_flag_1 (cgraph_node *node, void *data)
+static void
+set_nothrow_flag_1 (cgraph_node *node, bool nothrow, bool non_call,
+ bool *changed)
{
cgraph_edge *e;
- TREE_NOTHROW (node->decl) = data != NULL;
-
- if (data != NULL)
- for (e = node->callers; e; e = e->next_caller)
- e->can_throw_external = false;
- return false;
+ if (nothrow && !TREE_NOTHROW (node->decl))
+ {
+ /* With non-call exceptions we can't say for sure if other function body
+ was not possibly optimized to stil throw. */
+ if (!non_call || node->binds_to_current_def_p ())
+ {
+ TREE_NOTHROW (node->decl) = true;
+ *changed = true;
+ for (e = node->callers; e; e = e->next_caller)
+ e->can_throw_external = false;
+ }
+ }
+ else if (!nothrow && TREE_NOTHROW (node->decl))
+ {
+ TREE_NOTHROW (node->decl) = false;
+ *changed = true;
+ }
+ ipa_ref *ref;
+ FOR_EACH_ALIAS (node, ref)
+ {
+ cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+ if (!nothrow || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_nothrow_flag_1 (alias, nothrow, non_call, changed);
+ }
+ for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+ if (e->caller->thunk.thunk_p
+ && (!nothrow || e->caller->get_availability () > AVAIL_INTERPOSABLE))
+ set_nothrow_flag_1 (e->caller, nothrow, non_call, changed);
}
/* Set TREE_NOTHROW on NODE's decl and on aliases of NODE
if any to NOTHROW. */
-void
+bool
cgraph_node::set_nothrow_flag (bool nothrow)
{
- call_for_symbol_thunks_and_aliases (cgraph_set_nothrow_flag_1,
- (void *)(size_t)nothrow, false);
+ bool changed = false;
+ bool non_call = opt_for_fn (decl, flag_non_call_exceptions);
+
+ if (!nothrow || get_availability () > AVAIL_INTERPOSABLE)
+ set_nothrow_flag_1 (this, nothrow, non_call, &changed);
+ else
+ {
+ ipa_ref *ref;
+
+ FOR_EACH_ALIAS (this, ref)
+ {
+ cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+ if (!nothrow || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_nothrow_flag_1 (alias, nothrow, non_call, &changed);
+ }
+ }
+ return changed;
}
-/* Worker to set const flag. */
+/* Worker to set malloc flag. */
+static void
+set_malloc_flag_1 (cgraph_node *node, bool malloc_p, bool *changed)
+{
+ if (malloc_p && !DECL_IS_MALLOC (node->decl))
+ {
+ DECL_IS_MALLOC (node->decl) = true;
+ *changed = true;
+ }
-static bool
-cgraph_set_const_flag_1 (cgraph_node *node, void *data)
+ ipa_ref *ref;
+ FOR_EACH_ALIAS (node, ref)
+ {
+ cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+ if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_malloc_flag_1 (alias, malloc_p, changed);
+ }
+
+ for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+ if (e->caller->thunk.thunk_p
+ && (!malloc_p || e->caller->get_availability () > AVAIL_INTERPOSABLE))
+ set_malloc_flag_1 (e->caller, malloc_p, changed);
+}
+
+/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any. */
+
+bool
+cgraph_node::set_malloc_flag (bool malloc_p)
+{
+ bool changed = false;
+
+ if (!malloc_p || get_availability () > AVAIL_INTERPOSABLE)
+ set_malloc_flag_1 (this, malloc_p, &changed);
+ else
+ {
+ ipa_ref *ref;
+
+ FOR_EACH_ALIAS (this, ref)
+ {
+ cgraph_node *alias = dyn_cast<cgraph_node *> (ref->referring);
+ if (!malloc_p || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_malloc_flag_1 (alias, malloc_p, &changed);
+ }
+ }
+ return changed;
+}
+
+/* Worker to set_const_flag. */
+
+static void
+set_const_flag_1 (cgraph_node *node, bool set_const, bool looping,
+ bool *changed)
{
/* Static constructors and destructors without a side effect can be
optimized out. */
- if (data && !((size_t)data & 2))
+ if (set_const && !looping)
{
if (DECL_STATIC_CONSTRUCTOR (node->decl))
- DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ {
+ DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ *changed = true;
+ }
if (DECL_STATIC_DESTRUCTOR (node->decl))
- DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ {
+ DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ *changed = true;
+ }
}
- TREE_READONLY (node->decl) = data != NULL;
- DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0;
- return false;
+ if (!set_const)
+ {
+ if (TREE_READONLY (node->decl))
+ {
+ TREE_READONLY (node->decl) = 0;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+ *changed = true;
+ }
+ }
+ else
+ {
+ /* Consider function:
+
+ bool a(int *p)
+ {
+ return *p==*p;
+ }
+
+ During early optimization we will turn this into:
+
+ bool a(int *p)
+ {
+ return true;
+ }
+
+ Now if this function will be detected as CONST however when interposed
+ it may end up being just pure. We always must assume the worst
+ scenario here. */
+ if (TREE_READONLY (node->decl))
+ {
+ if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl))
+ {
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+ *changed = true;
+ }
+ }
+ else if (node->binds_to_current_def_p ())
+ {
+ TREE_READONLY (node->decl) = true;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
+ DECL_PURE_P (node->decl) = false;
+ *changed = true;
+ }
+ else
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Dropping state to PURE because function does "
+ "not bind to current def.\n");
+ if (!DECL_PURE_P (node->decl))
+ {
+ DECL_PURE_P (node->decl) = true;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = looping;
+ *changed = true;
+ }
+ else if (!looping && DECL_LOOPING_CONST_OR_PURE_P (node->decl))
+ {
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+ *changed = true;
+ }
+ }
+ }
+
+ ipa_ref *ref;
+ FOR_EACH_ALIAS (node, ref)
+ {
+ cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+ if (!set_const || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_const_flag_1 (alias, set_const, looping, changed);
+ }
+ for (cgraph_edge *e = node->callers; e; e = e->next_caller)
+ if (e->caller->thunk.thunk_p
+ && (!set_const || e->caller->get_availability () > AVAIL_INTERPOSABLE))
+ {
+ /* Virtual thunks access virtual offset in the vtable, so they can
+ only be pure, never const. */
+ if (set_const
+ && (e->caller->thunk.virtual_offset_p
+ || !node->binds_to_current_def_p (e->caller)))
+ *changed |= e->caller->set_pure_flag (true, looping);
+ else
+ set_const_flag_1 (e->caller, set_const, looping, changed);
+ }
}
-/* Set TREE_READONLY on cgraph_node's decl and on aliases of the node
- if any to READONLY. */
+/* If SET_CONST is true, mark function, aliases and thunks to be ECF_CONST.
+ If SET_CONST if false, clear the flag.
-void
-cgraph_node::set_const_flag (bool readonly, bool looping)
+ When setting the flag be careful about possible interposition and
+ do not set the flag for functions that can be interposet and set pure
+ flag for functions that can bind to other definition.
+
+ Return true if any change was done. */
+
+bool
+cgraph_node::set_const_flag (bool set_const, bool looping)
{
- call_for_symbol_thunks_and_aliases (cgraph_set_const_flag_1,
- (void *)(size_t)(readonly + (int)looping * 2),
- false, true);
+ bool changed = false;
+ if (!set_const || get_availability () > AVAIL_INTERPOSABLE)
+ set_const_flag_1 (this, set_const, looping, &changed);
+ else
+ {
+ ipa_ref *ref;
+
+ FOR_EACH_ALIAS (this, ref)
+ {
+ cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
+ if (!set_const || alias->get_availability () > AVAIL_INTERPOSABLE)
+ set_const_flag_1 (alias, set_const, looping, &changed);
+ }
+ }
+ return changed;
}
-/* Worker to set pure flag. */
+/* Info used by set_pure_flag_1. */
+
+struct set_pure_flag_info
+{
+ bool pure;
+ bool looping;
+ bool changed;
+};
+
+/* Worker to set_pure_flag. */
static bool
-cgraph_set_pure_flag_1 (cgraph_node *node, void *data)
+set_pure_flag_1 (cgraph_node *node, void *data)
{
+ struct set_pure_flag_info *info = (struct set_pure_flag_info *)data;
/* Static constructors and destructors without a side effect can be
optimized out. */
- if (data && !((size_t)data & 2))
+ if (info->pure && !info->looping)
{
if (DECL_STATIC_CONSTRUCTOR (node->decl))
- DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ {
+ DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ info->changed = true;
+ }
if (DECL_STATIC_DESTRUCTOR (node->decl))
- DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ {
+ DECL_STATIC_DESTRUCTOR (node->decl) = 0;
+ info->changed = true;
+ }
+ }
+ if (info->pure)
+ {
+ if (!DECL_PURE_P (node->decl) && !TREE_READONLY (node->decl))
+ {
+ DECL_PURE_P (node->decl) = true;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = info->looping;
+ info->changed = true;
+ }
+ else if (DECL_LOOPING_CONST_OR_PURE_P (node->decl)
+ && !info->looping)
+ {
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+ info->changed = true;
+ }
+ }
+ else
+ {
+ if (DECL_PURE_P (node->decl))
+ {
+ DECL_PURE_P (node->decl) = false;
+ DECL_LOOPING_CONST_OR_PURE_P (node->decl) = false;
+ info->changed = true;
+ }
}
- DECL_PURE_P (node->decl) = data != NULL;
- DECL_LOOPING_CONST_OR_PURE_P (node->decl) = ((size_t)data & 2) != 0;
return false;
}
/* Set DECL_PURE_P on cgraph_node's decl and on aliases of the node
- if any to PURE. */
+ if any to PURE.
-void
+ When setting the flag, be careful about possible interposition.
+ Return true if any change was done. */
+
+bool
cgraph_node::set_pure_flag (bool pure, bool looping)
{
- call_for_symbol_thunks_and_aliases (cgraph_set_pure_flag_1,
- (void *)(size_t)(pure + (int)looping * 2),
- false, true);
+ struct set_pure_flag_info info = {pure, looping, false};
+ if (!pure)
+ looping = false;
+ call_for_symbol_thunks_and_aliases (set_pure_flag_1, &info, !pure, true);
+ return info.changed;
}
/* Return true when cgraph_node can not return or throw and thus
bool
cgraph_edge::maybe_hot_p (void)
{
- /* TODO: Export profile_status from cfun->cfg to cgraph_node. */
- if (profile_info
- && opt_for_fn (caller->decl, flag_branch_probabilities)
- && !maybe_hot_count_p (NULL, count))
+ if (!maybe_hot_count_p (NULL, count.ipa ()))
return false;
if (caller->frequency == NODE_FREQUENCY_UNLIKELY_EXECUTED
|| (callee
return false;
if (caller->frequency == NODE_FREQUENCY_HOT)
return true;
+ /* If profile is now known yet, be conservative.
+ FIXME: this predicate is used by early inliner and can do better there. */
+ if (symtab->state < IPA_SSA)
+ return true;
if (caller->frequency == NODE_FREQUENCY_EXECUTED_ONCE
- && frequency < CGRAPH_FREQ_BASE * 3 / 2)
+ && sreal_frequency () * 2 < 3)
+ return false;
+ if (PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION) == 0
+ || sreal_frequency () * PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION) <= 1)
return false;
- if (opt_for_fn (caller->decl, flag_guess_branch_prob))
- {
- if (PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION) == 0
- || frequency <= (CGRAPH_FREQ_BASE
- / PARAM_VALUE (HOT_BB_FREQUENCY_FRACTION)))
- return false;
- }
return true;
}
if (avail > AVAIL_INTERPOSABLE)
for (cs = node->callers; cs != NULL; cs = cs->next_caller)
- if (!cs->indirect_inlining_edge)
+ if (!cs->indirect_inlining_edge
+ && !cs->caller->thunk.thunk_p)
redirect_callers->safe_push (cs);
return false;
}
/* Verify edge count and frequency. */
bool
-cgraph_edge::verify_count_and_frequency ()
+cgraph_edge::verify_count ()
{
bool error_found = false;
- if (count < 0)
- {
- error ("caller edge count is negative");
- error_found = true;
- }
- if (frequency < 0)
+ if (!count.verify ())
{
- error ("caller edge frequency is negative");
- error_found = true;
- }
- if (frequency > CGRAPH_FREQ_MAX)
- {
- error ("caller edge frequency is too large");
+ error ("caller edge count invalid");
error_found = true;
}
return error_found;
node = node->ultimate_alias_target ();
/* Optimizers can redirect unreachable calls or calls triggering undefined
- behaviour to builtin_unreachable. */
+ behavior to builtin_unreachable. */
if (DECL_BUILT_IN_CLASS (callee->decl) == BUILT_IN_NORMAL
&& DECL_FUNCTION_CODE (callee->decl) == BUILT_IN_UNREACHABLE)
return false;
identifier_to_locale (e->callee->name ()));
error_found = true;
}
- if (count < 0)
+ if (!count.verify ())
{
- error ("execution count is negative");
+ error ("cgraph count invalid");
error_found = true;
}
if (global.inlined_to && same_comdat_group)
bool check_comdat = comdat_local_p ();
for (e = callers; e; e = e->next_caller)
{
- if (e->verify_count_and_frequency ())
+ if (e->verify_count ())
error_found = true;
if (check_comdat
&& !in_same_comdat_group_p (e->caller))
}
for (e = callees; e; e = e->next_callee)
{
- if (e->verify_count_and_frequency ())
+ if (e->verify_count ())
error_found = true;
if (gimple_has_body_p (e->caller->decl)
&& !e->caller->global.inlined_to
&& !e->speculative
/* Optimized out calls are redirected to __builtin_unreachable. */
- && (e->frequency
- || e->callee->decl
- != builtin_decl_implicit (BUILT_IN_UNREACHABLE))
- && (e->frequency
- != compute_call_stmt_bb_frequency (e->caller->decl,
- gimple_bb (e->call_stmt))))
+ && (e->count.nonzero_p ()
+ || ! e->callee->decl
+ || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
+ || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
+ && count
+ == ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (decl))->count
+ && (!e->count.ipa_p ()
+ && e->count.differs_from_p (gimple_bb (e->call_stmt)->count)))
{
- error ("caller edge frequency %i does not match BB frequency %i",
- e->frequency,
- compute_call_stmt_bb_frequency (e->caller->decl,
- gimple_bb (e->call_stmt)));
+ error ("caller edge count does not match BB count");
+ fprintf (stderr, "edge count: ");
+ e->count.dump (stderr);
+ fprintf (stderr, "\n bb count: ");
+ gimple_bb (e->call_stmt)->count.dump (stderr);
+ fprintf (stderr, "\n");
error_found = true;
}
}
for (e = indirect_calls; e; e = e->next_callee)
{
- if (e->verify_count_and_frequency ())
+ if (e->verify_count ())
error_found = true;
if (gimple_has_body_p (e->caller->decl)
&& !e->caller->global.inlined_to
&& !e->speculative
- && (e->frequency
- != compute_call_stmt_bb_frequency (e->caller->decl,
- gimple_bb (e->call_stmt))))
+ && e->count.ipa_p ()
+ && count
+ == ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (decl))->count
+ && (!e->count.ipa_p ()
+ && e->count.differs_from_p (gimple_bb (e->call_stmt)->count)))
{
- error ("indirect call frequency %i does not match BB frequency %i",
- e->frequency,
- compute_call_stmt_bb_frequency (e->caller->decl,
- gimple_bb (e->call_stmt)));
+ error ("indirect call count does not match BB count");
+ fprintf (stderr, "edge count: ");
+ e->count.dump (stderr);
+ fprintf (stderr, "\n bb count: ");
+ gimple_bb (e->call_stmt)->count.dump (stderr);
+ fprintf (stderr, "\n");
error_found = true;
}
}
error ("More than one edge out of thunk node");
error_found = true;
}
- if (gimple_has_body_p (decl))
+ if (gimple_has_body_p (decl) && !global.inlined_to)
{
error ("Thunk is not supposed to have body");
error_found = true;
/* Walk the alias chain to return the function cgraph_node is alias of.
Walk through thunks, too.
- When AVAILABILITY is non-NULL, get minimal availability in the chain. */
+ When AVAILABILITY is non-NULL, get minimal availability in the chain.
+ When REF is non-NULL, assume that reference happens in symbol REF
+ when determining the availability. */
cgraph_node *
-cgraph_node::function_symbol (enum availability *availability)
+cgraph_node::function_symbol (enum availability *availability,
+ struct symtab_node *ref)
{
- cgraph_node *node = ultimate_alias_target (availability);
+ cgraph_node *node = ultimate_alias_target (availability, ref);
while (node->thunk.thunk_p)
{
+ ref = node;
node = node->callees->callee;
if (availability)
{
enum availability a;
- a = node->get_availability ();
+ a = node->get_availability (ref);
if (a < *availability)
*availability = a;
}
- node = node->ultimate_alias_target (availability);
+ node = node->ultimate_alias_target (availability, ref);
}
return node;
}
/* Walk the alias chain to return the function cgraph_node is alias of.
Walk through non virtual thunks, too. Thus we return either a function
or a virtual thunk node.
- When AVAILABILITY is non-NULL, get minimal availability in the chain. */
+ When AVAILABILITY is non-NULL, get minimal availability in the chain.
+ When REF is non-NULL, assume that reference happens in symbol REF
+ when determining the availability. */
cgraph_node *
cgraph_node::function_or_virtual_thunk_symbol
- (enum availability *availability)
+ (enum availability *availability,
+ struct symtab_node *ref)
{
- cgraph_node *node = ultimate_alias_target (availability);
+ cgraph_node *node = ultimate_alias_target (availability, ref);
while (node->thunk.thunk_p && !node->thunk.virtual_offset_p)
{
+ ref = node;
node = node->callees->callee;
if (availability)
{
enum availability a;
- a = node->get_availability ();
+ a = node->get_availability (ref);
if (a < *availability)
*availability = a;
}
- node = node->ultimate_alias_target (availability);
+ node = node->ultimate_alias_target (availability, ref);
}
return node;
}
size_t len;
tree decl = this->decl;
- if (DECL_RESULT (decl))
+ /* Check if body is already there. Either we have gimple body or
+ the function is thunk and in that case we set DECL_ARGUMENTS. */
+ if (DECL_ARGUMENTS (decl) || gimple_has_body_p (decl))
return false;
- gcc_assert (in_lto_p);
+ gcc_assert (in_lto_p && !DECL_RESULT (decl));
timevar_push (TV_IPA_LTO_GIMPLE_IN);
/* We may have renamed the declaration, e.g., a static function. */
name = lto_get_decl_name_mapping (file_data, name);
+ struct lto_in_decl_state *decl_state
+ = lto_get_function_in_decl_state (file_data, decl);
data = lto_get_section_data (file_data, LTO_section_function_body,
- name, &len);
+ name, &len, decl_state->compressed);
if (!data)
fatal_error (input_location, "%s: section %s is missing",
file_data->file_name,
lto_input_function_body (file_data, this, data);
lto_stats.num_function_bodies++;
lto_free_section_data (file_data, LTO_section_function_body, name,
- data, len);
+ data, len, decl_state->compressed);
lto_free_function_in_decl_state_for_node (this);
/* Keep lto file data so ipa-inline-analysis knows about cross module
inlining. */
updated = get_untransformed_body ();
/* Getting transformed body makes no sense for inline clones;
- we should never use this on real clones becuase they are materialized
+ we should never use this on real clones because they are materialized
early.
TODO: Materializing clones here will likely lead to smaller LTRANS
footprint. */
{
opt_pass *saved_current_pass = current_pass;
FILE *saved_dump_file = dump_file;
- int saved_dump_flags = dump_flags;
+ const char *saved_dump_file_name = dump_file_name;
+ dump_flags_t saved_dump_flags = dump_flags;
+ dump_file_name = NULL;
+ dump_file = NULL;
push_cfun (DECL_STRUCT_FUNCTION (decl));
execute_all_ipa_transforms ();
current_pass = saved_current_pass;
dump_file = saved_dump_file;
+ dump_file_name = saved_dump_file_name;
dump_flags = saved_dump_flags;
}
return updated;
return false;
}
+/* Expected frequency of executions within the function. */
+
+sreal
+cgraph_edge::sreal_frequency ()
+{
+ return count.to_sreal_scale (caller->global.inlined_to
+ ? caller->global.inlined_to->count
+ : caller->count);
+}
+
#include "gt-cgraph.h"