Implement TARGET_CHECK_TARGET_CLONE_VERSION for x86 by reusing target
attribute parsing with optional diagnostics and location overrides.

This allows us to check the validity of target_clones versions and emit
warnings for invalid or empty versions instead of errors, which is
especially useful for next-generation targets like x86-64-v5 that may be
annotated in software but current compilers do not support them yet,
allowing for smoother transitions and better user experience.

Add an i386 warning test for invalid target_clones versions and update
existing i386 tests to expect warnings for invalid/empty versions.

Signed-off-by: Yangyu Chen <[email protected]>

gcc/ChangeLog:

        * config/i386/i386-builtins.cc (get_builtin_code_for_version):
        Update ix86_valid_target_attribute_tree call signature.
        * config/i386/i386-c.cc (ix86_pragma_target_parse):
        Ditto.
        * config/i386/i386-options.cc (ix86_valid_target_attribute_inner_p):
        Accept location and reporting control, and gate diagnostics.
        (ix86_valid_target_attribute_tree):
        Thread location into target attribute validation.
        (ix86_valid_target_attribute_p):
        Adjust for updated helper signature.
        (ix86_check_target_clone_version):
        New target_clones validation with warning-based diagnostics.
        * config/i386/i386-protos.h (ix86_check_target_clone_version):
        Declare new hook and update helper signature.
        * config/i386/i386.cc (TARGET_CHECK_TARGET_CLONE_VERSION):
        Wire x86 hook implementation.

gcc/testsuite/ChangeLog:

        * gcc.target/i386/mvc12.c:
        Adjust target_clones empty-string diagnostic to a warning.
        * gcc.target/i386/pr78419.c:
        Expect warning for invalid target_clones version.
        * gcc.target/i386/mvc-warning1.c: New test.
        Add warning coverage for invalid target_clones version.
---
Note: I think this patch would make our software system make use of new
extensions like x86-APX by annotating with target_clones. But when such code
was added to the codebase, it would be compiled by older compilers that do not
understand the new target_clones versions, and without this patch, that would
cause hard errors and break the build. With this patch, we can instead emit
warnings for unrecognized versions, allowing the code to compile successfully
while still alerting developers to the fact that their target_clones version is
not recognized by the current compiler, which is a much smoother experience
during transitions to new architectures.
---
 gcc/config/i386/i386-builtins.cc             |  2 +-
 gcc/config/i386/i386-c.cc                    |  3 +-
 gcc/config/i386/i386-options.cc              | 98 ++++++++++++++++----
 gcc/config/i386/i386-protos.h                |  4 +-
 gcc/config/i386/i386.cc                      |  3 +
 gcc/testsuite/gcc.target/i386/mvc-warning1.c |  9 ++
 gcc/testsuite/gcc.target/i386/mvc12.c        |  2 +-
 gcc/testsuite/gcc.target/i386/pr78419.c      |  4 +-
 8 files changed, 103 insertions(+), 22 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/mvc-warning1.c

diff --git a/gcc/config/i386/i386-builtins.cc b/gcc/config/i386/i386-builtins.cc
index f886c560f27..365e63279a6 100644
--- a/gcc/config/i386/i386-builtins.cc
+++ b/gcc/config/i386/i386-builtins.cc
@@ -1848,7 +1848,7 @@ get_builtin_code_for_version (tree decl, tree 
*predicate_list)
                             &global_options_set);
       target_node
        = ix86_valid_target_attribute_tree (decl, attrs, &global_options,
-                                           &global_options_set, 0);
+                                           &global_options_set, 0, NULL);
 
       gcc_assert (target_node);
       if (target_node == error_mark_node)
diff --git a/gcc/config/i386/i386-c.cc b/gcc/config/i386/i386-c.cc
index 15e82956d0d..3355da36c60 100644
--- a/gcc/config/i386/i386-c.cc
+++ b/gcc/config/i386/i386-c.cc
@@ -806,7 +806,8 @@ ix86_pragma_target_parse (tree args, tree pop_target)
     {
       cur_tree = ix86_valid_target_attribute_tree (NULL_TREE, args,
                                                   &global_options,
-                                                  &global_options_set, 0);
+                                                  &global_options_set,
+                                                  0, NULL);
       if (!cur_tree || cur_tree == error_mark_node)
        {
          cl_target_option_restore (&global_options, &global_options_set,
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index 7459cde4ba8..30e161ff22e 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -985,7 +985,9 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
                                     struct gcc_options *opts,
                                     struct gcc_options *opts_set,
                                     struct gcc_options *enum_opts_set,
-                                    bool target_clone_attr)
+                                    bool target_clone_attr,
+                                    location_t *loc_p,
+                                    bool report_errors)
 {
   char *next_optstr;
   bool ret = true;
@@ -1182,8 +1184,11 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
                   MASK_RELAX_CMPXCHG_LOOP),
   };
 
