#include "tree-flow.h"
#include "value-prof.h"
#include "except.h"
+#include "diagnostic.h"
static void cgraph_node_remove_callers (struct cgraph_node *node);
static inline void cgraph_edge_remove_caller (struct cgraph_edge *e);
}
-/* Return the cgraph node associated with function DECL. If none
- exists, return NULL. */
-
-struct cgraph_node *
-cgraph_node_for_decl (tree decl)
-{
- struct cgraph_node *node;
- void **slot;
-
- gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
-
- node = NULL;
- if (cgraph_hash)
- {
- struct cgraph_node key;
-
- key.decl = decl;
- slot = htab_find_slot (cgraph_hash, &key, NO_INSERT);
- if (slot && *slot)
- node = (struct cgraph_node *) *slot;
- }
-
- return node;
-}
-
-
/* Returns nonzero if P1 and P2 are equal. */
static int
return DECL_UID (n1->decl) == DECL_UID (n2->decl);
}
-/* Allocate new callgraph node and insert it into basic data structures. */
+/* Allocate new callgraph node. */
-static struct cgraph_node *
-cgraph_create_node (void)
+static inline struct cgraph_node *
+cgraph_allocate_node (void)
{
struct cgraph_node *node;
node->uid = cgraph_max_uid++;
}
+ return node;
+}
+
+/* Allocate new callgraph node and insert it into basic data structures. */
+
+static struct cgraph_node *
+cgraph_create_node (void)
+{
+ struct cgraph_node *node = cgraph_allocate_node ();
+
node->next = cgraph_nodes;
node->pid = -1;
node->order = cgraph_order++;
if (*slot)
{
node = *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
return node;
}
return node;
}
+/* Mark ALIAS as an alias to DECL. */
+
+static struct cgraph_node *
+cgraph_same_body_alias_1 (tree alias, tree decl)
+{
+ struct cgraph_node key, *alias_node, *decl_node, **slot;
+
+ gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
+ gcc_assert (TREE_CODE (alias) == FUNCTION_DECL);
+ decl_node = cgraph_node (decl);
+
+ key.decl = alias;
+
+ slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT);
+
+ /* If the cgraph_node has been already created, fail. */
+ if (*slot)
+ return NULL;
+
+ alias_node = cgraph_allocate_node ();
+ alias_node->decl = alias;
+ alias_node->same_body_alias = 1;
+ alias_node->same_body = decl_node;
+ alias_node->previous = NULL;
+ if (decl_node->same_body)
+ decl_node->same_body->previous = alias_node;
+ alias_node->next = decl_node->same_body;
+ alias_node->thunk.alias = decl;
+ decl_node->same_body = alias_node;
+ *slot = alias_node;
+ return alias_node;
+}
+
+/* Attempt to mark ALIAS as an alias to DECL. Return TRUE if successful.
+ Same body aliases are output whenever the body of DECL is output,
+ and cgraph_node (ALIAS) transparently returns cgraph_node (DECL). */
+
+bool
+cgraph_same_body_alias (tree alias, tree decl)
+{
+#ifndef ASM_OUTPUT_DEF
+ /* If aliases aren't supported by the assembler, fail. */
+ return false;
+#endif
+
+ /*gcc_assert (!assembler_name_hash);*/
+
+ return cgraph_same_body_alias_1 (alias, decl) != NULL;
+}
+
+void
+cgraph_add_thunk (tree alias, tree decl, bool this_adjusting,
+ HOST_WIDE_INT fixed_offset, HOST_WIDE_INT virtual_value,
+ tree virtual_offset,
+ tree real_alias)
+{
+ struct cgraph_node *node = cgraph_get_node (alias);
+
+ if (node)
+ {
+ gcc_assert (node->local.finalized);
+ gcc_assert (!node->same_body);
+ cgraph_remove_node (node);
+ }
+
+ node = cgraph_same_body_alias_1 (alias, decl);
+ gcc_assert (node);
+#ifdef ENABLE_CHECKING
+ gcc_assert (!virtual_offset
+ || tree_int_cst_equal (virtual_offset, size_int (virtual_value)));
+#endif
+ 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.thunk_p = true;
+}
+
/* Returns the cgraph node assigned to DECL or NULL if no cgraph node
is assigned. */
gcc_assert (TREE_CODE (decl) == FUNCTION_DECL);
if (!cgraph_hash)
- cgraph_hash = htab_create_ggc (10, hash_node, eq_node, NULL);
+ return NULL;
key.decl = decl;
NO_INSERT);
if (slot && *slot)
- node = *slot;
+ {
+ node = *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
+ }
return node;
}
it is __builtin_strlen and strlen, for instance. Do we need to
record them all? Original implementation marked just first one
so lets hope for the best. */
- if (*slot)
- continue;
- *slot = node;
+ if (!*slot)
+ *slot = node;
+ if (node->same_body)
+ {
+ struct cgraph_node *alias;
+
+ for (alias = node->same_body; alias; alias = alias->next)
+ {
+ hashval_t hash;
+ name = DECL_ASSEMBLER_NAME (alias->decl);
+ hash = decl_assembler_name_hash (name);
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ hash, INSERT);
+ if (!*slot)
+ *slot = alias;
+ }
+ }
}
}
NO_INSERT);
if (slot)
- return (struct cgraph_node *) *slot;
+ {
+ node = (struct cgraph_node *) *slot;
+ if (node->same_body_alias)
+ node = node->same_body;
+ return node;
+ }
return NULL;
}
}
/* Like cgraph_create_edge walk the clone tree and update all clones sharing
- same function body.
-
+ same function body.
+
TODO: COUNT and LOOP_DEPTH should be properly distributed based on relative
frequencies of the clones. */
DECL_INITIAL (node->decl) = error_mark_node;
}
+/* Remove same body alias node. */
+
+void
+cgraph_remove_same_body_alias (struct cgraph_node *node)
+{
+ void **slot;
+ int uid = node->uid;
+
+ gcc_assert (node->same_body_alias);
+ if (node->previous)
+ node->previous->next = node->next;
+ else
+ node->same_body->same_body = node->next;
+ if (node->next)
+ node->next->previous = node->previous;
+ node->next = NULL;
+ node->previous = NULL;
+ slot = htab_find_slot (cgraph_hash, node, NO_INSERT);
+ if (*slot == node)
+ htab_clear_slot (cgraph_hash, slot);
+ if (assembler_name_hash)
+ {
+ tree name = DECL_ASSEMBLER_NAME (node->decl);
+ slot = htab_find_slot_with_hash (assembler_name_hash, name,
+ decl_assembler_name_hash (name),
+ NO_INSERT);
+ if (slot && *slot == node)
+ htab_clear_slot (assembler_name_hash, slot);
+ }
+
+ /* Clear out the node to NULL all pointers and add the node to the free
+ list. */
+ memset (node, 0, sizeof(*node));
+ node->uid = uid;
+ NEXT_FREE_NODE (node) = free_nodes;
+ free_nodes = node;
+}
+
/* Remove the node from cgraph. */
void
node->clone_of->clones = node->clones;
}
+ while (node->same_body)
+ cgraph_remove_same_body_alias (node->same_body);
+
/* While all the clones are removed after being proceeded, the function
itself is kept in the cgraph even after it is compiled. Check whether
we are done with this body and reclaim it proactively if this is the case.
fprintf(f, "(can throw external) ");
}
fprintf (f, "\n");
+
+ if (node->same_body)
+ {
+ struct cgraph_node *n;
+ fprintf (f, " aliases & thunks:");
+ for (n = node->same_body; n; n = n->next)
+ {
+ fprintf (f, " %s/%i", cgraph_node_name (n), n->uid);
+ if (n->thunk.thunk_p)
+ {
+ fprintf (f, " (thunk of %s fixed ofset %i virtual value %i has "
+ "virtual offset %i",
+ lang_hooks.decl_printable_name (n->thunk.alias, 2),
+ (int)n->thunk.fixed_offset,
+ (int)n->thunk.virtual_value,
+ (int)n->thunk.virtual_offset_p);
+ fprintf (f, ")");
+ }
+ }
+ fprintf (f, "\n");
+ }
}
}
/* Create callgraph node clone with new declaration. The actual body will
- be copied later at compilation stage.
+ be copied later at compilation stage.
TODO: after merging in ipa-sra use function call notes instead of args_to_skip
bitmap interface.
GIMPLE.
The function is assumed to be reachable and have address taken (so no
- API breaking optimizations are performed on it).
+ API breaking optimizations are performed on it).
Main work done by this function is to enqueue the function for later
processing to avoid need the passes to be re-entrant. */