From: Simon Dardis <simon.dar...@imgtec.com>

New option for MIPS -mfunc-opt-list=FILE. This option takes a file which
has one function per line followed by a whitespace (space/tab) followed
by one or more attributes. Supported attributes are O2, Os,
code-read=pcrel, always_inline, noinline, mips16, nomips16, epi,
longcall.

Attributes are applied to functions that the compiler sees, so functions
listed that the compiler doesn't see are ignored.

Now understands the majority of function attributes. These are:
O1, O2, O3, Os, mips16, nomips16, always_inline, noinline, unused, used,
far, near, hot, cold, code_readable, alias, aligned, alloc_size,
alloc_align, assume_aligned, artifical, constructor, const, deprecated,
destructor, error, flatten, gnu_inline, interrupt,
keep_interrupts_masked, long_call, leaf, noclone, noreturn, malloc,
nonnull, nothrow, optimize, returns_nonnull, returns_twice, section,
pure, use_debug_exception_return, use_shadow_register_set, visibility,
warning, warn_unused_result, weak, weakref.

Syntax of attributes that take arguments is like: alias ("O2")
or nonnull (1,2)

Attach unknown attributes anyway.

Cherry-picked e2ff99868adedb1a563ee69b3076838dd7ae4450
from https://github.com/MIPS/gcc

Signed-off-by: Simon Dardis <simon.dar...@imgtec.com>
Signed-off-by: Faraz Shahbazker <fshahbaz...@wavecomp.com>
Signed-off-by: Aleksandar Rakic <aleksandar.ra...@htecgroup.com>
---
 gcc/config/mips/mips.cc  | 609 +++++++++++++++++++++++++++++++++++++++
 gcc/config/mips/mips.opt |   4 +
 gcc/doc/invoke.texi      |  33 +++
 3 files changed, 646 insertions(+)

diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index 55d06b87c0d..32fe62ce79b 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -697,6 +697,524 @@ mips_find_list (const char *var, struct mips_sdata_entry 
*list)
   return false;
 }
 
