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
This commit is contained in:
parent
431abe69f1
commit
871cc215f7
7 changed files with 193 additions and 39 deletions
|
@ -1,3 +1,11 @@
|
|||
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 Jan Hubicka <hubicka@ucw.cz>
|
||||
|
||||
* profile-count.c (profile_count::dump): Dump quality.
|
||||
|
|
|
@ -888,12 +888,8 @@ make_dispatcher_decl (const tree decl)
|
|||
tree func_decl;
|
||||
char *func_name;
|
||||
tree fn_type, func_type;
|
||||
bool is_uniq = false;
|
||||
|
||||
if (TREE_PUBLIC (decl) == 0)
|
||||
is_uniq = true;
|
||||
|
||||
func_name = make_unique_name (decl, "ifunc", is_uniq);
|
||||
func_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
|
||||
|
||||
fn_type = TREE_TYPE (decl);
|
||||
func_type = build_function_type (TREE_TYPE (fn_type),
|
||||
|
|
|
@ -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)
|
||||
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 ())
|
||||
{
|
||||
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;
|
||||
|
||||
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,
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
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.
|
||||
|
||||
2017-06-19 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
PR c++/66093
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-require-ifunc "" } */
|
||||
/* { dg-options "-fno-inline" } */
|
||||
/* { dg-final { scan-assembler-times "foo.ifunc" 6 } } */
|
||||
/* { dg-final { scan-assembler "foo,foo.resolver" } } */
|
||||
|
||||
__attribute__((target_clones("default","avx","avx2")))
|
||||
int
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
/* { dg-final { scan-assembler "foo.resolver" } } */
|
||||
/* { dg-final { scan-assembler "avx" } } */
|
||||
/* { dg-final { scan-assembler "slm" } } */
|
||||
/* { dg-final { scan-assembler-times "foo.ifunc" 4 } } */
|
||||
/* { dg-final { scan-assembler "foo,foo.resolver" } } */
|
||||
|
||||
__attribute__((target_clones("avx","default","arch=slm","arch=core-avx2")))
|
||||
int foo ();
|
||||
|
|
92
gcc/testsuite/gcc.target/i386/pr80732.c
Normal file
92
gcc/testsuite/gcc.target/i386/pr80732.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* PR ipa/80732 */
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-ldl -fPIC -rdynamic -O3 -g -pie" } */
|
||||
/* { dg-require-ifunc "" } */
|
||||
/* { dg-require-effective-target fma4 } */
|
||||
/* { dg-require-effective-target fpic } */
|
||||
/* { dg-require-effective-target pie } */
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
__attribute__((target_clones("default","fma"),noinline,optimize("fast-math")))
|
||||
double f1(double a, double b, double c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
double k1(double a, double b, double c, void **p)
|
||||
{
|
||||
*p = f1;
|
||||
return f1(a, b, c);
|
||||
}
|
||||
|
||||
__attribute__((target("fma"),optimize("fast-math")))
|
||||
static double f2_fma(double a, double b, double c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
__attribute__((optimize("fast-math")))
|
||||
static double f2_default(double a, double b, double c)
|
||||
{
|
||||
return a * b + c;
|
||||
}
|
||||
|
||||
static void *f2_resolve(void)
|
||||
{
|
||||
__builtin_cpu_init ();
|
||||
if (__builtin_cpu_supports("fma"))
|
||||
return f2_fma;
|
||||
else
|
||||
return f2_default;
|
||||
}
|
||||
|
||||
double f2(double a, double b, double c) __attribute__((ifunc("f2_resolve")));
|
||||
|
||||
double k2(double a, double b, double c, void **p)
|
||||
{
|
||||
*p = f2;
|
||||
return f2(a, b, c);
|
||||
}
|
||||
|
||||
double (*initializer) (double, double, double) = { &f1 };
|
||||
|
||||
int main()
|
||||
{
|
||||
char buffer[256];
|
||||
const char *expectation = "4.93038e-32, 4.93038e-32, 4.93038e-32";
|
||||
|
||||
volatile double a = 1.0000000000000002;
|
||||
volatile double b = -0.9999999999999998;
|
||||
volatile double c = 1.0;
|
||||
|
||||
void *hdl = dlopen(0, RTLD_NOW);
|
||||
|
||||
double (*pf1)(double, double, double) = dlsym(hdl, "f1");
|
||||
double (*pk1)(double, double, double, void**) = dlsym(hdl, "k1");
|
||||
double (*_pf1)(double, double, double);
|
||||
|
||||
double v1_1 = pf1(a, b, c);
|
||||
double v1_2 = pk1(a, b, c, (void**)&_pf1);
|
||||
double v1_3 = _pf1(a, b, c);
|
||||
__builtin_sprintf (buffer, "%g, %g, %g", v1_1, v1_2, v1_3);
|
||||
if (__builtin_strcmp (buffer, expectation) != 0)
|
||||
__builtin_abort ();
|
||||
|
||||
double (*pf2)(double, double, double) = dlsym(hdl, "f2");
|
||||
double (*pk2)(double, double, double, void**) = dlsym(hdl, "k2");
|
||||
double (*_pf2)(double, double, double);
|
||||
|
||||
double v2_1 = pf2(a, b, c);
|
||||
double v2_2 = pk2(a, b, c, (void**)&_pf2);
|
||||
double v2_3 = _pf2(a, b, c);
|
||||
__builtin_sprintf(buffer, "%g, %g, %g", v2_1, v2_2, v2_3);
|
||||
if (__builtin_strcmp (buffer, expectation) != 0)
|
||||
__builtin_abort ();
|
||||
|
||||
__builtin_sprintf(buffer, "%g, %g, %g", initializer (a, b, c), v2_2, v2_3);
|
||||
if (__builtin_strcmp (buffer, expectation) != 0)
|
||||
__builtin_abort ();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue