This patch enables passing TARGET_CLNOES as a GCC attribute, which allows C-style argument syntax:
`!GCC$ ATTRIBUTES TARGET_CLONES("default", "avx", "avx512f") :: MySub` or `!GCC$ ATTRIBUTES TARGET_CLONES('default', 'avx512f') :: MySub` This feature added a parser in f95-lang.cc to support parsing arguments, and added `TARGET_CLONES` as a attribute in `ext_attr_list` Signed-off-by: ZAMBAR <zam...@163.com> gcc/fortran/ChangeLog: * decl.cc (gfc_match_gcc_attributes): Add target_clones attribute parsing * f95-lang.cc (gfc_handle_target_clones_attribute): Add handler for target_clones attribute * gfortran.h: Add target_clones_args and target_clones_count to gfc_symbol * symbol.cc (gfc_free_symbol): Free target_clones_args * trans-decl.cc (add_attributes_to_decl): Add target_clones attribute to the declaration --- gcc/fortran/decl.cc | 116 ++++++++++++++++++++++++++++++++++++++ gcc/fortran/f95-lang.cc | 34 +++++++++++ gcc/fortran/gfortran.h | 5 ++ gcc/fortran/symbol.cc | 8 +++ gcc/fortran/trans-decl.cc | 24 +++++++- 5 files changed, 186 insertions(+), 1 deletion(-) diff --git a/gcc/fortran/decl.cc b/gcc/fortran/decl.cc index 69acd2da981..39d5fc31b82 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, "target_clones" }, { 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/fortran/trans-decl.cc b/gcc/fortran/trans-decl.cc index 43bd7be54cb..bf37b13830a 100644 --- a/gcc/fortran/trans-decl.cc +++ b/gcc/fortran/trans-decl.cc @@ -1507,7 +1507,29 @@ add_attributes_to_decl (tree *decl_p, const gfc_symbol *sym) if (sym_attr.ext_attr & (1 << id) && ext_attr_list[id].middle_end_name) { tree ident = get_identifier (ext_attr_list[id].middle_end_name); - list = tree_cons (ident, NULL_TREE, list); + + /* Special handling for target_clones attribute with arguments */ + if (id == EXT_ATTR_TARGET_CLONES && sym->target_clones_args && sym->target_clones_count > 0) + { + tree args = NULL_TREE; + + /* Create string constants for each target clone argument */ + for (int i = sym->target_clones_count - 1; i >= 0; i--) + { + tree str = build_string (strlen (sym->target_clones_args[i]), + sym->target_clones_args[i]); + TREE_TYPE (str) = build_array_type (char_type_node, + build_index_type (size_int (strlen (sym->target_clones_args[i])))); + args = tree_cons (NULL_TREE, str, args); + } + + /* Add the target_clones attribute with its arguments */ + list = tree_cons (ident, args, list); + } + else + { + list = tree_cons (ident, NULL_TREE, list); + } } tree clauses = NULL_TREE; -- 2.34.1