Function multiversioning is a mechanism that enables compiling multiple versions of a function, each specialized for different combinations of architecture extensions. Additionally, the compiler generates a resolver that the dynamic linker uses to detect architecture support and choose the appropriate version at runtime.
Function multiversioning relies on the indirect function extension to the ELF standard, and therefore Binutils version 2.20.1 or higher and GNU C Library version 2.11.1 are required to use this feature.
There are two versions of function multiversioning supported by GCC.
For targets supporting the target_version attribute (AArch64 and RISC-V),
when compiling for C or C++, a function version set can be defined by a
combination of function definitions with target_version and
target_clones attributes, across translation units.
For example:
// fmv.h:
int foo ();
int foo [[gnu::target_clones("sve", "sve2")]] ();
int foo [[gnu::target_version("dotprod;priority=1")]] ();
// fmv1.cc
#include "fmv.h"
int foo ()
{
// The default version of foo.
return 0;
}
// fmv2.cc:
#include "fmv.h"
int foo [[gnu::target_clones("sve", "sve2")]] ()
{
// foo versions for sve and sve2
return 1;
}
int foo [[gnu::target_version("dotprod")]] ()
{
// foo version for dotprod extension
return 2;
}
// main.cc
#include "fmv.h"
int main ()
{
int (*p)() = &foo;
assert ((*p) () == foo ());
return 0;
}
This example results in 4 versions of the foo function being generated, and a resolver which is used by the dynamic linker to choose the correct version.
For the AArch64 target GCC implements function multiversionsing, with the semantics and version strings as specified in the ARM C Language Extensions (ACLE).
For targets that support multiversioning with the target attribute
(x86) a multiversioned function can be defined with either multiple function
definitions with the target attribute (in C++) within a translation unit,
or a single definition with the target_clones attribute.
Here is an example.
__attribute__ ((target ("default")))
int foo ()
{
// The default version of foo.
return 0;
}
__attribute__ ((target ("sse4.2")))
int foo ()
{
// foo version for SSE4.2
return 1;
}
__attribute__ ((target ("arch=atom")))
int foo ()
{
// foo version for the Intel ATOM processor
return 2;
}
__attribute__ ((target ("arch=amdfam10")))
int foo ()
{
// foo version for the AMD Family 0x10 processors.
return 3;
}
int main ()
{
int (*p)() = &foo;
assert ((*p) () == foo ());
return 0;
}
In the above example, four versions of function foo are created. The first version of foo with the target attribute "default" is the default version. This version gets executed when no other target specific version qualifies for execution on a particular platform. A new version of foo is created by using the same function signature but with a different target string. Function foo is called or a pointer to it is taken just like a regular function. GCC takes care of doing the dispatching to call the right version at runtime. Refer to the GCC wiki on Function Multiversioning for more details.