-  location_t loc
-    = fndecl == NULL ? UNKNOWN_LOCATION : DECL_SOURCE_LOCATION (fndecl);
+  location_t loc = UNKNOWN_LOCATION;
+  if (loc_p && *loc_p != UNKNOWN_LOCATION)
+    loc = *loc_p;
+  else if (fndecl != NULL)
+    loc = DECL_SOURCE_LOCATION (fndecl);
   const char *attr_name = target_clone_attr ? "target_clone" : "target";
 
   /* If this is a list, recurse to get the options.  */
@@ -1194,7 +1199,8 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
            && !ix86_valid_target_attribute_inner_p (fndecl, TREE_VALUE (args),
                                                     p_strings, opts, opts_set,
                                                     enum_opts_set,
-                                                    target_clone_attr))
+                                                    target_clone_attr,
+                                                    loc_p, report_errors))
          ret = false;
 
       return ret;
@@ -1202,7 +1208,8 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
 
   else if (TREE_CODE (args) != STRING_CST)
     {
-      error_at (loc, "attribute %qs argument is not a string", attr_name);
+      if (report_errors)
+       error_at (loc, "attribute %qs argument is not a string", attr_name);
       return false;
     }
 
@@ -1266,8 +1273,9 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
       /* Process the option.  */
       if (opt == N_OPTS)
        {
-         error_at (loc, "attribute %qs argument %qs is unknown",
-                   attr_name, orig_p);
+         if (report_errors)
+           error_at (loc, "attribute %qs argument %qs is unknown",
+                     attr_name, orig_p);
          ret = false;
        }
 
@@ -1299,8 +1307,9 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
            {
              if (!opt_set_p)
                {
-                 error_at (loc, "pragma or attribute %<target(\"%s\")%> "
-                           "does not allow a negated form", p);
+                 if (report_errors)
+                   error_at (loc, "pragma or attribute %<target(\"%s\")%> "
+                             "does not allow a negated form", p);
                  return false;
                }
 
@@ -1331,8 +1340,9 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
        {
          if (p_strings[opt])
            {
-             error_at (loc, "attribute value %qs was already specified "
-                       "in %qs attribute", orig_p, attr_name);
+             if (report_errors)
+               error_at (loc, "attribute value %qs was already specified "
+                         "in %qs attribute", orig_p, attr_name);
              ret = false;
            }
          else
@@ -1371,8 +1381,10 @@ ix86_valid_target_attribute_inner_p (tree fndecl, tree 
args, char *p_strings[],
                        global_dc);
          else
            {
-             error_at (loc, "attribute value %qs is unknown in %qs attribute",
-                       orig_p, attr_name);
+             if (report_errors)
+               error_at (loc, "attribute value %qs is unknown in %qs "
+                         "attribute",
+                         orig_p, attr_name);
              ret = false;
            }
        }
@@ -1399,7 +1411,8 @@ tree
 ix86_valid_target_attribute_tree (tree fndecl, tree args,
                                  struct gcc_options *opts,
                                  struct gcc_options *opts_set,
-                                 bool target_clone_attr)
+                                 bool target_clone_attr,
+                                 location_t *loc_p)
 {
   const char *orig_arch_string = opts->x_ix86_arch_string;
   const char *orig_tune_string = opts->x_ix86_tune_string;
@@ -1420,7 +1433,7 @@ ix86_valid_target_attribute_tree (tree fndecl, tree args,
   /* Process each of the options on the chain.  */
   if (!ix86_valid_target_attribute_inner_p (fndecl, args, option_strings, opts,
                                            opts_set, &enum_opts_set,
-                                           target_clone_attr))
+                                           target_clone_attr, loc_p, true))
     return error_mark_node;
 
   /* If the changed options are different from the default, rerun
@@ -1561,7 +1574,7 @@ ix86_valid_target_attribute_p (tree fndecl,
   /* FLAGS == 1 is used for target_clones attribute.  */
   new_target
     = ix86_valid_target_attribute_tree (fndecl, args, &func_options,
-                                       &func_options_set, flags == 1);
+                                       &func_options_set, flags == 1, NULL);
 
   new_optimize = build_optimization_node (&func_options, &func_options_set);
 
@@ -1588,6 +1601,59 @@ ix86_valid_target_attribute_p (tree fndecl,
   return ret;
 }
 
