+
+/* The edges representing the callers of the NEW_VERSION node were
+ fixed by cgraph_function_versioning (), now the call_expr in their
+ respective tree code should be updated to call the NEW_VERSION. */
+
+static void
+update_call_expr (struct cgraph_node *new_version)
+{
+ struct cgraph_edge *e;
+
+ gcc_assert (new_version);
+ for (e = new_version->callers; e; e = e->next_caller)
+ /* Update the call expr on the edges
+ to call the new version. */
+ TREE_OPERAND (CALL_EXPR_FN (get_call_expr_in (e->call_stmt)), 0) = new_version->decl;
+}
+
+
+/* Create a new cgraph node which is the new version of
+ OLD_VERSION node. REDIRECT_CALLERS holds the callers
+ edges which should be redirected to point to
+ NEW_VERSION. ALL the callees edges of OLD_VERSION
+ are cloned to the new version node. Return the new
+ version node. */
+
+static struct cgraph_node *
+cgraph_copy_node_for_versioning (struct cgraph_node *old_version,
+ tree new_decl,
+ VEC(cgraph_edge_p,heap) *redirect_callers)
+ {
+ struct cgraph_node *new_version;
+ struct cgraph_edge *e, *new_e;
+ struct cgraph_edge *next_callee;
+ unsigned i;
+
+ gcc_assert (old_version);
+
+ new_version = cgraph_node (new_decl);
+
+ new_version->analyzed = true;
+ new_version->local = old_version->local;
+ new_version->global = old_version->global;
+ new_version->rtl = new_version->rtl;
+ new_version->reachable = true;
+ new_version->count = old_version->count;
+
+ /* Clone the old node callees. Recursive calls are
+ also cloned. */
+ for (e = old_version->callees;e; e=e->next_callee)
+ {
+ new_e = cgraph_clone_edge (e, new_version, e->call_stmt, 0, e->frequency,
+ e->loop_nest, true);
+ new_e->count = e->count;
+ }
+ /* Fix recursive calls.
+ If OLD_VERSION has a recursive call after the
+ previous edge cloning, the new version will have an edge
+ pointing to the old version, which is wrong;
+ Redirect it to point to the new version. */
+ for (e = new_version->callees ; e; e = next_callee)
+ {
+ next_callee = e->next_callee;
+ if (e->callee == old_version)
+ cgraph_redirect_edge_callee (e, new_version);
+
+ if (!next_callee)
+ break;
+ }
+ for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++)
+ {
+ /* Redirect calls to the old version node to point to its new
+ version. */
+ cgraph_redirect_edge_callee (e, new_version);
+ }
+
+ return new_version;
+ }
+
+ /* Perform function versioning.
+ Function versioning includes copying of the tree and
+ a callgraph update (creating a new cgraph node and updating
+ its callees and callers).
+
+ REDIRECT_CALLERS varray includes the edges to be redirected
+ to the new version.
+
+ TREE_MAP is a mapping of tree nodes we want to replace with
+ new ones (according to results of prior analysis).
+ OLD_VERSION_NODE is the node that is versioned.
+ It returns the new version's cgraph node. */
+
+struct cgraph_node *
+cgraph_function_versioning (struct cgraph_node *old_version_node,
+ VEC(cgraph_edge_p,heap) *redirect_callers,
+ varray_type tree_map)
+{
+ tree old_decl = old_version_node->decl;
+ struct cgraph_node *new_version_node = NULL;
+ tree new_decl;
+
+ if (!tree_versionable_function_p (old_decl))
+ return NULL;
+
+ /* Make a new FUNCTION_DECL tree node for the
+ new version. */
+ new_decl = copy_node (old_decl);
+
+ /* Create the new version's call-graph node.
+ and update the edges of the new node. */
+ new_version_node =
+ cgraph_copy_node_for_versioning (old_version_node, new_decl,
+ redirect_callers);
+
+ /* Copy the OLD_VERSION_NODE function tree to the new version. */
+ tree_function_versioning (old_decl, new_decl, tree_map, false);
+ /* Update the call_expr on the edges to call the new version node. */
+ update_call_expr (new_version_node);
+
+ /* Update the new version's properties.
+ Make The new version visible only within this translation unit.
+ ??? We cannot use COMDAT linkage because there is no
+ ABI support for this. */
+ DECL_EXTERNAL (new_version_node->decl) = 0;
+ DECL_ONE_ONLY (new_version_node->decl) = 0;
+ TREE_PUBLIC (new_version_node->decl) = 0;
+ DECL_COMDAT (new_version_node->decl) = 0;
+ new_version_node->local.externally_visible = 0;
+ new_version_node->local.local = 1;
+ new_version_node->lowered = true;
+ return new_version_node;
+}
+
+/* Produce separate function body for inline clones so the offline copy can be
+ modified without affecting them. */
+struct cgraph_node *
+save_inline_function_body (struct cgraph_node *node)
+{
+ struct cgraph_node *first_clone;
+
+ gcc_assert (node == cgraph_node (node->decl));
+
+ cgraph_lower_function (node);
+
+ /* In non-unit-at-a-time we construct full fledged clone we never output to
+ assembly file. This clone is pointed out by inline_decl of original function
+ and inlining infrastructure knows how to deal with this. */
+ if (!flag_unit_at_a_time)
+ {
+ struct cgraph_edge *e;
+
+ first_clone = cgraph_clone_node (node, node->count, 0, CGRAPH_FREQ_BASE,
+ false);
+ first_clone->needed = 0;
+ first_clone->reachable = 1;
+ /* Recursively clone all bodies. */
+ for (e = first_clone->callees; e; e = e->next_callee)
+ if (!e->inline_failed)
+ cgraph_clone_inlined_nodes (e, true, false);
+ }
+ else
+ first_clone = node->next_clone;
+
+ first_clone->decl = copy_node (node->decl);
+ node->next_clone = NULL;
+ if (!flag_unit_at_a_time)
+ node->inline_decl = first_clone->decl;
+ first_clone->prev_clone = NULL;
+ cgraph_insert_node_to_hashtable (first_clone);
+ gcc_assert (first_clone == cgraph_node (first_clone->decl));
+
+ /* Copy the OLD_VERSION_NODE function tree to the new version. */
+ tree_function_versioning (node->decl, first_clone->decl, NULL, true);
+
+ DECL_EXTERNAL (first_clone->decl) = 0;
+ DECL_ONE_ONLY (first_clone->decl) = 0;
+ TREE_PUBLIC (first_clone->decl) = 0;
+ DECL_COMDAT (first_clone->decl) = 0;
+
+ for (node = first_clone->next_clone; node; node = node->next_clone)
+ node->decl = first_clone->decl;
+#ifdef ENABLE_CHECKING
+ verify_cgraph_node (first_clone);
+#endif
+ return first_clone;
+}
+
+#include "gt-cgraphunit.h"