Hi
> > +  if (!fnspec.arg_specified_p (arg))
> > +    ;
> > +  else if (!fnspec.arg_used_p (arg))
> > +    flags = EAF_UNUSED;
> > +  else
> > +    {
> > +      if (!fnspec.arg_direct_p (arg))
> 
> negated test
> 
> > +   flags |= EAF_DIRECT;
> > +      if (!fnspec.arg_noescape_p (arg))
> > +   flags |= EAF_NOESCAPE;
> 
> likewise.
> 
> > +      if (!fnspec.arg_readonly_p (arg))
> > +   flags |= EAF_NOCLOBBER;
> 
> 
> likewise.

Oops, sorry for that.  I got carried away trying to make sense of
fortran specifiers. Thanks for catching this. I wonder how that chould
have passed testing.
> 
> >      }
> > +  return flags;
> >  }
> >  
> >  /* Detects return flags for the call STMT.  */
> > @@ -1546,24 +1542,16 @@ gimple_call_return_flags (const gcall *stmt)
> >      return ERF_NOALIAS;
> >  
> >    attr = gimple_call_fnspec (stmt);
> > -  if (!attr || TREE_STRING_LENGTH (attr) < 1)
> > +  if (!attr)
> >      return 0;
> > +  attr_fnspec fnspec (attr);
> >  
> > -  switch (TREE_STRING_POINTER (attr)[0])
> > -    {
> > -    case '1':
> > -    case '2':
> > -    case '3':
> > -    case '4':
> > -      return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
> > -
> > -    case 'm':
> > -      return ERF_NOALIAS;
> > +  if (fnspec.returns_arg () >= 0)
> > +    return ERF_RETURNS_ARG | fnspec.returns_arg ();
> 
> hmm, maybe
> 
>      if (fnspec.returns_arg_p (&arg))
>        return ERF_RETURNS_ARG | arg;

I added arg variable, but I think returning -1 for unknown arg is kind
of more consistent with what we do elsewhere (plus referneces are not
cool)
> > +  /* True if memory reached is only written into (but not read).  */
> > +  bool
> > +  arg_writeonly_p (unsigned int i)
> 
> This is actually arg_readwrite_p (), there's no flag for write-only.
> wW are merely for noescape & only direct read/write.

I dropped this for now, but for tree-ssa-alias we will need to have way
to specify that parameter is only written into.
> 
> Currently all specified args imply noescape btw.

Yep, but I think we may want to change this, so I think it is safer to
list those that do.

This is updated patch I am re-testing. Does it look OK?

Next I would like to proceed by blowing up all specifiers to double of
size (without functional changes) and then add the extra letters.

I was wondering if we want to use ' ' instead of '.' for the second
char.  It may make it easier to read ". . . R " than "......R." but it
also may be bit misleading in a way that the there must be precisely one
space.

Honza

gcc/ChangeLog:

2020-10-01  Jan Hubicka  <hubi...@ucw.cz>

        * attr-fnspec.h: New file.
        * calls.c (decl_return_flags): Implement using attr_fnspec.
        * gimple.c (gimple_call_arg_flags): Likewise
        (gimple_call_return_flags): Likewise
        * tree-into-ssa.c (pass_build_ssa::execute): Likewise.
        * tree-ssa-alias.c (attr_fnspec::verify): New

diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
new file mode 100644
index 00000000000..4ad4b8758e0
--- /dev/null
+++ b/gcc/attr-fnspec.h
@@ -0,0 +1,141 @@
+/* Handling of fnspec attribute specifiers
+   Copyright (C) 2008-2020 Free Software Foundation, Inc.
+   Contributed by Richard Guenther  <rguent...@suse.de>
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   GCC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Parse string of attribute "fn spec".  This is an internal attribute
+   describing side effects of a function as follows:
+
+   character 0  specifies properties of return values as follows:
+     '1'...'4'  specifies number of argument function returns (as in memset)
+     'm'       specifies that returned value is noalias (as in malloc)
+     '.'       specifies that nothing is known.
+
+   character 1+i specifies properties of argument number i as follows:
+     'x' or 'X' specifies that parameter is unused.
+     'r' or 'R' specifies that parameter is only read and memory pointed to is
+               never dereferenced.
+     'w' or 'W' specifies that parameter is only written to.
+     '.'       specifies that nothing is known.
+   The uppercase letter in addition specifies that parameter
+   is non-escaping.  */
+
+#ifndef ATTR_FNSPEC_H
+#define ATTR_FNSPEC_H
+
+class attr_fnspec
+{
+private:
+  /* fn spec attribute string.  */
+  const char *str;
+  /* length of the fn spec string.  */
+  const unsigned len;
+  /* Number of characters specifying return value.  */
+  const unsigned int return_desc_size = 1;
+  /* Number of characters specifying size.  */
+  const unsigned int arg_desc_size = 1;
+
+  /* Return start of specifier of arg i.  */
+  unsigned int arg_idx (int i)
+  {
+    return return_desc_size + arg_desc_size * i;
+  }
+
+public:
+  attr_fnspec (const char *str, unsigned len)
+  : str (str), len (len)
+  {
+    if (flag_checking)
+      verify ();
+  }
+  attr_fnspec (const_tree identifier)
+  : str (TREE_STRING_POINTER (identifier)),
+    len (TREE_STRING_LENGTH (identifier))
+  {
+    if (flag_checking)
+      verify ();
+  }
+
+  /* Return true if arg I is specified.  */
+  bool
+  arg_specified_p (unsigned int i)
+  {
+    return len >= arg_idx (i + 1);
+  }
+
+  /* True if the argument is not dereferenced recursively, thus only
+     directly reachable memory is read or written.  */
+  bool
+  arg_direct_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] == 'R' || str[idx] == 'W';
+  }
+
+  /* True if argument is used.  */
+  bool
+  arg_used_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] != 'x' && str[idx] != 'X';
+  }
+
+  /* True if memory reached by the argument is readonly (not clobbered).  */
+  bool
+  arg_readonly_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] == 'r' || str[idx] == 'R';
+  }
+
+  /* True if the argument does not escape.  */
+  bool
+  arg_noescape_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] == 'w' || str[idx] == 'W'
+          || str[idx] == 'R' || str[idx] == 'r';
+  }
+
+  /* If not equal to -1 then it specifies number of argument returned by
+     the function.  */
+  int
+  returns_arg ()
+  {
+    if (str[0] >= '1' && str[0] <= '4')
+      return str[0]-'1';
+    return -1;
+  }
+
+  /* Nonzero if the return value does not alias with anything.  Functions
+     with the malloc attribute have this set on their return value.  */
+  int
+  returns_noalias_p ()
+  {
+    return str[0] == 'm';
+  }
+
+  /* Check validity of the string.  */
+  void verify ();
+};
+
+#endif /* ATTR_FNSPEC_H  */
diff --git a/gcc/calls.c b/gcc/calls.c
index ed4363811c8..40f4863a89b 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "builtins.h"
 #include "gimple-fold.h"
