Function Multiversioning

========================

Sriraman Tallam, tmsriram@google.com

Overview of the patch which adds support to specify function versions.  This is
only enabled for target i386.

Example:

int foo ();  /* Default version */
int foo () __attribute__ ((target("avx,popcnt")));/*Specialized for avx and popcnt */
int foo () __attribute__ ((target("arch=core2,ssse3")));/*Specialized for core2 and ssse3*/

int main ()
{
 int (*p)() = &foo;
 return foo () + (*p)();
}

int foo ()
{
 return 0;
}

int __attribute__ ((target("avx,popcnt")))
foo ()
{
 return 0;
}

int __attribute__ ((target("arch=core2,ssse3")))
foo ()
{
 return 0;
}

The above example has foo defined 3 times, but all 3 definitions of foo are
different versions of the same function. The call to foo in main, directly and
via a pointer, are calls to the multi-versioned function foo which is dispatched
to the right foo at run-time.

Front-end changes:

The front-end changes are calls at appropriate places to target hooks that
determine the following:

* Determine if two function decls with the same signature are versions.
* Determine the assembler name of a function version.
* Generate the dispatcher function for a set of function versions.
* Compare versions to see if one has a higher priority over the other.

All the implementation happens in the target-specific config/i386/i386.c.

What does the patch do?

* Tracking decls that correspond to function versions of function
name, say "foo":

When the front-end sees more than one decl for "foo", it calls a target hook to
determine if they are versions. To prevent duplicate definition errors with
other versions of "foo", "decls_match" function in cp/decl.c is made to return
false when 2 decls have are deemed versions by the target. This will make all
function versions of "foo" to be added to the overload list of "foo".

* Change the assembler names of the function versions.

For i386, the target changes the assembler names of the function versions by
 suffixing the sorted list of args to "target" to the function name of "foo".
For example, the assembler name of
 "void foo () __attribute__ ((target ("sse4")))" will
become _Z3foov.sse4.  The target hook mangle_decl_assembler_name is used
for this.

* Overload resolution:

 Function "build_over_call" in cp/call.c sees a call to function
"foo", which is multi-versioned. The overload resolution happens in
function "joust" in "cp/call.c". Here, the call to "foo" has all
possible versions of "foo" as candidates. All the candidates of "foo" are
stored in the cgraph side data structure. Each version of foo is chained in a 
doubly-linked list with the default function as the first element.  This allows
any pass to access all the semantically identical versions. A call to a
multi-versioned function will be replaced by a call to a dispatcher function,
determined by a target hook, to execute the right function version at run-time.

Optimization to directly call a version when possible:
Also, in joust, where overload resolution happens, a multiversioned function
resolution is made to return the most specialized version.  This is the version
that will be checked for dispatching first and is determined by the target.
Now, if the caller can inline this function version then a direct call is made
to this function version rather than go through the dispatcher. When a direct
call cannot be made, a call to the dispatcher function is created.

* Creating the dispatcher body.

The dispatcher body, called the resolver is made only when there is a call to a
multiversioned function dispatcher or the address of a function is taken. This
is generated during cgraph_analyze_function. This is done by another target hook.

* Dispatch ordering.

The order in which the function versions are checked during dispatch is based
on a priority value assigned for the ISA that is catered. More specialized 
versions are checked for dispatching first.  This is to mitigate the ambiguity
that can arise when more than one function version is valid for execution on
a particular platform.  This is not a perfect solution, and in future the user
should be allowed to assign a dispatching priority value to each version.

Function MV in the Intel compiler:

The intel compiler supports function multiversioning and the syntax is
similar to the patch proposed here.  Here is an example of how to
generate multiple function versions with the intel compiler.

/* Create a stub function to specify the various versions of function that
   will be created, using declspec attribute cpu_dispatch.  */
__declspec (cpu_dispatch (core_i7_sse4_2, atom, generic))
void foo () {};

/* Bodies of each function version.  */

/* Intel Corei7 processor + SSE4.2 version.  */
__declspec (cpu_specific(core_i7_sse4_2))
void foo ()
{
  printf ("corei7 + sse4.2");
}

/* Atom processor.  */
__declspec (cpu_specific(atom))
void foo ()
{
  printf ("atom");
}

/* The generic or the default version.  */
__declspec (cpu_specific(generic))
void foo ()
{
  printf ("This is generic");
}

A new function version is generated by defining a new function with the same
signature but with a different cpu_specific declspec attribute string.  The
set of cpu_specific strings that are allowed is the following:

"core_2nd_gen_avx"
"core_aes_pclmulqdq"
"core_i7_sse4_2"
"core_2_duo_sse4_1"
"core_2_duo_ssse3"
"atom"
"pentium_4_sse3"
"pentium_4"
"pentium_m"
"pentium_iii"
"generic"

Comparison with the GCC MV implementation in this patch:

* Version creation syntax:

The implementation in this patch also has a similar syntax to specify function
versions. The first stub function is not needed.  Here is the code to generate
the function versions with this patch:

/* Intel Corei7 processor + SSE4.2 version.  */
__attribute__ ((target ("arch=corei7, sse4.2")))
void foo ()
{
  printf ("corei7 + sse4.2");
}

/* Atom processor.  */
__attribute__ ((target ("arch=atom")))
void foo ()
{
  printf ("atom");
}

void foo ()
{
}

The target attribute can have one of the following arch names:

"amd"
"intel"
"atom"
"core2"
"corei7"
"nehalem"
"westmere"
"sandybridge"
"amdfam10h"
"barcelona"
"shanghai"
"istanbul"
"amdfam15h"
"bdver1"
"bdver2"

and any number of the following ISA names:

