#include "tree-flow.h"
#include "value-prof.h"
#include "except.h"
-#include "diagnostic.h"
+#include "diagnostic-core.h"
#include "rtl.h"
#include "ipa-utils.h"
+#include "lto-streamer.h"
static void cgraph_node_remove_callers (struct cgraph_node *node);
static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
}
else
{
- node = GGC_CNEW (struct cgraph_node);
+ node = ggc_alloc_cleared_cgraph_node ();
node->uid = cgraph_max_uid++;
}
is assigned. */
struct cgraph_node *
+cgraph_get_node_or_alias (tree decl)
+{
+ struct cgraph_node key, *node = NULL, **slot;
+
+ gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+ if (!cgraph_hash)
+ return NULL;
+
+ key.decl = decl;
+
+ slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key,
+ NO_INSERT);
+
+ if (slot && *slot)
+ node = *slot;
+ return node;
+}
+
+/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
+ is assigned. */
+
+struct cgraph_node *
cgraph_get_node (tree decl)
{
struct cgraph_node key, *node = NULL, **slot;
}
else
{
- edge = GGC_NEW (struct cgraph_edge);
+ edge = ggc_alloc_cgraph_edge ();
edge->uid = cgraph_edge_max_uid++;
}
edge->indirect_unknown_callee = 1;
initialize_inline_failed (edge);
- edge->indirect_info = GGC_NEW (struct cgraph_indirect_call_info);
+ edge->indirect_info = ggc_alloc_cleared_cgraph_indirect_call_info ();
edge->indirect_info->param_index = -1;
edge->indirect_info->ecf_flags = ecf_flags;
/* Verify that function does not appear to be needed out of blue
during the optimization process. This can happen for extern
inlines when bodies was removed after inlining. */
- gcc_assert ((node->analyzed || DECL_EXTERNAL (node->decl)));
+ gcc_assert ((node->analyzed || node->in_other_partition
+ || DECL_EXTERNAL (node->decl)));
}
else
notice_global_symbol (node->decl);
fprintf (f, " local");
if (node->local.externally_visible)
fprintf (f, " externally_visible");
+ if (node->local.used_from_object_file)
+ fprintf (f, " used_from_object_file");
if (node->local.finalized)
fprintf (f, " finalized");
if (node->local.disregard_inline_limits)
/* Dump call graph node NODE to stderr. */
-void
+DEBUG_FUNCTION void
debug_cgraph_node (struct cgraph_node *node)
{
dump_cgraph_node (stderr, node);
/* Dump the call graph to stderr. */
-void
+DEBUG_FUNCTION void
debug_cgraph (void)
{
dump_cgraph (stderr);
void
change_decl_assembler_name (tree decl, tree name)
{
- gcc_assert (!assembler_name_hash);
+ struct cgraph_node *node;
+ void **slot;
if (!DECL_ASSEMBLER_NAME_SET_P (decl))
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ else
{
- SET_DECL_ASSEMBLER_NAME (decl, name);
- return;
- }
- if (name == DECL_ASSEMBLER_NAME (decl))
- return;
+ if (name == DECL_ASSEMBLER_NAME (decl))
+ return;
- if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
- && DECL_RTL_SET_P (decl))
- warning (0, "%D renamed after being referenced in assembly", decl);
+ if (assembler_name_hash
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && (node = cgraph_get_node_or_alias (decl)) != NULL)
+ {
+ tree old_name = DECL_ASSEMBLER_NAME (decl);
+ slot = htab_find_slot_with_hash (assembler_name_hash, old_name,
+ decl_assembler_name_hash (old_name),
+ NO_INSERT);
+ /* Inline clones are not hashed. */
+ if (slot && *slot == node)
+ htab_clear_slot (assembler_name_hash, slot);
+ }
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl))
+ && DECL_RTL_SET_P (decl))
+ warning (0, "%D renamed after being referenced in assembly", decl);
- SET_DECL_ASSEMBLER_NAME (decl, name);
+ SET_DECL_ASSEMBLER_NAME (decl, name);
+ }
+ if (assembler_name_hash
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && (node = cgraph_get_node_or_alias (decl)) != NULL)
+ {
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ INSERT);
+ gcc_assert (!*slot);
+ *slot = node;
+ }
}
/* Add a top-level asm statement to the list. */
{
struct cgraph_asm_node *node;
- node = GGC_CNEW (struct cgraph_asm_node);
+ node = ggc_alloc_cleared_cgraph_asm_node ();
node->asm_str = asm_str;
node->order = cgraph_order++;
node->next = NULL;
e->indirect_info->ecf_flags,
count, freq,
e->loop_nest + loop_nest);
- new_edge->indirect_info->param_index = e->indirect_info->param_index;
+ *new_edge->indirect_info = *e->indirect_info;
}
}
else
new_node->analyzed = n->analyzed;
new_node->local = n->local;
new_node->local.externally_visible = false;
+ new_node->local.used_from_object_file = false;
new_node->local.local = true;
new_node->local.vtable_method = false;
new_node->global = n->global;
return new_node;
}
-/* Create a new name for omp child function. Returns an identifier. */
+/* Create a new name for clone of DECL, add SUFFIX. Returns an identifier. */
static GTY(()) unsigned int clone_fn_id_num;
-static tree
-clone_function_name (tree decl)
+tree
+clone_function_name (tree decl, const char *suffix)
{
tree name = DECL_ASSEMBLER_NAME (decl);
size_t len = IDENTIFIER_LENGTH (name);
char *tmp_name, *prefix;
- prefix = XALLOCAVEC (char, len + strlen ("_clone") + 1);
+ prefix = XALLOCAVEC (char, len + strlen (suffix) + 2);
memcpy (prefix, IDENTIFIER_POINTER (name), len);
- strcpy (prefix + len, "_clone");
+ strcpy (prefix + len + 1, suffix);
#ifndef NO_DOT_IN_LABEL
prefix[len] = '.';
#elif !defined NO_DOLLAR_IN_LABEL
prefix[len] = '$';
+#else
+ prefix[len] = '_';
#endif
ASM_FORMAT_PRIVATE_NAME (tmp_name, prefix, clone_fn_id_num++);
return get_identifier (tmp_name);
cgraph_create_virtual_clone (struct cgraph_node *old_node,
VEC(cgraph_edge_p,heap) *redirect_callers,
VEC(ipa_replace_map_p,gc) *tree_map,
- bitmap args_to_skip)
+ bitmap args_to_skip,
+ const char * suffix)
{
tree old_decl = old_node->decl;
struct cgraph_node *new_node = NULL;
size_t i;
struct ipa_replace_map *map;
- gcc_assert (tree_versionable_function_p (old_decl));
+#ifdef ENABLE_CHECKING
+ if (!flag_wpa)
+ gcc_assert (tree_versionable_function_p (old_decl));
+#endif
/* Make a new FUNCTION_DECL tree node */
if (!args_to_skip)
DECL_STRUCT_FUNCTION (new_decl) = NULL;
/* Generate a new name for the new version. */
- DECL_NAME (new_decl) = clone_function_name (old_decl);
+ DECL_NAME (new_decl) = clone_function_name (old_decl, suffix);
SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl));
SET_DECL_RTL (new_decl, NULL);
??? We cannot use COMDAT linkage because there is no
ABI support for this. */
DECL_EXTERNAL (new_node->decl) = 0;
+ if (DECL_ONE_ONLY (old_decl))
+ DECL_SECTION_NAME (new_node->decl) = NULL;
DECL_COMDAT_GROUP (new_node->decl) = 0;
TREE_PUBLIC (new_node->decl) = 0;
DECL_COMDAT (new_node->decl) = 0;
struct cgraph_node *orig_node;
for (orig_node = old_node; orig_node->clone_of; orig_node = orig_node->clone_of)
;
- for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = TREE_CHAIN (arg), oldi++)
+ for (arg = DECL_ARGUMENTS (orig_node->decl); arg; arg = DECL_CHAIN (arg), oldi++)
{
if (bitmap_bit_p (old_node->clone.combined_args_to_skip, oldi))
{
else
new_node->clone.combined_args_to_skip = args_to_skip;
new_node->local.externally_visible = 0;
+ new_node->local.used_from_object_file = 0;
new_node->local.local = 1;
new_node->lowered = true;
new_node->reachable = true;
if (TREE_CODE (decl) == VAR_DECL)
DECL_COMMON (decl) = 0;
- else if (TREE_CODE (decl) == FUNCTION_DECL)
+ else gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+
+ if (DECL_COMDAT (decl))
{
+ /* It is possible that we are linking against library defining same COMDAT
+ function. To avoid conflict we need to rename our local name of the
+ function just in the case WHOPR partitioning decide to make it hidden
+ to avoid cross partition references. */
+ if (flag_wpa)
+ {
+ const char *old_name;
+
+ old_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ struct cgraph_node *node = cgraph_get_node_or_alias (decl);
+ change_decl_assembler_name (decl,
+ clone_function_name (decl, "local"));
+ if (node->local.lto_file_data)
+ lto_record_renamed_decl (node->local.lto_file_data,
+ old_name,
+ IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (decl)));
+ }
+ else if (TREE_CODE (decl) == VAR_DECL)
+ {
+ struct varpool_node *vnode = varpool_get_node (decl);
+ /* change_decl_assembler_name will warn here on vtables because
+ C++ frontend still sets TREE_SYMBOL_REFERENCED on them. */
+ SET_DECL_ASSEMBLER_NAME (decl,
+ clone_function_name (decl, "local"));
+ if (vnode->lto_file_data)
+ lto_record_renamed_decl (vnode->lto_file_data,
+ old_name,
+ IDENTIFIER_POINTER
+ (DECL_ASSEMBLER_NAME (decl)));
+ }
+ }
+ DECL_SECTION_NAME (decl) = 0;
DECL_COMDAT (decl) = 0;
- DECL_COMDAT_GROUP (decl) = 0;
- DECL_WEAK (decl) = 0;
- DECL_EXTERNAL (decl) = 0;
}
- else
- gcc_unreachable ();
+ DECL_COMDAT_GROUP (decl) = 0;
+ DECL_WEAK (decl) = 0;
+ DECL_EXTERNAL (decl) = 0;
TREE_PUBLIC (decl) = 0;
if (!DECL_RTL_SET_P (decl))
return;
return false;
}
+/* Return true when NODE can not return or throw and thus
+ it is safe to ignore its side effects for IPA analysis. */
+
+bool
+cgraph_node_cannot_return (struct cgraph_node *node)
+{
+ int flags = flags_from_decl_or_type (node->decl);
+ if (!flag_exceptions)
+ return (flags & ECF_NORETURN) != 0;
+ else
+ return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+ == (ECF_NORETURN | ECF_NOTHROW));
+}
+
+/* Return true when call of E can not lead to return from caller
+ and thus it is safe to ignore its side effects for IPA analysis
+ when computing side effects of the caller.
+ FIXME: We could actually mark all edges that have no reaching
+ patch to EXIT_BLOCK_PTR or throw to get better results. */
+bool
+cgraph_edge_cannot_lead_to_return (struct cgraph_edge *e)
+{
+ if (cgraph_node_cannot_return (e->caller))
+ return true;
+ if (e->indirect_unknown_callee)
+ {
+ int flags = e->indirect_info->ecf_flags;
+ if (!flag_exceptions)
+ return (flags & ECF_NORETURN) != 0;
+ else
+ return ((flags & (ECF_NORETURN | ECF_NOTHROW))
+ == (ECF_NORETURN | ECF_NOTHROW));
+ }
+ else
+ return cgraph_node_cannot_return (e->callee);
+}
+
+/* Return true when function NODE can be excpected to be removed
+ from program when direct calls in this compilation unit are removed.
+
+ As a special case COMDAT functions are
+ cgraph_can_remove_if_no_direct_calls_p while the are not
+ cgraph_only_called_directly_p (it is possible they are called from other
+ unit)
+
+ This function behaves as cgraph_only_called_directly_p because eliminating
+ all uses of COMDAT function does not make it neccesarily disappear from
+ the program unless we are compiling whole program or we do LTO. In this
+ case we know we win since dynamic linking will not really discard the
+ linkonce section. */
+
+bool
+cgraph_will_be_removed_from_program_if_no_direct_calls (struct cgraph_node *node)
+{
+ if (node->local.used_from_object_file)
+ return false;
+ if (!in_lto_p && !flag_whole_program)
+ return cgraph_only_called_directly_p (node);
+ else
+ return cgraph_can_remove_if_no_direct_calls_p (node);
+}
+
#include "gt-cgraph.h"