+#include "attr-fnspec.h"
 
 #include "tree-pretty-print.h"
 
@@ -642,25 +643,15 @@ decl_return_flags (tree fndecl)
   if (!attr)
     return 0;
 
-  attr = TREE_VALUE (TREE_VALUE (attr));
-  if (!attr || TREE_STRING_LENGTH (attr) < 1)
-    return 0;
-
-  switch (TREE_STRING_POINTER (attr)[0])
-    {
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-      return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
+  attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (attr)));
 
-    case 'm':
-      return ERF_NOALIAS;
+  int arg = fnspec.returns_arg ();
+  if (arg >= 0)
+    return ERF_RETURNS_ARG | arg;
 
-    case '.':
-    default:
-      return 0;
-    }
+  if (fnspec.returns_noalias_p ())
+    return ERF_NOALIAS;
+  return 0;
 }
 
 /* Return nonzero when FNDECL represents a call to setjmp.  */
diff --git a/gcc/gimple.c b/gcc/gimple.c
index fd4e0fac0d4..8fb82570374 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "asan.h"
 #include "langhooks.h"
+#include "attr-fnspec.h"
 
 
 /* All the tuples have their operand vector (if present) at the very bottom
@@ -1508,31 +1509,26 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg)
 {
   const_tree attr = gimple_call_fnspec (stmt);
 
-  if (!attr || 1 + arg >= (unsigned) TREE_STRING_LENGTH (attr))
+  if (!attr)
     return 0;
 
-  switch (TREE_STRING_POINTER (attr)[1 + arg])
-    {
-    case 'x':
-    case 'X':
-      return EAF_UNUSED;
-
-    case 'R':
-      return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE;
-
-    case 'r':
-      return EAF_NOCLOBBER | EAF_NOESCAPE;
-
-    case 'W':
-      return EAF_DIRECT | EAF_NOESCAPE;
-
-    case 'w':
-      return EAF_NOESCAPE;
+  int flags = 0;
+  attr_fnspec fnspec (attr);
 
-    case '.':
-    default:
-      return 0;
+  if (!fnspec.arg_specified_p (arg))
+    ;
+  else if (!fnspec.arg_used_p (arg))
+    flags = EAF_UNUSED;
+  else
+    {
+      if (fnspec.arg_direct_p (arg))
+       flags |= EAF_DIRECT;
+      if (fnspec.arg_noescape_p (arg))
+       flags |= EAF_NOESCAPE;
+      if (fnspec.arg_readonly_p (arg))
+       flags |= EAF_NOCLOBBER;
     }
+  return flags;
 }
 
 /* Detects return flags for the call STMT.  */