+/* Argument type descriptor.  */
+
+enum mips_func_opt_list_arg_t
+{
+  FOL_ARG_NONE,
+  FOL_ARG_STRING,
+  FOL_ARG_SINGLE_NUM,
+  FOL_ARG_OPTIONAL_NUM_LIST,
+  FOL_ARG_NUM_ONE_OR_TWO,
+  FOL_ARG_OPTIONAL_STRING,
+  FOL_ARG_OPTIONAL_NUM,
+  FOL_ARG_UNKNOWN
+};
+
+/* Collisons for FUNC_OPT_LIST.  Rather that just relying on the middle to
+   complain, check at parse time so we can produce accurate diagnositics.  */
+
+enum mips_fol_collides
+{
+  FOLC_O1,
+  FOLC_O2,
+  FOLC_O3,
+  FOLC_OS,
+  FOLC_MIPS16,
+  FOLC_NOMIPS16,
+  FOLC_ALWAYS_INLINE,
+  FOLC_NOINLINE,
+  FOLC_UNUSED,
+  FOLC_USED,
+  FOLC_FAR,
+  FOLC_NEAR,
+  FOLC_HOT,
+  FOLC_COLD,
+  FOLC_END
+};
+
+/* Part of FUNC_OPT_LIST.  Use a tuple to record the name to be matched against
+   which GCC uses internally, an optional second string if the name is required
+   to be an argument of a different attribute and a bitmask describing which
+   other entries collide with this entry.  */
+
+struct attr_desc
+{
+  const char * optstring;
+  const char * maintype;
+  enum mips_func_opt_list_arg_t arg_type;
+  int collisions;
+};
+
+/* This table encodes the strings to match against for parsing func-opt-list,
+   an optional string which the first is argument of, e.g. optimize ("O2")
+   and the colliding attributes.  */
+
+static const struct attr_desc mips_func_opt_list_strings[] = {
+  {"O1",       "optimize", FOL_ARG_NONE,
+   1 << FOLC_O2 | 1 << FOLC_O3 | 1 << FOLC_OS },
+  {"O2",       "optimize", FOL_ARG_NONE,
+   1 << FOLC_O1 | 1 << FOLC_O3 | 1 << FOLC_OS },
+  {"O3",       "optimize", FOL_ARG_NONE,
+   1 << FOLC_O1 | 1 << FOLC_O2 | 1 << FOLC_OS },
+  {"Os",       "optimize", FOL_ARG_NONE,
+   1 << FOLC_O1 | 1 << FOLC_O2 | 1 << FOLC_O3 },
+  {"mips16",            0,          FOL_ARG_NONE, 1 << FOLC_NOMIPS16 },
+  {"nomips16",          0,          FOL_ARG_NONE, 1 << FOLC_MIPS16 },
+  {"always_inline",     0,          FOL_ARG_NONE, 1 << FOLC_NOINLINE },
+  {"noinline",          0,          FOL_ARG_NONE, 1 << FOLC_ALWAYS_INLINE },
+  {"unused",            0,          FOL_ARG_NONE, 1 << FOLC_USED },
+  {"used",              0,          FOL_ARG_NONE, 1 << FOLC_UNUSED },
+  {"far",               0,          FOL_ARG_NONE, 1 << FOLC_NEAR },
+  {"near",              0,          FOL_ARG_NONE, 1 << FOLC_FAR },
+  {"hot",               0,          FOL_ARG_NONE, 1 << FOLC_COLD },
+  {"cold",              0,          FOL_ARG_NONE, 1 << FOLC_HOT },
+  {"code_readable",     0,          FOL_ARG_STRING, 0 },
+  {"alias",             0,          FOL_ARG_STRING, 0 },
+  {"aligned",           0,          FOL_ARG_SINGLE_NUM, 0},
+  {"alloc_size",        0,          FOL_ARG_NUM_ONE_OR_TWO, 0},
+  {"alloc_align",       0,          FOL_ARG_SINGLE_NUM, 0},
+  {"assume_aligned",    0,          FOL_ARG_NUM_ONE_OR_TWO, 0},
+  {"artifical",                 0,          FOL_ARG_NONE, 0 },
+  {"constructor",       0,          FOL_ARG_OPTIONAL_NUM, 0},
+  {"const",             0,          FOL_ARG_NONE, 0 },
+  {"deprecated",        0,          FOL_ARG_OPTIONAL_STRING, 0},
+  {"destructor",        0,          FOL_ARG_OPTIONAL_NUM, 0},
+  {"error",             0,          FOL_ARG_OPTIONAL_STRING, 0},
+  {"flatten",           0,          FOL_ARG_NONE, 0 },
+  {"gnu_inline",        0,          FOL_ARG_NONE, 0 },
+  {"interrupt",                 0,          FOL_ARG_NONE, 0 },
+  {"keep_interrupts_masked", 0,             FOL_ARG_NONE, 0 },
+  {"long_call",                 0,          FOL_ARG_NONE, 0 },
+  {"leaf",              0,          FOL_ARG_NONE, 0 },
+  {"noclone",           0,          FOL_ARG_NONE, 0 },
+  {"noreturn",          0,          FOL_ARG_NONE, 0 },
+  {"malloc",            0,          FOL_ARG_NONE, 0 },
+  {"nonnull",           0,          FOL_ARG_OPTIONAL_NUM_LIST, 0 },
+  {"nothrow",           0,          FOL_ARG_NONE, 0 },
+  {"optimize",          0,          FOL_ARG_STRING, 0 },
+  {"returns_nonnull",   0,          FOL_ARG_NONE, 0 },
+  {"returns_twice",     0,          FOL_ARG_NONE, 0 },
+  {"section",           0,          FOL_ARG_STRING, 0 },
+  {"pure",              0,          FOL_ARG_NONE, 0 },
+  {"use_debug_exception_return", 0,  FOL_ARG_NONE, 0 },
+  {"use_shadow_register_set", 0,     FOL_ARG_NONE, 0 },
+  {"visibility",        0,          FOL_ARG_STRING, 0 },
+  {"warning",           0,          FOL_ARG_OPTIONAL_STRING, 0 },
+  {"warn_unused_result", 0,         FOL_ARG_NONE, 0 },
+  {"weak",              0,          FOL_ARG_NONE, 0 },
+  {"weakref",           0,          FOL_ARG_NONE, 0 },
+  /* End of table marker, FOL_ARG_NONE is required to stop the attribute
+     argument parsing.  */
+  {"\0",                0,          FOL_ARG_UNKNOWN, 0 },
+};
+
+/* Argument list...  */
+
+struct GTY ((chain_next ("%h.next"))) mips_func_opt_list_arg
+{
+  char * GTY ((skip (""))) arg;
+  unsigned int optimization;
+  mips_func_opt_list_arg_t arg_type;
+  struct mips_func_opt_list_arg * next;
+};
+
+/* Unknown attribute list.  */
+
+struct GTY ((chain_next ("%h.next"))) mips_func_opt_unknown_list
+{
+  char * GTY ((skip (""))) attribute;
+  struct mips_func_opt_list_arg * args;
+  struct mips_func_opt_unknown_list * next;
+};
+
+/* ... For this.  */
+
+struct GTY ((chain_next ("%h.next"))) mips_func_opt_list
+{
+  char * GTY ((skip (""))) func_name;
+  sbitmap attributes;
+  struct mips_func_opt_unknown_list * unknowns;
+  struct mips_func_opt_list_arg * args;
+  struct mips_func_opt_list * next;
+};
+
+/* Head of the function optimization list.  */
+
+static struct GTY ((chain_next ("%h.next")))
+mips_func_opt_list * mips_fn_opt_list;
+
+/* Search the func-opt-list for func's entry and return it.  */
+
+static struct mips_func_opt_list *
+mips_func_opt_list_find (const char * func)
+{
+  struct mips_func_opt_list * i;
+  for (i = mips_fn_opt_list; i != NULL; i = i->next)
+    if (strcmp (i->func_name, func) == 0)
+      return i;
+
+  return NULL;
+}
+
+/* Check if OPT conflicts with the current attributes of f.  Return
+   the entry corresponding the existing attribute that conflicts.  */
+
+static int
+mips_fol_attr_conflicts (struct mips_func_opt_list * f,
+                       unsigned int opt)
+{
+  /* Trival case: opt has no conflicting attrs / leave for the middle end to
+     diagnose.  */
+  if (opt > FOLC_END)
+    return 0;
+
+  for (int i = 0; i < FOLC_END; i++)
+    if (bitmap_bit_p (f->attributes, i)
+       && ((1 << i) & mips_func_opt_list_strings[opt].collisions))
+      return i;
+
+  return 0;
+}
+
+#define MATCH_WHITESPACE(A) ISSPACE (A)
+#define MATCH_EMPTYSTRING(A) (A == '\n' || A == 0 || A == EOF)
+
+/* Subroutinue for below.  */
+
+static void
+mips_func_opt_list_parse_arg_1 (const char * line,
+                               struct mips_func_opt_list * f,
+                               unsigned int pos,
+                               unsigned int length,
+                               unsigned int opt)
+{
+  struct mips_func_opt_list_arg * arg
+    = (struct mips_func_opt_list_arg *)xcalloc (1,
+                                      sizeof (struct mips_func_opt_list_arg));
+
+  arg->arg = xstrndup (&(line[pos]), length);
+  arg->optimization = opt;
+  arg->next = f->args;
+
+  if (mips_func_opt_list_strings[opt].optstring == 0)
+    f->unknowns->args = arg;
+  else
+    f->args = arg;
+}
+
+/* Parse an argument of an attribute of the form:
+   ("string") or (N<,N,N,...>)
+   and attach it to the passed struct mips_func_opt_list.
+   Return the position: either just after ')' or the start of next
+   word.  */
+
+static unsigned int
+mips_func_opt_list_parse_arg (const char * line, struct mips_func_opt_list * f,
+                             unsigned int opt, unsigned int pos,
+                             const char * file, int lineno)
+{
+  enum mips_func_opt_list_arg_t arg_type;
+  arg_type = mips_func_opt_list_strings[opt].arg_type;
+  unsigned int length = 0;
+  unsigned int arg_count = 0;
+
+  if (arg_type == FOL_ARG_NONE)
+    return pos;
+
+  /* Match "<whitespace>+("  */
+  while (MATCH_WHITESPACE (line[pos]))
+    pos++;
+
+  if (MATCH_EMPTYSTRING (line[pos])
+      || line[pos] != '(')
+    {
+      if (line[pos] != '('
+         && (arg_type == FOL_ARG_OPTIONAL_NUM_LIST
+             || arg_type == FOL_ARG_OPTIONAL_STRING
+             || arg_type == FOL_ARG_OPTIONAL_NUM
+             || arg_type == FOL_ARG_UNKNOWN))
+       return pos;
+      else
+       error ("%s:%d:%d: Expected '('", file, lineno, pos);
+    }
+
+  pos += 1;
+  while (MATCH_WHITESPACE (line[pos])
+        && !MATCH_EMPTYSTRING (line[pos]))
+    pos++;
+
+  /* Handle the case of an unknown optimization.  Despite not knowing the
+     format of the arguments, we assume its either a string or an list of
+     numbers.  Peek at the input to correct arg_type.  */
+
+  if (arg_type == FOL_ARG_UNKNOWN)
+    {
+      if (line[pos] == '\"')
+       arg_type = FOL_ARG_STRING;
+      else
+       arg_type = FOL_ARG_OPTIONAL_NUM_LIST;
+    }
+
+  switch (arg_type)
+  {
+    /* Parse something of the form ("<string>") and add <string> to e->args.  
*/
+    case FOL_ARG_OPTIONAL_STRING:
+    case FOL_ARG_STRING:
+      if (MATCH_EMPTYSTRING (line[pos])
+         || line[pos] != '\"')
+       /* Unexpected line end.  */
+       error ("%s:%d:%d: Expected '\"'", file, lineno, pos);
+
+      pos += 1;
+
+      while (!MATCH_EMPTYSTRING (line[pos+length])
+            && line[pos+length] != '\"')
+       length += 1;
+
+      mips_func_opt_list_parse_arg_1 (line, f, pos, length, opt);
+
+      pos += length + 1;
+      break;
+
+    /* Parse something of the form (N<,N,N,N,..>) and add them individually
+       to e->args.  */
+    case FOL_ARG_SINGLE_NUM:
+    case FOL_ARG_OPTIONAL_NUM:
+    case FOL_ARG_OPTIONAL_NUM_LIST:
+    case FOL_ARG_NUM_ONE_OR_TWO:
+      while (1)
+       {
+         while (ISDIGIT (line[pos+length]))
+           length++;
+
+         mips_func_opt_list_parse_arg_1 (line, f, pos, length, opt);
+         pos += length;
+         length = 0;
+         arg_count++;
+
+         while (MATCH_WHITESPACE (line[pos]))
+           pos++;
+
+         if (MATCH_EMPTYSTRING (line[pos])
+             && (line[pos] != ','
+                 || line[pos] != ')'))
+           error ("%s:%d:%d: Expected ',' or ')'", file, lineno, pos);
+
+         if (line[pos] == ','
+             && (arg_type == FOL_ARG_SINGLE_NUM
+                 || (arg_type == FOL_ARG_NUM_ONE_OR_TWO
+                     && arg_count > 2)))
+           error ("%s:%d:%d: Unexpected ','", file, lineno, pos);
+
+         if (line[pos] == ')')
+           break;
+
+         pos += 1;
+         while (MATCH_WHITESPACE (line[pos]))
+           pos++;
+       }
+      break;
+    default:
+      gcc_unreachable ();
+  }
+
+  while (MATCH_WHITESPACE (line[pos])
+        && !MATCH_EMPTYSTRING (line[pos]))
+   pos++;
+
+  /* Unmatched ')'.  */
+  if (MATCH_EMPTYSTRING (line[pos])
+     || line[pos] != ')')
+  error ("%s:%d:%d: Expected ')'", file, lineno, pos);
+
+  pos += 1;
+
+  return pos;
+}
+
+/* Read a line and return a struct describing attributes for the function.
+   Odd case: If the function has already appeared in mips_fn_opt_list update
+   the entry in place but return NULL, otherwise we may end up building a
+   circular list.  Error out nicely when: misspelled optimization, conflicting
+   attributes.  */
+
+static struct mips_func_opt_list *
+mips_func_opt_list_read_line (const char * line, const char * file, int lineno)
+{
+  size_t identifier_length = 0;
+  unsigned int opt = 0;
+  struct mips_func_opt_list * fl = NULL;
+  unsigned int pos = 0;
+  bool matched = false;
+  bool update = false;
+
+  /* Take all leading whitespace.  */
+  while (MATCH_WHITESPACE (line[pos]))
+    pos++;
+
+  if (MATCH_EMPTYSTRING (line[pos]))
+      return NULL;
+
+  /* Take all non-whitespace for the function name.  */
+  while (!MATCH_WHITESPACE (line[pos + identifier_length])
+        && !MATCH_EMPTYSTRING (line[pos + identifier_length]))
+    identifier_length++;
+
+  /* Construct a new struct mips_func_opt_list temporarily and search for an
+     existing entry.  Use existing entry over a new one.  */
+  fl = (struct mips_func_opt_list *)
+       xcalloc (1, sizeof (struct mips_func_opt_list));
+  fl->func_name = xstrndup (&line[pos], identifier_length);
+  if (mips_func_opt_list_find (fl->func_name) == NULL)
+    {
+      fl->args = NULL;
+      fl->unknowns = NULL;
+      fl->next = NULL;
+      fl->attributes = sbitmap_alloc (sizeof (mips_func_opt_list_strings)
+                                     / sizeof (struct attr_desc));
+      bitmap_clear (fl->attributes);
+    }
+  else
+    {
+      char * n = fl->func_name;
+      free (fl);
+      fl = mips_func_opt_list_find (n);
+      free (n);
+      update = true;
+    }
+
+  pos += identifier_length;
+  identifier_length = 0;
+
+  /* Warn for <func name> <empty string>  */
+  if (MATCH_EMPTYSTRING (line[pos]))
+    {
+      warning (OPT_Wattributes, "%s:%d: No optimizations specified for %qs",
+              file, lineno, fl->func_name);
+      return NULL;
+    }
+
+  /* Parse a (possibly empty) list of attributes.  */
+  while (1)
+  {
+    while (MATCH_WHITESPACE (line[pos]))
+      pos++;
+
+    if (MATCH_EMPTYSTRING (line[pos]))
+      {
+       if (update)
+         return NULL;
+       else
+         return fl;
+      }
+
+    /* Parse and match an attribute.  Warn and blame -mfunc-opt-list= if
+       the attributes are known to conflict.  The middle-end will complain as
+       well, but won't be able to blame the source properly.  */
+    while (!MATCH_WHITESPACE (line[pos + identifier_length])
+          && !MATCH_EMPTYSTRING (line[pos + identifier_length])
+          && line[pos + identifier_length] != '(')
+      identifier_length++;
+
+    for (opt = 0; *mips_func_opt_list_strings[opt].optstring != 0; opt++)
+      {
+       if (mips_func_opt_list_strings[opt].optstring != 0
+           && strncmp (mips_func_opt_list_strings[opt].optstring, &(line[pos]),
+                       identifier_length) == 0)
+         {
+           int conflict_attr = mips_fol_attr_conflicts (fl, opt);
+           if (conflict_attr)
+             warning (OPT_Wattributes, "%s:%d:%d: Attribute %qs cannot be"
+                      " applied to function %qs as it has the conflicting "
+                      "attribute %qs", file, lineno, pos,
+                      mips_func_opt_list_strings[opt].optstring,
+                      fl->func_name,
+                      mips_func_opt_list_strings[conflict_attr].optstring);
+
+           bitmap_set_bit (fl->attributes, opt);
+           matched = true;
+           break;
+         }
+      }
+
+    /* Correctly blame the unknown attribute.  */
+    if (!matched)
+      {
+       struct mips_func_opt_unknown_list * e
+        = (struct mips_func_opt_unknown_list*)
+            xcalloc (1, sizeof (struct mips_func_opt_unknown_list));
+       e->next = fl->unknowns;
+       e->attribute = xstrndup (&(line[pos]), identifier_length);
+       warning (OPT_Wattributes, "%s:%d:%d: Unknown attribute %qs for %qs",
+                file, lineno, pos, e->attribute, fl->func_name);
+       fl->unknowns = e;
+      }
+
+    matched = false;
+    pos += identifier_length;
+    identifier_length = 0;
+
+    /* Parse any arguments if required, get new position.  */
+    pos = mips_func_opt_list_parse_arg (line, fl, opt, pos, file, lineno);
+  }
+}
+
+#undef MATCH_WHITESPACE
+#undef MATCH_EMPTYSTRING
+
+/* Entry point for FUNC_OPT_LIST.  Grab the conents of a file and build
+   a list of functions with a bitmap describing attributes desired.  */
+
+static void
+mips_func_opt_list_read ()
+{
+  FILE * fd;
+  int lineno = 0;
+  char line[512];
+  struct mips_func_opt_list *trial = NULL;
+
+  unsigned int i;
+  cl_deferred_option *opt;
+  vec<cl_deferred_option> *v;
+  v = (vec<cl_deferred_option> *) mips_func_opt_list_file;
+
+  FOR_EACH_VEC_ELT (*v, i, opt)
+    {
+      if (opt->opt_index != OPT_mfunc_opt_list_)
+       continue;
+
+      const char * filename = opt->arg;
+      if (filename == NULL)
+       continue;
+
+      fd = fopen (filename, "r");
+      if (fd == NULL)
+       {
+         error ("Cannot read %qs for -mfunc-opt-list=\n", filename);
+         return;
+       }
+
+      while (fgets (line, sizeof (line), fd))
+       {
+         trial = mips_func_opt_list_read_line ((const char *)&line,
+                                               filename, lineno);
+         lineno++;
+
+         /* trial can be null if we didn't read a well formed line or if an
+            update in place occurred.  Otherwise insert new entry at the head
+            of the list.  */
+         if (trial)
+           {
+             trial->next = mips_fn_opt_list;
+             mips_fn_opt_list = trial;
+           }
+       }
+
+      fclose (fd);
+    }
+}
+
 /* A table describing all the processors GCC knows about; see
    mips-cpus.def for details.  */
 static const struct mips_cpu_info mips_cpu_info_table[] = {
@@ -1569,6 +2087,86 @@ mips_comp_type_attributes (const_tree type1, const_tree 
type2)
   return 1;
 }
 
