===================================================================
@@ -640,7 +640,13 @@ cgraph_analyze_function (struct cgraph_node *node)
{
tree resolver = NULL_TREE;
gcc_assert (targetm.generate_version_dispatcher_body);
- resolver = targetm.generate_version_dispatcher_body (node);
+ /* When -fmultiversion-dynamic-dispatch is not turned on, the
+ dispatcher should be invoked optimally (once using ifunc support).
+ When -fmultiversion-dynamic-dispatch is on, the dispatcher should
+ be invoked every time a call to the multiversioned function is
+ made. */
+ resolver = targetm.generate_version_dispatcher_body (node,
+ flag_multiversion_dynamic_dispatch);
gcc_assert (resolver != NULL_TREE);
}
}
===================================================================
@@ -1555,6 +1555,10 @@ fmove-loop-invariants
Common Report Var(flag_move_loop_invariants) Init(1) Optimization
Move loop invariant computations out of loops
+fmultiversion-dynamic-dispatch
+Common Report Var(flag_multiversion_dynamic_dispatch) Init(0)
+Invoke the function version dispatcher for every multiversioned function call.
+
fdce
Common Var(flag_dce) Init(1) Optimization
Use the RTL dead code elimination pass
===================================================================
@@ -1323,11 +1323,12 @@ DEFHOOK
/* 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. */
+ must be generated. The version dispatcher is invoked on every call when
+ debug_mode is 1. */
DEFHOOK
(generate_version_dispatcher_body,
"",
- tree, (void *arg), NULL)
+ tree, (void *arg, int debug_mode), 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
===================================================================
@@ -10961,11 +10961,13 @@ version at run-time. @var{decl} is one version fro
identical versions.
@end deftypefn
-@deftypefn {Target Hook} tree TARGET_GENERATE_VERSION_DISPATCHER_BODY (void *@var{arg})
+@deftypefn {Target Hook} tree TARGET_GENERATE_VERSION_DISPATCHER_BODY (void *@var{arg}, int @var{debug_mode})
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.
+body must be generated. When @var{debug_mode} is 1, the dispatcher
+logic is invoked on every call. Otherwise, the dispatcher is invoked
+only at start up to minimize call overhead.
@end deftypefn
@deftypefn {Target Hook} {const char *} TARGET_INVALID_WITHIN_DOLOOP (const_rtx @var{insn})
===================================================================
@@ -10804,7 +10804,9 @@ identical versions.
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.
+body must be generated. When @var{debug_mode} is 1, the dispatcher
+logic is invoked on every call. Otherwise, the dispatcher is invoked
+only at start up to minimize call overhead.
@end deftypefn
@hook TARGET_INVALID_WITHIN_DOLOOP
===================================================================
@@ -178,6 +178,7 @@ in the following sections.
@xref{C++ Dialect Options,,Options Controlling C++ Dialect}.
@gccoptlist{-fabi-version=@var{n} -fno-access-control -fcheck-new @gol
-fconstexpr-depth=@var{n} -ffriend-injection @gol
+-fmultiversion-dynamic-dispatch @gol
-fno-elide-constructors @gol
-fno-enforce-eh-specs @gol
-ffor-scope -fno-for-scope -fno-gnu-keywords @gol
@@ -2023,6 +2024,13 @@ earlier releases.
This option is for compatibility, and may be removed in a future
release of G++.
+@item -fmultiversion-dynamic-dispatch
+@opindex fmultiversion-dynamic-dispatch
+When using function multiversioning, the function versions dispatcher is
+invoked only once at start-up using IFUNC support to minimize call overhead.
+This flag can be used to instead invoke the dispatcher every time a call to
+a multiversioned function is made.
+
@item -fno-elide-constructors
@opindex fno-elide-constructors
The C++ standard allows an implementation to omit creating a temporary
===================================================================
@@ -0,0 +1,4 @@
+/* Test case to check if mv1.C works with -fmultiversion-dynamic-dispatch additionally added. */
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-O2 -fPIC -fmultiversion-dynamic-dispatch" } */
+/* { dg-additional-sources "mv1.C" } */
===================================================================
@@ -0,0 +1,214 @@
+/* Test case to show how code coverage testing of of a multiversioned function
+ can be done using cpu mocks. */
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-O2 -fmultiversion-dynamic-dispatch" } */
+
+#include <assert.h>
+#include <string.h>
+
+/* Temporary code till the libgcc hooks for this are checked in. Override
+ __builtin_mock_cpu_* builtins to change the mock cpu. */
+const char *mock_cpu = NULL;
+int __builtin_mock_cpu_is (const char *cpu)
+{
+ if (strcmp (cpu, mock_cpu) == 0)
+ return 1;
+ return 0;
+}
+
+/* Temporary code till the libgcc hooks for this are checked in.
+ Only mock one ISA type. The libgcc hooks will allow mocking multiple
+ ISA features together, like popcnt and avx2. */
+const char *mock_isa = NULL;
+int __builtin_mock_cpu_supports (const char *isa)
+{
+ if (strcmp (isa, mock_isa) == 0)
+ return 1;
+ return 0;
+}
+/* End of temporary code. */
+
+
+/* Default version. */
+int foo () __attribute__ ((target ("default")));
+
+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 foo () __attribute__ ((target ("arch=corei7")));
+
+int main ()
+{
+ /* Using CPU mocks run each version of foo() when possible and
+ check the return value. */
+
+ /* Run Intel corei7 version if possible. Test if this
+ CPU can mock corei7. It should support SSE4.2 and
+ below, SSSE3 and MMX. */
+ if (__builtin_cpu_supports ("sse4.2")
+ && __builtin_cpu_supports ("ssse3")
+ && __builtin_cpu_supports ("mmx"))
+ {
+ mock_cpu = "corei7";
+ mock_isa = "";
+ assert (foo () == 11);
+ }
+
+ /* Run avx2 version if possible. */
+ if (__builtin_cpu_supports ("avx2"))
+ {
+ mock_cpu = "";
+ mock_isa = "avx2";
+ assert (foo () == 1);
+ }
+ /* Run avx version if possible. */
+ if (__builtin_cpu_supports ("avx"))
+ {
+ mock_cpu = "";
+ mock_isa = "avx";
+ assert (foo () == 2);
+ }
+ /* Run popcnt version if possible. */
+ if (__builtin_cpu_supports ("popcnt"))
+ {
+ mock_cpu = "";
+ mock_isa = "popcnt";
+ assert (foo () == 3);
+ }
+ /* Run sse4.2 version if possible. */
+ if (__builtin_cpu_supports ("sse4.2"))
+ {
+ mock_cpu = "";
+ mock_isa = "sse4.2";
+ assert (foo () == 4);
+ }
+ /* Run sse4.1 version if possible. */
+ if (__builtin_cpu_supports ("sse4.1"))
+ {
+ mock_cpu = "";
+ mock_isa = "sse4.1";
+ assert (foo () == 5);
+ }
+ /* Run ssse3 version if possible. */
+ if (__builtin_cpu_supports ("ssse3"))
+ {
+ mock_cpu = "";
+ mock_isa = "ssse3";
+ assert (foo () == 6);
+ }
+ /* Run sse3 version if possible. */
+ if (__builtin_cpu_supports ("sse3"))
+ {
+ mock_cpu = "";
+ mock_isa = "sse3";
+ assert (foo () == 7);
+ }
+ /* Run sse2 version if possible. */
+ if (__builtin_cpu_supports ("sse2"))
+ {
+ mock_cpu = "";
+ mock_isa = "sse2";
+ assert (foo () == 8);
+ }
+ /* Run sse version if possible. */
+ if (__builtin_cpu_supports ("sse"))
+ {
+ mock_cpu = "";
+ mock_isa = "sse";
+ assert (foo () == 9);
+ }
+ /* Run mmx version if possible. */
+ if (__builtin_cpu_supports ("mmx"))
+ {
+ mock_cpu = "";
+ mock_isa = "mmx";
+ assert (foo () == 10);
+ }
+
+ /* Run the default version. */
+ mock_cpu = "";
+ mock_isa = "";
+ assert (foo () == 0);
+
+ return 0;
+}
+
+int __attribute__ ((target("default")))
+foo ()
+{
+ return 0;
+}
+
+int __attribute__ ((target("arch=corei7")))
+foo ()
+{
+ return 11;
+}
+
+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;
+}
===================================================================
@@ -0,0 +1,4 @@
+/* Test case to check if mv2.C works with -fmultiversion-dynamic-dispatch additionally added. */
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-O2 -fmultiversion-dynamic-dispatch" } */
+/* { dg-additional-sources "mv2.C" } */
===================================================================
@@ -0,0 +1,4 @@
+/* Test case to check if mv6.C works with -fmultiversion-dynamic-dispatch additionally added. */
+/* { dg-do run { target i?86-*-* x86_64-*-* } } */
+/* { dg-options "-march=x86-64 -fmultiversion-dynamic-dispatch" } */
+/* { dg-additional-sources "mv6.C" } */
===================================================================
@@ -26779,6 +26779,11 @@ enum ix86_builtins
IX86_BUILTIN_CPU_IS,
IX86_BUILTIN_CPU_SUPPORTS,
+ /* Builtins to mock CPU and ISA features, for
+ testing multiversioned functions. */
+ IX86_BUILTIN_MOCK_CPU_IS,
+ IX86_BUILTIN_MOCK_CPU_SUPPORTS,
+
IX86_BUILTIN_MAX
};
@@ -28631,11 +28636,14 @@ ix86_init_mmx_sse_builtins (void)
to return a pointer to VERSION_DECL if the outcome of the expression
formed by PREDICATE_CHAIN is true. This function will be called during
version dispatch to decide which function version to execute. It returns
- the basic block at the end, to which more conditions can be added. */
+ the basic block at the end, to which more conditions can be added. When
+ DEBUG_MODE is 1, the version dispatcher is invoked for every call
+ to the multiversioned function. */
static basic_block
add_condition_to_bb (tree function_decl, tree version_decl,
- tree predicate_chain, basic_block new_bb)
+ tree predicate_chain, basic_block new_bb,
+ int debug_mode)
{
gimple return_stmt;
tree convert_expr, result_var;
@@ -28656,11 +28664,43 @@ add_condition_to_bb (tree function_decl, tree vers
gcc_assert (new_bb != NULL);
gseq = bb_seq (new_bb);
+ /* If debug_mode is true, generate a call to the versioned function
+ and return the output of the call. Otherwise, return a pointer to
+ the versioned function. */
- convert_expr = build1 (CONVERT_EXPR, ptr_type_node,
- build_fold_addr_expr (version_decl));
- result_var = create_tmp_var (ptr_type_node, NULL);
- convert_stmt = gimple_build_assign (result_var, convert_expr);
+ if (debug_mode)
+ {
+ tree arg;
+ tree ret_type = TREE_TYPE (TREE_TYPE (function_decl));
+ vec<tree> tmp_vec = vNULL;
+ tmp_vec.create (2);
+
+ arg = DECL_ARGUMENTS (function_decl);
+
+ while (arg)
+ {
+ tmp_vec.safe_push (arg);
+ arg = DECL_CHAIN (arg);
+ }
+
+ convert_stmt = gimple_build_call_vec (version_decl, tmp_vec);
+ tmp_vec.release ();
+ result_var = NULL;
+
+ if (ret_type != void_type_node)
+ {
+ result_var = DECL_RESULT (function_decl);
+ gimple_call_set_lhs (convert_stmt, result_var);
+ }
+ }
+ else
+ {
+ convert_expr = build1 (CONVERT_EXPR, ptr_type_node,
+ build_fold_addr_expr (version_decl));
+ result_var = DECL_RESULT (function_decl);
+ convert_stmt = gimple_build_assign (result_var, convert_expr);
+ }
+
return_stmt = gimple_build_return (result_var);
if (predicate_chain == NULL_TREE)
@@ -28742,10 +28782,11 @@ add_condition_to_bb (tree function_decl, tree vers
the right builtin to use to match the platform specification.
It returns the priority value for this version decl. If PREDICATE_LIST
is not NULL, it stores the list of cpu features that need to be checked
- before dispatching this function. */
+ before dispatching this function. When debug_mode is 1, use the mock
+ cpu check builtins to do the dispatch. */
static unsigned int
-get_builtin_code_for_version (tree decl, tree *predicate_list)
+get_builtin_code_for_version (tree decl, tree *predicate_list, int debug_mode)
{
tree attrs;
struct cl_target_option cur_target;
@@ -28882,7 +28923,10 @@ static unsigned int
if (predicate_list)
{
- predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_IS];
+ if (debug_mode)
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_MOCK_CPU_IS];
+ else
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_IS];
/* For a C string literal the length includes the trailing NULL. */
predicate_arg = build_string_literal (strlen (arg_str) + 1, arg_str);
predicate_chain = tree_cons (predicate_decl, predicate_arg,
@@ -28894,8 +28938,12 @@ static unsigned int
tok_str = (char *) xmalloc (strlen (attrs_str) + 1);
strcpy (tok_str, attrs_str);
token = strtok (tok_str, ",");
- predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_SUPPORTS];
+ if (debug_mode)
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_MOCK_CPU_SUPPORTS];
+ else
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_SUPPORTS];
+
while (token != NULL)
{
/* Do not process "arch=" */
@@ -28957,8 +29005,8 @@ static unsigned int
static int
ix86_compare_version_priority (tree decl1, tree decl2)
{
- unsigned int priority1 = get_builtin_code_for_version (decl1, NULL);
- unsigned int priority2 = get_builtin_code_for_version (decl2, NULL);
+ unsigned int priority1 = get_builtin_code_for_version (decl1, NULL, false);
+ unsigned int priority2 = get_builtin_code_for_version (decl2, NULL, false);
return (int)priority1 - (int)priority2;
}
@@ -28985,12 +29033,15 @@ feature_compare (const void *v1, const void *v2)
multi-versioned functions. DISPATCH_DECL is the function which will
contain the dispatch logic. FNDECLS are the function choices for
dispatch, and is a tree chain. EMPTY_BB is the basic block pointer
- in DISPATCH_DECL in which the dispatch code is generated. */
+ in DISPATCH_DECL in which the dispatch code is generated. When
+ DEBUG_MODE is 1, the version dispatcher is invoked for every call
+ to the multiversioned function. */
static int
dispatch_function_versions (tree dispatch_decl,
void *fndecls_p,
- basic_block *empty_bb)
+ basic_block *empty_bb,
+ int debug_mode)
{
tree default_decl;
gimple ifunc_cpu_init_stmt;
@@ -29048,8 +29099,8 @@ dispatch_function_versions (tree dispatch_decl,
/* Get attribute string, parse it and find the right predicate decl.
The predicate function could be a lengthy combination of many
features, like arch-type and various isa-variants. */
- priority = get_builtin_code_for_version (version_decl,
- &predicate_chain);
+ priority = get_builtin_code_for_version (version_decl, &predicate_chain,
+ debug_mode);
if (predicate_chain == NULL_TREE)
continue;
@@ -29072,11 +29123,11 @@ dispatch_function_versions (tree dispatch_decl,
*empty_bb = add_condition_to_bb (dispatch_decl,
function_version_info[i].version_decl,
function_version_info[i].predicate_chain,
- *empty_bb);
+ *empty_bb, debug_mode);
/* dispatch default version at the end. */
*empty_bb = add_condition_to_bb (dispatch_decl, default_decl,
- NULL, *empty_bb);
+ NULL, *empty_bb, debug_mode);
free (function_version_info);
return 0;
@@ -29446,7 +29497,7 @@ ix86_get_function_versions_dispatcher (void *decl)
default_node = default_version_info->this_node;
#if defined (ASM_OUTPUT_TYPE_DIRECTIVE)
- if (targetm.has_ifunc_p ())
+ if (targetm.has_ifunc_p () || flag_multiversion_dynamic_dispatch)
{
struct cgraph_function_version_info *it_v = NULL;
struct cgraph_node *dispatcher_node = NULL;
@@ -29475,8 +29526,9 @@ ix86_get_function_versions_dispatcher (void *decl)
#endif
{
error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl),
- "multiversioning needs ifunc which is not supported "
- "on this target");
+ "multiversioning needs ifunc"
+ " (or use -fmultiversion-dynamic-dispatch)"
+ " which is not supported on this target");
}
return dispatch_decl;
@@ -29503,15 +29555,19 @@ make_attribute (const char *name, const char *arg_
/* 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
- EMPTY_BB. Return the decl of the resolver function. */
+ EMPTY_BB. Return the decl of the resolver function. When
+ DEBUG_MODE is 1, the resolver function body is not an
+ ifunc resolver; it simply calls the appropriate function
+ version and returns the call output. */
static tree
make_resolver_func (const tree default_decl,
const tree dispatch_decl,
- basic_block *empty_bb)
+ basic_block *empty_bb,
+ int debug_mode)
{
char *resolver_name;
- tree decl, type, decl_name, t;
+ tree decl, type, decl_name, t = NULL;
bool is_uniq = false;
/* IFUNC's have to be globally visible. So, if the default_decl is
@@ -29526,8 +29582,19 @@ make_resolver_func (const tree default_decl,
another module which is based on the same version name. */
resolver_name = make_name (default_decl, "resolver", is_uniq);
- /* The resolver function should return a (void *). */
- type = build_function_type_list (ptr_type_node, NULL_TREE);
+ if (debug_mode)
+ {
+ /* In debug_mode, the resolver function calls the appropriate
+ function version. Its type is same as dispatch_decl. */
+ tree fn_type = TREE_TYPE (dispatch_decl);
+ type = build_function_type (TREE_TYPE (fn_type),
+ TYPE_ARG_TYPES (fn_type));
+ }
+ else
+ {
+ /* The resolver function should return a (void *). */
+ type = build_function_type_list (ptr_type_node, NULL_TREE);
+ }
decl = build_fn_decl (resolver_name, type);
decl_name = get_identifier (resolver_name);
@@ -29549,6 +29616,16 @@ make_resolver_func (const tree default_decl,
DECL_INITIAL (decl) = make_node (BLOCK);
DECL_STATIC_CONSTRUCTOR (decl) = 0;
+ /* In debug_mode, the resolver function is not an ifunc resolver. Its
+ signature is the same as the dispatch_decl or default_decl. */
+ if (debug_mode)
+ {
+ tree arg;
+ DECL_ARGUMENTS (decl) = copy_list (DECL_ARGUMENTS (default_decl));
+ for (arg = DECL_ARGUMENTS (decl); arg ; arg = DECL_CHAIN (arg))
+ DECL_CONTEXT (arg) = decl;
+ }
+
if (DECL_COMDAT_GROUP (default_decl)
|| TREE_PUBLIC (default_decl))
{
@@ -29559,7 +29636,9 @@ make_resolver_func (const tree default_decl,
make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl));
}
/* Build result decl and add to function_decl. */
- t = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, ptr_type_node);
+ t = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+ TREE_TYPE (TREE_TYPE (decl)));
+
DECL_ARTIFICIAL (t) = 1;
DECL_IGNORED_P (t) = 1;
DECL_RESULT (decl) = t;
@@ -29574,9 +29653,17 @@ make_resolver_func (const tree default_decl,
pop_cfun ();
gcc_assert (dispatch_decl != NULL);
- /* Mark dispatch_decl as "ifunc" with resolver as resolver_name. */
- DECL_ATTRIBUTES (dispatch_decl)
- = make_attribute ("ifunc", resolver_name, DECL_ATTRIBUTES (dispatch_decl));
+
+ /* Mark dispatch_decl as "alias" or "ifunc" with resolver as
+ resolver_name. */
+ if (debug_mode)
+ DECL_ATTRIBUTES (dispatch_decl)
+ = make_attribute ("alias", resolver_name,
+ DECL_ATTRIBUTES (dispatch_decl));
+ else
+ DECL_ATTRIBUTES (dispatch_decl)
+ = make_attribute ("ifunc", resolver_name,
+ DECL_ATTRIBUTES (dispatch_decl));
/* Create the alias for dispatch to resolver here. */
/*cgraph_create_function_alias (dispatch_decl, decl);*/
@@ -29588,10 +29675,13 @@ make_resolver_func (const tree default_decl,
/* Generate the dispatching code body to dispatch multi-versioned function
DECL. The target hook is called to process the "target" attributes and
provide the code to dispatch the right function at run-time. NODE points
- to the dispatcher decl whose body will be created. */
+ to the dispatcher decl whose body will be created. When DEBUG_MODE is
+ 1, the dispatch checks should be made during every call to the versioned
+ function. When DEBUG_MODE is 0, ifunc based dispatching is used to
+ keep the call overhead small. */
static tree
-ix86_generate_version_dispatcher_body (void *node_p)
+ix86_generate_version_dispatcher_body (void *node_p, int debug_mode)
{
tree resolver_decl;
basic_block empty_bb;
@@ -29618,8 +29708,8 @@ static tree
/* node is going to be an alias, so remove the finalized bit. */
node->local.finalized = false;
- resolver_decl = make_resolver_func (default_ver_decl,
- node->symbol.decl, &empty_bb);
+ resolver_decl = make_resolver_func (default_ver_decl, node->symbol.decl,
+ &empty_bb, debug_mode);
node_version_info->dispatcher_resolver = resolver_decl;
@@ -29642,7 +29732,8 @@ static tree
fn_ver_vec.safe_push (versn->symbol.decl);
}
- dispatch_function_versions (resolver_decl, &fn_ver_vec, &empty_bb);
+ dispatch_function_versions (resolver_decl, &fn_ver_vec,
+ &empty_bb, debug_mode);
fn_ver_vec.release ();
rebuild_cgraph_edges ();
pop_cfun ();
@@ -29828,7 +29919,8 @@ fold_builtin_cpu (tree fndecl, tree *args)
gcc_assert (param_string_cst);
- if (fn_code == IX86_BUILTIN_CPU_IS)
+ if (fn_code == IX86_BUILTIN_CPU_IS
+ || fn_code == IX86_BUILTIN_MOCK_CPU_IS)
{
tree ref;
tree field;
@@ -29877,7 +29969,8 @@ fold_builtin_cpu (tree fndecl, tree *args)
build_int_cstu (unsigned_type_node, field_val));
return build1 (CONVERT_EXPR, integer_type_node, final);
}
- else if (fn_code == IX86_BUILTIN_CPU_SUPPORTS)
+ else if (fn_code == IX86_BUILTIN_CPU_SUPPORTS
+ || fn_code == IX86_BUILTIN_MOCK_CPU_SUPPORTS)
{
tree ref;
tree array_elt;
@@ -29931,7 +30024,9 @@ ix86_fold_builtin (tree fndecl, int n_args,
enum ix86_builtins fn_code = (enum ix86_builtins)
DECL_FUNCTION_CODE (fndecl);
if (fn_code == IX86_BUILTIN_CPU_IS
- || fn_code == IX86_BUILTIN_CPU_SUPPORTS)
+ || fn_code == IX86_BUILTIN_CPU_SUPPORTS
+ || fn_code == IX86_BUILTIN_MOCK_CPU_IS
+ || fn_code == IX86_BUILTIN_MOCK_CPU_SUPPORTS)
{
gcc_assert (n_args == 1);
return fold_builtin_cpu (fndecl, args);
@@ -29981,6 +30076,13 @@ ix86_init_platform_type_builtins (void)
INT_FTYPE_PCCHAR, true);
make_cpu_type_builtin ("__builtin_cpu_supports", IX86_BUILTIN_CPU_SUPPORTS,
INT_FTYPE_PCCHAR, true);
+ /* Create builtins that mock cpu type and isa features. This is meant to
+ be used for code coverage testing of multiversioned functions. */
+ make_cpu_type_builtin ("__builtin_mock_cpu_is", IX86_BUILTIN_MOCK_CPU_IS,
+ INT_FTYPE_PCCHAR, false);
+ make_cpu_type_builtin ("__builtin_mock_cpu_supports",
+ IX86_BUILTIN_MOCK_CPU_SUPPORTS,
+ INT_FTYPE_PCCHAR, false);
}
/* Internal method for ix86_init_builtins. */
@@ -31701,6 +31803,8 @@ ix86_expand_builtin (tree exp, rtx target, rtx sub
call_expr = build_call_expr (fndecl, 0);
return expand_expr (call_expr, target, mode, EXPAND_NORMAL);
}
+ case IX86_BUILTIN_MOCK_CPU_IS:
+ case IX86_BUILTIN_MOCK_CPU_SUPPORTS:
case IX86_BUILTIN_CPU_IS:
case IX86_BUILTIN_CPU_SUPPORTS:
{