"cmov"
"mmx"
"popcnt"
"sse"
"sse2"
"sse3"
"ssse3"
"sse4.1"
"sse4.2"
"avx"
"avx2"



	* doc/tm.texi.in (TARGET_OPTION_FUNCTION_VERSIONS): New hook
	description.
	* (TARGET_COMPARE_VERSION_PRIORITY): New hook description.
	* (TARGET_GET_FUNCTION_VERSIONS_DISPATCHER): New hook description.
	* (TARGET_GENERATE_VERSION_DISPATCHER_BODY): New hook description.
	* doc/tm.texi: Regenerate.
	* target.def (compare_version_priority): New target hook.
	* (generate_version_dispatcher_body): New target hook.
	* (get_function_versions_dispatcher): New target hook.
	* (function_versions): New target hook.
	* cgraph.c (cgraph_fnver_htab): New htab.
	(cgraph_fn_ver_htab_hash): New function.
	(cgraph_fn_ver_htab_eq): New function.
	(version_info_node): New pointer.
	(insert_new_cgraph_node_version): New function.
	(get_cgraph_node_version): New function.
	(delete_function_version): New function.
	(record_function_versions): New function.
	* cgraph.h (cgraph_node): New bitfield dispatcher_function.
	(cgraph_function_version_info): New struct.
	(get_cgraph_node_version): New function.
	(insert_new_cgraph_node_version): New function.
	(record_function_versions): New function.
	(delete_function_version): New function.
	(init_lowered_empty_function): Expose function.
	* tree.h (DECL_FUNCTION_VERSIONED): New macro.
	(tree_function_decl): New bit-field versioned_function.
	* cgraphunit.c (cgraph_analyze_function): Generate body of multiversion
	function dispatcher.
	(cgraph_analyze_functions): Analyze dispatcher function.
	(init_lowered_empty_function): Make non-static. New parameter in_ssa.
	(assemble_thunk): Add parameter to call to init_lowered_empty_function.
	* config/i386/i386.c (add_condition_to_bb): New function.
	(get_builtin_code_for_version): New function.
	(ix86_compare_version_priority): New function.
	(feature_compare): New function.
	(dispatch_function_versions): New function.
	(ix86_function_versions): New function.
	(attr_strcmp): New function.
	(ix86_mangle_function_version_assembler_name): New function.
	(ix86_mangle_decl_assembler_name): New function.
	(make_name): New function.
	(make_dispatcher_decl): New function.
	(is_function_default_version): New function.
	(ix86_get_function_versions_dispatcher): New function.
	(make_attribute): New function.
	(make_resolver_func): New function.
	(ix86_generate_version_dispatcher_body): New function.
	(fold_builtin_cpu): Return integer for cpu builtins.
	(TARGET_MANGLE_DECL_ASSEMBLER_NAME): New macro.
	(TARGET_COMPARE_VERSION_PRIORITY): New macro.
	(TARGET_GENERATE_VERSION_DISPATCHER_BODY): New macro.
	(TARGET_GET_FUNCTION_VERSIONS_DISPATCHER): New macro.
	(TARGET_OPTION_FUNCTION_VERSIONS): New macro.

	* class.c (add_method): Change assembler names of function versions.
	(mark_versions_used): New static function.
	(resolve_address_of_overloaded_function): Create dispatcher decl and
	return address of dispatcher instead.
	* decl.c (decls_match): Make decls unmatched for versioned
	functions.
	(duplicate_decls): Remove ambiguity for versioned functions.
	Delete versioned function data for merged decls. 
	* decl2.c (check_classfn): Check attributes of versioned functions
	for match.
	* call.c (get_function_version_dispatcher): New function.
	(mark_versions_used): New static function.
	(build_over_call): Make calls to multiversioned functions
	to call the dispatcher.
	(joust): For calls to multi-versioned functions, make the most
	specialized function version win.

	* testsuite/g++.dg/mv1.C: New test.
	* testsuite/g++.dg/mv2.C: New test.
	* testsuite/g++.dg/mv3.C: New test.
	* testsuite/g++.dg/mv4.C: New test.
	* testsuite/g++.dg/mv5.C: New test.
	* testsuite/g++.dg/mv6.C: New test.

From-SVN: r193204
This commit is contained in:
Sriraman Tallam 2012-11-06 02:35:17 +00:00 committed by Sriraman Tallam
parent 9dddaecf2f
commit 3649b9b791
21 changed files with 2011 additions and 19 deletions

View File

@ -1,3 +1,60 @@
2012-11-05 Sriraman Tallam <tmsriram@google.com>
* doc/tm.texi.in (TARGET_OPTION_FUNCTION_VERSIONS): New hook
description.
* (TARGET_COMPARE_VERSION_PRIORITY): New hook description.
* (TARGET_GET_FUNCTION_VERSIONS_DISPATCHER): New hook description.
* (TARGET_GENERATE_VERSION_DISPATCHER_BODY): New hook description.
* doc/tm.texi: Regenerate.
* target.def (compare_version_priority): New target hook.
* (generate_version_dispatcher_body): New target hook.
* (get_function_versions_dispatcher): New target hook.
* (function_versions): New target hook.
* cgraph.c (cgraph_fnver_htab): New htab.
(cgraph_fn_ver_htab_hash): New function.
(cgraph_fn_ver_htab_eq): New function.
(version_info_node): New pointer.
(insert_new_cgraph_node_version): New function.
(get_cgraph_node_version): New function.
(delete_function_version): New function.
(record_function_versions): New function.
* cgraph.h (cgraph_node): New bitfield dispatcher_function.
(cgraph_function_version_info): New struct.
(get_cgraph_node_version): New function.
(insert_new_cgraph_node_version): New function.
(record_function_versions): New function.
(delete_function_version): New function.
(init_lowered_empty_function): Expose function.
* tree.h (DECL_FUNCTION_VERSIONED): New macro.
(tree_function_decl): New bit-field versioned_function.
* cgraphunit.c (cgraph_analyze_function): Generate body of multiversion
function dispatcher.
(cgraph_analyze_functions): Analyze dispatcher function.
(init_lowered_empty_function): Make non-static. New parameter in_ssa.
(assemble_thunk): Add parameter to call to init_lowered_empty_function.
* config/i386/i386.c (add_condition_to_bb): New function.
(get_builtin_code_for_version): New function.
(ix86_compare_version_priority): New function.
(feature_compare): New function.
(dispatch_function_versions): New function.
(ix86_function_versions): New function.
(attr_strcmp): New function.
(ix86_mangle_function_version_assembler_name): New function.
(ix86_mangle_decl_assembler_name): New function.
(make_name): New function.
(make_dispatcher_decl): New function.
(is_function_default_version): New function.
(ix86_get_function_versions_dispatcher): New function.
(make_attribute): New function.
(make_resolver_func): New function.
(ix86_generate_version_dispatcher_body): New function.
(fold_builtin_cpu): Return integer for cpu builtins.
(TARGET_MANGLE_DECL_ASSEMBLER_NAME): New macro.
(TARGET_COMPARE_VERSION_PRIORITY): New macro.
(TARGET_GENERATE_VERSION_DISPATCHER_BODY): New macro.
(TARGET_GET_FUNCTION_VERSIONS_DISPATCHER): New macro.
(TARGET_OPTION_FUNCTION_VERSIONS): New macro.
2012-11-05 Joern Rennecke <joern.rennecke@embecosm.com>
* recog.c (extract_insn): Enabled alternative defaults to 1.

