This patch adds support for the "target_version" attribute to the middle end and the C++ frontend, which will be used to implement function multiversioning in the aarch64 backend.
Note that C++ is currently the only frontend which supports multiversioning using the "target" attribute, whereas the "target_clones" attribute is additionally supported in C, D and Ada. Support for the target_version attribute will be extended to C at a later date. Targets that currently use the "target" attribute for function multiversioning (i.e. i386 and rs6000) are not affected by this patch. I could have implemented the target hooks slightly differently, by reusing the valid_attribute_p hook and adding attribute name checks to each backend implementation (c.f. the aarch64 implementation in patch 2/3). Would this be preferable? Otherwise, is this ok for master? gcc/c-family/ChangeLog: * c-attribs.cc (handle_target_version_attribute): New. (c_common_attribute_table): Add target_version. (handle_target_clones_attribute): Add conflict with target_version attribute. gcc/ChangeLog: * attribs.cc (is_function_default_version): Update comment to specify incompatibility with target_version attributes. * cgraphclones.cc (cgraph_node::create_version_clone_with_body): Call valid_version_attribute_p for target_version attributes. * target.def (valid_version_attribute_p): New hook. (expanded_clones_attribute): New hook. * doc/tm.texi.in: Add new hooks. * doc/tm.texi: Regenerate. * multiple_target.cc (create_dispatcher_calls): Remove redundant is_function_default_version check. (expand_target_clones): Use target hook for attribute name. * targhooks.cc (default_target_option_valid_version_attribute_p): New. * targhooks.h (default_target_option_valid_version_attribute_p): New. * tree.h (DECL_FUNCTION_VERSIONED): Update comment to include target_version attributes. gcc/cp/ChangeLog: * decl2.cc (check_classfn): Update comment to include target_version attributes. diff --git a/gcc/attribs.cc b/gcc/attribs.cc index b1300018d1e8ed8e02ded1ea721dc192a6d32a49..a3c4a81e8582ea4fd06b9518bf51fad7c998ddd6 100644 --- a/gcc/attribs.cc +++ b/gcc/attribs.cc @@ -1233,8 +1233,9 @@ make_dispatcher_decl (const tree decl) return func_decl; } -/* Returns true if decl is multi-versioned and DECL is the default function, - that is it is not tagged with target specific optimization. */ +/* Returns true if DECL is multi-versioned using the target attribute, and this + is the default version. This function can only be used for targets that do + not support the "target_version" attribute. */ bool is_function_default_version (const tree decl) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 072cfb69147bd6b314459c0bd48a0c1fb92d3e4d..1a224c036277d51ab4dc0d33a403177bd226e48a 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -148,6 +148,7 @@ static tree handle_alloc_align_attribute (tree *, tree, tree, int, bool *); static tree handle_assume_aligned_attribute (tree *, tree, tree, int, bool *); static tree handle_assume_attribute (tree *, tree, tree, int, bool *); static tree handle_target_attribute (tree *, tree, tree, int, bool *); +static tree handle_target_version_attribute (tree *, tree, tree, int, bool *); static tree handle_target_clones_attribute (tree *, tree, tree, int, bool *); static tree handle_optimize_attribute (tree *, tree, tree, int, bool *); static tree ignore_attribute (tree *, tree, tree, int, bool *); @@ -480,6 +481,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_error_attribute, NULL }, { "target", 1, -1, true, false, false, false, handle_target_attribute, NULL }, + { "target_version", 1, -1, true, false, false, false, + handle_target_version_attribute, NULL }, { "target_clones", 1, -1, true, false, false, false, handle_target_clones_attribute, NULL }, { "optimize", 1, -1, true, false, false, false, @@ -5569,6 +5572,45 @@ handle_target_attribute (tree *node, tree name, tree args, int flags, return NULL_TREE; } +/* Handle a "target_version" attribute. */ + +static tree +handle_target_version_attribute (tree *node, tree name, tree args, int flags, + bool *no_add_attrs) +{ + /* Ensure we have a function type. */ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + else if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (*node))) + { + warning (OPT_Wattributes, "%qE attribute ignored due to conflict " + "with %qs attribute", name, "target_clones"); + *no_add_attrs = true; + } + else if (!targetm.target_option.valid_version_attribute_p (*node, name, args, + flags)) + *no_add_attrs = true; + + /* Check that there's no empty string in values of the attribute. */ + for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t)) + { + tree value = TREE_VALUE (t); + if (TREE_CODE (value) == STRING_CST + && TREE_STRING_LENGTH (value) == 1 + && TREE_STRING_POINTER (value)[0] == '\0') + { + warning (OPT_Wattributes, + "empty string in attribute %<target_version%>"); + *no_add_attrs = true; + } + } + + return NULL_TREE; +} + /* Handle a "target_clones" attribute. */ static tree @@ -5601,6 +5643,12 @@ handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args), "with %qs attribute", name, "target"); *no_add_attrs = true; } + else if (lookup_attribute ("target_version", DECL_ATTRIBUTES (*node))) + { + warning (OPT_Wattributes, "%qE attribute ignored due to conflict " + "with %qs attribute", name, "target_version"); + *no_add_attrs = true; + } else if (get_target_clone_attr_len (args) == -1) { warning (OPT_Wattributes, diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc index 29d28ef895a73a223695cbb86aafbc845bbe7688..8af6b23d8c0306920e0fdcb3559ef047a16689f4 100644 --- a/gcc/cgraphclones.cc +++ b/gcc/cgraphclones.cc @@ -78,6 +78,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-eh.h" #include "tree-cfg.h" #include "tree-inline.h" +#include "attribs.h" #include "dumpfile.h" #include "gimple-pretty-print.h" #include "alloc-pool.h" @@ -1048,7 +1049,17 @@ cgraph_node::create_version_clone_with_body location_t saved_loc = input_location; tree v = TREE_VALUE (target_attributes); input_location = DECL_SOURCE_LOCATION (new_decl); - bool r = targetm.target_option.valid_attribute_p (new_decl, NULL, v, 1); + bool r; + tree name_id = get_attribute_name (target_attributes); + const char* name_str = IDENTIFIER_POINTER (name_id); + if (strcmp (name_str, "target") == 0) + r = targetm.target_option.valid_attribute_p (new_decl, name_id, v, 1); + else if (strcmp (name_str, "target_version") == 0) + r = targetm.target_option.valid_version_attribute_p (new_decl, name_id, + v, 1); + else + gcc_assert(false); + input_location = saved_loc; if (!r) return NULL; diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 9594be4092c3c00fddc9d4c6da5931ea3b7e8792..ec78d5a5440bedd360ac8e5bc44e164da3dab410 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -829,8 +829,8 @@ check_classfn (tree ctype, tree function, tree template_parms) tree c2 = get_constraints (fndecl); /* While finding a match, same types and params are not enough - if the function is versioned. Also check version ("target") - attributes. */ + if the function is versioned. Also check for different target + specific attributes. */ if (same_type_p (TREE_TYPE (TREE_TYPE (function)), TREE_TYPE (TREE_TYPE (fndecl))) && compparms (p1, p2) diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 33e7ffc8af5a9d48430145ef9b8e7924613b7dd7..97b3e9c31631a943d95f1cf7739716a574afcfb7 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -10507,6 +10507,23 @@ the function declaration to hold a pointer to a target-specific @code{struct cl_target_option} structure. @end deftypefn +@deftypefn {Target Hook} bool TARGET_OPTION_VALID_VERSION_ATTRIBUTE_P (tree @var{fndecl}, tree @var{name}, tree @var{args}, int @var{flags}) +This hook is called to parse @code{attribute(target_version("..."))}, +which allows setting target-specific options on individual function versions. +These function-specific options may differ +from the options specified on the command line. The hook should return +@code{true} if the options are valid. + +The hook should set the @code{DECL_FUNCTION_SPECIFIC_TARGET} field in +the function declaration to hold a pointer to a target-specific +@code{struct cl_target_option} structure. +@end deftypefn + +@deftypevr {Target Hook} {const char *} TARGET_OPTION_EXPANDED_CLONES_ATTRIBUTE +Contains the name of the attribute used for the version description string +when expanding clones for a function with the target_clones attribute. +@end deftypevr + @deftypefn {Target Hook} void TARGET_OPTION_SAVE (struct cl_target_option *@var{ptr}, struct gcc_options *@var{opts}, struct gcc_options *@var{opts_set}) This hook is called to save any additional target-specific information in the @code{struct cl_target_option} structure for function-specific diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index c98b2447e28aa17996b1cbf8af7ed02d70db54f2..56fa3de6bba06bc0ac124bb3a41324be1997e209 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -6979,6 +6979,10 @@ on this implementation detail. @hook TARGET_OPTION_VALID_ATTRIBUTE_P +@hook TARGET_OPTION_VALID_VERSION_ATTRIBUTE_P + +@hook TARGET_OPTION_EXPANDED_CLONES_ATTRIBUTE + @hook TARGET_OPTION_SAVE @hook TARGET_OPTION_RESTORE diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc index a2ed048d7dd28ec470953fcd8a0dc86817e4b7dc..3db57c2b13d612a37240d9dcf58ad21b2286633c 100644 --- a/gcc/multiple_target.cc +++ b/gcc/multiple_target.cc @@ -66,10 +66,6 @@ create_dispatcher_calls (struct cgraph_node *node) { ipa_ref *ref; - if (!DECL_FUNCTION_VERSIONED (node->decl) - || !is_function_default_version (node->decl)) - return; - if (!targetm.has_ifunc_p ()) { error_at (DECL_SOURCE_LOCATION (node->decl), @@ -377,6 +373,7 @@ expand_target_clones (struct cgraph_node *node, bool definition) return false; } + const char *new_attr_name = targetm.target_option.expanded_clones_attribute; cgraph_function_version_info *decl1_v = NULL; cgraph_function_version_info *decl2_v = NULL; cgraph_function_version_info *before = NULL; @@ -392,7 +389,7 @@ expand_target_clones (struct cgraph_node *node, bool definition) char *attr = attrs[i]; /* Create new target clone. */ - tree attributes = make_attribute ("target", attr, + tree attributes = make_attribute (new_attr_name, attr, DECL_ATTRIBUTES (node->decl)); char *suffix = XNEWVEC (char, strlen (attr) + 1); @@ -430,7 +427,7 @@ expand_target_clones (struct cgraph_node *node, bool definition) XDELETEVEC (attr_str); /* Setting new attribute to initial function. */ - tree attributes = make_attribute ("target", "default", + tree attributes = make_attribute (new_attr_name, "default", DECL_ATTRIBUTES (node->decl)); DECL_ATTRIBUTES (node->decl) = attributes; node->local = false; diff --git a/gcc/target.def b/gcc/target.def index cda6c51e5167f85625168c7c26b777d6c8ccad82..39acea04db01ebaf918910b7dd73d397de6a84ec 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -6492,6 +6492,31 @@ the function declaration to hold a pointer to a target-specific\n\ bool, (tree fndecl, tree name, tree args, int flags), default_target_option_valid_attribute_p) +/* Function to validate the attribute((target_version(...))) strings. If + the option is validated, the hook should also fill in + DECL_FUNCTION_SPECIFIC_TARGET in the function decl node. */ +DEFHOOK +(valid_version_attribute_p, + "This hook is called to parse @code{attribute(target_version(\"...\"))},\n\ +which allows setting target-specific options on individual function versions.\n\ +These function-specific options may differ\n\ +from the options specified on the command line. The hook should return\n\ +@code{true} if the options are valid.\n\ +\n\ +The hook should set the @code{DECL_FUNCTION_SPECIFIC_TARGET} field in\n\ +the function declaration to hold a pointer to a target-specific\n\ +@code{struct cl_target_option} structure.", + bool, (tree fndecl, tree name, tree args, int flags), + default_target_option_valid_version_attribute_p) + +/* Attribute to be used when expanding clones for functions with + target_clones attribute. */ +DEFHOOKPOD +(expanded_clones_attribute, + "Contains the name of the attribute used for the version description string\n\ +when expanding clones for a function with the target_clones attribute.", + const char *, "target") + /* Function to save any extra target state in the target options structure. */ DEFHOOK (save, diff --git a/gcc/targhooks.h b/gcc/targhooks.h index 1a0db8dddd594d9b1fb04ae0d9a66ad6b7a396dc..0efc993d82ef59b581a1df74ee0de71135a28703 100644 --- a/gcc/targhooks.h +++ b/gcc/targhooks.h @@ -192,6 +192,7 @@ extern bool default_hard_regno_scratch_ok (unsigned int); extern bool default_mode_dependent_address_p (const_rtx, addr_space_t); extern bool default_new_address_profitable_p (rtx, rtx_insn *, rtx); extern bool default_target_option_valid_attribute_p (tree, tree, tree, int); +extern bool default_target_option_valid_version_attribute_p (tree, tree, tree, int); extern bool default_target_option_pragma_parse (tree, tree); extern bool default_target_can_inline_p (tree, tree); extern bool default_update_ipa_fn_target_info (unsigned int &, const gimple *); diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc index e190369f87a92e6a92372dc348d9374c3a965c0a..7fc7bf455e80c333cced1bac7085210c2b108f8d 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -1787,7 +1787,19 @@ default_target_option_valid_attribute_p (tree ARG_UNUSED (fndecl), int ARG_UNUSED (flags)) { warning (OPT_Wattributes, - "target attribute is not supported on this machine"); + "%<target%> attribute is not supported on this machine"); + + return false; +} + +bool +default_target_option_valid_version_attribute_p (tree ARG_UNUSED (fndecl), + tree ARG_UNUSED (name), + tree ARG_UNUSED (args), + int ARG_UNUSED (flags)) +{ + warning (OPT_Wattributes, + "%<target_version%> attribute is not supported on this machine"); return false; } diff --git a/gcc/tree.h b/gcc/tree.h index 0b72663e6a1a94406127f6253460f498b7a3ea9c..ebd89ce79566c350eaaab210c0dca3cc1ac2048e 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -3438,8 +3438,8 @@ extern vec<tree, va_gc> **decl_debug_args_insert (tree); (FUNCTION_DECL_CHECK (NODE)->function_decl.function_specific_optimization) /* In FUNCTION_DECL, this is set if this function has other versions generated - using "target" attributes. The default version is the one which does not - have any "target" attribute set. */ + to support different architecture feature sets, e.g. using "target" or + "target_version" attributes. */ #define DECL_FUNCTION_VERSIONED(NODE)\ (FUNCTION_DECL_CHECK (NODE)->function_decl.versioned_function)