/* 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 "coretypes.h"
-#include "tm.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 "wide-int.h"
-#include "inchash.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
#include "tree.h"
+#include "gimple.h"
+#include "predict.h"
+#include "alloc-pool.h"
+#include "gimple-ssa.h"
+#include "cgraph.h"
+#include "lto-streamer.h"
#include "fold-const.h"
#include "varasm.h"
#include "calls.h"
#include "print-tree.h"
-#include "tree-inline.h"
#include "langhooks.h"
-#include "hashtab.h"
-#include "toplev.h"
-#include "flags.h"
-#include "debug.h"
-#include "target.h"
-#include "predict.h"
-#include "dominance.h"
-#include "cfg.h"
-#include "basic-block.h"
-#include "hash-map.h"
-#include "is-a.h"
-#include "plugin-api.h"
-#include "hard-reg-set.h"
-#include "function.h"
-#include "ipa-ref.h"
-#include "cgraph.h"
#include "intl.h"
-#include "tree-ssa-alias.h"
-#include "internal-fn.h"
#include "tree-eh.h"
-#include "gimple-expr.h"
-#include "gimple.h"
#include "gimple-iterator.h"
-#include "timevar.h"
-#include "dumpfile.h"
-#include "gimple-ssa.h"
#include "tree-cfg.h"
#include "tree-ssa.h"
#include "value-prof.h"
-#include "except.h"
-#include "diagnostic-core.h"
-#include "rtl.h"
#include "ipa-utils.h"
-#include "lto-streamer.h"
-#include "alloc-pool.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 "statistics.h"
-#include "real.h"
-#include "fixed-value.h"
-#include "insn-config.h"
-#include "expmed.h"
-#include "dojump.h"
-#include "explow.h"
-#include "emit-rtl.h"
-#include "stmt.h"
-#include "expr.h"
#include "tree-dfa.h"
#include "profile.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"
/* Hash descriptor for cgraph_function_version_info. */
-struct function_version_hasher : ggc_hasher<cgraph_function_version_info *>
+struct function_version_hasher : ggc_ptr_hash<cgraph_function_version_info>
{
static hashval_t hash (cgraph_function_version_info *);
static bool equal (cgraph_function_version_info *,
static GTY(()) struct cgraph_function_version_info *
version_info_node = NULL;
+/* Return true if NODE's address can be compared. */
+
+bool
+symtab_node::address_can_be_compared_p ()
+{
+ /* Address of virtual tables and functions is never compared. */
+ if (DECL_VIRTUAL_P (decl))
+ return false;
+ /* Address of C++ cdtors is never compared. */
+ if (is_a <cgraph_node *> (this)
+ && (DECL_CXX_CONSTRUCTOR_P (decl)
+ || DECL_CXX_DESTRUCTOR_P (decl)))
+ return false;
+ /* Constant pool symbols addresses are never compared.
+ flag_merge_constants permits us to assume the same on readonly vars. */
+ if (is_a <varpool_node *> (this)
+ && (DECL_IN_CONSTANT_POOL (decl)
+ || (flag_merge_constants >= 2
+ && TREE_READONLY (decl) && !TREE_THIS_VOLATILE (decl))))
+ return false;
+ return true;
+}
+
/* Get the cgraph_function_version_info node corresponding to node. */
cgraph_function_version_info *
cgraph_node::function_version (void)
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->offloadable = 1;
-#ifdef ENABLE_OFFLOADING
- g->have_offload = true;
-#endif
+ if (ENABLE_OFFLOADING)
+ g->have_offload = true;
}
node->register_symbol ();
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",
- xstrdup_for_dump (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", xstrdup_for_dump (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;
hashval_t
cgraph_edge_hasher::hash (cgraph_edge *e)
{
- return htab_hash_pointer (e->call_stmt);
+ /* This is a really poor hash function, but it is what htab_hash_pointer
+ uses. */
+ return (hashval_t) ((intptr_t)e->call_stmt >> 3);
}
-/* Return nonzero if the call_stmt of of cgraph_edge X is stmt *Y. */
+/* Returns a hash value for X (which really is a cgraph_edge). */
+
+hashval_t
+cgraph_edge_hasher::hash (gimple *call_stmt)
+{
+ /* This is a really poor hash function, but it is what htab_hash_pointer
+ uses. */
+ return (hashval_t) ((intptr_t)call_stmt >> 3);
+}
+
+/* Return nonzero if the call_stmt of cgraph_edge X is stmt *Y. */
inline bool
-cgraph_edge_hasher::equal (cgraph_edge *x, gimple y)
+cgraph_edge_hasher::equal (cgraph_edge *x, gimple *y)
{
return x->call_stmt == y;
}
static inline void
cgraph_update_edge_in_call_site_hash (cgraph_edge *e)
{
- gimple call = e->call_stmt;
- *e->caller->call_site_hash->find_slot_with_hash (call,
- htab_hash_pointer (call),
- INSERT) = e;
+ gimple *call = e->call_stmt;
+ *e->caller->call_site_hash->find_slot_with_hash
+ (call, cgraph_edge_hasher::hash (call), INSERT) = e;
}
/* Add call graph edge E to call site hash of its caller. */
if (e->speculative && e->indirect_unknown_callee)
return;
cgraph_edge **slot = e->caller->call_site_hash->find_slot_with_hash
- (e->call_stmt,
- htab_hash_pointer (e->call_stmt), INSERT);
+ (e->call_stmt, cgraph_edge_hasher::hash (e->call_stmt), INSERT);
if (*slot)
{
gcc_assert (((cgraph_edge *)*slot)->speculative);
CALL_STMT. */
cgraph_edge *
-cgraph_node::get_edge (gimple call_stmt)
+cgraph_node::get_edge (gimple *call_stmt)
{
cgraph_edge *e, *e2;
int n = 0;
if (call_site_hash)
- return call_site_hash->find_with_hash (call_stmt,
- htab_hash_pointer (call_stmt));
+ return call_site_hash->find_with_hash
+ (call_stmt, cgraph_edge_hasher::hash (call_stmt));
/* This loop may turn out to be performance problem. In such case adding
hashtables into call nodes with very many edges is probably best
&& (!speculative || !indirect_unknown_callee))
{
caller->call_site_hash->remove_elt_with_hash
- (call_stmt, htab_hash_pointer (call_stmt));
+ (call_stmt, cgraph_edge_hasher::hash (call_stmt));
}
cgraph_edge *e = this;
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;
{
/* This is a rather expensive check possibly triggering
construction of call stmt hashtable. */
-#ifdef ENABLE_CHECKING
cgraph_edge *e;
- gcc_checking_assert (
- !(e = caller->get_edge (call_stmt)) || e->speculative);
-#endif
+ gcc_checking_assert (!(e = caller->get_edge (call_stmt))
+ || e->speculative);
gcc_assert (is_gimple_call (call_stmt));
}
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);
caller->callees = next_callee;
}
if (caller->call_site_hash)
- caller->call_site_hash->remove_elt_with_hash (call_stmt,
- htab_hash_pointer (call_stmt));
+ caller->call_site_hash->remove_elt_with_hash
+ (call_stmt, cgraph_edge_hasher::hash (call_stmt));
}
/* Put the edge onto the free list. */
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);
/* If necessary, change the function declaration in the call statement
associated with E so that it corresponds to the edge callee. */
-gimple
+gimple *
cgraph_edge::redirect_call_stmt_to_callee (void)
{
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;
-#ifdef ENABLE_CHECKING
- cgraph_node *node;
-#endif
+ bool skip_bounds = false;
if (e->speculative)
{
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.
}
}
+ /* We might propagate instrumented function pointer into
+ not instrumented function and vice versa. In such a
+ case we need to either fix function declaration or
+ remove bounds from call statement. */
+ if (flag_check_pointer_bounds && e->callee)
+ skip_bounds = chkp_redirect_edge (e);
+
if (e->indirect_unknown_callee
- || decl == e->callee->decl)
+ || (decl == e->callee->decl
+ && !skip_bounds))
return e->call_stmt;
-#ifdef ENABLE_CHECKING
- if (decl)
+ if (flag_checking && decl)
{
- node = cgraph_node::get (decl);
+ cgraph_node *node = cgraph_node::get (decl);
gcc_assert (!node || !node->clone.combined_args_to_skip);
}
-#endif
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 (e->callee->clone.combined_args_to_skip)
+ if (e->callee->clone.combined_args_to_skip
+ || skip_bounds)
{
int lp_nr;
- new_stmt
- = gimple_call_copy_skip_args (e->call_stmt,
- e->callee->clone.combined_args_to_skip);
+ new_stmt = e->call_stmt;
+ if (e->callee->clone.combined_args_to_skip)
+ new_stmt
+ = gimple_call_copy_skip_args (new_stmt,
+ 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 the call becomes noreturn, remove the lhs. */
- if (lhs && (gimple_call_flags (new_stmt) & ECF_NORETURN))
+ /* 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_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)
{
TREE_TYPE (lhs), NULL);
var = get_or_create_ssa_default_def
(DECL_STRUCT_FUNCTION (e->caller->decl), var);
- gimple set_stmt = gimple_build_assign (lhs, var);
+ gimple *set_stmt = gimple_build_assign (lhs, var);
gsi = gsi_for_stmt (new_stmt);
gsi_insert_before_without_update (&gsi, set_stmt, GSI_SAME_STMT);
update_stmt_fn (DECL_STRUCT_FUNCTION (e->caller->decl), set_stmt);
static void
cgraph_update_edges_for_call_stmt_node (cgraph_node *node,
- gimple old_stmt, tree old_call,
- gimple new_stmt)
+ gimple *old_stmt, tree old_call,
+ gimple *new_stmt)
{
tree new_call = (new_stmt && is_gimple_call (new_stmt))
? gimple_call_fndecl (new_stmt) : 0;
{
cgraph_edge *e = node->get_edge (old_stmt);
cgraph_edge *ne = NULL;
- gcov_type count;
- int frequency;
+ profile_count count;
if (e)
{
+ /* Keep calls marked as dead dead. */
+ if (new_stmt && is_gimple_call (new_stmt) && e->callee
+ && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_UNREACHABLE)
+ {
+ node->get_edge (old_stmt)->set_call_stmt
+ (as_a <gcall *> (new_stmt));
+ return;
+ }
/* See if the edge is already there and has the correct callee. It
might be so because of indirect inlining has already updated
it. We also might've cloned and redirected the edge. */
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);
}
}
of OLD_STMT before it was updated (updating can happen inplace). */
void
-cgraph_update_edges_for_call_stmt (gimple old_stmt, tree old_decl, gimple new_stmt)
+cgraph_update_edges_for_call_stmt (gimple *old_stmt, tree old_decl,
+ gimple *new_stmt)
{
cgraph_node *orig = cgraph_node::get (cfun->decl);
cgraph_node *node;
void
release_function_body (tree decl)
{
- if (DECL_STRUCT_FUNCTION (decl))
+ function *fn = DECL_STRUCT_FUNCTION (decl);
+ if (fn)
{
- if (DECL_STRUCT_FUNCTION (decl)->cfg
- || DECL_STRUCT_FUNCTION (decl)->gimple_df)
+ if (fn->cfg
+ && loops_for_fn (fn))
{
- push_cfun (DECL_STRUCT_FUNCTION (decl));
- if (cfun->cfg
- && current_loops)
- {
- cfun->curr_properties &= ~PROP_loops;
- loop_optimizer_finalize ();
- }
- if (cfun->gimple_df)
- {
- delete_tree_ssa ();
- delete_tree_cfg_annotations ();
- cfun->eh = NULL;
- }
- if (cfun->cfg)
- {
- gcc_assert (!dom_info_available_p (CDI_DOMINATORS));
- gcc_assert (!dom_info_available_p (CDI_POST_DOMINATORS));
- clear_edges ();
- cfun->cfg = NULL;
- }
- if (cfun->value_histograms)
- free_histograms ();
- pop_cfun ();
+ 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. */
- ggc_free (DECL_STRUCT_FUNCTION (decl));
+ ggc_free (fn);
DECL_STRUCT_FUNCTION (decl) = NULL;
}
DECL_SAVED_TREE (decl) = NULL;
if (!keep_arguments)
DECL_ARGUMENTS (decl) = NULL;
}
- /* If the node is abstract and needed, then do not clear DECL_INITIAL
- of its associated function function declaration because it's
+ /* If the node is abstract and needed, then do not clear
+ DECL_INITIAL of its associated function declaration because it's
needed to emit debug info later. */
if (!used_as_abstract_origin && DECL_INITIAL (decl))
DECL_INITIAL (decl) = error_mark_node;
release_function_body (decl);
if (lto_file_data)
- lto_free_function_in_decl_state_for_node (this);
+ {
+ lto_free_function_in_decl_state_for_node (this);
+ lto_file_data = NULL;
+ }
}
/* Remove function from symbol table. */
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.
*/
n = cgraph_node::get (decl);
if (!n
|| (!n->clones && !n->clone_of && !n->global.inlined_to
- && (symtab->global_info_ready
+ && ((symtab->global_info_ready || in_lto_p)
&& (TREE_ASM_WRITTEN (n->decl)
|| DECL_EXTERNAL (n->decl)
|| !n->analyzed
|| (!flag_wpa && n->in_other_partition)))))
release_body ();
}
+ else
+ {
+ lto_free_function_in_decl_state_for_node (this);
+ lto_file_data = NULL;
+ }
decl = NULL;
if (call_site_hash)
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;
- 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, " only_called_at_exit");
if (opt_for_fn (decl, optimize_size))
fprintf (f, " optimize_size");
+ if (parallelized_function)
+ fprintf (f, " parallelized_function");
fprintf (f, "\n");
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 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;
+ }
+
+ 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);
}
-/* Worker to set const flag. */
+/* Set DECL_IS_MALLOC on NODE's decl and on NODE's aliases if any. */
-static bool
-cgraph_set_const_flag_1 (cgraph_node *node, void *data)
+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;
}
return !node->can_remove_if_no_direct_calls_and_refs_p ();
}
-/* Return true when function cgraph_node and its aliases can be removed from
- callgraph if all direct calls are eliminated. */
+/* Return true if whole comdat group can be removed if there are no direct
+ calls to THIS. */
bool
-cgraph_node::can_remove_if_no_direct_calls_p (void)
+cgraph_node::can_remove_if_no_direct_calls_p (bool will_inline)
{
- /* Extern inlines can always go, we will use the external definition. */
- if (DECL_EXTERNAL (decl))
- return true;
- if (address_taken)
+ struct ipa_ref *ref;
+
+ /* For local symbols or non-comdat group it is the same as
+ can_remove_if_no_direct_calls_p. */
+ if (!externally_visible || !same_comdat_group)
+ {
+ if (DECL_EXTERNAL (decl))
+ return true;
+ if (address_taken)
+ return false;
+ return !call_for_symbol_and_aliases (nonremovable_p, NULL, true);
+ }
+
+ if (will_inline && address_taken)
return false;
- return !call_for_symbol_and_aliases (nonremovable_p, NULL, true);
+
+ /* Otheriwse check if we can remove the symbol itself and then verify
+ that only uses of the comdat groups are direct call to THIS
+ or its aliases. */
+ if (!can_remove_if_no_direct_calls_and_refs_p ())
+ return false;
+
+ /* Check that all refs come from within the comdat group. */
+ for (int i = 0; iterate_referring (i, ref); i++)
+ if (ref->referring->get_comdat_group () != get_comdat_group ())
+ return false;
+
+ struct cgraph_node *target = ultimate_alias_target ();
+ for (cgraph_node *next = dyn_cast<cgraph_node *> (same_comdat_group);
+ next != this; next = dyn_cast<cgraph_node *> (next->same_comdat_group))
+ {
+ if (!externally_visible)
+ continue;
+ if (!next->alias
+ && !next->can_remove_if_no_direct_calls_and_refs_p ())
+ return false;
+
+ /* If we see different symbol than THIS, be sure to check calls. */
+ if (next->ultimate_alias_target () != target)
+ for (cgraph_edge *e = next->callers; e; e = e->next_caller)
+ if (e->caller->get_comdat_group () != get_comdat_group ()
+ || will_inline)
+ return false;
+
+ /* If function is not being inlined, we care only about
+ references outside of the comdat group. */
+ if (!will_inline)
+ for (int i = 0; next->iterate_referring (i, ref); i++)
+ if (ref->referring->get_comdat_group () != get_comdat_group ())
+ return false;
+ }
+ return true;
}
/* Return true when function cgraph_node can be expected to be removed
linkonce section. */
bool
-cgraph_node::will_be_removed_from_program_if_no_direct_calls_p (void)
+cgraph_node::will_be_removed_from_program_if_no_direct_calls_p
+ (bool will_inline)
{
gcc_assert (!global.inlined_to);
+ if (DECL_EXTERNAL (decl))
+ return true;
- if (call_for_symbol_and_aliases (used_from_object_file_p_worker,
- NULL, true))
- return false;
if (!in_lto_p && !flag_whole_program)
- return only_called_directly_p ();
- else
{
- if (DECL_EXTERNAL (decl))
- return true;
- return can_remove_if_no_direct_calls_p ();
+ /* If the symbol is in comdat group, we need to verify that whole comdat
+ group becomes unreachable. Technically we could skip references from
+ within the group, too. */
+ if (!only_called_directly_p ())
+ return false;
+ if (same_comdat_group && externally_visible)
+ {
+ struct cgraph_node *target = ultimate_alias_target ();
+
+ if (will_inline && address_taken)
+ return true;
+ for (cgraph_node *next = dyn_cast<cgraph_node *> (same_comdat_group);
+ next != this;
+ next = dyn_cast<cgraph_node *> (next->same_comdat_group))
+ {
+ if (!externally_visible)
+ continue;
+ if (!next->alias
+ && !next->only_called_directly_p ())
+ return false;
+
+ /* If we see different symbol than THIS,
+ be sure to check calls. */
+ if (next->ultimate_alias_target () != target)
+ for (cgraph_edge *e = next->callers; e; e = e->next_caller)
+ if (e->caller->get_comdat_group () != get_comdat_group ()
+ || will_inline)
+ return false;
+ }
+ }
+ return true;
}
+ else
+ return can_remove_if_no_direct_calls_p (will_inline);
}
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_found = true;
- }
- if (gimple_has_body_p (caller->decl)
- && !caller->global.inlined_to
- && !speculative
- /* FIXME: Inline-analysis sets frequency to 0 when edge is optimized out.
- Remove this once edges are actually removed from the function at that time. */
- && (frequency
- || (inline_edge_summary_vec.exists ()
- && ((inline_edge_summary_vec.length () <= (unsigned) uid)
- || !inline_edge_summary (this)->predicate)))
- && (frequency
- != compute_call_stmt_bb_frequency (caller->decl,
- gimple_bb (call_stmt))))
- {
- error ("caller edge frequency %i does not match BB frequency %i",
- frequency,
- compute_call_stmt_bb_frequency (caller->decl,
- gimple_bb (call_stmt)));
+ error ("caller edge count invalid");
error_found = true;
}
return error_found;
/* Switch to THIS_CFUN if needed and print STMT to stderr. */
static void
-cgraph_debug_gimple_stmt (function *this_cfun, gimple stmt)
+cgraph_debug_gimple_stmt (function *this_cfun, gimple *stmt)
{
bool fndecl_was_null = false;
/* debug_gimple_stmt needs correct cfun */
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))
error_found = true;
}
}
+ for (e = callees; e; e = e->next_callee)
+ {
+ 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->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 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 ())
- error_found = true;
+ {
+ if (e->verify_count ())
+ error_found = true;
+ if (gimple_has_body_p (e->caller->decl)
+ && !e->caller->global.inlined_to
+ && !e->speculative
+ && 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 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;
+ }
+ }
if (!callers && global.inlined_to)
{
error ("inlined_to pointer is set but no predecessors found");
}
else
ref_found = true;
- if (!ref_found)
- {
- error ("Analyzed alias has no reference");
- error_found = true;
- }
+ if (!ref_found)
+ {
+ error ("Analyzed alias has no reference");
+ error_found = true;
+ }
}
/* Check instrumented version reference. */
}
}
+ if (instrumentation_clone
+ && DECL_BUILT_IN_CLASS (decl) == NOT_BUILT_IN)
+ {
+ tree name = DECL_ASSEMBLER_NAME (decl);
+ tree orig_name = DECL_ASSEMBLER_NAME (orig_decl);
+
+ if (!IDENTIFIER_TRANSPARENT_ALIAS (name)
+ || TREE_CHAIN (name) != orig_name)
+ {
+ error ("Alias chain for instrumented node is broken");
+ error_found = true;
+ }
+ }
+
if (analyzed && thunk.thunk_p)
{
if (!callees)
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;
{
if (this_cfun->cfg)
{
- hash_set<gimple> stmts;
+ hash_set<gimple *> stmts;
int i;
ipa_ref *ref = NULL;
!gsi_end_p (gsi);
gsi_next (&gsi))
{
- gimple stmt = gsi_stmt (gsi);
+ gimple *stmt = gsi_stmt (gsi);
stmts.add (stmt);
if (is_gimple_call (stmt))
{
/* 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. */
timevar_pop (TV_IPA_LTO_GIMPLE_IN);
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. */
static bool
-gimple_check_call_args (gimple stmt, tree fndecl, bool args_count_match)
+gimple_check_call_args (gimple *stmt, tree fndecl, bool args_count_match)
{
tree parms, p;
unsigned int i, nargs;
If we cannot verify this or there is a mismatch, return false. */
bool
-gimple_check_call_matching_types (gimple call_stmt, tree callee,
+gimple_check_call_matching_types (gimple *call_stmt, tree callee,
bool args_count_match)
{
tree lhs;
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"