View File

@ -132,6 +132,144 @@ static GTY(()) struct cgraph_edge *free_edges;
/* Did procss_same_body_aliases run? */
bool same_body_aliases_done;
/* Map a cgraph_node to cgraph_function_version_info using this htab.
The cgraph_function_version_info has a THIS_NODE field that is the
corresponding cgraph_node.. */
static htab_t GTY((param_is (struct cgraph_function_version_info *)))
cgraph_fnver_htab = NULL;
/* Hash function for cgraph_fnver_htab. */
static hashval_t
cgraph_fnver_htab_hash (const void *ptr)
{
int uid = ((const struct cgraph_function_version_info *)ptr)->this_node->uid;
return (hashval_t)(uid);
}
/* eq function for cgraph_fnver_htab. */
static int
cgraph_fnver_htab_eq (const void *p1, const void *p2)
{
const struct cgraph_function_version_info *n1
= (const struct cgraph_function_version_info *)p1;
const struct cgraph_function_version_info *n2
= (const struct cgraph_function_version_info *)p2;
return n1->this_node->uid == n2->this_node->uid;
}
/* Mark as GC root all allocated nodes. */
static GTY(()) struct cgraph_function_version_info *
version_info_node = NULL;
/* Get the cgraph_function_version_info node corresponding to node. */
struct cgraph_function_version_info *
get_cgraph_node_version (struct cgraph_node *node)
{
struct cgraph_function_version_info *ret;
struct cgraph_function_version_info key;
key.this_node = node;
if (cgraph_fnver_htab == NULL)
return NULL;
ret = (struct cgraph_function_version_info *)
htab_find (cgraph_fnver_htab, &key);
return ret;
}
/* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
corresponding to cgraph_node NODE. */
struct cgraph_function_version_info *
insert_new_cgraph_node_version (struct cgraph_node *node)
{
void **slot;
version_info_node = NULL;
version_info_node = ggc_alloc_cleared_cgraph_function_version_info ();
version_info_node->this_node = node;
if (cgraph_fnver_htab == NULL)
cgraph_fnver_htab = htab_create_ggc (2, cgraph_fnver_htab_hash,
cgraph_fnver_htab_eq, NULL);
slot = htab_find_slot (cgraph_fnver_htab, version_info_node, INSERT);
gcc_assert (slot != NULL);
*slot = version_info_node;
return version_info_node;
}
/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
DECL is a duplicate declaration. */
void
delete_function_version (tree decl)
{
struct cgraph_node *decl_node = cgraph_get_create_node (decl);
struct cgraph_function_version_info *decl_v = NULL;
if (decl_node == NULL)
return;
decl_v = get_cgraph_node_version (decl_node);
if (decl_v == NULL)
return;
if (decl_v->prev != NULL)
decl_v->prev->next = decl_v->next;
if (decl_v->next != NULL)
decl_v->next->prev = decl_v->prev;
if (cgraph_fnver_htab != NULL)
htab_remove_elt (cgraph_fnver_htab, decl_v);
cgraph_remove_node (decl_node);
}
/* Record that DECL1 and DECL2 are semantically identical function
versions. */
void
record_function_versions (tree decl1, tree decl2)
{
struct cgraph_node *decl1_node = cgraph_get_create_node (decl1);
struct cgraph_node *decl2_node = cgraph_get_create_node (decl2);
struct cgraph_function_version_info *decl1_v = NULL;
struct cgraph_function_version_info *decl2_v = NULL;
struct cgraph_function_version_info *before;
struct cgraph_function_version_info *after;
gcc_assert (decl1_node != NULL && decl2_node != NULL);
decl1_v = get_cgraph_node_version (decl1_node);
decl2_v = get_cgraph_node_version (decl2_node);
if (decl1_v != NULL && decl2_v != NULL)
return;
if (decl1_v == NULL)
decl1_v = insert_new_cgraph_node_version (decl1_node);
if (decl2_v == NULL)
decl2_v = insert_new_cgraph_node_version (decl2_node);
/* Chain decl2_v and decl1_v. All semantically identical versions
will be chained together. */
before = decl1_v;
after = decl2_v;
while (before->next != NULL)
before = before->next;
while (after->prev != NULL)
after= after->prev;
before->next = after;
after->prev = before;
}
/* Macros to access the next item in the list of free cgraph nodes and
edges. */
#define NEXT_FREE_NODE(NODE) cgraph ((NODE)->symbol.next)

View File

@ -280,6 +280,8 @@ struct GTY(()) cgraph_node {
/* ?? We should be able to remove this. We have enough bits in
cgraph to calculate it. */
unsigned tm_clone : 1;
/* True if this decl is a dispatcher for function versions. */
unsigned dispatcher_function : 1;
};
DEF_VEC_P(symtab_node);
@ -292,6 +294,47 @@ DEF_VEC_P(cgraph_node_ptr);
DEF_VEC_ALLOC_P(cgraph_node_ptr,heap);
DEF_VEC_ALLOC_P(cgraph_node_ptr,gc);
/* Function Multiversioning info. */
struct GTY(()) cgraph_function_version_info {
/* The cgraph_node for which the function version info is stored. */
struct cgraph_node *this_node;
/* Chains all the semantically identical function versions. The
first function in this chain is the version_info node of the
default function. */
struct cgraph_function_version_info *prev;
/* If this version node corresponds to a dispatcher for function
versions, this points to the version info node of the default
function, the first node in the chain. */
struct cgraph_function_version_info *next;
/* If this node corresponds to a function version, this points
to the dispatcher function decl, which is the function that must
be called to execute the right function version at run-time.
If this cgraph node is a dispatcher (if dispatcher_function is
true, in the cgraph_node struct) for function versions, this
points to resolver function, which holds the function body of the
dispatcher. The dispatcher decl is an alias to the resolver
function decl. */
tree dispatcher_resolver;
};
/* Get the cgraph_function_version_info node corresponding to node. */
struct cgraph_function_version_info *
get_cgraph_node_version (struct cgraph_node *node);
/* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
corresponding to cgraph_node NODE. */
struct cgraph_function_version_info *
insert_new_cgraph_node_version (struct cgraph_node *node);
/* Record that DECL1 and DECL2 are semantically identical function
versions. */
void record_function_versions (tree decl1, tree decl2);
/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
DECL is a duplicate declaration. */
void delete_function_version (tree decl);
/* A cgraph node set is a collection of cgraph nodes. A cgraph node
can appear in multiple sets. */
struct cgraph_node_set_def
@ -638,6 +681,9 @@ void init_cgraph (void);
bool cgraph_process_new_functions (void);
void cgraph_process_same_body_aliases (void);
void fixup_same_cpp_alias_visibility (symtab_node node, symtab_node target, tree alias);
/* Initialize datastructures so DECL is a function in lowered gimple form.
IN_SSA is true if the gimple is in SSA. */
basic_block init_lowered_empty_function (tree decl, bool in_ssa);
/* In cgraphclones.c */