+/* Return a tree of the arguments of an attribute list.  */
+
+static tree
+mips_insert_fol_args (struct mips_func_opt_list_arg * args,
+                     unsigned int optimization,
+                     mips_func_opt_list_arg_t arg_type)
+{
+  tree ret = NULL;
+  for (; args; args = args->next)
+      if (args->optimization == optimization)
+       {
+         tree arg;
+         if (arg_type == FOL_ARG_OPTIONAL_STRING
+             || arg_type == FOL_ARG_STRING)
+           arg = build_string (strlen (args->arg), args->arg);
+         else
+           arg = build_int_cst (NULL, atoi (args->arg));
+
+         if (ret == NULL)
+           ret = build_tree_list (NULL_TREE, arg);
+         else
+           ret = chainon (ret, build_tree_list (NULL_TREE, arg));
+       }
+  return ret;
+}
+
+/* Implement -mfunc-opt-list.  With the struct of optmizations and attributes,
+   insert the attributes.  */
+
+static void
+mips_insert_fol_attributes (struct mips_func_opt_list * func_opt_list,
+                          tree *attributes)
+{
+  for (int i = 0; *mips_func_opt_list_strings[i].optstring != 0; i++)
+    if (bitmap_bit_p (func_opt_list->attributes, i))
+      {
+       const char * opstr = mips_func_opt_list_strings[i].optstring;
+       const char * maintype = mips_func_opt_list_strings[i].maintype;
+       int l = strlen (opstr);
+
+       tree attr_args = NULL;
+       if (mips_func_opt_list_strings[i].arg_type != FOL_ARG_NONE)
+           attr_args = mips_insert_fol_args (func_opt_list->args, i,
+                                       mips_func_opt_list_strings[i].arg_type);
+
+       /* Some strange logic:  If .maintype == 0, .optstring is the
+          attribute.  Otherwise the actual attribute is
+          .maintype (".optstring").  */
+       if (mips_func_opt_list_strings[i].maintype == 0)
+         {
+             *attributes = tree_cons (get_identifier (opstr), attr_args,
+                                      *attributes);
+         }
+       else
+         {  attr_args = build_tree_list (NULL_TREE,
+                                         build_string (l, opstr));
+
+           *attributes = tree_cons (get_identifier (maintype), attr_args,
+                                    *attributes);
+         }
+      }
+
+  if (func_opt_list->unknowns)
+    {
+      struct mips_func_opt_unknown_list        * l = func_opt_list->unknowns;
+      while (l)
+       {
+         tree attr_args = NULL;
+         if (l->args)
+           attr_args = mips_insert_fol_args (l->args, l->args->optimization,
+                                                l->args->arg_type);
+
+         *attributes = tree_cons (get_identifier (l->attribute), attr_args,
+                                  *attributes);
+         l = l->next;
+       }
+
+    }
+}
+
 /* Implement TARGET_INSERT_ATTRIBUTES.  */
 
 static void
