2013-01-21 Jakub Jelinek PR c++/55742 * config/i386/i386.c (ix86_valid_target_attribute_inner_p): Diagnose invalid args instead of ICEing on it. (ix86_valid_target_attribute_tree): Return error_mark_node if ix86_valid_target_attribute_inner_p failed. (ix86_valid_target_attribute_p): Return false only if ix86_valid_target_attribute_tree returned error_mark_node. Allow target("default") attribute. (sorted_attr_string): Change argument from const char * to tree, merge in all target attribute arguments rather than just one. Formatting fix. Use XNEWVEC instead of xmalloc and XDELETEVEC instead of free. Avoid using strcat. (ix86_mangle_function_version_assembler_name): Mangle target("default") as if no target attribute is present. Adjust sorted_attr_string caller. Avoid leaking memory. Use XNEWVEC instead of xmalloc and XDELETEVEC instead of free. (ix86_function_versions): Don't return true if one of the decls doesn't have target attribute. If they don't and one of the decls is DECL_FUNCTION_VERSIONED, report an error. Adjust sorted_attr_string caller. Use XDELETEVEC instead of free. (ix86_supports_function_versions): Remove. (make_name): Fix up formatting. (make_dispatcher_decl): Remove resolver_name and its initialization. Avoid leaking memory. (is_function_default_version): Return true if there is target("default") attribute rather than no target attribute at all. (make_resolver_func): Avoid leaking memory. (ix86_generate_version_dispatcher_body): Likewise. (TARGET_OPTION_SUPPORTS_FUNCTION_VERSIONS): Remove. * target.def (supports_function_versions): Remove. * doc/tm.texi.in (SUPPORTS_FUNCTION_VERSIONS): Remove. * doc/tm.texi: Regenerated. * c-common.c (handle_target_attribute): Revert 2012-12-26 change. * g++.dg/mv1.C: Moved to... * g++.dg/ext/mv1.C: ... here. Adjust test. * g++.dg/mv2.C: Moved to... * g++.dg/ext/mv2.C: ... here. Adjust test. * g++.dg/mv3.C: Moved to... * g++.dg/ext/mv3.C: ... here. * g++.dg/mv4.C: Moved to... * g++.dg/ext/mv4.C: ... here. * g++.dg/mv5.C: Moved to... * g++.dg/ext/mv5.C: ... here. Adjust test. * g++.dg/mv6.C: Moved to... * g++.dg/ext/mv6.C: ... here. Adjust test. * g++.dg/ext/mv7.C: New test. * g++.dg/ext/mv8.C: New test. * g++.dg/ext/mv9.C: New test. * g++.dg/ext/mv10.C: New test. * g++.dg/ext/mv11.C: New test. --- gcc/config/i386/i386.c.jj 2013-01-21 10:57:08.325945469 +0100 +++ gcc/config/i386/i386.c 2013-01-21 12:02:59.384701394 +0100 @@ -4223,7 +4223,10 @@ ix86_valid_target_attribute_inner_p (tre } else if (TREE_CODE (args) != STRING_CST) - gcc_unreachable (); + { + error ("attribute % argument not a string"); + return false; + } /* Handle multiple arguments separated by commas. */ next_optstr = ASTRDUP (TREE_STRING_POINTER (args)); @@ -4368,7 +4371,7 @@ ix86_valid_target_attribute_tree (tree a /* Process each of the options on the chain. */ if (! ix86_valid_target_attribute_inner_p (args, option_strings, &enum_opts_set)) - return NULL_TREE; + return error_mark_node; /* If the changed options are different from the default, rerun ix86_option_override_internal, and then save the options away. @@ -4433,6 +4436,15 @@ ix86_valid_target_attribute_p (tree fnde { struct cl_target_option cur_target; bool ret = true; + + /* attribute((target("default"))) does nothing, beyond + affecting multi-versioning. */ + if (TREE_VALUE (args) + && TREE_CODE (TREE_VALUE (args)) == STRING_CST + && TREE_CHAIN (args) == NULL_TREE + && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "default") == 0) + return true; + tree old_optimize = build_optimization_node (); tree new_target, new_optimize; tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl); @@ -4449,10 +4461,10 @@ ix86_valid_target_attribute_p (tree fnde new_target = ix86_valid_target_attribute_tree (args); new_optimize = build_optimization_node (); - if (!new_target) + if (new_target == error_mark_node) ret = false; - else if (fndecl) + else if (fndecl && new_target) { DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target; @@ -28946,26 +28958,44 @@ attr_strcmp (const void *v1, const void return strcmp (c1, c2); } -/* STR is the argument to target attribute. This function tokenizes +/* ARGLIST is the argument to target attribute. This function tokenizes the comma separated arguments, sorts them and returns a string which is a unique identifier for the comma separated arguments. It also replaces non-identifier characters "=,-" with "_". */ static char * -sorted_attr_string (const char *str) +sorted_attr_string (tree arglist) { + tree arg; + size_t str_len_sum = 0; char **args = NULL; char *attr_str, *ret_str; char *attr = NULL; unsigned int argnum = 1; unsigned int i; - for (i = 0; i < strlen (str); i++) - if (str[i] == ',') - argnum++; - - attr_str = (char *)xmalloc (strlen (str) + 1); - strcpy (attr_str, str); + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + { + const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); + size_t len = strlen (str); + str_len_sum += len + 1; + if (arg != arglist) + argnum++; + for (i = 0; i < strlen (str); i++) + if (str[i] == ',') + argnum++; + } + + attr_str = XNEWVEC (char, str_len_sum); + str_len_sum = 0; + for (arg = arglist; arg; arg = TREE_CHAIN (arg)) + { + const char *str = TREE_STRING_POINTER (TREE_VALUE (arg)); + size_t len = strlen (str); + memcpy (attr_str + str_len_sum, str, len); + attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0'; + str_len_sum += len + 1; + } /* Replace "=,-" with "_". */ for (i = 0; i < strlen (attr_str); i++) @@ -28986,18 +29016,20 @@ sorted_attr_string (const char *str) attr = strtok (NULL, ","); } - qsort (args, argnum, sizeof (char*), attr_strcmp); + qsort (args, argnum, sizeof (char *), attr_strcmp); - ret_str = (char *)xmalloc (strlen (str) + 1); - strcpy (ret_str, args[0]); - for (i = 1; i < argnum; i++) + ret_str = XNEWVEC (char, str_len_sum); + str_len_sum = 0; + for (i = 0; i < argnum; i++) { - strcat (ret_str, "_"); - strcat (ret_str, args[i]); + size_t len = strlen (args[i]); + memcpy (ret_str + str_len_sum, args[i], len); + ret_str[str_len_sum + len] = i < argnum - 1 ? '_' : '\0'; + str_len_sum += len + 1; } - free (args); - free (attr_str); + XDELETEVEC (args); + XDELETEVEC (attr_str); return ret_str; } @@ -29009,8 +29041,8 @@ static tree ix86_mangle_function_version_assembler_name (tree decl, tree id) { tree version_attr; - const char *orig_name, *version_string, *attr_str; - char *assembler_name; + const char *orig_name, *version_string; + char *attr_str, *assembler_name; if (DECL_DECLARED_INLINE_P (decl) && lookup_attribute ("gnu_inline", @@ -29034,9 +29066,11 @@ ix86_mangle_function_version_assembler_n version_string = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr))); - attr_str = sorted_attr_string (version_string); - assembler_name = (char *) xmalloc (strlen (orig_name) - + strlen (attr_str) + 2); + if (strcmp (version_string, "default") == 0) + return id; + + attr_str = sorted_attr_string (TREE_VALUE (version_attr)); + assembler_name = XNEWVEC (char, strlen (orig_name) + strlen (attr_str) + 2); sprintf (assembler_name, "%s.%s", orig_name, attr_str); @@ -29044,7 +29078,10 @@ ix86_mangle_function_version_assembler_n if (DECL_ASSEMBLER_NAME_SET_P (decl)) SET_DECL_RTL (decl, NULL); - return get_identifier (assembler_name); + tree ret = get_identifier (assembler_name); + XDELETEVEC (attr_str); + XDELETEVEC (assembler_name); + return ret; } /* This function returns true if FN1 and FN2 are versions of the same function, @@ -29055,10 +29092,9 @@ static bool ix86_function_versions (tree fn1, tree fn2) { tree attr1, attr2; - const char *attr_str1, *attr_str2; char *target1, *target2; bool result; - + if (TREE_CODE (fn1) != FUNCTION_DECL || TREE_CODE (fn2) != FUNCTION_DECL) return false; @@ -29070,15 +29106,35 @@ ix86_function_versions (tree fn1, tree f if (attr1 == NULL_TREE && attr2 == NULL_TREE) return false; - /* If one function does not have a target attribute, these are versions. */ + /* Diagnose missing target attribute if one of the decls is already + multi-versioned. */ if (attr1 == NULL_TREE || attr2 == NULL_TREE) - return true; - - attr_str1 = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr1))); - attr_str2 = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr2))); + { + if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2)) + { + if (attr2 != NULL_TREE) + { + tree tem = fn1; + fn1 = fn2; + fn2 = tem; + attr1 = attr2; + } + error_at (DECL_SOURCE_LOCATION (fn2), + "missing % attribute for multi-versioned %D", + fn2); + error_at (DECL_SOURCE_LOCATION (fn1), + "previous declaration of %D", fn1); + /* Prevent diagnosing of the same error multiple times. */ + DECL_ATTRIBUTES (fn2) + = tree_cons (get_identifier ("target"), + copy_node (TREE_VALUE (attr1)), + DECL_ATTRIBUTES (fn2)); + } + return false; + } - target1 = sorted_attr_string (attr_str1); - target2 = sorted_attr_string (attr_str2); + target1 = sorted_attr_string (TREE_VALUE (attr1)); + target2 = sorted_attr_string (TREE_VALUE (attr2)); /* The sorted target strings must be different for fn1 and fn2 to be versions. */ @@ -29087,20 +29143,12 @@ ix86_function_versions (tree fn1, tree f else result = true; - free (target1); - free (target2); + XDELETEVEC (target1); + XDELETEVEC (target2); return result; } -/* This target supports function multiversioning. */ - -static bool -ix86_supports_function_versions (void) -{ - return true; -} - static tree ix86_mangle_decl_assembler_name (tree decl, tree id) { @@ -29141,10 +29189,10 @@ make_name (tree decl, const char *suffix /* Use '.' to concatenate names as it is demangler friendly. */ if (make_unique) - snprintf (global_var_name, name_len, "%s.%s.%s", name, - unique_name, suffix); + snprintf (global_var_name, name_len, "%s.%s.%s", name, unique_name, + suffix); else - snprintf (global_var_name, name_len, "%s.%s", name, suffix); + snprintf (global_var_name, name_len, "%s.%s", name, suffix); return global_var_name; } @@ -29159,7 +29207,7 @@ static tree make_dispatcher_decl (const tree decl) { tree func_decl; - char *func_name, *resolver_name; + char *func_name; tree fn_type, func_type; bool is_uniq = false; @@ -29167,14 +29215,13 @@ make_dispatcher_decl (const tree decl) is_uniq = true; func_name = make_name (decl, "ifunc", is_uniq); - resolver_name = make_name (decl, "resolver", is_uniq); - gcc_assert (resolver_name); fn_type = TREE_TYPE (decl); func_type = build_function_type (TREE_TYPE (fn_type), TYPE_ARG_TYPES (fn_type)); func_decl = build_fn_decl (func_name, func_type); + XDELETEVEC (func_name); TREE_USED (func_decl) = 1; DECL_CONTEXT (func_decl) = NULL_TREE; DECL_INITIAL (func_decl) = error_mark_node; @@ -29196,9 +29243,14 @@ make_dispatcher_decl (const tree decl) static bool is_function_default_version (const tree decl) { - return (TREE_CODE (decl) == FUNCTION_DECL - && DECL_FUNCTION_VERSIONED (decl) - && lookup_attribute ("target", DECL_ATTRIBUTES (decl)) == NULL_TREE); + if (TREE_CODE (decl) != FUNCTION_DECL + || !DECL_FUNCTION_VERSIONED (decl)) + return false; + tree attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl)); + gcc_assert (attr); + attr = TREE_VALUE (TREE_VALUE (attr)); + return (TREE_CODE (attr) == STRING_CST + && strcmp (TREE_STRING_POINTER (attr), "default") == 0); } /* Make a dispatcher declaration for the multi-versioned function DECL. @@ -29392,6 +29444,7 @@ make_resolver_func (const tree default_d /* Create the alias for dispatch to resolver here. */ /*cgraph_create_function_alias (dispatch_decl, decl);*/ cgraph_same_body_alias (NULL, dispatch_decl, decl); + XDELETEVEC (resolver_name); return decl; } @@ -29453,7 +29506,7 @@ ix86_generate_version_dispatcher_body (v } dispatch_function_versions (resolver_decl, &fn_ver_vec, &empty_bb); - + fn_ver_vec.release (); rebuild_cgraph_edges (); pop_cfun (); return resolver_decl; @@ -42441,10 +42494,6 @@ ix86_memmodel_check (unsigned HOST_WIDE_ #undef TARGET_OPTION_FUNCTION_VERSIONS #define TARGET_OPTION_FUNCTION_VERSIONS ix86_function_versions -#undef TARGET_OPTION_SUPPORTS_FUNCTION_VERSIONS -#define TARGET_OPTION_SUPPORTS_FUNCTION_VERSIONS \ - ix86_supports_function_versions - #undef TARGET_CAN_INLINE_P #define TARGET_CAN_INLINE_P ix86_can_inline_p --- gcc/target.def.jj 2013-01-11 09:02:32.000000000 +0100 +++ gcc/target.def 2013-01-21 11:25:29.145124702 +0100 @@ -2831,14 +2831,6 @@ DEFHOOK bool, (tree decl1, tree decl2), hook_bool_tree_tree_false) -/* This function returns true if the target supports function - multiversioning. */ -DEFHOOK -(supports_function_versions, - "", - bool, (void), - hook_bool_void_false) - /* Function to determine if one function can inline another function. */ #undef HOOK_PREFIX #define HOOK_PREFIX "TARGET_" --- gcc/doc/tm.texi.in.jj 2013-01-11 09:02:26.000000000 +0100 +++ gcc/doc/tm.texi.in 2013-01-21 11:25:57.789962584 +0100 @@ -9768,11 +9768,6 @@ different target specific attributes, th different target machines. @end deftypefn -@hook TARGET_OPTION_SUPPORTS_FUNCTION_VERSIONS -This target hook returns @code{true} if the target supports function -multiversioning. -@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 --- gcc/doc/tm.texi.jj 2013-01-11 09:02:26.000000000 +0100 +++ gcc/doc/tm.texi 2013-01-21 11:26:08.647897014 +0100 @@ -9907,11 +9907,6 @@ different target specific attributes, th different target machines. @end deftypefn -@deftypefn {Target Hook} bool TARGET_OPTION_SUPPORTS_FUNCTION_VERSIONS (void) -This target hook returns @code{true} if the target supports function -multiversioning. -@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 --- gcc/c-family/c-common.c.jj 2013-01-21 10:56:55.165014693 +0100 +++ gcc/c-family/c-common.c 2013-01-21 10:59:04.305285038 +0100 @@ -8759,12 +8759,8 @@ handle_target_attribute (tree *node, tre warning (OPT_Wattributes, "%qE attribute ignored", name); *no_add_attrs = true; } - /* Do not strip invalid target attributes for targets which support function - multiversioning as the target string is used to determine versioned - functions. */ else if (! targetm.target_option.valid_attribute_p (*node, name, args, - flags) - && ! targetm.target_option.supports_function_versions ()) + flags)) *no_add_attrs = true; return NULL_TREE; --- gcc/testsuite/g++.dg/mv1.C.jj 2013-01-21 10:56:54.564017815 +0100 +++ gcc/testsuite/g++.dg/mv1.C 2013-01-21 11:35:42.260684508 +0100 @@ -1,130 +0,0 @@ -/* Test case to check if Multiversioning works. */ -/* { dg-do run { target i?86-*-* x86_64-*-* } } */ -/* { dg-require-ifunc "" } */ -/* { dg-options "-O2 -fPIC" } */ - -#include - -/* 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; -} --- gcc/testsuite/g++.dg/ext/mv1.C.jj 2013-01-21 11:36:16.084491175 +0100 +++ gcc/testsuite/g++.dg/ext/mv1.C 2013-01-21 11:42:52.975315718 +0100 @@ -0,0 +1,132 @@ +/* Test case to check if Multiversioning works. */ +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O2 -fPIC" } */ + +#include + +/* Default version. */ +int foo (); // Extra declaration that is merged with the second one. +int foo () __attribute__ ((target("default"))); +/* 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 __attribute__ ((target("default"))) +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; +} --- gcc/testsuite/g++.dg/mv2.C.jj 2013-01-21 10:56:54.465018354 +0100 +++ gcc/testsuite/g++.dg/mv2.C 2013-01-21 11:35:45.088666745 +0100 @@ -1,118 +0,0 @@ -/* 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" } */ - -#include - -/* 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; -} --- gcc/testsuite/g++.dg/ext/mv2.C.jj 2013-01-21 11:36:16.085491165 +0100 +++ gcc/testsuite/g++.dg/ext/mv2.C 2013-01-21 10:58:57.000000000 +0100 @@ -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" } */ + +#include + +/* Default version. */ +int foo () __attribute__ ((target ("default"))); +/* 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 __attribute__ ((target("default"))) +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; +} --- gcc/testsuite/g++.dg/mv3.C.jj 2013-01-18 21:23:45.000000000 +0100 +++ gcc/testsuite/g++.dg/mv3.C 2013-01-21 11:35:52.400626435 +0100 @@ -1,36 +0,0 @@ -/* 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" } */ - - -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 (); -} --- gcc/testsuite/g++.dg/ext/mv3.C.jj 2013-01-21 11:36:16.086491156 +0100 +++ gcc/testsuite/g++.dg/ext/mv3.C 2013-01-18 21:23:45.000000000 +0100 @@ -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" } */ + + +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 (); +} --- gcc/testsuite/g++.dg/mv4.C.jj 2013-01-18 21:23:45.000000000 +0100 +++ gcc/testsuite/g++.dg/mv4.C 2013-01-21 11:35:54.308616223 +0100 @@ -1,24 +0,0 @@ -/* 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-require-ifunc "" } */ -/* { dg-options "-O2" } */ - -int __attribute__ ((target ("sse"))) -foo () -{ - return 1; -} -int __attribute__ ((target ("popcnt"))) -foo () -{ - return 0; -} - -int main () -{ - int (*p)() = &foo; /* { dg-error "use of multiversioned function without a default" {} } */ - return (*p)(); -} --- gcc/testsuite/g++.dg/ext/mv4.C.jj 2013-01-21 11:36:16.088491133 +0100 +++ gcc/testsuite/g++.dg/ext/mv4.C 2013-01-18 21:23:45.000000000 +0100 @@ -0,0 +1,24 @@ +/* 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-require-ifunc "" } */ +/* { dg-options "-O2" } */ + +int __attribute__ ((target ("sse"))) +foo () +{ + return 1; +} +int __attribute__ ((target ("popcnt"))) +foo () +{ + return 0; +} + +int main () +{ + int (*p)() = &foo; /* { dg-error "use of multiversioned function without a default" {} } */ + return (*p)(); +} --- gcc/testsuite/g++.dg/mv5.C.jj 2013-01-21 10:56:54.582017723 +0100 +++ gcc/testsuite/g++.dg/mv5.C 2013-01-21 11:35:56.027606185 +0100 @@ -1,25 +0,0 @@ -/* 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-require-ifunc "" } */ -/* { dg-options "-O2" } */ - - -/* Default version. */ -inline int -foo () -{ - return 0; -} - -inline int __attribute__ ((target ("popcnt"))) -foo () -{ - return 0; -} - -int main () -{ - return foo (); -} --- gcc/testsuite/g++.dg/ext/mv5.C.jj 2013-01-21 11:36:16.089491123 +0100 +++ gcc/testsuite/g++.dg/ext/mv5.C 2013-01-21 10:58:57.000000000 +0100 @@ -0,0 +1,25 @@ +/* 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-require-ifunc "" } */ +/* { dg-options "-O2" } */ + + +/* Default version. */ +inline int __attribute__ ((target ("default"))) +foo () +{ + return 0; +} + +inline int __attribute__ ((target ("popcnt"))) +foo () +{ + return 0; +} + +int main () +{ + return foo (); +} --- gcc/testsuite/g++.dg/mv6.C.jj 2013-01-21 10:56:54.504018081 +0100 +++ gcc/testsuite/g++.dg/mv6.C 2013-01-21 11:35:57.514597697 +0100 @@ -1,27 +0,0 @@ -/* Test to check if member version multiversioning works correctly. */ - -/* { dg-do run { target i?86-*-* x86_64-*-* } } */ -/* { dg-require-ifunc "" } */ -/* { dg-options "-march=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 (); -} --- gcc/testsuite/g++.dg/ext/mv6.C.jj 2013-01-21 11:36:16.090491117 +0100 +++ gcc/testsuite/g++.dg/ext/mv6.C 2013-01-21 10:58:57.000000000 +0100 @@ -0,0 +1,28 @@ +/* Test to check if member version multiversioning works correctly. */ + +/* { dg-do run { target i?86-*-* x86_64-*-* } } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-march=x86-64" } */ + +class Foo +{ + public: + /* Default version of foo. */ + __attribute__ ((target("default"))) + int foo () + { + return 0; + } + /* corei7 version of foo. */ + __attribute__ ((target("arch=corei7"))) + int foo () + { + return 0; + } +}; + +int main () +{ + Foo f; + return f.foo (); +} --- gcc/testsuite/g++.dg/ext/mv7.C.jj 2013-01-21 11:39:21.789460493 +0100 +++ gcc/testsuite/g++.dg/ext/mv7.C 2013-01-21 11:45:05.098589632 +0100 @@ -0,0 +1,12 @@ +// { dg-do compile { target i?86-*-* x86_64-*-* } } +// { dg-options "" } + +__attribute__((target ("default"))) +void foo (void) // { dg-error "previously defined here" } +{ +} + +__attribute__((target (128))) +void foo (void) // { dg-error "(not a string|redefinition)" } +{ +} --- gcc/testsuite/g++.dg/ext/mv8.C.jj 2013-01-21 11:41:17.147836101 +0100 +++ gcc/testsuite/g++.dg/ext/mv8.C 2013-01-21 11:45:13.493544256 +0100 @@ -0,0 +1,7 @@ +// { dg-do compile { target i?86-*-* x86_64-*-* } } +// { dg-options "" } + +__attribute__((target (11,12))) +void foo (void) // { dg-error "not a string" } +{ +} --- gcc/testsuite/g++.dg/ext/mv9.C.jj 2013-01-21 11:44:31.634774921 +0100 +++ gcc/testsuite/g++.dg/ext/mv9.C 2013-01-21 11:45:18.275516266 +0100 @@ -0,0 +1,9 @@ +// { dg-do compile { target i?86-*-* x86_64-*-* } } +// { dg-options "" } + +void foo (); +void foo () __attribute__((target ("sse4"))); +void foo () __attribute__((target ("default"))); // { dg-error "previous declaration" } +void foo () // { dg-error "attribute for multi-versioned" } +{ +} --- gcc/testsuite/g++.dg/ext/mv10.C.jj 2013-01-21 12:12:29.354541630 +0100 +++ gcc/testsuite/g++.dg/ext/mv10.C 2013-01-21 12:12:56.207392886 +0100 @@ -0,0 +1,12 @@ +// { dg-do assemble { target i?86-*-* x86_64-*-* } } +// { dg-options "" } + +__attribute__((target ("popcnt"), used)) +void foo (void) +{ +} + +__attribute__((target ("popcnt","avx"), used)) +void foo (void) +{ +} --- gcc/testsuite/g++.dg/ext/mv11.C.jj 2013-01-21 12:16:26.381285535 +0100 +++ gcc/testsuite/g++.dg/ext/mv11.C 2013-01-21 12:17:33.380918996 +0100 @@ -0,0 +1,23 @@ +// { dg-do compile { target i?86-*-* x86_64-*-* } } +// { dg-options "-msse2" } + +int foo () __attribute__ ((target("default"))); +int foo () __attribute__ ((target("sse2"))); + +int +main () +{ + return foo (); +} + +int __attribute__ ((target("default"))) +foo () +{ + return 0; +} + +int __attribute__ ((target("sse2"))) +foo () +{ + return 0; +}