View File

@ -630,6 +630,21 @@ cgraph_analyze_function (struct cgraph_node *node)
cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
NULL, 0, CGRAPH_FREQ_BASE);
}
else if (node->dispatcher_function)
{
/* Generate the dispatcher body of multi-versioned functions. */
struct cgraph_function_version_info *dispatcher_version_info
= get_cgraph_node_version (node);
if (dispatcher_version_info != NULL
&& (dispatcher_version_info->dispatcher_resolver
== NULL_TREE))
{
tree resolver = NULL_TREE;
gcc_assert (targetm.generate_version_dispatcher_body);
resolver = targetm.generate_version_dispatcher_body (node);
gcc_assert (resolver != NULL_TREE);
}
}
else
{
push_cfun (DECL_STRUCT_FUNCTION (decl));
@ -938,7 +953,8 @@ cgraph_analyze_functions (void)
See gcc.c-torture/compile/20011119-1.c */
if (!DECL_STRUCT_FUNCTION (decl)
&& (!cnode->alias || !cnode->thunk.alias)
&& !cnode->thunk.thunk_p)
&& !cnode->thunk.thunk_p
&& !cnode->dispatcher_function)
{
cgraph_reset_node (cnode);
cnode->local.redefined_extern_inline = true;
@ -1219,13 +1235,13 @@ mark_functions_to_output (void)
}
/* DECL is FUNCTION_DECL. Initialize datastructures so DECL is a function
in lowered gimple form.
in lowered gimple form. IN_SSA is true if the gimple is in SSA.
Set current_function_decl and cfun to newly constructed empty function body.
return basic block in the function body. */
static basic_block
init_lowered_empty_function (tree decl)
basic_block
init_lowered_empty_function (tree decl, bool in_ssa)
{
basic_block bb;
@ -1233,9 +1249,14 @@ init_lowered_empty_function (tree decl)
allocate_struct_function (decl, false);
gimple_register_cfg_hooks ();
init_empty_tree_cfg ();
init_tree_ssa (cfun);
init_ssa_operands (cfun);
cfun->gimple_df->in_ssa_p = true;
if (in_ssa)
{
init_tree_ssa (cfun);
init_ssa_operands (cfun);
cfun->gimple_df->in_ssa_p = true;
}
DECL_INITIAL (decl) = make_node (BLOCK);
DECL_SAVED_TREE (decl) = error_mark_node;
@ -1442,7 +1463,7 @@ assemble_thunk (struct cgraph_node *node)
else
resdecl = DECL_RESULT (thunk_fndecl);
bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
bsi = gsi_start_bb (bb);

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,22 @@
2012-11-05 Sriraman Tallam <tmsriram@google.com>
* class.c (add_method): Change assembler names of function versions.
(mark_versions_used): New static function.
(resolve_address_of_overloaded_function): Create dispatcher decl and
return address of dispatcher instead.
* decl.c (decls_match): Make decls unmatched for versioned
functions.
(duplicate_decls): Remove ambiguity for versioned functions.
Delete versioned function data for merged decls.
* decl2.c (check_classfn): Check attributes of versioned functions
for match.
* call.c (get_function_version_dispatcher): New function.
(mark_versions_used): New static function.
(build_over_call): Make calls to multiversioned functions
to call the dispatcher.
(joust): For calls to multi-versioned functions, make the most
specialized function version win.
2012-10-31 Lawrence Crowl <crowl@google.com>
* decl2.c (var_finalized_p): Rename varpool_node to

View File

@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "langhooks.h"
#include "c-family/c-objc.h"
#include "timevar.h"
#include "cgraph.h"
/* The various kinds of conversion. */
@ -6514,6 +6515,63 @@ magic_varargs_p (tree fn)
return false;
}
/* Returns the decl of the dispatcher function if FN is a function version. */
static tree
get_function_version_dispatcher (tree fn)
{
tree dispatcher_decl = NULL;
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL
&& DECL_FUNCTION_VERSIONED (fn));
gcc_assert (targetm.get_function_versions_dispatcher);
dispatcher_decl = targetm.get_function_versions_dispatcher (fn);
if (dispatcher_decl == NULL)
{
error_at (input_location, "Call to multiversioned function"
" without a default is not allowed");
return NULL;
}
retrofit_lang_decl (dispatcher_decl);
gcc_assert (dispatcher_decl != NULL);
return dispatcher_decl;
}
/* fn is a function version dispatcher that is marked used. Mark all the
semantically identical function versions it will dispatch as used. */
static void
mark_versions_used (tree fn)
{
struct cgraph_node *node;
struct cgraph_function_version_info *node_v;
struct cgraph_function_version_info *it_v;
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
node = cgraph_get_node (fn);
if (node == NULL)
return;
gcc_assert (node->dispatcher_function);
node_v = get_cgraph_node_version (node);
if (node_v == NULL)
return;
/* All semantically identical versions are chained. Traverse and mark each
one of them as used. */
it_v = node_v->next;
while (it_v != NULL)
{
mark_used (it_v->this_node->symbol.decl);
it_v = it_v->next;
}
}
/* Subroutine of the various build_*_call functions. Overload resolution
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a
@ -6963,6 +7021,22 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
return fold_convert (void_type_node, argarray[0]);
/* FIXME handle trivial default constructor, too. */
/* For calls to a multi-versioned function, overload resolution
returns the function with the highest target priority, that is,
the version that will checked for dispatching first. If this
version is inlinable, a direct call to this version can be made
otherwise the call should go through the dispatcher. */
if (DECL_FUNCTION_VERSIONED (fn)
&& !targetm.target_option.can_inline_p (current_function_decl, fn))
{
fn = get_function_version_dispatcher (fn);
if (fn == NULL)
return NULL;
if (!already_used)
mark_versions_used (fn);
}
if (!already_used)
mark_used (fn);
@ -8481,6 +8555,38 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
}
}
/* For candidates of a multi-versioned function, make the version with
the highest priority win. This version will be checked for dispatching
first. If this version can be inlined into the caller, the front-end
will simply make a direct call to this function. */
if (TREE_CODE (cand1->fn) == FUNCTION_DECL
&& DECL_FUNCTION_VERSIONED (cand1->fn)
&& TREE_CODE (cand2->fn) == FUNCTION_DECL
&& DECL_FUNCTION_VERSIONED (cand2->fn))
{
tree f1 = TREE_TYPE (cand1->fn);
tree f2 = TREE_TYPE (cand2->fn);
tree p1 = TYPE_ARG_TYPES (f1);
tree p2 = TYPE_ARG_TYPES (f2);
/* Check if cand1->fn and cand2->fn are versions of the same function. It
is possible that cand1->fn and cand2->fn are function versions but of
different functions. Check types to see if they are versions of the same
function. */
if (compparms (p1, p2)
&& same_type_p (TREE_TYPE (f1), TREE_TYPE (f2)))
{
/* Always make the version with the higher priority, more
specialized, win. */
gcc_assert (targetm.compare_version_priority);
if (targetm.compare_version_priority (cand1->fn, cand2->fn) >= 0)
return 1;
else
return -1;
}
}
/* If the two function declarations represent the same function (this can
happen with declarations in multiple scopes and arg-dependent lookup),
arbitrarily choose one. But first make sure the default args we're

View File

@ -1087,6 +1087,35 @@ add_method (tree type, tree method, tree using_decl)
|| same_type_p (TREE_TYPE (fn_type),
TREE_TYPE (method_type))))
{
/* For function versions, their parms and types match
but they are not duplicates. Record function versions
as and when they are found. extern "C" functions are
not treated as versions. */
if (TREE_CODE (fn) == FUNCTION_DECL
&& TREE_CODE (method) == FUNCTION_DECL
&& !DECL_EXTERN_C_P (fn)
&& !DECL_EXTERN_C_P (method)
&& (DECL_FUNCTION_SPECIFIC_TARGET (fn)
|| DECL_FUNCTION_SPECIFIC_TARGET (method))
&& targetm.target_option.function_versions (fn, method))
{
/* Mark functions as versions if necessary. Modify the mangled
decl name if necessary. */
if (!DECL_FUNCTION_VERSIONED (fn))
{
DECL_FUNCTION_VERSIONED (fn) = 1;
if (DECL_ASSEMBLER_NAME_SET_P (fn))
mangle_decl (fn);
}
if (!DECL_FUNCTION_VERSIONED (method))
{
DECL_FUNCTION_VERSIONED (method) = 1;
if (DECL_ASSEMBLER_NAME_SET_P (method))
mangle_decl (method);
}
record_function_versions (fn, method);
continue;
}
if (DECL_INHERITED_CTOR_BASE (method))
{
if (DECL_INHERITED_CTOR_BASE (fn))
@ -6951,6 +6980,38 @@ pop_lang_context (void)
{
current_lang_name = VEC_pop (tree, current_lang_base);
}
/* fn is a function version dispatcher that is marked used. Mark all the
semantically identical function versions it will dispatch as used. */
static void
mark_versions_used (tree fn)
{
struct cgraph_node *node;
struct cgraph_function_version_info *node_v;
struct cgraph_function_version_info *it_v;
gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
node = cgraph_get_node (fn);
if (node == NULL)
return;
gcc_assert (node->dispatcher_function);
node_v = get_cgraph_node_version (node);
if (node_v == NULL)
return;
/* All semantically identical versions are chained. Traverse and mark each
one of them as used. */
it_v = node_v->next;
while (it_v != NULL)
{
mark_used (it_v->this_node->symbol.decl);
it_v = it_v->next;
}
}
/* Type instantiation routines. */
@ -7162,12 +7223,26 @@ resolve_address_of_overloaded_function (tree target_type,
{
/* There were too many matches. First check if they're all
the same function. */
tree match;
tree match = NULL_TREE;
fn = TREE_PURPOSE (matches);
for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
if (!decls_match (fn, TREE_PURPOSE (match)))
break;
/* For multi-versioned functions, more than one match is just fine.
Call decls_match to make sure they are different because they are
versioned. */
if (DECL_FUNCTION_VERSIONED (fn))
{
for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
if (!DECL_FUNCTION_VERSIONED (TREE_PURPOSE (match))
|| decls_match (fn, TREE_PURPOSE (match)))
break;
}
else
{
for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
if (!decls_match (fn, TREE_PURPOSE (match)))
break;
}
if (match)
{
@ -7208,6 +7283,28 @@ resolve_address_of_overloaded_function (tree target_type,
}
}
/* If a pointer to a function that is multi-versioned is requested, the
pointer to the dispatcher function is returned instead. This works
well because indirectly calling the function will dispatch the right
function version at run-time. */
if (DECL_FUNCTION_VERSIONED (fn))
{
tree dispatcher_decl = NULL;
gcc_assert (targetm.get_function_versions_dispatcher);
dispatcher_decl = targetm.get_function_versions_dispatcher (fn);
if (!dispatcher_decl)
{
error_at (input_location, "Pointer to a multiversioned function"
" without a default is not allowed");
return error_mark_node;
}
retrofit_lang_decl (dispatcher_decl);
fn = dispatcher_decl;
/* Mark all the versions corresponding to the dispatcher as used. */
if (!(flags & tf_conv))
mark_versions_used (fn);
}
/* If we're doing overload resolution purely for the purpose of
determining conversion sequences, we should not consider the
function used. If this conversion sequence is selected, the

View File

@ -53,6 +53,7 @@ along with GCC; see the file COPYING3. If not see
#include "pointer-set.h"
#include "splay-tree.h"
#include "plugin.h"
#include "cgraph.h"
/* Possible cases of bad specifiers type used by bad_specifiers. */
enum bad_spec_place {
@ -981,6 +982,36 @@ decls_match (tree newdecl, tree olddecl)
if (t1 != t2)
return 0;
/* The decls dont match if they correspond to two different versions
of the same function. Disallow extern "C" functions to be
versions for now. */
if (compparms (p1, p2)
&& same_type_p (TREE_TYPE (f1), TREE_TYPE (f2))
&& !DECL_EXTERN_C_P (newdecl)
&& !DECL_EXTERN_C_P (olddecl)
&& targetm.target_option.function_versions (newdecl, olddecl))
{
/* Mark functions as versions if necessary. Modify the mangled decl
name if necessary. */
if (DECL_FUNCTION_VERSIONED (newdecl)
&& DECL_FUNCTION_VERSIONED (olddecl))
return 0;
if (!DECL_FUNCTION_VERSIONED (newdecl))
{
DECL_FUNCTION_VERSIONED (newdecl) = 1;
if (DECL_ASSEMBLER_NAME_SET_P (newdecl))
mangle_decl (newdecl);
}
if (!DECL_FUNCTION_VERSIONED (olddecl))
{
DECL_FUNCTION_VERSIONED (olddecl) = 1;
if (DECL_ASSEMBLER_NAME_SET_P (olddecl))
mangle_decl (olddecl);
}
record_function_versions (olddecl, newdecl);
return 0;
}
if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)
&& ! (DECL_EXTERN_C_P (newdecl)
&& DECL_EXTERN_C_P (olddecl)))
@ -1499,7 +1530,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
error ("previous declaration %q+#D here", olddecl);
return NULL_TREE;
}
else if (compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
/* For function versions, params and types match, but they
are not ambiguous. */
else if ((!DECL_FUNCTION_VERSIONED (newdecl)
&& !DECL_FUNCTION_VERSIONED (olddecl))
&& compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
{
error ("new declaration %q#D", newdecl);
@ -2272,6 +2307,15 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
else if (DECL_PRESERVE_P (newdecl))
DECL_PRESERVE_P (olddecl) = 1;
/* If the olddecl is a version, so is the newdecl. */
if (TREE_CODE (newdecl) == FUNCTION_DECL
&& DECL_FUNCTION_VERSIONED (olddecl))
{
DECL_FUNCTION_VERSIONED (newdecl) = 1;
/* newdecl will be purged and is no longer a version. */
delete_function_version (newdecl);
}
if (TREE_CODE (newdecl) == FUNCTION_DECL)
{
int function_size;

View File

@ -674,9 +674,13 @@ check_classfn (tree ctype, tree function, tree template_parms)
if (is_template != (TREE_CODE (fndecl) == TEMPLATE_DECL))
continue;
/* While finding a match, same types and params are not enough
if the function is versioned. Also check version ("target")
attributes. */
if (same_type_p (TREE_TYPE (TREE_TYPE (function)),
TREE_TYPE (TREE_TYPE (fndecl)))
&& compparms (p1, p2)
&& !targetm.target_option.function_versions (function, fndecl)
&& (!is_template
|| comp_template_parms (template_parms,
DECL_TEMPLATE_PARMS (fndecl)))

View File

@ -9929,6 +9929,14 @@ changed via the optimize attribute or pragma, see
@code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
@end deftypefn
@deftypefn {Target Hook} bool TARGET_OPTION_FUNCTION_VERSIONS (tree @var{decl1}, tree @var{decl2})
This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are
versions of the same function. @var{DECL1} and @var{DECL2} are function
versions if and only if they have the same function signature and
different target specific attributes, that is, they are compiled for
different target machines.
@end deftypefn
@deftypefn {Target Hook} bool TARGET_CAN_INLINE_P (tree @var{caller}, tree @var{callee})
This target hook returns @code{false} if the @var{caller} function
cannot inline @var{callee}, based on target specific information. By
@ -10952,6 +10960,29 @@ The result is another tree containing a simplified expression for the
call's result. If @var{ignore} is true the value will be ignored.
@end deftypefn
@deftypefn {Target Hook} int TARGET_COMPARE_VERSION_PRIORITY (tree @var{decl1}, tree @var{decl2})
This hook is used to compare the target attributes in two functions to
determine which function's features get higher priority. This is used
during function multi-versioning to figure out the order in which two
versions must be dispatched. A function version with a higher priority
is checked for dispatching earlier. @var{decl1} and @var{decl2} are
the two function decls that will be compared.
@end deftypefn
@deftypefn {Target Hook} tree TARGET_GET_FUNCTION_VERSIONS_DISPATCHER (void *@var{decl})
This hook is used to get the dispatcher function for a set of function
versions. The dispatcher function is called to invoke the right function
version at run-time. @var{decl} is one version from a set of semantically
identical versions.
@end deftypefn
@deftypefn {Target Hook} tree TARGET_GENERATE_VERSION_DISPATCHER_BODY (void *@var{arg})
This hook is used to generate the dispatcher logic to invoke the right
function version at run-time for a given set of function versions.
@var{arg} points to the callgraph node of the dispatcher function whose
body must be generated.
@end deftypefn
@deftypefn {Target Hook} {const char *} TARGET_INVALID_WITHIN_DOLOOP (const_rtx @var{insn})
Take an instruction in @var{insn} and return NULL if it is valid within a

View File

@ -9790,6 +9790,14 @@ changed via the optimize attribute or pragma, see
@code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
@end deftypefn
@hook TARGET_OPTION_FUNCTION_VERSIONS
This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are
versions of the same function. @var{DECL1} and @var{DECL2} are function
versions if and only if they have the same function signature and
different target specific attributes, that is, they are compiled for
different target machines.
@end deftypefn
@hook TARGET_CAN_INLINE_P
This target hook returns @code{false} if the @var{caller} function
cannot inline @var{callee}, based on target specific information. By
@ -10798,6 +10806,29 @@ The result is another tree containing a simplified expression for the
call's result. If @var{ignore} is true the value will be ignored.
@end deftypefn
@hook TARGET_COMPARE_VERSION_PRIORITY
This hook is used to compare the target attributes in two functions to
determine which function's features get higher priority. This is used
during function multi-versioning to figure out the order in which two
versions must be dispatched. A function version with a higher priority
is checked for dispatching earlier. @var{decl1} and @var{decl2} are
the two function decls that will be compared.
@end deftypefn
@hook TARGET_GET_FUNCTION_VERSIONS_DISPATCHER
This hook is used to get the dispatcher function for a set of function
versions. The dispatcher function is called to invoke the right function
version at run-time. @var{decl} is one version from a set of semantically
identical versions.
@end deftypefn
@hook TARGET_GENERATE_VERSION_DISPATCHER_BODY
This hook is used to generate the dispatcher logic to invoke the right
function version at run-time for a given set of function versions.
@var{arg} points to the callgraph node of the dispatcher function whose
body must be generated.
@end deftypefn
@hook TARGET_INVALID_WITHIN_DOLOOP
Take an instruction in @var{insn} and return NULL if it is valid within a

View File

@ -1298,6 +1298,37 @@ DEFHOOK
tree, (tree fndecl, int n_args, tree *argp, bool ignore),
hook_tree_tree_int_treep_bool_null)
/* Target hook is used to compare the target attributes in two functions to
determine which function's features get higher priority. This is used
during function multi-versioning to figure out the order in which two
versions must be dispatched. A function version with a higher priority
is checked for dispatching earlier. DECL1 and DECL2 are
the two function decls that will be compared. It returns positive value
if DECL1 is higher priority, negative value if DECL2 is higher priority
and 0 if they are the same. */
DEFHOOK
(compare_version_priority,
"",
int, (tree decl1, tree decl2), NULL)
/* Target hook is used to generate the dispatcher logic to invoke the right
function version at run-time for a given set of function versions.
ARG points to the callgraph node of the dispatcher function whose body
must be generated. */
DEFHOOK
(generate_version_dispatcher_body,
"",
tree, (void *arg), NULL)
/* Target hook is used to get the dispatcher function for a set of function
versions. The dispatcher function is called to invoke the right function
version at run-time. DECL is one version from a set of semantically
identical versions. */
DEFHOOK
(get_function_versions_dispatcher,
"",
tree, (void *decl), NULL)
/* Returns a code for a target-specific builtin that implements
reciprocal of the function, or NULL_TREE if not available. */
DEFHOOK
@ -2774,6 +2805,16 @@ DEFHOOK
void, (void),
hook_void_void)
/* This function returns true if DECL1 and DECL2 are versions of the same
function. DECL1 and DECL2 are function versions if and only if they
have the same function signature and different target specific attributes,
that is, they are compiled for different target machines. */
DEFHOOK
(function_versions,
"",
bool, (tree decl1, tree decl2),
hook_bool_tree_tree_false)
/* Function to determine if one function can inline another function. */
#undef HOOK_PREFIX
#define HOOK_PREFIX "TARGET_"

View File

@ -1,3 +1,12 @@
2012-11-05 Sriraman Tallam <tmsriram@google.com>
* testsuite/g++.dg/mv1.C: New test.
* testsuite/g++.dg/mv2.C: New test.
* testsuite/g++.dg/mv3.C: New test.
* testsuite/g++.dg/mv4.C: New test.
* testsuite/g++.dg/mv5.C: New test.
* testsuite/g++.dg/mv6.C: New test.
2012-11-05 Hans-Peter Nilsson <hp@axis.com>
PR testsuite/55186

130
gcc/testsuite/g++.dg/mv1.C Normal file
View File

@ -0,0 +1,130 @@
/* Test case to check if Multiversioning works. */
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
/* { dg-require-ifunc "" } */
/* { dg-options "-O2 -fPIC -mno-avx -mno-popcnt" } */
#include <assert.h>
/* Default version. */
int foo ();
/* The other versions of foo. Mix up the ordering and
check if the dispatching does it in the order of priority. */
/* Check combination of target attributes. */
int foo () __attribute__ ((target("arch=corei7,popcnt")));
/* The target operands in this declaration and the definition are re-ordered.
This should still work. */
int foo () __attribute__ ((target("ssse3,avx2")));
/* Check for all target attributes for which dispatchers are available. */
/* Check arch= */
int foo () __attribute__((target("arch=core2")));
int foo () __attribute__((target("arch=corei7")));
int foo () __attribute__((target("arch=atom")));
/* Check ISAs */
int foo () __attribute__((target("avx")));
int foo () __attribute__ ((target("arch=core2,sse4.2")));
/* Check more arch=. */
int foo () __attribute__((target("arch=amdfam10")));
int foo () __attribute__((target("arch=bdver1")));
int foo () __attribute__((target("arch=bdver2")));
int (*p)() = &foo;
int main ()
{
int val = foo ();
assert (val == (*p)());
/* Check in the exact same order in which the dispatching
is expected to happen. */
if (__builtin_cpu_is ("bdver1"))
assert (val == 1);
else if (__builtin_cpu_is ("bdver2"))
assert (val == 2);
else if (__builtin_cpu_supports ("avx2")
&& __builtin_cpu_supports ("ssse3"))
assert (val == 3);
else if (__builtin_cpu_supports ("avx"))
assert (val == 4);
else if (__builtin_cpu_is ("corei7")
&& __builtin_cpu_supports ("popcnt"))
assert (val == 5);
else if (__builtin_cpu_is ("corei7"))
assert (val == 6);
else if (__builtin_cpu_is ("amdfam10h"))
assert (val == 7);
else if (__builtin_cpu_is ("core2")
&& __builtin_cpu_supports ("sse4.2"))
assert (val == 8);
else if (__builtin_cpu_is ("core2"))
assert (val == 9);
else if (__builtin_cpu_is ("atom"))
assert (val == 10);
else
assert (val == 0);
return 0;
}
int foo ()
{
return 0;
}
int __attribute__ ((target("arch=corei7,popcnt")))
foo ()
{
return 5;
}
int __attribute__ ((target("avx2,ssse3")))
foo ()
{
return 3;
}
int __attribute__ ((target("arch=core2")))
foo ()
{
return 9;
}
int __attribute__ ((target("arch=corei7")))
foo ()
{
return 6;
}
int __attribute__ ((target("arch=atom")))
foo ()
{
return 10;
}
int __attribute__ ((target("avx")))
foo ()
{
return 4;
}
int __attribute__ ((target("arch=core2,sse4.2")))
foo ()
{
return 8;
}
int __attribute__ ((target("arch=amdfam10")))
foo ()
{
return 7;
}
int __attribute__ ((target("arch=bdver1")))
foo ()
{
return 1;
}
int __attribute__ ((target("arch=bdver2")))
foo ()
{
return 2;
}

118
gcc/testsuite/g++.dg/mv2.C Normal file
View File

@ -0,0 +1,118 @@
/* Test case to check if Multiversioning chooses the correct
dispatching order when versions are for various ISAs. */
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
/* { dg-require-ifunc "" } */
/* { dg-options "-O2 -mno-sse -mno-mmx -mno-popcnt -mno-avx" } */
#include <assert.h>
/* Default version. */
int foo ();
/* The dispatch checks should be in the exact reverse order of the
declarations below. */
int foo () __attribute__ ((target ("mmx")));
int foo () __attribute__ ((target ("sse")));
int foo () __attribute__ ((target ("sse2")));
int foo () __attribute__ ((target ("sse3")));
int foo () __attribute__ ((target ("ssse3")));
int foo () __attribute__ ((target ("sse4.1")));
int foo () __attribute__ ((target ("sse4.2")));
int foo () __attribute__ ((target ("popcnt")));
int foo () __attribute__ ((target ("avx")));
int foo () __attribute__ ((target ("avx2")));
int main ()
{
int val = foo ();
if (__builtin_cpu_supports ("avx2"))
assert (val == 1);
else if (__builtin_cpu_supports ("avx"))
assert (val == 2);
else if (__builtin_cpu_supports ("popcnt"))
assert (val == 3);
else if (__builtin_cpu_supports ("sse4.2"))
assert (val == 4);
else if (__builtin_cpu_supports ("sse4.1"))
assert (val == 5);
else if (__builtin_cpu_supports ("ssse3"))
assert (val == 6);
else if (__builtin_cpu_supports ("sse3"))
assert (val == 7);
else if (__builtin_cpu_supports ("sse2"))
assert (val == 8);
else if (__builtin_cpu_supports ("sse"))
assert (val == 9);
else if (__builtin_cpu_supports ("mmx"))
assert (val == 10);
else
assert (val == 0);
return 0;
}
int
foo ()
{
return 0;
}
int __attribute__ ((target("mmx")))
foo ()
{
return 10;
}
int __attribute__ ((target("sse")))
foo ()
{
return 9;
}
int __attribute__ ((target("sse2")))
foo ()
{
return 8;
}
int __attribute__ ((target("sse3")))
foo ()
{
return 7;
}
int __attribute__ ((target("ssse3")))
foo ()
{
return 6;
}
int __attribute__ ((target("sse4.1")))
foo ()
{
return 5;
}
int __attribute__ ((target("sse4.2")))
foo ()
{
return 4;
}
int __attribute__ ((target("popcnt")))
foo ()
{
return 3;
}
int __attribute__ ((target("avx")))
foo ()
{
return 2;
}
int __attribute__ ((target("avx2")))
foo ()
{
return 1;
}

View File

@ -0,0 +1,36 @@
/* Test case to check if a call to a multiversioned function
is replaced with a direct call to the particular version when
the most specialized version's target attributes match the
caller.
In this program, foo is multiversioned but there is no default
function. This is an error if the call has to go through a
dispatcher. However, the call to foo in bar can be replaced
with a direct call to the popcnt version of foo. Hence, this
test should pass. */
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 -mno-sse -mno-popcnt" } */
int __attribute__ ((target ("sse")))
foo ()
{
return 1;
}
int __attribute__ ((target ("popcnt")))
foo ()
{
return 0;
}
int __attribute__ ((target ("popcnt")))
bar ()
{
return foo ();
}
int main ()
{
return bar ();
}

View File

@ -0,0 +1,23 @@
/* Test case to check if the compiler generates an error message
when the default version of a multiversioned function is absent
and its pointer is taken. */
/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 -mno-sse -mno-popcnt" } */
int __attribute__ ((target ("sse")))
foo ()
{
return 1;
}
int __attribute__ ((target ("popcnt")))
foo ()
{
return 0;
}
int main ()
{
int (*p)() = &foo; /* { dg-error "Pointer to a multiversioned function without a default is not allowed" {} } */
return (*p)();
}

View File

@ -0,0 +1,24 @@
/* Test case to check if multiversioned functions are still generated if they are
marked comdat with inline keyword. */
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
/* { dg-options "-O2 -mno-popcnt" } */
/* Default version. */
inline int
foo ()
{
return 0;
}
inline int __attribute__ ((target ("popcnt")))
foo ()
{
return 0;
}
int main ()
{
return foo ();
}

View File

@ -0,0 +1,25 @@
/* Test to check if member version multiversioning works correctly. */
/* { dg-do run { target i?86-*-* x86_64-*-* } } */
class Foo
{
public:
/* Default version of foo. */
int foo ()
{
return 0;
}
/* corei7 version of foo. */
__attribute__ ((target("arch=corei7")))
int foo ()
{
return 0;
}
};
int main ()
{
Foo f;
return f.foo ();
}

View File

@ -3480,6 +3480,12 @@ extern VEC(tree, gc) **decl_debug_args_insert (tree);
#define DECL_FUNCTION_SPECIFIC_OPTIMIZATION(NODE) \
(FUNCTION_DECL_CHECK (NODE)->function_decl.function_specific_optimization)
/* In FUNCTION_DECL, this is set if this function has other versions generated
using "target" attributes. The default version is the one which does not
have any "target" attribute set. */
#define DECL_FUNCTION_VERSIONED(NODE)\
(FUNCTION_DECL_CHECK (NODE)->function_decl.versioned_function)
/* FUNCTION_DECL inherits from DECL_NON_COMMON because of the use of the
arguments/result/saved_tree fields by front ends. It was either inherit
FUNCTION_DECL from non_common, or inherit non_common from FUNCTION_DECL,
@ -3524,8 +3530,8 @@ struct GTY(()) tree_function_decl {
unsigned looping_const_or_pure_flag : 1;
unsigned has_debug_args_flag : 1;
unsigned tm_clone_flag : 1;
/* 1 bit left */
unsigned versioned_function : 1;
/* No bits left. */
};
/* The source language of the translation-unit. */