Index: cgraphunit.c =================================================================== --- cgraphunit.c (revision 196618) +++ cgraphunit.c (working copy) @@ -942,7 +942,12 @@ 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); + /* flag_mv_debug is 0 means that the dispatcher should be invoked + optimally (once using ifunc support). When flag_mv_debug is 1, + the dispatcher should be invoked every time a call to the + multiversioned function is made. */ + resolver + = targetm.generate_version_dispatcher_body (node, flag_mv_debug); gcc_assert (resolver != NULL_TREE); } } Index: common.opt =================================================================== --- common.opt (revision 196618) +++ common.opt (working copy) @@ -1600,6 +1600,10 @@ fmove-loop-invariants Common Report Var(flag_move_loop_invariants) Init(1) Optimization Move loop invariant computations out of loops +fmv-debug +Common RejectNegative Report Var(flag_mv_debug) Init(0) +Invoke the function version dispatcher for every multiversioned function call. + ftsan Common RejectNegative Report Var(flag_tsan) Add ThreadSanitizer instrumentation Index: doc/tm.texi =================================================================== --- doc/tm.texi (revision 196618) +++ doc/tm.texi (working copy) @@ -11032,11 +11032,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}) Index: doc/tm.texi.in =================================================================== --- doc/tm.texi.in (revision 196618) +++ doc/tm.texi.in (working copy) @@ -10908,7 +10908,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 Index: testsuite/g++.dg/ext/mv14_debug_code_coverage.C =================================================================== --- testsuite/g++.dg/ext/mv14_debug_code_coverage.C (revision 0) +++ testsuite/g++.dg/ext/mv14_debug_code_coverage.C (revision 0) @@ -0,0 +1,213 @@ +/* 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 -fmv-debug" } */ + +#include +#include + +/* 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; +} + +/* 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; +} Index: testsuite/g++.dg/ext/mv2_debug.C =================================================================== --- testsuite/g++.dg/ext/mv2_debug.C (revision 0) +++ testsuite/g++.dg/ext/mv2_debug.C (revision 0) @@ -0,0 +1,4 @@ +/* Test case to check if mv2.C works with -fmv-debug additionally added. */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-O2 -fmv-debug" } */ +/* { dg-additional-sources "mv2.C" } */ Index: testsuite/g++.dg/ext/mv6_debug.C =================================================================== --- testsuite/g++.dg/ext/mv6_debug.C (revision 0) +++ testsuite/g++.dg/ext/mv6_debug.C (revision 0) @@ -0,0 +1,4 @@ +/* Test case to check if mv6.C works with -fmv-debug additionally added. */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-march=x86-64 -fmv-debug" } */ +/* { dg-additional-sources "mv6.C" } */ Index: testsuite/g++.dg/ext/mv1_debug.C =================================================================== --- testsuite/g++.dg/ext/mv1_debug.C (revision 0) +++ testsuite/g++.dg/ext/mv1_debug.C (revision 0) @@ -0,0 +1,4 @@ +/* Test case to check if mv1.C works with -fmv-debug additionally added. */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-options "-O2 -fPIC -fmv-debug" } */ +/* { dg-additional-sources "mv1.C" } */ Index: config/i386/i386.c =================================================================== --- config/i386/i386.c (revision 196618) +++ config/i386/i386.c (working copy) @@ -26173,6 +26173,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 }; @@ -28001,11 +28006,14 @@ ix86_slow_unaligned_vector_memop (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; @@ -28026,11 +28034,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, heap) *vec = NULL; + vec = VEC_alloc (tree, heap, 2); + + arg = DECL_ARGUMENTS (function_decl); + + while (arg) + { + VEC_safe_push (tree, heap, vec, arg); + arg = DECL_CHAIN (arg); + } + + convert_stmt = gimple_build_call_vec (version_decl, vec); + VEC_free (tree, heap, vec); + 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) @@ -28112,10 +28152,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; @@ -28254,7 +28295,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, @@ -28266,8 +28310,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=" */ @@ -28329,8 +28377,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; } @@ -28357,12 +28405,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; @@ -28420,8 +28471,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; @@ -28444,11 +28495,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; @@ -28813,8 +28864,19 @@ ix86_get_function_versions_dispatcher (void *decl) default_node = default_version_info->this_node; + + /* Right now, the dispatching at startup non-debug mode is done via ifunc. */ #if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION - /* Right now, the dispatching is done via ifunc. */ +#else + if (!debug_mode) + { + error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl), + "multiversioning needs ifunc which is not supported " + "in this configuration"); + return NULL; + } +#endif + dispatch_decl = make_dispatcher_decl (default_node->decl); dispatcher_node = cgraph_get_create_node (dispatch_decl); @@ -28832,11 +28894,7 @@ ix86_get_function_versions_dispatcher (void *decl) it_v->dispatcher_resolver = dispatch_decl; it_v = it_v->next; } -#else - error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl), - "multiversioning needs ifunc which is not supported " - "in this configuration"); -#endif + return dispatch_decl; } @@ -28861,15 +28919,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 @@ -28884,8 +28946,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); @@ -28907,6 +28980,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)) { @@ -28917,7 +29000,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; @@ -28932,9 +29017,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);*/ @@ -28946,10 +29039,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; @@ -28976,8 +29072,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->decl, &empty_bb); + resolver_decl = make_resolver_func (default_ver_decl, node->decl, + &empty_bb, debug_mode); node_version_info->dispatcher_resolver = resolver_decl; @@ -29000,7 +29096,8 @@ static tree VEC_safe_push (tree, heap, fn_ver_vec, versn->decl); } - dispatch_function_versions (resolver_decl, fn_ver_vec, &empty_bb); + dispatch_function_versions (resolver_decl, fn_ver_vec, + &empty_bb, debug_mode); VEC_free (tree, heap, fn_ver_vec); rebuild_cgraph_edges (); pop_cfun (); @@ -29185,7 +29282,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; @@ -29234,7 +29332,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; @@ -29288,7 +29387,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); @@ -29334,6 +29435,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. */ @@ -31050,6 +31158,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: { Index: target.def =================================================================== --- target.def (revision 196618) +++ target.def (working copy) @@ -1271,11 +1271,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