+/* Implement TARGET_CHECK_TARGET_CLONE_VERSION.  */
+
+bool
+ix86_check_target_clone_version (string_slice str, location_t *loc)
+{
+  str = str.strip ();
+
+  if (str == "default")
+    return true;
+
+  if (str.size () == 0)
+    {
+      if (loc)
+       warning_at (*loc, OPT_Wattributes,
+                   "empty string not valid for a %<target_clones%> version");
+      return false;
+    }
+
+  size_t len = str.size ();
+  char *buf = XNEWVEC (char, len + 1);
+  memcpy (buf, str.begin (), len);
+  buf[len] = '\0';
+
+  tree arg = build_string (len + 1, buf);
+  XDELETEVEC (buf);
+
+  struct gcc_options opts, opts_set;
+  struct gcc_options enum_opts_set;
+  memset (&opts, 0, sizeof (opts));
+  init_options_struct (&opts, NULL);
+  lang_hooks.init_options_struct (&opts);
+  memset (&opts_set, 0, sizeof (opts_set));
+  memset (&enum_opts_set, 0, sizeof (enum_opts_set));
+
+  cl_target_option_restore (&opts, &opts_set,
+         TREE_TARGET_OPTION (target_option_default_node));
+
+  char *option_strings[IX86_FUNCTION_SPECIFIC_MAX] = { NULL, NULL };
+  bool ok
+    = ix86_valid_target_attribute_inner_p (NULL_TREE, arg, option_strings,
+          &opts, &opts_set,
+          &enum_opts_set, true,
+          loc, false);
+  release_options_strings (option_strings);
+
+  if (!ok && loc)
+    warning_at (*loc, OPT_Wattributes,
+               "invalid version %qB for %<target_clones%> attribute",
+               &str);
+
+  return ok;
+}
+
 const char *stringop_alg_names[] = {
 #define DEF_ALG(alg, name) #name,
 #include "stringop.def"
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 20b96474858..1856a57060a 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -281,7 +281,9 @@ extern tree ix86_handle_shared_attribute (tree *, tree, 
tree, int, bool *);
 extern int x86_field_alignment (tree, int);
 extern tree ix86_valid_target_attribute_tree (tree, tree,
                                              struct gcc_options *,
-                                             struct gcc_options *, bool);
+                                             struct gcc_options *, bool,
+                                             location_t *);
+extern bool ix86_check_target_clone_version (string_slice, location_t *);
 extern unsigned int ix86_get_callcvt (const_tree);
 extern bool ix86_type_no_callee_saved_registers_p (const_tree);
 
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 42ae9ccb051..ea90ab69b30 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -28112,6 +28112,9 @@ static const scoped_attribute_specs *const 
ix86_attribute_table[] =
 #undef TARGET_GIMPLE_FOLD_BUILTIN
 #define TARGET_GIMPLE_FOLD_BUILTIN ix86_gimple_fold_builtin
 
+#undef TARGET_CHECK_TARGET_CLONE_VERSION
+#define TARGET_CHECK_TARGET_CLONE_VERSION ix86_check_target_clone_version
+
 #undef TARGET_COMPARE_VERSION_PRIORITY
 #define TARGET_COMPARE_VERSION_PRIORITY ix86_compare_version_priority
 
diff --git a/gcc/testsuite/gcc.target/i386/mvc-warning1.c 
b/gcc/testsuite/gcc.target/i386/mvc-warning1.c
new file mode 100644
index 00000000000..4247c264ade
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/mvc-warning1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "random-arch-string")))
+int foo () /* { dg-warning "invalid version 
.*random-arch-string.*target_clones" } */
+{
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.target/i386/mvc12.c 
b/gcc/testsuite/gcc.target/i386/mvc12.c
index f42ae8080e6..155fc68bf1e 100644
--- a/gcc/testsuite/gcc.target/i386/mvc12.c
+++ b/gcc/testsuite/gcc.target/i386/mvc12.c
@@ -2,7 +2,7 @@
 /* { dg-require-ifunc "" } */
 
 __attribute__((target_clones("","arch=slm","arch=core-avx2", "default")))
-int foo (); /* { dg-error "an empty string cannot be in .target_clones. 
attribute" } */
+int foo (); /* { dg-warning "empty string not valid for a .target_clones. 
version" } */
 
 int
 bar ()
diff --git a/gcc/testsuite/gcc.target/i386/pr78419.c 
b/gcc/testsuite/gcc.target/i386/pr78419.c
index 0f2431bef20..6a148eb0ed8 100644
--- a/gcc/testsuite/gcc.target/i386/pr78419.c
+++ b/gcc/testsuite/gcc.target/i386/pr78419.c
@@ -3,7 +3,7 @@
 /* { dg-require-ifunc "" } */
 
 static double bar (double *__restrict, double *__restrict, int)
-__attribute__ ((target_clones("avx,foo,avx2,avx512f,default")));
+__attribute__ ((target_clones("avx,foo,avx2,avx512f,default"))); /* { 
dg-warning "invalid version .*foo.*target_clones" } */
 
 double
 foo (double *__restrict a, double *__restrict b, int n)
@@ -12,7 +12,7 @@ foo (double *__restrict a, double *__restrict b, int n)
 }
 
 double
-bar (double *__restrict a, double *__restrict b, int n)        /* { dg-error 
"attribute\[^\n\r]*foo\[^\n\r]* is unknown" } */
+bar (double *__restrict a, double *__restrict b, int n)
 {
   double s;
   int i;
-- 
2.51.0

Reply via email to