diff options
author | tmsriram <tmsriram@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-11-06 02:35:17 +0000 |
---|---|---|
committer | tmsriram <tmsriram@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-11-06 02:35:17 +0000 |
commit | cc8ef84f080417899bd41b06d4c73126a20d41a6 (patch) | |
tree | 9f75f10875e97a88f4cbb481f476857a29e3bbb2 /gcc/target.def | |
parent | a52f36e7469048a09a02b625fdb3100a09d2a4af (diff) | |
download | gcc-cc8ef84f080417899bd41b06d4c73126a20d41a6.tar.gz |
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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@193204 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/target.def')
-rw-r--r-- | gcc/target.def | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/gcc/target.def b/gcc/target.def index 586522435a2..2801aea5a17 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -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_" |