[RFC 1/1] fortran: Add TARGET_CLONES attribute support

ZAMBAR zambar@163.com
Sun Jul 13 09:18:00 GMT 2025


From: ZAMBAR <hezb2023@shanghaitech.edu.cn>

This patch implements the TARGET_CLONES attribute for Fortran functions,
using C-style comma-separated syntax for target specifications.

The implementation adds:
- Parsing support for TARGET_CLONES("target1", "target2", ...)
- Integration with existing multiple target infrastructure
- Proper memory management for target arguments
- Error handling for malformed syntax

Example usage:
    !GCC$ ATTRIBUTES TARGET_CLONES("default", "avx", "avx512f") :: function_name

---
 gcc/fortran/decl.cc     | 116 ++++++++++++++++++++++++++++++++++++++++
 gcc/fortran/f95-lang.cc |  34 ++++++++++++
 gcc/fortran/gfortran.h  |   5 ++
 gcc/fortran/symbol.cc   |   8 +++
 gcc/multiple_target.cc  |   9 +++-
 5 files changed, 170 insertions(+), 2 deletions(-)

diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc
index 69acd2da981..ed7c82dd8a3 100644
--- a/gcc/fortran/decl.cc
+++ b/gcc/fortran/decl.cc
@@ -11879,6 +11879,7 @@ const ext_attr_t ext_attr_list[] = {
   { "noinline",     EXT_ATTR_NOINLINE,     NULL	       },
   { "noreturn",     EXT_ATTR_NORETURN,     NULL	       },
   { "weak",	    EXT_ATTR_WEAK,	   NULL	       },
+  { "target_clones", EXT_ATTR_TARGET_CLONES, NULL       },
   { NULL,           EXT_ATTR_LAST,         NULL        }
 };
 
@@ -11896,6 +11897,13 @@ const ext_attr_t ext_attr_list[] = {
 
    As there is absolutely no risk of confusion, we should never return
    MATCH_NO.  */
+
+/* Structure to temporarily hold target_clones arguments during parsing */
+typedef struct {
+  char **args;
+  int count;
+} target_clones_args_t;
+
 match
 gfc_match_gcc_attributes (void)
 {
@@ -11904,6 +11912,7 @@ gfc_match_gcc_attributes (void)
   unsigned id;
   gfc_symbol *sym;
   match m;
+  target_clones_args_t target_clones_data = { NULL, 0 };
 
   gfc_clear_attr (&attr);
   for(;;)
@@ -11926,6 +11935,85 @@ gfc_match_gcc_attributes (void)
       if (!gfc_add_ext_attribute (&attr, (ext_attr_id_t)id, &gfc_current_locus))
 	return MATCH_ERROR;
 
+      /* Handle target_clones attribute with arguments */
+      if (id == EXT_ATTR_TARGET_CLONES)
+	{
+	  /* Expect opening parenthesis for target_clones */
+	  if (gfc_match_char ('(') != MATCH_YES)
+	    {
+	      gfc_error ("Expected '(' after TARGET_CLONES attribute at %C");
+	      return MATCH_ERROR;
+	    }
+	  
+	  /* Parse comma-separated list of target specifications */
+	  for (;;)
+	    {
+	      gfc_expr *expr = NULL;
+	      
+	      /* Match quoted string argument */
+	      if (gfc_match_literal_constant (&expr, 0) == MATCH_YES)
+		{
+		  /* Verify it's a character constant */
+		  if (expr->expr_type == EXPR_CONSTANT && expr->ts.type == BT_CHARACTER)
+		    {
+		      target_clones_data.count++;
+		      target_clones_data.args = (char **) xrealloc (target_clones_data.args, 
+						         target_clones_data.count * sizeof (char *));
+		      
+		      /* Convert gfc_char_t* to char* */
+		      int len = expr->value.character.length;
+		      char *arg_str = (char *) xmalloc (len + 1);
+		      for (int i = 0; i < len; i++)
+			arg_str[i] = (char) expr->value.character.string[i];
+		      arg_str[len] = '\0';
+		      
+		      target_clones_data.args[target_clones_data.count - 1] = arg_str;
+		      gfc_free_expr (expr);
+		      
+		      /* Check for comma (more arguments) or closing parenthesis */
+		      gfc_gobble_whitespace ();
+		      if (gfc_match_char (',') == MATCH_YES)
+			{
+			  gfc_gobble_whitespace ();
+			  continue;
+			}
+		      else if (gfc_match_char (')') == MATCH_YES)
+			break;
+		      else
+			{
+			  gfc_error ("Expected ',' or ')' in TARGET_CLONES argument list at %C");
+			  goto target_clones_error;
+			}
+		    }
+		  else
+		    {
+		      gfc_free_expr (expr);
+		      gfc_error ("TARGET_CLONES arguments must be character constants at %C");
+		      goto target_clones_error;
+		    }
+		}
+	      else
+		{
+		  gfc_error ("Expected quoted string argument in TARGET_CLONES at %C");
+		  goto target_clones_error;
+		}
+	    }
+	  
+	  goto attribute_parsed;
+
+target_clones_error:
+	  if (target_clones_data.args)
+	    {
+	      for (int i = 0; i < target_clones_data.count; i++)
+		free (target_clones_data.args[i]);
+	      free (target_clones_data.args);
+	      target_clones_data.args = NULL;
+	      target_clones_data.count = 0;
+	    }
+	  return MATCH_ERROR;
+	}
+
+attribute_parsed:
       gfc_gobble_whitespace ();
       ch = gfc_next_ascii_char ();
       if (ch == ':')
@@ -11955,6 +12043,19 @@ gfc_match_gcc_attributes (void)
 
       sym->attr.ext_attr |= attr.ext_attr;
 
+      /* Apply target_clones arguments if this attribute was specified */
+      if (attr.ext_attr & (1 << EXT_ATTR_TARGET_CLONES))
+	{
+	  if (target_clones_data.args && target_clones_data.count > 0)
+	    {
+	      sym->target_clones_args = (char **) xmalloc (target_clones_data.count * sizeof (char *));
+	      sym->target_clones_count = target_clones_data.count;
+	      
+	      for (int i = 0; i < target_clones_data.count; i++)
+		sym->target_clones_args[i] = xstrdup (target_clones_data.args[i]);
+	    }
+	}
+
       if (gfc_match_eos () == MATCH_YES)
 	break;
 
@@ -11962,9 +12063,24 @@ gfc_match_gcc_attributes (void)
 	goto syntax;
     }
 
+  /* Clean up target_clones temporary data */
+  if (target_clones_data.args)
+    {
+      for (int i = 0; i < target_clones_data.count; i++)
+	free (target_clones_data.args[i]);
+      free (target_clones_data.args);
+    }
+
   return MATCH_YES;
 
 syntax:
+  /* Clean up target_clones temporary data on error */
+  if (target_clones_data.args)
+    {
+      for (int i = 0; i < target_clones_data.count; i++)
+	free (target_clones_data.args[i]);
+      free (target_clones_data.args);
+    }
   gfc_error ("Syntax error in !GCC$ ATTRIBUTES statement at %C");
   return MATCH_ERROR;
 }
diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index 1f09553142d..427b8f5b51c 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -87,6 +87,38 @@ gfc_handle_omp_declare_target_attribute (tree *, tree, tree, int, bool *)
   return NULL_TREE;
 }
 
+/* Handle a "target_clones" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+gfc_handle_target_clones_attribute (tree *node, tree name, tree args,
+				     int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  /* Ensure we have a function declaration.  */
+  if (TREE_CODE (*node) == FUNCTION_DECL)
+    {
+      for (tree t = args; t != NULL_TREE; t = TREE_CHAIN (t))
+	{
+	  tree value = TREE_VALUE (t);
+	  if (TREE_CODE (value) != STRING_CST)
+	    {
+	      error ("%qE attribute argument not a string constant", name);
+	      *no_add_attrs = true;
+	      return NULL_TREE;
+	    }
+	}
+
+      /* Do not inline functions with multiple clone targets.  */
+      DECL_UNINLINABLE (*node) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Table of valid Fortran attributes.  */
 static const attribute_spec gfc_gnu_attributes[] =
 {
@@ -100,6 +132,8 @@ static const attribute_spec gfc_gnu_attributes[] =
     gfc_handle_omp_declare_target_attribute, NULL },
   { "oacc function", 0, -1, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
+  { "target_clones", 1, -1, true, false, false, false,
+    gfc_handle_target_clones_attribute, NULL },
 };
 
 static const scoped_attribute_specs gfc_gnu_attribute_table =
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 46310a088f2..4baa54828ba 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -865,6 +865,7 @@ typedef enum
   EXT_ATTR_NOINLINE,
   EXT_ATTR_NORETURN,
   EXT_ATTR_WEAK,
+  EXT_ATTR_TARGET_CLONES,
   EXT_ATTR_LAST, EXT_ATTR_NUM = EXT_ATTR_LAST
 }
 ext_attr_id_t;
@@ -2077,6 +2078,10 @@ typedef struct gfc_symbol
   /* This is for determining where the symbol has been used first, for better
      location of error messages.  */
   locus formal_at;
+
+  /* Storage for target_clones attribute arguments.  */
+  char **target_clones_args;
+  int target_clones_count;
 }
 gfc_symbol;
 
diff --git a/gcc/fortran/symbol.cc b/gcc/fortran/symbol.cc
index 81aa81df2ee..ff0564491f9 100644
--- a/gcc/fortran/symbol.cc
+++ b/gcc/fortran/symbol.cc
@@ -3179,6 +3179,14 @@ gfc_free_symbol (gfc_symbol *&sym)
   if (sym->param_list)
     gfc_free_actual_arglist (sym->param_list);
 
+  /* Free target_clones arguments if present */
+  if (sym->target_clones_args)
+    {
+      for (int i = 0; i < sym->target_clones_count; i++)
+	      free (sym->target_clones_args[i]);
+      free (sym->target_clones_args);
+    }
+
   free (sym);
   sym = NULL;
 }
diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc
index d25277c0a93..c205aaa05ff 100644
--- a/gcc/multiple_target.cc
+++ b/gcc/multiple_target.cc
@@ -166,8 +166,8 @@ create_dispatcher_calls (struct cgraph_node *node)
 	}
     }
 
-  tree fname = clone_function_name (node->decl, "default");
-  symtab->change_decl_assembler_name (node->decl, fname);
+  /* Note: Original function is already renamed to .default in expand_target_clones */
+  /* No need to rename again - just proceed with dispatcher creation */
 
   if (node->definition)
     {
@@ -441,6 +441,11 @@ expand_target_clones (struct cgraph_node *node, bool definition)
 				    DECL_ATTRIBUTES (node->decl));
   DECL_ATTRIBUTES (node->decl) = attributes;
   node->local = false;
+  
+  /* Immediately rename original function to .default to avoid conflicts */
+  tree default_fname = clone_function_name (node->decl, "default");
+  symtab->change_decl_assembler_name (node->decl, default_fname);
+  
   return true;
 }
 
-- 
2.34.1



More information about the Gcc-patches mailing list