Makefile.in (OBJS): Add multiple_target.o.
2015-10-30 Evgeny Stupachenko <evstupac@gmail.com> gcc/ * Makefile.in (OBJS): Add multiple_target.o. * attrib.c (make_attribute): Moved from config/i386/i386.c * config/i386/i386.c (make_attribute): Deleted. * multiple_target.c (create_dispatcher_calls): New. (get_attr_len): Ditto. (get_attr_str): Ditto. (separate_attrs): Ditto. (is_valid_asm_symbol): Ditto. (create_new_asm_name): Ditto. (create_target_clone): Ditto. (expand_target_clones): Ditto. (ipa_target_clone): Ditto. (ipa_dispatcher_calls): Ditto. * passes.def (pass_target_clone): Two new ipa passes. * tree-pass.h (make_pass_target_clone): Ditto. * doc/extend.texi (target_clones): New attribute description. gcc/c-family/ * c-common.c (handle_target_clones_attribute): New. (c_common_attribute_table): Add handle_target_clones_attribute. (handle_always_inline_attribute): Add check on target_clones attribute. (handle_target_attribute): Ditto. gcc/testsuite/ * gcc.dg/mvc1.c: New test for multiple targets cloning. * gcc.dg/mvc2.c: Ditto. * gcc.dg/mvc3.c: Ditto. * gcc.dg/mvc4.c: Ditto. * gcc.dg/mvc5.c: Ditto. * gcc.dg/mvc6.c: Ditto. * gcc.dg/mvc7.c: Ditto. * g++.dg/ext/mvc1.C: Ditto. * g++.dg/ext/mvc2.C: Ditto. * g++.dg/ext/mvc3.C: Ditto. * g++.dg/ext/mvc4.C: Ditto. From-SVN: r229595
This commit is contained in:
parent
834e9dbb39
commit
3b1661a9b9
23 changed files with 755 additions and 18 deletions
|
@ -1,3 +1,22 @@
|
|||
2015-10-30 Evgeny Stupachenko <evstupac@gmail.com>
|
||||
|
||||
* Makefile.in (OBJS): Add multiple_target.o.
|
||||
* attrib.c (make_attribute): Moved from config/i386/i386.c
|
||||
* config/i386/i386.c (make_attribute): Deleted.
|
||||
* multiple_target.c (create_dispatcher_calls): New.
|
||||
(get_attr_len): Ditto.
|
||||
(get_attr_str): Ditto.
|
||||
(separate_attrs): Ditto.
|
||||
(is_valid_asm_symbol): Ditto.
|
||||
(create_new_asm_name): Ditto.
|
||||
(create_target_clone): Ditto.
|
||||
(expand_target_clones): Ditto.
|
||||
(ipa_target_clone): Ditto.
|
||||
(ipa_dispatcher_calls): Ditto.
|
||||
* passes.def (pass_target_clone): Two new ipa passes.
|
||||
* tree-pass.h (make_pass_target_clone): Ditto.
|
||||
* doc/extend.texi (target_clones): New attribute description.
|
||||
|
||||
2015-10-30 Vladimir Makarov <vmakarov@redhat.com>
|
||||
|
||||
PR rtl-optimization/68106
|
||||
|
|
|
@ -1356,6 +1356,7 @@ OBJS = \
|
|||
mcf.o \
|
||||
mode-switching.o \
|
||||
modulo-sched.o \
|
||||
multiple_target.o \
|
||||
omp-low.o \
|
||||
optabs.o \
|
||||
optabs-libfuncs.o \
|
||||
|
|
|
@ -672,3 +672,21 @@ apply_tm_attr (tree fndecl, tree attr)
|
|||
{
|
||||
decl_attributes (&TREE_TYPE (fndecl), tree_cons (attr, NULL, NULL), 0);
|
||||
}
|
||||
|
||||
/* Makes a function attribute of the form NAME(ARG_NAME) and chains
|
||||
it to CHAIN. */
|
||||
|
||||
tree
|
||||
make_attribute (const char *name, const char *arg_name, tree chain)
|
||||
{
|
||||
tree attr_name;
|
||||
tree attr_arg_name;
|
||||
tree attr_args;
|
||||
tree attr;
|
||||
|
||||
attr_name = get_identifier (name);
|
||||
attr_arg_name = build_string (strlen (arg_name), arg_name);
|
||||
attr_args = tree_cons (NULL_TREE, attr_arg_name, NULL_TREE);
|
||||
attr = tree_cons (attr_name, attr_args, chain);
|
||||
return attr;
|
||||
}
|
||||
|
|
|
@ -36,5 +36,6 @@ extern tree decl_attributes (tree *, tree, int);
|
|||
extern bool cxx11_attribute_p (const_tree);
|
||||
extern tree get_attribute_name (const_tree);
|
||||
extern void apply_tm_attr (tree, tree);
|
||||
extern tree make_attribute (const char *, const char *, tree);
|
||||
|
||||
#endif // GCC_ATTRIBS_H
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2015-10-30 Evgeny Stupachenko <evstupac@gmail.com>
|
||||
|
||||
* c-common.c (handle_target_clones_attribute): New.
|
||||
(c_common_attribute_table): Add handle_target_clones_attribute.
|
||||
(handle_always_inline_attribute): Add check on target_clones attribute.
|
||||
(handle_target_attribute): Ditto.
|
||||
|
||||
2015-10-29 Andrew MacLeod <amacleod@redhat.com>
|
||||
|
||||
* array-notation-common.c: Reorder #include's and remove duplicates.
|
||||
|
|
|
@ -379,6 +379,7 @@ static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
|
|||
static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_target_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree ignore_attribute (tree *, tree, tree, int, bool *);
|
||||
static tree handle_no_split_stack_attribute (tree *, tree, tree, int, bool *);
|
||||
|
@ -793,6 +794,8 @@ const struct attribute_spec c_common_attribute_table[] =
|
|||
handle_error_attribute, false },
|
||||
{ "target", 1, -1, true, false, false,
|
||||
handle_target_attribute, false },
|
||||
{ "target_clones", 1, -1, true, false, false,
|
||||
handle_target_clones_attribute, false },
|
||||
{ "optimize", 1, -1, true, false, false,
|
||||
handle_optimize_attribute, false },
|
||||
/* For internal use only. The leading '*' both prevents its usage in
|
||||
|
@ -7390,6 +7393,12 @@ handle_always_inline_attribute (tree *node, tree name,
|
|||
"with %qs attribute", name, "noinline");
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
|
||||
"with %qs attribute", name, "target_clones");
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else
|
||||
/* Set the attribute and mark it for disregarding inline
|
||||
limits. */
|
||||
|
@ -9818,6 +9827,12 @@ handle_target_attribute (tree *node, tree name, tree args, int flags,
|
|||
warning (OPT_Wattributes, "%qE attribute ignored", name);
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node)))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
|
||||
"with %qs attribute", name, "target_clones");
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else if (! targetm.target_option.valid_attribute_p (*node, name, args,
|
||||
flags))
|
||||
*no_add_attrs = true;
|
||||
|
@ -9825,6 +9840,39 @@ handle_target_attribute (tree *node, tree name, tree args, int flags,
|
|||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Handle a "target_clones" attribute. */
|
||||
|
||||
static tree
|
||||
handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args),
|
||||
int ARG_UNUSED (flags), bool *no_add_attrs)
|
||||
{
|
||||
/* Ensure we have a function type. */
|
||||
if (TREE_CODE (*node) == FUNCTION_DECL)
|
||||
{
|
||||
if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node)))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
|
||||
"with %qs attribute", name, "always_inline");
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else if (lookup_attribute ("target", DECL_ATTRIBUTES (*node)))
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored due to conflict "
|
||||
"with %qs attribute", name, "target");
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
else
|
||||
/* Do not inline functions with multiple clone targets. */
|
||||
DECL_UNINLINABLE (*node) = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
warning (OPT_Wattributes, "%qE attribute ignored", name);
|
||||
*no_add_attrs = true;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Arguments being collected for optimization. */
|
||||
typedef const char *const_char_p; /* For DEF_VEC_P. */
|
||||
static GTY(()) vec<const_char_p, va_gc> *optimize_args;
|
||||
|
|
|
@ -36515,24 +36515,6 @@ ix86_get_function_versions_dispatcher (void *decl)
|
|||
return dispatch_decl;
|
||||
}
|
||||
|
||||
/* Makes a function attribute of the form NAME(ARG_NAME) and chains
|
||||
it to CHAIN. */
|
||||
|
||||
static tree
|
||||
make_attribute (const char *name, const char *arg_name, tree chain)
|
||||
{
|
||||
tree attr_name;
|
||||
tree attr_arg_name;
|
||||
tree attr_args;
|
||||
tree attr;
|
||||
|
||||
attr_name = get_identifier (name);
|
||||
attr_arg_name = build_string (strlen (arg_name), arg_name);
|
||||
attr_args = tree_cons (NULL_TREE, attr_arg_name, NULL_TREE);
|
||||
attr = tree_cons (attr_name, attr_args, chain);
|
||||
return attr;
|
||||
}
|
||||
|
||||
/* Make the resolver function decl to dispatch the versions of
|
||||
a multi-versioned function, DEFAULT_DECL. Create an
|
||||
empty basic block in the resolver and store the pointer in
|
||||
|
|
|
@ -3087,6 +3087,19 @@ This function attribute make a stack protection of the function if
|
|||
flags @option{fstack-protector} or @option{fstack-protector-strong}
|
||||
or @option{fstack-protector-explicit} are set.
|
||||
|
||||
@item target_clones (@var{options})
|
||||
@cindex @code{target_clones} function attribute
|
||||
The @code{target_clones} attribute is used to specify that a function is to
|
||||
be cloned into multiple versions compiled with different target options
|
||||
than specified on the command line. The supported options and restrictions
|
||||
are the same as for @code{target} attribute.
|
||||
|
||||
For instance on an x86, you could compile a function with
|
||||
@code{target_clones("sse4.1,avx")}. It will create 2 function clones,
|
||||
one compiled with @option{-msse4.1} and another with @option{-mavx}.
|
||||
At the function call it will create resolver @code{ifunc}, that will
|
||||
dynamically call a clone suitable for current architecture.
|
||||
|
||||
@item target (@var{options})
|
||||
@cindex @code{target} function attribute
|
||||
Multiple target back ends implement the @code{target} attribute
|
||||
|
|
437
gcc/multiple_target.c
Normal file
437
gcc/multiple_target.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
/* Pass for parsing functions with multiple target attributes.
|
||||
|
||||
Contributed by Evgeny Stupachenko <evstupac@gmail.com>
|
||||
|
||||
Copyright (C) 2015 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "backend.h"
|
||||
#include "tree.h"
|
||||
#include "stringpool.h"
|
||||
#include "gimple.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "gimple-ssa.h"
|
||||
#include "cgraph.h"
|
||||
#include "tree-pass.h"
|
||||
#include "target.h"
|
||||
#include "attribs.h"
|
||||
#include "pretty-print.h"
|
||||
|
||||
/* If the call in NODE has multiple target attribute with multiple fields,
|
||||
replace it with dispatcher call and create dispatcher (once). */
|
||||
|
||||
static void
|
||||
create_dispatcher_calls (struct cgraph_node *node)
|
||||
{
|
||||
cgraph_edge *e;
|
||||
cgraph_edge *e_next;
|
||||
|
||||
/* 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;
|
||||
|
||||
e_next = e->next_caller;
|
||||
idecl = targetm.get_function_versions_dispatcher (decl);
|
||||
if (!idecl)
|
||||
{
|
||||
error_at (gimple_location (call),
|
||||
"default target_clones attribute was not set");
|
||||
}
|
||||
inode = cgraph_node::get (idecl);
|
||||
gcc_assert (inode);
|
||||
resolver_decl = targetm.generate_version_dispatcher_body (inode);
|
||||
|
||||
/* Update aliases. */
|
||||
inode->alias = true;
|
||||
inode->alias_target = resolver_decl;
|
||||
if (!inode->analyzed)
|
||||
inode->resolve_alias (cgraph_node::get (resolver_decl));
|
||||
|
||||
e->redirect_callee (inode);
|
||||
/* Since REDIRECT_CALLEE modifies NEXT_CALLER field we move to
|
||||
previously set NEXT_CALLER. */
|
||||
e = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return length of attribute names string,
|
||||
if arglist chain > 1, -1 otherwise. */
|
||||
|
||||
static int
|
||||
get_attr_len (tree arglist)
|
||||
{
|
||||
tree arg;
|
||||
int str_len_sum = 0;
|
||||
int argnum = 1;
|
||||
|
||||
for (arg = arglist; arg; arg = TREE_CHAIN (arg))
|
||||
{
|
||||
unsigned int i;
|
||||
const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
|
||||
int len = strlen (str);
|
||||
|
||||
str_len_sum += len + 1;
|
||||
if (arg != arglist)
|
||||
argnum++;
|
||||
for (i = 0; i < strlen (str); i++)
|
||||
if (str[i] == ',')
|
||||
argnum++;
|
||||
}
|
||||
if (argnum == 1)
|
||||
return -1;
|
||||
return str_len_sum;
|
||||
}
|
||||
|
||||
/* Create string with attributes separated by comma.
|
||||
Return number of attributes. */
|
||||
|
||||
static int
|
||||
get_attr_str (tree arglist, char *attr_str)
|
||||
{
|
||||
tree arg;
|
||||
size_t str_len_sum = 0;
|
||||
int argnum = 0;
|
||||
|
||||
for (arg = arglist; arg; arg = TREE_CHAIN (arg))
|
||||
{
|
||||
const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
|
||||
|
||||
size_t len = strlen (str);
|
||||
memcpy (attr_str + str_len_sum, str, len);
|
||||
attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
|
||||
str_len_sum += len + 1;
|
||||
argnum++;
|
||||
}
|
||||
return argnum;
|
||||
}
|
||||
|
||||
/* Return number of attributes separated by comma and put them into ARGS.
|
||||
If there is no DEFAULT attribute return -1. */
|
||||
|
||||
static int
|
||||
separate_attrs (char *attr_str, char **attrs)
|
||||
{
|
||||
int i = 0;
|
||||
bool has_default = false;
|
||||
char *attr = strtok (attr_str, ",");
|
||||
|
||||
while (attr != NULL)
|
||||
{
|
||||
if (strcmp (attr, "default") == 0)
|
||||
{
|
||||
has_default = true;
|
||||
attr = strtok (NULL, ",");
|
||||
continue;
|
||||
}
|
||||
attrs[i] = attr;
|
||||
attr = strtok (NULL, ",");
|
||||
i++;
|
||||
}
|
||||
if (!has_default)
|
||||
return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Return true if symbol is valid in assembler name. */
|
||||
|
||||
static bool
|
||||
is_valid_asm_symbol (char c)
|
||||
{
|
||||
if ('a' <= c && c <= 'z')
|
||||
return true;
|
||||
if ('A' <= c && c <= 'Z')
|
||||
return true;
|
||||
if ('0' <= c && c <= '9')
|
||||
return true;
|
||||
if (c == '_')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Replace all not valid assembler symbols with '_'. */
|
||||
|
||||
static void
|
||||
create_new_asm_name (char *old_asm_name, char *new_asm_name)
|
||||
{
|
||||
int i;
|
||||
int old_name_len = strlen (old_asm_name);
|
||||
|
||||
/* Replace all not valid assembler symbols with '_'. */
|
||||
for (i = 0; i < old_name_len; i++)
|
||||
if (!is_valid_asm_symbol (old_asm_name[i]))
|
||||
new_asm_name[i] = '_';
|
||||
else
|
||||
new_asm_name[i] = old_asm_name[i];
|
||||
new_asm_name[old_name_len] = '\0';
|
||||
}
|
||||
|
||||
/* Creates target clone of NODE. */
|
||||
|
||||
static cgraph_node *
|
||||
create_target_clone (cgraph_node *node, bool definition, char *name)
|
||||
{
|
||||
cgraph_node *new_node;
|
||||
|
||||
if (definition)
|
||||
{
|
||||
new_node = node->create_version_clone_with_body (vNULL, NULL,
|
||||
NULL, false,
|
||||
NULL, NULL,
|
||||
name);
|
||||
new_node->force_output = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree new_decl = copy_node (node->decl);
|
||||
new_node = cgraph_node::get_create (new_decl);
|
||||
}
|
||||
return new_node;
|
||||
}
|
||||
|
||||
/* If the function in NODE has multiple target attributes
|
||||
create the appropriate clone for each valid target attribute. */
|
||||
|
||||
static bool
|
||||
expand_target_clones (struct cgraph_node *node, bool defenition)
|
||||
{
|
||||
int i;
|
||||
/* Parsing target attributes separated by comma. */
|
||||
tree attr_target = lookup_attribute ("target_clones",
|
||||
DECL_ATTRIBUTES (node->decl));
|
||||
/* No targets specified. */
|
||||
if (!attr_target)
|
||||
return false;
|
||||
|
||||
tree arglist = TREE_VALUE (attr_target);
|
||||
int attr_len = get_attr_len (arglist);
|
||||
|
||||
/* No need to clone for 1 target attribute. */
|
||||
if (attr_len == -1)
|
||||
{
|
||||
warning_at (DECL_SOURCE_LOCATION (node->decl),
|
||||
0,
|
||||
"single target_clones attribute is ignored");
|
||||
return false;
|
||||
}
|
||||
|
||||
char *attr_str = XNEWVEC (char, attr_len);
|
||||
int attrnum = get_attr_str (arglist, attr_str);
|
||||
char **attrs = XNEWVEC (char *, attrnum);
|
||||
|
||||
attrnum = separate_attrs (attr_str, attrs);
|
||||
if (attrnum == -1)
|
||||
{
|
||||
error_at (DECL_SOURCE_LOCATION (node->decl),
|
||||
"default target was not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
cgraph_function_version_info *decl1_v = NULL;
|
||||
cgraph_function_version_info *decl2_v = NULL;
|
||||
cgraph_function_version_info *before = NULL;
|
||||
cgraph_function_version_info *after = NULL;
|
||||
decl1_v = node->function_version ();
|
||||
if (decl1_v == NULL)
|
||||
decl1_v = node->insert_new_function_version ();
|
||||
before = decl1_v;
|
||||
DECL_FUNCTION_VERSIONED (node->decl) = 1;
|
||||
|
||||
for (i = 0; i < attrnum; i++)
|
||||
{
|
||||
char *attr = attrs[i];
|
||||
char *suffix = XNEWVEC (char, strlen (attr) + 1);
|
||||
|
||||
create_new_asm_name (attr, suffix);
|
||||
/* Create new target clone. */
|
||||
cgraph_node *new_node = create_target_clone (node, defenition, suffix);
|
||||
XDELETEVEC (suffix);
|
||||
|
||||
/* Set new attribute for the clone. */
|
||||
tree attributes = make_attribute ("target", attr,
|
||||
DECL_ATTRIBUTES (new_node->decl));
|
||||
DECL_ATTRIBUTES (new_node->decl) = attributes;
|
||||
if (!targetm.target_option.valid_attribute_p (new_node->decl, NULL,
|
||||
TREE_VALUE (attributes), 0))
|
||||
{
|
||||
warning_at (DECL_SOURCE_LOCATION (node->decl), 0,
|
||||
"attribute(target_clones(\"%s\")) is not "
|
||||
"valid for current target", attr);
|
||||
continue;
|
||||
}
|
||||
|
||||
decl2_v = new_node->function_version ();
|
||||
if (decl2_v != NULL)
|
||||
continue;
|
||||
decl2_v = new_node->insert_new_function_version ();
|
||||
|
||||
/* Chain decl2_v and decl1_v. All semantically identical versions
|
||||
will be chained together. */
|
||||
after = decl2_v;
|
||||
while (before->next != NULL)
|
||||
before = before->next;
|
||||
while (after->prev != NULL)
|
||||
after = after->prev;
|
||||
|
||||
before->next = after;
|
||||
after->prev = before;
|
||||
DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
|
||||
}
|
||||
|
||||
/* Setting new attribute to initial function. */
|
||||
tree attributes = make_attribute ("target", "default",
|
||||
DECL_ATTRIBUTES (node->decl));
|
||||
DECL_ATTRIBUTES (node->decl) = attributes;
|
||||
if (!targetm.target_option.valid_attribute_p (node->decl, NULL,
|
||||
TREE_VALUE (attributes), 0))
|
||||
{
|
||||
error_at (DECL_SOURCE_LOCATION (node->decl),
|
||||
"attribute(target_clones(\"default\")) is not "
|
||||
"valid for current target");
|
||||
return false;
|
||||
}
|
||||
|
||||
XDELETEVEC (attrs);
|
||||
XDELETEVEC (attr_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool target_clone_pass;
|
||||
|
||||
static unsigned int
|
||||
ipa_target_clone (void)
|
||||
{
|
||||
struct cgraph_node *node;
|
||||
|
||||
target_clone_pass = false;
|
||||
FOR_EACH_FUNCTION (node)
|
||||
if (node->definition)
|
||||
target_clone_pass |= expand_target_clones (node, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const pass_data pass_data_target_clone =
|
||||
{
|
||||
SIMPLE_IPA_PASS, /* type */
|
||||
"targetclone", /* name */
|
||||
OPTGROUP_NONE, /* optinfo_flags */
|
||||
TV_NONE, /* tv_id */
|
||||
( PROP_ssa | PROP_cfg ), /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0 /* todo_flags_finish */
|
||||
};
|
||||
|
||||
class pass_target_clone : public simple_ipa_opt_pass
|
||||
{
|
||||
public:
|
||||
pass_target_clone (gcc::context *ctxt)
|
||||
: simple_ipa_opt_pass (pass_data_target_clone, ctxt)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *);
|
||||
virtual unsigned int execute (function *) { return ipa_target_clone (); }
|
||||
};
|
||||
|
||||
bool
|
||||
pass_target_clone::gate (function *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
simple_ipa_opt_pass *
|
||||
make_pass_target_clone (gcc::context *ctxt)
|
||||
{
|
||||
return new pass_target_clone (ctxt);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
ipa_dispatcher_calls (void)
|
||||
{
|
||||
struct cgraph_node *node;
|
||||
|
||||
FOR_EACH_FUNCTION (node)
|
||||
if (!node->definition)
|
||||
target_clone_pass |= expand_target_clones (node, false);
|
||||
if (target_clone_pass)
|
||||
FOR_EACH_FUNCTION (node)
|
||||
create_dispatcher_calls (node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const pass_data pass_data_dispatcher_calls =
|
||||
{
|
||||
SIMPLE_IPA_PASS, /* type */
|
||||
"dispachercalls", /* name */
|
||||
OPTGROUP_NONE, /* optinfo_flags */
|
||||
TV_NONE, /* tv_id */
|
||||
( PROP_ssa | PROP_cfg ), /* properties_required */
|
||||
0, /* properties_provided */
|
||||
0, /* properties_destroyed */
|
||||
0, /* todo_flags_start */
|
||||
0 /* todo_flags_finish */
|
||||
};
|
||||
|
||||
class pass_dispatcher_calls : public simple_ipa_opt_pass
|
||||
{
|
||||
public:
|
||||
pass_dispatcher_calls (gcc::context *ctxt)
|
||||
: simple_ipa_opt_pass (pass_data_dispatcher_calls, ctxt)
|
||||
{}
|
||||
|
||||
/* opt_pass methods: */
|
||||
virtual bool gate (function *);
|
||||
virtual unsigned int execute (function *) { return ipa_dispatcher_calls (); }
|
||||
};
|
||||
|
||||
bool
|
||||
pass_dispatcher_calls::gate (function *)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anon namespace
|
||||
|
||||
simple_ipa_opt_pass *
|
||||
make_pass_dispatcher_calls (gcc::context *ctxt)
|
||||
{
|
||||
return new pass_dispatcher_calls (ctxt);
|
||||
}
|
|
@ -124,6 +124,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
NEXT_PASS (pass_ipa_devirt);
|
||||
NEXT_PASS (pass_ipa_cp);
|
||||
NEXT_PASS (pass_ipa_cdtor_merge);
|
||||
NEXT_PASS (pass_target_clone);
|
||||
NEXT_PASS (pass_ipa_inline);
|
||||
NEXT_PASS (pass_ipa_pure_const);
|
||||
NEXT_PASS (pass_ipa_reference);
|
||||
|
@ -140,6 +141,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
compiled unit. */
|
||||
INSERT_PASSES_AFTER (all_late_ipa_passes)
|
||||
NEXT_PASS (pass_ipa_pta);
|
||||
NEXT_PASS (pass_dispatcher_calls);
|
||||
NEXT_PASS (pass_omp_simd_clone);
|
||||
TERMINATE_PASS_LIST ()
|
||||
|
||||
|
|
|
@ -1,3 +1,17 @@
|
|||
2015-10-30 Evgeny Stupachenko <evstupac@gmail.com>
|
||||
|
||||
* gcc.dg/mvc1.c: New test for multiple targets cloning.
|
||||
* gcc.dg/mvc2.c: Ditto.
|
||||
* gcc.dg/mvc3.c: Ditto.
|
||||
* gcc.dg/mvc4.c: Ditto.
|
||||
* gcc.dg/mvc5.c: Ditto.
|
||||
* gcc.dg/mvc6.c: Ditto.
|
||||
* gcc.dg/mvc7.c: Ditto.
|
||||
* g++.dg/ext/mvc1.C: Ditto.
|
||||
* g++.dg/ext/mvc2.C: Ditto.
|
||||
* g++.dg/ext/mvc3.C: Ditto.
|
||||
* g++.dg/ext/mvc4.C: Ditto.
|
||||
|
||||
2015-10-15 Steven G. Kargl <kargl@gcc.gnu.org>
|
||||
|
||||
PR fortran/51993
|
||||
|
|
34
gcc/testsuite/g++.dg/ext/mvc1.C
Normal file
34
gcc/testsuite/g++.dg/ext/mvc1.C
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","arch=core-avx2","default")))
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
__attribute__((target("avx","arch=core-avx2")))
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
__attribute__((target("default")))
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int r = 0;
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
return r - 2;
|
||||
}
|
8
gcc/testsuite/g++.dg/ext/mvc2.C
Normal file
8
gcc/testsuite/g++.dg/ext/mvc2.C
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","default")))
|
||||
__attribute__((target("avx")))
|
||||
int foo (); /* { dg-warning "'target' attribute ignored due to conflict with 'target_clones' attribute" } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","default"),always_inline))
|
||||
int bar (); /* { dg-warning "'always_inline' attribute ignored due to conflict with 'target_clones' attribute" } */
|
8
gcc/testsuite/g++.dg/ext/mvc3.C
Normal file
8
gcc/testsuite/g++.dg/ext/mvc3.C
Normal file
|
@ -0,0 +1,8 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target("avx")))
|
||||
__attribute__((target_clones("avx","arch=slm","default")))
|
||||
int foo (); /* { dg-warning "'target_clones' attribute ignored due to conflict with 'target' attribute" } */
|
||||
|
||||
__attribute__((always_inline,target_clones("avx","arch=slm","default")))
|
||||
int bar (); /* { dg-warning "'target_clones' attribute ignored due to conflict with 'always_inline' attribute" } */
|
34
gcc/testsuite/g++.dg/ext/mvc4.C
Normal file
34
gcc/testsuite/g++.dg/ext/mvc4.C
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-options "-mavx" } */
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
__m256 x, y, z;
|
||||
|
||||
__attribute__((target("avx")))
|
||||
int bar()
|
||||
{
|
||||
x = _mm256_add_ps (y, z);
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((target("default")))
|
||||
int bar()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
foobar()
|
||||
{
|
||||
if (__builtin_cpu_supports ("avx"))
|
||||
return bar();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((target_clones("default","sse3")))
|
||||
int foo()
|
||||
{
|
||||
return foobar();
|
||||
}
|
26
gcc/testsuite/gcc.dg/mvc1.c
Normal file
26
gcc/testsuite/gcc.dg/mvc1.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","arch=core-avx2","default")))
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int r = 0;
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
return r - 2;
|
||||
}
|
4
gcc/testsuite/gcc.dg/mvc2.c
Normal file
4
gcc/testsuite/gcc.dg/mvc2.c
Normal file
|
@ -0,0 +1,4 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","arch=core-avx2")))
|
||||
int foo ();
|
10
gcc/testsuite/gcc.dg/mvc3.c
Normal file
10
gcc/testsuite/gcc.dg/mvc3.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("avx","arch=slm","arch=core-avx2")))
|
||||
int foo (); /* { dg-error "default target was not set" } */
|
||||
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return foo();
|
||||
}
|
26
gcc/testsuite/gcc.dg/mvc4.c
Normal file
26
gcc/testsuite/gcc.dg/mvc4.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
|
||||
|
||||
__attribute__((target_clones("default","avx","default")))
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int r = 0;
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
r += foo ();
|
||||
r += bar ();
|
||||
return r - 2;
|
||||
}
|
17
gcc/testsuite/gcc.dg/mvc5.c
Normal file
17
gcc/testsuite/gcc.dg/mvc5.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-options "-fno-inline" } */
|
||||
/* { dg-final { scan-assembler-times "foo.ifunc" 6 } } */
|
||||
|
||||
__attribute__((target_clones("default","avx","avx2")))
|
||||
int
|
||||
foo ()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
__attribute__((target_clones("default","avx","avx2")))
|
||||
int
|
||||
bar ()
|
||||
{
|
||||
return -foo ();
|
||||
}
|
16
gcc/testsuite/gcc.dg/mvc6.c
Normal file
16
gcc/testsuite/gcc.dg/mvc6.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-options "-O3" } */
|
||||
/* { dg-final { scan-assembler "vpshufb" } } */
|
||||
/* { dg-final { scan-assembler "punpcklbw" } } */
|
||||
|
||||
__attribute__((target_clones("arch=core-avx2","arch=slm","default")))
|
||||
void
|
||||
foo(char *in, char *out, int size)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < size; i++)
|
||||
{
|
||||
out[2 * i] = in[i];
|
||||
out[2 * i + 1] = in[i];
|
||||
}
|
||||
}
|
10
gcc/testsuite/gcc.dg/mvc7.c
Normal file
10
gcc/testsuite/gcc.dg/mvc7.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
|
||||
/* { dg-final { scan-assembler-times "foo.ifunc" 4 } } */
|
||||
|
||||
__attribute__((target_clones("avx","default","arch=slm","arch=core-avx2")))
|
||||
int foo ();
|
||||
|
||||
int main()
|
||||
{
|
||||
return foo();
|
||||
}
|
|
@ -487,6 +487,8 @@ extern ipa_opt_pass_d *make_pass_ipa_reference (gcc::context *ctxt);
|
|||
extern ipa_opt_pass_d *make_pass_ipa_pure_const (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_ipa_pta (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_ipa_tm (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_target_clone (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_dispatcher_calls (gcc::context *ctxt);
|
||||
extern simple_ipa_opt_pass *make_pass_omp_simd_clone (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_profile (gcc::context *ctxt);
|
||||
extern ipa_opt_pass_d *make_pass_ipa_cdtor_merge (gcc::context *ctxt);
|
||||
|
|
Loading…
Add table
Reference in a new issue