@@ -1546,24 +1542,16 @@ gimple_call_return_flags (const gcall *stmt)
     return ERF_NOALIAS;
 
   attr = gimple_call_fnspec (stmt);
-  if (!attr || TREE_STRING_LENGTH (attr) < 1)
+  if (!attr)
     return 0;
+  attr_fnspec fnspec (attr);
 
-  switch (TREE_STRING_POINTER (attr)[0])
-    {
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-      return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1');
-
-    case 'm':
-      return ERF_NOALIAS;
+  if (fnspec.returns_arg () >= 0)
+    return ERF_RETURNS_ARG | fnspec.returns_arg ();
 
-    case '.':
-    default:
-      return 0;
-    }
+  if (fnspec.returns_noalias_p ())
+    return ERF_NOALIAS;
+  return 0;
 }
 
 
diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c
index 0d016134774..1493b323956 100644
--- a/gcc/tree-into-ssa.c
+++ b/gcc/tree-into-ssa.c
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "attr-fnspec.h"
 
 #define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y))
 
@@ -2492,19 +2493,19 @@ pass_build_ssa::execute (function *fun)
     }
 
   /* Initialize SSA_NAME_POINTS_TO_READONLY_MEMORY.  */
-  tree fnspec = lookup_attribute ("fn spec",
-                                 TYPE_ATTRIBUTES (TREE_TYPE (fun->decl)));
-  if (fnspec)
+  tree fnspec_tree
+        = lookup_attribute ("fn spec",
+                            TYPE_ATTRIBUTES (TREE_TYPE (fun->decl)));
+  if (fnspec_tree)
     {
-      fnspec = TREE_VALUE (TREE_VALUE (fnspec));
-      unsigned i = 1;
+      attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (fnspec_tree)));
+      unsigned i = 0;
       for (tree arg = DECL_ARGUMENTS (cfun->decl);
           arg; arg = DECL_CHAIN (arg), ++i)
        {
-         if (i >= (unsigned) TREE_STRING_LENGTH (fnspec))
-           break;
-         if (TREE_STRING_POINTER (fnspec)[i]  == 'R'
-             || TREE_STRING_POINTER (fnspec)[i] == 'r')
+         if (!fnspec.arg_specified_p (i))
+          break;
+         if (fnspec.arg_readonly_p (i))
            {
              tree name = ssa_default_def (fun, arg);
              if (name)
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index fe390d4ffbe..2b2d53ebb88 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "varasm.h"
 #include "ipa-modref-tree.h"
 #include "ipa-modref.h"
+#include "attr-fnspec.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -3984,3 +3985,42 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
   return ret;
 }
 
+/* Verify validity of the fnspec string.
+   See attr-fnspec.h for details.  */
+
+void
+attr_fnspec::verify ()
+{
+  /* FIXME: Fortran trans-decl.c contains multiple wrong fnspec strings.
+     re-enable verification after these are fixed.  */
+  return;
+  bool err = false;
+
+  /* Check return value specifier.  */
+  if (len < return_desc_size)
+    err = true;
+  else if (str[0] < '1' || str[0] > '4')
+          && str[0] != '.' && str[0] != 'm')
+    err = true;
+
+  /* Now check all parameters.  */
+  for (unsigned int i = 0; arg_specified_p (i); i++)
+    {
+      unsigned int idx = arg_idx (i);
+      switch (str[idx])
+       {
+         case 'x':
+         case 'X':
+         case 'r':
+         case 'R':
+         case 'w':
+         case 'W':
+         case '.':
+           break;
+         default:
+           err = true;
+       }
+    }
+  if (err)
+    internal_error ("invalid fn spec attribute %s", str);
+}

Reply via email to