aboutsummaryrefslogtreecommitdiff
path: root/gcc/multiple_target.c
diff options
context:
space:
mode:
authorMartin Liska <mliska@suse.cz>2017-06-19 15:12:51 +0200
committerMartin Liska <marxin@gcc.gnu.org>2017-06-19 13:12:51 +0000
commit871cc215f7507cbe9ed4b1a5a9fd884deb65c18a (patch)
tree1537ccd22e6aad4a626a95a0ef356332c810e7a1 /gcc/multiple_target.c
parent431abe69f1c4ba0b4b05609f2f4f9206cc6e041a (diff)
downloadgcc-871cc215f7507cbe9ed4b1a5a9fd884deb65c18a.zip
gcc-871cc215f7507cbe9ed4b1a5a9fd884deb65c18a.tar.gz
gcc-871cc215f7507cbe9ed4b1a5a9fd884deb65c18a.tar.bz2
Fix multi-versioning issues (PR ipa/80732).
2017-06-19 Martin Liska <mliska@suse.cz> PR ipa/80732 * attribs.c (make_dispatcher_decl): Do not append '.ifunc' to dispatcher function name. * multiple_target.c (replace_function_decl): New function. (create_dispatcher_calls): Redirect both edges and references. 2017-06-19 Martin Liska <mliska@suse.cz> PR ipa/80732 * gcc.target/i386/mvc5.c: Scan indirect_function. * gcc.target/i386/mvc7.c: Likewise. * gcc.target/i386/pr80732.c: New test. From-SVN: r249365
Diffstat (limited to 'gcc/multiple_target.c')
-rw-r--r--gcc/multiple_target.c115
1 files changed, 83 insertions, 32 deletions
diff --git a/gcc/multiple_target.c b/gcc/multiple_target.c
index 2ee6a959..bdb5b3b 100644
--- a/gcc/multiple_target.c
+++ b/gcc/multiple_target.c
@@ -34,6 +34,27 @@ along with GCC; see the file COPYING3. If not see
#include "target.h"
#include "attribs.h"
#include "pretty-print.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
+
+/* Walker callback that replaces all FUNCTION_DECL of a function that's
+ going to be versioned. */
+
+static tree
+replace_function_decl (tree *op, int *walk_subtrees, void *data)
+{
+ struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+ cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
+
+ if (TREE_CODE (*op) == FUNCTION_DECL
+ && info->this_node->decl == *op)
+ {
+ *op = info->dispatcher_resolver;
+ *walk_subtrees = 0;
+ }
+
+ return NULL;
+}
/* If the call in NODE has multiple target attribute with multiple fields,
replace it with dispatcher call and create dispatcher (once). */
@@ -41,51 +62,48 @@ along with GCC; see the file COPYING3. If not see
static void
create_dispatcher_calls (struct cgraph_node *node)
{
- cgraph_edge *e;
- cgraph_edge *e_next = NULL;
+ ipa_ref *ref;
+
+ if (!DECL_FUNCTION_VERSIONED (node->decl))
+ return;
+
+ auto_vec<cgraph_edge *> edges_to_redirect;
+ auto_vec<ipa_ref *> references_to_redirect;
+
+ for (unsigned i = 0; node->iterate_referring (i, ref); i++)
+ references_to_redirect.safe_push (ref);
/* We need to remember NEXT_CALLER as it could be modified in the loop. */
- for (e = node->callers; e ;e = (e == NULL) ? e_next : e->next_caller)
- {
- tree resolver_decl;
- tree idecl;
- tree decl;
- gimple *call = e->call_stmt;
- struct cgraph_node *inode;
-
- /* Checking if call of function is call of versioned function.
- Versioned function are not inlined, so there is no need to
- check for inline. */
- if (!call
- || !(decl = gimple_call_fndecl (call))
- || !DECL_FUNCTION_VERSIONED (decl))
- continue;
+ for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
+ edges_to_redirect.safe_push (e);
+ if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
+ {
if (!targetm.has_ifunc_p ())
{
- error_at (gimple_location (call),
+ error_at (DECL_SOURCE_LOCATION (node->decl),
"the call requires ifunc, which is not"
" supported by this target");
- break;
+ return;
}
else if (!targetm.get_function_versions_dispatcher)
{
- error_at (gimple_location (call),
+ error_at (DECL_SOURCE_LOCATION (node->decl),
"target does not support function version dispatcher");
- break;
+ return;
}
- e_next = e->next_caller;
- idecl = targetm.get_function_versions_dispatcher (decl);
+ tree idecl = targetm.get_function_versions_dispatcher (node->decl);
if (!idecl)
{
- error_at (gimple_location (call),
+ error_at (DECL_SOURCE_LOCATION (node->decl),
"default target_clones attribute was not set");
- break;
+ return;
}
- inode = cgraph_node::get (idecl);
+
+ cgraph_node *inode = cgraph_node::get (idecl);
gcc_assert (inode);
- resolver_decl = targetm.generate_version_dispatcher_body (inode);
+ tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
/* Update aliases. */
inode->alias = true;
@@ -93,12 +111,45 @@ create_dispatcher_calls (struct cgraph_node *node)
if (!inode->analyzed)
inode->resolve_alias (cgraph_node::get (resolver_decl));
- e->redirect_callee (inode);
- e->redirect_call_stmt_to_callee ();
- /* Since REDIRECT_CALLEE modifies NEXT_CALLER field we move to
- previously set NEXT_CALLER. */
- e = NULL;
+ /* Redirect edges. */
+ unsigned i;
+ cgraph_edge *e;
+ FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
+ {
+ e->redirect_callee (inode);
+ e->redirect_call_stmt_to_callee ();
+ }
+
+ /* Redirect references. */
+ FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
+ {
+ if (ref->use == IPA_REF_ADDR)
+ {
+ struct walk_stmt_info wi;
+ memset (&wi, 0, sizeof (wi));
+ wi.info = (void *)node->function_version ();
+
+ if (dyn_cast<varpool_node *> (ref->referring))
+ {
+ hash_set<tree> visited_nodes;
+ walk_tree (&DECL_INITIAL (ref->referring->decl),
+ replace_function_decl, &wi, &visited_nodes);
+ }
+ else
+ {
+ gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
+ if (ref->referring->decl != resolver_decl)
+ walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
+ }
+ }
+ else
+ gcc_unreachable ();
+ }
}
+
+ symtab->change_decl_assembler_name (node->decl,
+ clone_function_name (node->decl,
+ "default"));
}
/* Return length of attribute names string,