@@ -1631,6 +2229,14 @@ mips_insert_attributes (tree decl, tree *attributes)
        error ("%qE cannot have both %qs and %qs attributes",
               DECL_NAME (decl), "mips16", "micromips");
 
+      if (mips_fn_opt_list)
+       {
+         struct mips_func_opt_list * func_opt_list
+           = mips_func_opt_list_find (IDENTIFIER_POINTER (DECL_NAME (decl)));
+         if (func_opt_list)
+           mips_insert_fol_attributes (func_opt_list, attributes);
+       }
+
       if (TARGET_FLIP_MIPS16
          && !DECL_ARTIFICIAL (decl)
          && compression_flags == 0
@@ -20712,6 +21318,9 @@ mips_option_override (void)
   mips_unique_sections_list = mips_read_list (mips_unique_sections_file,
                                              "-munique-sections");
 
+  if (mips_func_opt_list_file)
+    mips_func_opt_list_read ();
+
   /* Set the small data limit.  */
   mips_small_data_threshold = (OPTION_SET_P (g_switch_value)
                               ? g_switch_value
diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt
index e0a305aec22..012ca91560f 100644
--- a/gcc/config/mips/mips.opt
+++ b/gcc/config/mips/mips.opt
@@ -552,3 +552,7 @@ msdata-opt-list=FILE    Use to specify variables to go in 
the .sdata section.
 munique-sections=
 Target RejectNegative Joined Var(mips_unique_sections_file)
 munique-sections=FILE   Use to specify sections that should be made unique.
+
+mfunc-opt-list=
+Target RejectNegative Joined Var(mips_func_opt_list_file) Init(0) Defer
+mfunc-opt-list=FILE    Use to specify per function optimizations.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 952baf872ec..cd84cafafd5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1147,6 +1147,7 @@ Objective-C and Objective-C++ Dialects}.
 -msmartmips  -mno-smartmips
 -mpaired-single  -mno-paired-single  -mdmx  -mno-mdmx
 -mips3d  -mno-mips3d  -mmt  -mno-mt  -mllsc  -mno-llsc
+-mfunc-opt-list=@var{file}
 -mlong64  -mlong32  -msym32  -mno-sym32
 -G@var{num}  -mlocal-sdata  -mno-local-sdata
 -mextern-sdata  -mno-extern-sdata  -mgpopt  -mno-gopt
@@ -28709,6 +28710,38 @@ Use (do not use) the MIPS Loongson EXTensions (EXT) 
instructions.
 @itemx -mno-loongson-ext2
 Use (do not use) the MIPS Loongson EXTensions r2 (EXT2) instructions.
 
+@opindex mfunc-opt-list
+@item -mfunc-opt-list=
+Read a list of functions from a file and apply a series of attributes to
+them as if they had been specified in the source file.  The format of the
+file is plain text with one function per line.  The first non-whitespace
+word of each line is the function name, followed by whitespace, then a
+whitespace separated list of function attributes.  Attributes are written
+in the same format as they appear in the parentheses of the __attribute__
+directive.
+
+For example:
+@smallexample
+foo   mips16 optimize ("Os")
+bar   always_inline
+@end smallexample
+
+The following attributes are recognised:
+@smallexample
+alias, aligned, alloc_size, alloc_align, always_inline, artifical,
+assume_aligned, code_readable, cold, const, constructor, deprecated,
+destructor, error, far, flatten, gnu_inline, hot, interrupt,
+keep_interrupts_masked, leaf, long_call, malloc, mips16, near, noclone,
+noinline, nomips16, nonnull, noreturn, nothrow, optimize, pure,
+returns_nonnull, returns_twice, section, unused, used,
+use_debug_exception_return, use_shadow_register_set, visibility, warning,
+warn_unused_result, weak, weakref.
+@end smallexample
+
+Unrecognised attributes will produce a warning diagnostic giving the location
+where it was found in the list.  A second diagnostic will be produced citing
+the name of the function and unknown attribute.
+
 @opindex mlong64
 @item -mlong64
 Force @code{long} types to be 64 bits wide.  See @option{-mlong32} for
-- 
2.34.1

Reply via email to