> > +  /* True if memory reached by the argument is read.
> > +     Valid only if all loads are known.  */
> > +  bool
> > +  arg_read_p (unsigned int i)
> > +  {
> > +    unsigned int idx = arg_idx (i);
> > +    gcc_checking_assert (arg_specified_p (i));
> > +    gcc_checking_assert (loads_known_p ());
> 
> I see loads_known_p () is 'const' (introducing new terminology
> as alias for sth else is IMHO bad).  So what do you think
> arg_read_p () guarantees?  Even on a not 'const' function
> 'r' or 'R' or 'w' or 'W' means the argument could be read??!

Original intention was !arg_read_p to guarantee that argument is not
read from (and for that you need const), but I updated it.
> 
> > +    return str[idx] == 'r' || str[idx] == 'R'
> > +      || str[idx] == 'w' || str[idx] == 'W';
> > +  }
> > +
> > +  /* True if memory reached by the argument is read.
> > +     Valid only if all loads are known.  */
> > +  bool
> > +  arg_written_p (unsigned int i)
> > +  {
> > +    unsigned int idx = arg_idx (i);
> > +    gcc_checking_assert (arg_specified_p (i));
> > +    gcc_checking_assert (stores_known_p ());
> 
> Likewise.  IMHO those will cause lots of confusion.  For example
> arg_readonly_p doesn't imply arg_read_p.
> 
> Please keep the number of core predicates at a minimum!

Well, my intention is/was that while fnspec strings themselves are
necessarily bit ad-hoc (trying to pack multiple things into 2 characters
and choosing just few special cases we care about) we should abstract
this and have predicates for individual properties we care about.

Indeed I did not name them very well, hopefully things are better now :)
In future modref can detect those properties of functions and provide
symetric API for testing them without need to go to fnspec limitations.
Similarly I would like to decouple logic around const/pure functions
better as well (since they pack multiple things together - presence of
side effects, whether function is deterministic and info about global
memory accesses).

Concerning arg_read_p and arg_written_p I introduced the while I was
still intending to make 'w' and 'W' mean what 'o' and 'O' does.
With original predicates the code handling loads

if (fnspec.loads_known_p ())
  for each argument i
    if (fnspec.arg_readonly_p (i))
      argument is read from
    else
      argument is not read, ignore it.
else
 ask pta if base is local

Which looked like odd re-use of readonly_p predicate intended for
something else.  I think main confussion is that I interpreted the specs
with cCpP bit weirdly. In particular:

 ". W . R "
means
 - arg 1 is written&read directly and noescapes,
 - arg 2 is written, read directly or indirectly and escapes,
 - arg 3 is read only directly, not written to and odes not escape.

With previus patch 
 ".cW . R "
means:
 - arg 1 is written&read directly and noescapes
 - arg 2 is used only as pointer value (i.e. for NULL check)
 - arg 3 is read only directly, not written to and does not escape.

With current patch is means:
 - arg 1 is written&read directly and noescapes
 - arg 2 is written&read directly or indirectly and may escape (in other stores 
allowed by the function)
 - arg 3 is read only directly, not written to and does not escape.

With current patch the loop is:
if (!fnspec.global_memory_read_p ())
  for each argument i
    if (POINTER_TYPE_P (TREE_TYPE (arg))
        && fnspec.arg_maybe_read_p (i))
      argument is read from
    else
      argument is not read.
else
  ask pta if base is local

Which seems better and follows what we did before.
However the extra POINTER_TYPE_P test is needed since we do not want to
disambiguate non-pointer parameters that also have '.' specifier. I am
not sure that this is safe/feasible with gimple type system.

I think it should be:

for each argument i
  if (POINTER_TYPE_P (TREE_TYPE (arg))
      && fnspec.arg_maybe_read_p (i))
    argument is read from
  else
    argument is not read.
if (fnspec.global_memory_read_p ())
  give up if pta thinks this is not local.

Because if I have function with spec say
 ". R "
and cal it with foo (&localvar) I think it should end up non-escaping
and thus not alias with function calls except when it is an parameter.

We do not have builtins like this right now though.
> 
> > +    return str[idx] == 'w' || str[idx] == 'W'
> > +      || str[idx] == 'o' || str[idx] == 'O';
> > +  }
> > +
> > +  /* Return true if load of memory pointed to by argument I is specified
> > +     by another argument.  In this case set ARG.  */
> > +  bool
> > +  arg_access_size_given_by_arg_p (unsigned int i, unsigned int *arg)
> 
> I think we need to document that this and all the other predicates are
> _may_ predicates, thus strncat "1cW R3" means src is _possibly_
> accessed from offset 0 to what argument 3 specifies.  That's important
> for stores I think.  While memcpy has "1cW3R3" the fnspec can _not_
> be used to DSE a previous write to the destination covering [0, n].
> 
> That is, those are all may_ predicates as used for alias analysis.
> 
> I think we want to reflect that in the predicate names, like
> 
> arg_maybe_read_p or arg_max_access_size_given_by_arg_p

arg_max_access_size_given_by_arg_p sounds good to me.

> 
> arg_access_size_given_by_type_p?

Better for sure :)
> > +/* If CALLEE has known side effects, fill in INFO and return true.
> > +   See tree-ssa-structalias.c:find_func_aliases
> > +   for the list of builtins we might need to handle here.  */
> > +
> > +attr_fnspec
> > +builtin_fnspec (tree callee)
> > +{
> > +  built_in_function code = DECL_FUNCTION_CODE (callee);
> > +
> > +  switch (code)
> > +    {
> > +      /* All the following functions read memory pointed to by
> > +    their second argument and write memory pointed to by first
> > +    argument.
> > +    strcat/strncat additionally reads memory pointed to by the first
> > +    argument.  */
> > +      case BUILT_IN_STRCAT:
> > +      case BUILT_IN_STRCAT_CHK:
> > +   return "1cw r ";
> 
> shouldn't that be "1cW R "?  Likewise below, for all functions I think
> (the uppercase - direct access only).

Sorry I mixed up lowercase and uppercase in my mind.
Will try to stick it on my wall, since this is a systematic error.
> 
> > +      case BUILT_IN_STRNCAT:
> > +      case BUILT_IN_STRNCAT_CHK:
> > +   return "1cw r3";
> > +      case BUILT_IN_STRCPY:
> > +      case BUILT_IN_STRCPY_CHK:
> > +   return "1co r ";
> > +      case BUILT_IN_STPCPY:
> > +      case BUILT_IN_STPCPY_CHK:
> > +   return ".co r ";
> > +      case BUILT_IN_STRNCPY:
> > +      case BUILT_IN_MEMCPY:
> > +      case BUILT_IN_MEMMOVE:
> > +      case BUILT_IN_TM_MEMCPY:
> > +      case BUILT_IN_TM_MEMMOVE:
> > +      case BUILT_IN_STRNCPY_CHK:
> > +      case BUILT_IN_MEMCPY_CHK:
> > +      case BUILT_IN_MEMMOVE_CHK:
> > +   return "1co3r3";
> 
> See above for max vs. exact size - do we want to somehow distinguish
> that?  ao_ref has offset, size and max_size - if size == max_size
> then the access is exactly [offset, offset + size].  I guess at
> the moment you fill size = -1 and max_size from the argument 
> specification?

I fill 1 in ipa modref (which I indeded to mean that the access is
somehwere between 1 and known size).  I see it is not quite precise.

However old code called ao_ref_init_from_ptr_and_size which does:

  if (size                                                                      
      && poly_int_tree_p (size, &size_hwi)                                      
      && coeffs_in_range_p (size_hwi, 0, HOST_WIDE_INT_MAX / BITS_PER_UNIT))    
    ref->max_size = ref->size = size_hwi * BITS_PER_UNIT;                       
  else                                                                          
    ref->max_size = ref->size = -1;                                             

This is done even for string functions which looks like a bug.
I suppose -1 is safe value here because ref->size seems to be ever used
only to compare with ref->max_size to see if we have full sized
load/store.
> 
> > +      case BUILT_IN_MEMPCPY:
> > +      case BUILT_IN_MEMPCPY_CHK:
> > +   return ".co3r3";
> > +      case BUILT_IN_STPNCPY:
> > +      case BUILT_IN_STPNCPY_CHK:
> > +   return ".co3r3";
> > +      case BUILT_IN_BCOPY:
> > +   return ".cr3o3";
> > +
> > +      /* The following functions read memory pointed to by their
> > +    first argument.  */
> > +      CASE_BUILT_IN_TM_LOAD (1):
> > +      CASE_BUILT_IN_TM_LOAD (2):
> > +      CASE_BUILT_IN_TM_LOAD (4):
> > +      CASE_BUILT_IN_TM_LOAD (8):
> > +      CASE_BUILT_IN_TM_LOAD (FLOAT):
> > +      CASE_BUILT_IN_TM_LOAD (DOUBLE):
> > +      CASE_BUILT_IN_TM_LOAD (LDOUBLE):
> > +      CASE_BUILT_IN_TM_LOAD (M64):
> > +      CASE_BUILT_IN_TM_LOAD (M128):
> > +      CASE_BUILT_IN_TM_LOAD (M256):
> > +      case BUILT_IN_TM_LOG:
> > +      case BUILT_IN_TM_LOG_1:
> > +      case BUILT_IN_TM_LOG_2:
> > +      case BUILT_IN_TM_LOG_4:
> > +      case BUILT_IN_TM_LOG_8:
> > +      case BUILT_IN_TM_LOG_FLOAT:
> > +      case BUILT_IN_TM_LOG_DOUBLE:
> > +      case BUILT_IN_TM_LOG_LDOUBLE:
> > +      case BUILT_IN_TM_LOG_M64:
> > +      case BUILT_IN_TM_LOG_M128:
> > +      case BUILT_IN_TM_LOG_M256:
> > +   return ".crt";
> 
> ".cRt" and I think BUILT_IN_TM_LOG has a size specification so
> that one needs to be ".cR2"

They does not seem to. 
However I noticed that all the buitins are declared with
BT_VOLATILE_PTR so the size is not defined.  I changed them to ".cR "
for time being.
> 
> > +
> > +      case BUILT_IN_INDEX:
> > +      case BUILT_IN_STRCHR:
> > +      case BUILT_IN_STRRCHR:
> > +   return ".cr ";
> > +
> > +      /* These read memory pointed to by the first argument.
> > +    Allocating memory does not have any side-effects apart from
> > +    being the definition point for the pointer.
> > +    Unix98 specifies that errno is set on allocation failure.  */
> > +      case BUILT_IN_STRDUP:
> > +   return "mCr ";
> > +      case BUILT_IN_STRNDUP:
> > +   return "mCr2";
> > +      /* Allocating memory does not have any side-effects apart from
> > +    being the definition point for the pointer.  */
> > +      case BUILT_IN_MALLOC:
> > +      case BUILT_IN_ALIGNED_ALLOC:
> > +      case BUILT_IN_CALLOC:
> > +   return "mC";
> > +      CASE_BUILT_IN_ALLOCA:
> > +   return "mc";
> > +      /* These read memory pointed to by the first argument with size
> > +    in the third argument.  */
> > +      case BUILT_IN_MEMCHR:
> > +   return ".cr3";
> > +      /* These read memory pointed to by the first and second arguments.  
> > */
> > +      case BUILT_IN_STRSTR:
> > +      case BUILT_IN_STRPBRK:
> > +   return ".cr r ";
> > +      /* Freeing memory kills the pointed-to memory.  More importantly
> > +    the call has to serve as a barrier for moving loads and stores
> > +    across it.  */
> > +      case BUILT_IN_FREE:
> > +      case BUILT_IN_VA_END:
> > +   return ".co ";
> 
> va_end is probably ".cO " while free should be ".co " for its
> barrier effect.

Is it really necessary?  I can see that free is modelled as something
that kills the allocated block of memory, but why it would kill things
pointed from that block?

I updated it though.

Thanks a lot!

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

        * attr-fnspec.h: Update toplevel comment.
        (attr_fnspec::attr_fnspec): New constructor.
        (attr_fnspec::arg_read_p,
        attr_fnspec::arg_written_p,
        attr_fnspec::arg_access_size_given_by_arg_p,
        attr_fnspec::arg_single_access_p
        attr_fnspec::loads_known_p
        attr_fnspec::stores_known_p,
        attr_fnspec::clobbers_errno_p): New member functions.
        (gimple_call_fnspec): Declare.
        (builtin_fnspec): Declare.
        * builtins.c: Include attr-fnspec.h
        (builtin_fnspec): New function.
        * builtins.def (BUILT_IN_MEMCPY): Do not specify RET1 fnspec.
        (BUILT_IN_MEMMOVE): Do not specify RET1 fnspec.
        (BUILT_IN_MEMSET): Do not specify RET1 fnspec.
        (BUILT_IN_STRCAT): Do not specify RET1 fnspec.
        (BUILT_IN_STRCPY): Do not specify RET1 fnspec.
        (BUILT_IN_STRNCAT): Do not specify RET1 fnspec.
        (BUILT_IN_STRNCPY): Do not specify RET1 fnspec.
        (BUILT_IN_MEMCPY_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_MEMMOVE_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_MEMSET_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_STRCAT_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_STRCPY_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_STRNCAT_CHK): Do not specify RET1 fnspec.
        (BUILT_IN_STRNCPY_CHK): Do not specify RET1 fnspec.
        * gimple.c (gimple_call_fnspec): Return attr_fnspec.
        (gimple_call_arg_flags): Update.
        (gimple_call_return_flags): Update.
        * tree-ssa-alias.c (check_fnspec): New function.
        (ref_maybe_used_by_call_p_1): Use fnspec for builtin handling.
        (call_may_clobber_ref_p_1): Likewise.
        (attr_fnspec::verify): Update verifier.
        * calls.c (decl_fnspec): New function.
        (decl_return_flags): Use it.
diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
index d38b84a969e..78b1a5a2b1c 100644
--- a/gcc/attr-fnspec.h
+++ b/gcc/attr-fnspec.h
@@ -27,11 +27,18 @@
      '.'       specifies that nothing is known.
    character 1  specifies additional function properties
      ' '        specifies that nothing is known
+     'p' or 'P' specifies that function is pure except for described side
+               effects.
+     'c' or 'C' specifies that function is const except for described side
+               effects.
+   The uppercase letter in addition specifies that function clobbers errno.
 
    character 2+2i specifies properties of argument number i as follows:
      'x' or 'X' specifies that parameter is unused.
      'r' or 'R' specifies that the memory pointed to by the parameter is only
                read and does not escape
+     'o' or 'O' specifies that the memory pointed to by the parameter is only
+               written and does not escape
      'w' or 'W' specifies that the memory pointed to by the parameter does not
                escape
      '.'       specifies that nothing is known.
@@ -42,6 +49,10 @@
    character 3+2i specifies additional properties of argument number i
    as follows:
      ' '        nothing is known
+     't'       the size of value written/read corresponds to the size of
+               of the pointed-to type of the argument type
+     '1'...'9'  the size of value written/read is given by the specified
+               argument
  */
 
 #ifndef ATTR_FNSPEC_H
@@ -72,6 +83,12 @@ public:
     if (flag_checking)
       verify ();
   }
+  attr_fnspec (const char *str)
+  : str (str), len (strlen (str))
+  {
+    if (flag_checking)
+      verify ();
+  }
   attr_fnspec (const_tree identifier)
   : str (TREE_STRING_POINTER (identifier)),
     len (TREE_STRING_LENGTH (identifier))
@@ -79,6 +96,17 @@ public:
     if (flag_checking)
       verify ();
   }
+  attr_fnspec ()
+  : str (NULL), len (0)
+  {
+  }
+
+  /* Return true if fn spec is known.  */
+  bool
+  known_p ()
+  {
+    return len;
+  }
 
   /* Return true if arg I is specified.  */
   bool
@@ -94,7 +122,7 @@ public:
   {
     unsigned int idx = arg_idx (i);
     gcc_checking_assert (arg_specified_p (i));
-    return str[idx] == 'R' || str[idx] == 'W';
+    return str[idx] == 'R' || str[idx] == 'O' || str[idx] == 'W';
   }
 
   /* True if argument is used.  */
@@ -115,6 +143,53 @@ public:
     return str[idx] == 'r' || str[idx] == 'R';
   }
 
+  /* True if memory reached by the argument is read (directly or indirectly)  
*/
+  bool
+  arg_maybe_read_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] != 'o' && str[idx] != 'O'
+          && str[idx] != 'x' && str[idx] != 'X';
+  }
+
+  /* True if memory reached by the argument is written.
+     (directly or indirectly)  */
+  bool
+  arg_maybe_written_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx] != 'r' && str[idx] != 'R'
+          && str[idx] != 'x' && str[idx] != 'X';
+  }
+
+  /* Return true if load of memory pointed to by argument I is specified
+     by another argument.  In this case set ARG.  */
+  bool
+  arg_max_access_size_given_by_arg_p (unsigned int i, unsigned int *arg)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    if (str[idx + 1] >= '1' && str[idx + 1] <= '9')
+      {
+       *arg = str[idx + 1] - '1';
+       return true;
+      }
+    else
+      return false;
+  }
+
+  /* Return true if the pointed-to type of the argument correspond to the
+     size of the memory acccess.  */
+  bool
+  arg_access_size_given_by_type_p (unsigned int i)
+  {
+    unsigned int idx = arg_idx (i);
+    gcc_checking_assert (arg_specified_p (i));
+    return str[idx + 1] == 't';
+  }
+
   /* True if the argument does not escape.  */
   bool
   arg_noescape_p (unsigned int i)
@@ -122,7 +197,8 @@ public:
     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';
+          || str[idx] == 'r' || str[idx] == 'R'
+          || str[idx] == 'o' || str[idx] == 'O';
   }
 
   /* Return true if function returns value of its parameter.  If ARG_NO is
@@ -147,8 +223,32 @@ public:
     return str[0] == 'm';
   }
 
+  /* Return true if all memory read by the function is specified by fnspec.  */
+  bool
+  global_memory_read_p ()
+  {
+    return str[1] != 'c' && str[1] != 'C';
+  }
+
+  /* Return true if all memory written by the function 
+     is specified by fnspec.  */
+  bool
+  global_memory_written_p ()
+  {
+    return str[1] != 'c' && str[1] != 'C' && str[1] != 'p' && str[1] != 'P';
+  }
+
+  bool
+  errno_maybe_written_p ()
+  {
+    return str[1] == 'C' || str[1] == 'P';
+  }
+
   /* Check validity of the string.  */
   void verify ();
 };
 
+extern attr_fnspec gimple_call_fnspec (const gcall *stmt);
+extern attr_fnspec builtin_fnspec (tree);
+
 #endif /* ATTR_FNSPEC_H  */
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 72627b5b859..cf9a4ee0746 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -76,6 +76,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-ssa.h"
 #include "tree-ssa-live.h"
 #include "tree-outof-ssa.h"
+#include "attr-fnspec.h"
 
 struct target_builtins default_target_builtins;
 #if SWITCHABLE_TARGET
@@ -12913,3 +12914,167 @@ access_ref::offset_bounded () const
   tree max = TYPE_MAX_VALUE (ptrdiff_type_node);
   return wi::to_offset (min) <= offrng[0] && offrng[1] <= wi::to_offset (max);
 }
+
+/* If CALLEE has known side effects, fill in INFO and return true.
+   See tree-ssa-structalias.c:find_func_aliases
+   for the list of builtins we might need to handle here.  */
+
+attr_fnspec
+builtin_fnspec (tree callee)
+{
+  built_in_function code = DECL_FUNCTION_CODE (callee);
+
+  switch (code)
+    {
+      /* All the following functions read memory pointed to by
+        their second argument and write memory pointed to by first
+        argument.
+        strcat/strncat additionally reads memory pointed to by the first
+        argument.  */
+      case BUILT_IN_STRCAT:
+      case BUILT_IN_STRCAT_CHK:
+       return "1cW R ";
+      case BUILT_IN_STRNCAT:
+      case BUILT_IN_STRNCAT_CHK:
+       return "1cW R3";
+      case BUILT_IN_STRCPY:
+      case BUILT_IN_STRCPY_CHK:
+       return "1cO R ";
+      case BUILT_IN_STPCPY:
+      case BUILT_IN_STPCPY_CHK:
+       return ".cO R ";
+      case BUILT_IN_STRNCPY:
+      case BUILT_IN_MEMCPY:
+      case BUILT_IN_MEMMOVE:
+      case BUILT_IN_TM_MEMCPY:
+      case BUILT_IN_TM_MEMMOVE:
+      case BUILT_IN_STRNCPY_CHK:
+      case BUILT_IN_MEMCPY_CHK:
+      case BUILT_IN_MEMMOVE_CHK:
+       return "1cO3R3";
+      case BUILT_IN_MEMPCPY:
+      case BUILT_IN_MEMPCPY_CHK:
+       return ".cO3R3";
+      case BUILT_IN_STPNCPY:
+      case BUILT_IN_STPNCPY_CHK:
+       return ".cO3R3";
+      case BUILT_IN_BCOPY:
+       return ".cR3O3";
+
+      /* The following functions read memory pointed to by their
+        first argument.  */
+      CASE_BUILT_IN_TM_LOAD (1):
+      CASE_BUILT_IN_TM_LOAD (2):
+      CASE_BUILT_IN_TM_LOAD (4):
+      CASE_BUILT_IN_TM_LOAD (8):
+      CASE_BUILT_IN_TM_LOAD (FLOAT):
+      CASE_BUILT_IN_TM_LOAD (DOUBLE):
+      CASE_BUILT_IN_TM_LOAD (LDOUBLE):
+      CASE_BUILT_IN_TM_LOAD (M64):
+      CASE_BUILT_IN_TM_LOAD (M128):
+      CASE_BUILT_IN_TM_LOAD (M256):
+      case BUILT_IN_TM_LOG:
+      case BUILT_IN_TM_LOG_1:
+      case BUILT_IN_TM_LOG_2:
+      case BUILT_IN_TM_LOG_4:
+      case BUILT_IN_TM_LOG_8:
+      case BUILT_IN_TM_LOG_FLOAT:
+      case BUILT_IN_TM_LOG_DOUBLE:
+      case BUILT_IN_TM_LOG_LDOUBLE:
+      case BUILT_IN_TM_LOG_M64:
+      case BUILT_IN_TM_LOG_M128:
+      case BUILT_IN_TM_LOG_M256:
+       return ".cR ";
+
+      case BUILT_IN_INDEX:
+      case BUILT_IN_STRCHR:
+      case BUILT_IN_STRRCHR:
+       return ".cR ";
+
+      /* These read memory pointed to by the first argument.
+        Allocating memory does not have any side-effects apart from
+        being the definition point for the pointer.
+        Unix98 specifies that errno is set on allocation failure.  */
+      case BUILT_IN_STRDUP:
+       return "mCR ";
+      case BUILT_IN_STRNDUP:
+       return "mCR2";
+      /* Allocating memory does not have any side-effects apart from
+        being the definition point for the pointer.  */
+      case BUILT_IN_MALLOC:
+      case BUILT_IN_ALIGNED_ALLOC:
+      case BUILT_IN_CALLOC:
+       return "mC";
+      CASE_BUILT_IN_ALLOCA:
+       return "mc";
+      /* These read memory pointed to by the first argument with size
+        in the third argument.  */
+      case BUILT_IN_MEMCHR:
+       return ".cR3";
+      /* These read memory pointed to by the first and second arguments.  */
+      case BUILT_IN_STRSTR:
+      case BUILT_IN_STRPBRK:
+       return ".cR R ";
+      /* Freeing memory kills the pointed-to memory.  More importantly
+        the call has to serve as a barrier for moving loads and stores
+        across it.  */
+      case BUILT_IN_FREE:
+       return ".co ";
+      case BUILT_IN_VA_END:
+       return ".cO ";
+      /* Realloc serves both as allocation point and deallocation point.  */
+      case BUILT_IN_REALLOC:
+       return ".cw ";
+      case BUILT_IN_GAMMA_R:
+      case BUILT_IN_GAMMAF_R:
+      case BUILT_IN_GAMMAL_R:
+      case BUILT_IN_LGAMMA_R:
+      case BUILT_IN_LGAMMAF_R:
+      case BUILT_IN_LGAMMAL_R:
+       return ".C. Ot";
+      case BUILT_IN_FREXP:
+      case BUILT_IN_FREXPF:
+      case BUILT_IN_FREXPL:
+      case BUILT_IN_MODF:
+      case BUILT_IN_MODFF:
+      case BUILT_IN_MODFL:
+       return ".c. Ot";
+      case BUILT_IN_REMQUO:
+      case BUILT_IN_REMQUOF:
+      case BUILT_IN_REMQUOL:
+       return ".c. . Ot";
+      case BUILT_IN_SINCOS:
+      case BUILT_IN_SINCOSF:
+      case BUILT_IN_SINCOSL:
+       return ".c. OtOt";
+      case BUILT_IN_MEMSET:
+      case BUILT_IN_MEMSET_CHK:
+      case BUILT_IN_TM_MEMSET:
+       return "1cO3";
+      CASE_BUILT_IN_TM_STORE (1):
+      CASE_BUILT_IN_TM_STORE (2):
+      CASE_BUILT_IN_TM_STORE (4):
+      CASE_BUILT_IN_TM_STORE (8):
+      CASE_BUILT_IN_TM_STORE (FLOAT):
+      CASE_BUILT_IN_TM_STORE (DOUBLE):
+      CASE_BUILT_IN_TM_STORE (LDOUBLE):
+      CASE_BUILT_IN_TM_STORE (M64):
+      CASE_BUILT_IN_TM_STORE (M128):
+      CASE_BUILT_IN_TM_STORE (M256):
+       return ".cO ";
+      case BUILT_IN_STACK_SAVE:
+       return ".c";
+      case BUILT_IN_ASSUME_ALIGNED:
+       return "1c";
+      /* But posix_memalign stores a pointer into the memory pointed to
+        by its first argument.  */
+      case BUILT_IN_POSIX_MEMALIGN:
+       return ".cOt";
+      /* The following builtins do not read from memory.  */
+      case BUILT_IN_STACK_RESTORE:
+       return ".c";
+
+      default:
+       return "";
+    }
+}
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 95428c010d9..61aff89e658 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -701,26 +701,26 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_BZERO, "bzero", 
BT_FN_VOID_PTR_SIZE, ATTR_NOTHR
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_INDEX, "index", 
BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_MEMCHR, "memchr", 
BT_FN_PTR_CONST_PTR_INT_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_MEMCMP, "memcmp", 
BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN               (BUILT_IN_MEMCPY, "memcpy", 
BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN               (BUILT_IN_MEMMOVE, "memmove", 
BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN               (BUILT_IN_MEMCPY, "memcpy", 
BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN               (BUILT_IN_MEMMOVE, "memmove", 
BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY, "mempcpy", 
BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_LIB_BUILTIN               (BUILT_IN_MEMSET, "memset", 
BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN               (BUILT_IN_MEMSET, "memset", 
BT_FN_PTR_PTR_INT_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_RINDEX, "rindex", 
BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPCPY, "stpcpy", 
BT_FN_STRING_STRING_CONST_STRING, ATTR_RETNONNULL_NOTHROW_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPNCPY, "stpncpy", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCASECMP, "strcasecmp", 
BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN        (BUILT_IN_STRCAT, "strcat", 
BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN        (BUILT_IN_STRCAT, "strcat", 
BT_FN_STRING_STRING_CONST_STRING, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRCHR, "strchr", 
BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRCMP, "strcmp", 
BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN        (BUILT_IN_STRCPY, "strcpy", 
BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN        (BUILT_IN_STRCPY, "strcpy", 
BT_FN_STRING_STRING_CONST_STRING, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRCSPN, "strcspn", 
BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_C2X_BUILTIN        (BUILT_IN_STRDUP, "strdup", BT_FN_STRING_CONST_STRING, 
ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
 DEF_C2X_BUILTIN        (BUILT_IN_STRNDUP, "strndup", 
BT_FN_STRING_CONST_STRING_SIZE, 
ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, 
ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCASECMP, "strncasecmp", 
BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN        (BUILT_IN_STRNCAT, "strncat", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN        (BUILT_IN_STRNCAT, "strncat", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRNCMP, "strncmp", 
BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
-DEF_LIB_BUILTIN        (BUILT_IN_STRNCPY, "strncpy", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_LIB_BUILTIN        (BUILT_IN_STRNCPY, "strncpy", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNLEN, "strnlen", 
BT_FN_SIZE_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRPBRK, "strpbrk", 
BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRRCHR, "strrchr", 
BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
@@ -970,16 +970,16 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, 
"__builtin_strncmp_eq")
 
 /* Object size checking builtins.  */
 DEF_GCC_BUILTIN               (BUILT_IN_OBJECT_SIZE, "object_size", 
BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", 
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", 
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", 
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", 
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", 
BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMSET_CHK, "__memset_chk", 
BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMSET_CHK, "__memset_chk", 
BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_STPNCPY_CHK, "__stpncpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCAT_CHK, "__strcat_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCPY_CHK, "__strcpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCAT_CHK, "__strncat_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
-DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCAT_CHK, "__strcat_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRCPY_CHK, "__strcpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCAT_CHK, "__strncat_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", 
BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", 
BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_5_6)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", 
BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, 
ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_4_5)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", 
BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, 
ATTR_FORMAT_PRINTF_NOTHROW_5_0)
diff --git a/gcc/calls.c b/gcc/calls.c
index d3120b23f60..dab37f1f213 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -629,21 +629,32 @@ special_function_p (const_tree fndecl, int flags)
   return flags;
 }
 
+/* Return fnspec for DECL.  */
+
+static attr_fnspec
+decl_fnspec (tree fndecl)
+{
+  tree attr;
+  tree type = TREE_TYPE (fndecl);
+  if (type)
+    {
+      attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
+      if (attr)
+       {
+         return TREE_VALUE (TREE_VALUE (attr));
+       }
+    }
+  if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
+    return builtin_fnspec (fndecl);
+  return "";
+}
+
 /* Similar to special_function_p; return a set of ERF_ flags for the
    function FNDECL.  */
 static int
 decl_return_flags (tree fndecl)
 {
-  tree attr;
-  tree type = TREE_TYPE (fndecl);
-  if (!type)
-    return 0;
-
-  attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
-  if (!attr)
-    return 0;
-
-  attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (attr)));
+  attr_fnspec fnspec = decl_fnspec (fndecl);
 
   unsigned int arg;
   if (fnspec.returns_arg (&arg))
diff --git a/gcc/gimple.c b/gcc/gimple.c
index f19e24d29b3..469e6f369f3 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1487,23 +1487,30 @@ gimple_call_flags (const gimple *stmt)
 
 /* Return the "fn spec" string for call STMT.  */
 
-static const_tree
+attr_fnspec
 gimple_call_fnspec (const gcall *stmt)
 {
   tree type, attr;
 
   if (gimple_call_internal_p (stmt))
-    return internal_fn_fnspec (gimple_call_internal_fn (stmt));
+    {
+      const_tree spec = internal_fn_fnspec (gimple_call_internal_fn (stmt));
+      if (spec)
+       return spec;
+      else
+       return "";
+    }
 
   type = gimple_call_fntype (stmt);
-  if (!type)
-    return NULL_TREE;
-
-  attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
-  if (!attr)
-    return NULL_TREE;
-
-  return TREE_VALUE (TREE_VALUE (attr));
+  if (type)
+    {
+      attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
+      if (attr)
+       return TREE_VALUE (TREE_VALUE (attr));
+    }
+  if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    return builtin_fnspec (gimple_call_fndecl (stmt));
+  return "";
 }
 
 /* Detects argument flags for argument number ARG on call STMT.  */
@@ -1511,13 +1518,12 @@ gimple_call_fnspec (const gcall *stmt)
 int
 gimple_call_arg_flags (const gcall *stmt, unsigned arg)
 {
-  const_tree attr = gimple_call_fnspec (stmt);
+  attr_fnspec fnspec = gimple_call_fnspec (stmt);
 
-  if (!attr)
+  if (!fnspec.known_p ())
     return 0;
 
   int flags = 0;
-  attr_fnspec fnspec (attr);
 
   if (!fnspec.arg_specified_p (arg))
     ;
@@ -1540,15 +1546,10 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg)
 int
 gimple_call_return_flags (const gcall *stmt)
 {
-  const_tree attr;
-
   if (gimple_call_flags (stmt) & ECF_MALLOC)
     return ERF_NOALIAS;
 
-  attr = gimple_call_fnspec (stmt);
-  if (!attr)
-    return 0;
-  attr_fnspec fnspec (attr);
+  attr_fnspec fnspec = gimple_call_fnspec (stmt);
 
   unsigned int arg_no;
   if (fnspec.returns_arg (&arg_no))
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 877e4999f0f..50e46b1f654 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -44,6 +44,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "errors.h"
 #include "dbgcnt.h"
 #include "gimple-pretty-print.h"
+#include "print-tree.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -2572,6 +2573,99 @@ modref_may_conflict (const gimple *stmt,
   return false;
 }
 
+/* Check if REF conflicts with call using "fn spec" attribute.
+   If CLOBBER is true we are checking for writes, otherwise check loads.
+
+   Return 0 if there are no conflicts (except for possible function call
+   argument reads), 1 if there are conflicts and -1 if we can not decide by
+   fn spec.  */
+
+static int
+check_fnspec (gcall *call, ao_ref *ref, bool clobber)
+{
+  attr_fnspec fnspec = gimple_call_fnspec (call);
+  if (fnspec.known_p ())
+    {
+      if (clobber
+         ? !fnspec.global_memory_written_p ()
+         : !fnspec.global_memory_read_p ())
+       {
+         for (unsigned int i = 0; i < gimple_call_num_args (call); i++)
+           if (POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, i)))
+               && (!fnspec.arg_specified_p (i)
+                   || (clobber ? fnspec.arg_maybe_written_p (i)
+                       : fnspec.arg_maybe_read_p (i))))
+             {
+               ao_ref dref;
+               tree size = NULL_TREE;
+               unsigned int size_arg;
+
+               if (!fnspec.arg_specified_p (i))
+                 ;
+               else if (fnspec.arg_max_access_size_given_by_arg_p
+                          (i, &size_arg))
+                 size = gimple_call_arg (call, size_arg);
+               else if (fnspec.arg_access_size_given_by_type_p (i))
+                 {
+                   tree callee = gimple_call_fndecl (call);
+                   tree t = TYPE_ARG_TYPES (TREE_TYPE (callee));
+
+                   for (unsigned int p = 0; p < i; p++)
+                     t = TREE_CHAIN (t);
+                   size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_VALUE (t)));
+                 }
+               ao_ref_init_from_ptr_and_size (&dref,
+                                              gimple_call_arg (call, i),
+                                              size);
+               if (refs_may_alias_p_1 (&dref, ref, false))
+                 return 1;
+             }
+         if (clobber
+             && fnspec.errno_maybe_written_p ()
+             && flag_errno_math
+             && targetm.ref_may_alias_errno (ref))
+           return 1;
+         return 0;
+       }
+    }
+
+ /* FIXME: we should handle barriers more consistently, but for now leave the
+    check here.  */
+  if (gimple_call_builtin_p (call, BUILT_IN_NORMAL))
+    switch (DECL_FUNCTION_CODE (gimple_call_fndecl (call)))
+      {
+      /* __sync_* builtins and some OpenMP builtins act as threading
+         barriers.  */
+#undef DEF_SYNC_BUILTIN
+#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM:
+#include "sync-builtins.def"
+#undef DEF_SYNC_BUILTIN
+      case BUILT_IN_GOMP_ATOMIC_START:
+      case BUILT_IN_GOMP_ATOMIC_END:
+      case BUILT_IN_GOMP_BARRIER:
+      case BUILT_IN_GOMP_BARRIER_CANCEL:
+      case BUILT_IN_GOMP_TASKWAIT:
+      case BUILT_IN_GOMP_TASKGROUP_END:
+      case BUILT_IN_GOMP_CRITICAL_START:
+      case BUILT_IN_GOMP_CRITICAL_END:
+      case BUILT_IN_GOMP_CRITICAL_NAME_START:
+      case BUILT_IN_GOMP_CRITICAL_NAME_END:
+      case BUILT_IN_GOMP_LOOP_END:
+      case BUILT_IN_GOMP_LOOP_END_CANCEL:
+      case BUILT_IN_GOMP_ORDERED_START:
+      case BUILT_IN_GOMP_ORDERED_END:
+      case BUILT_IN_GOMP_SECTIONS_END:
+      case BUILT_IN_GOMP_SECTIONS_END_CANCEL:
+      case BUILT_IN_GOMP_SINGLE_COPY_START:
+      case BUILT_IN_GOMP_SINGLE_COPY_END:
+       return 1;
+
+      default:
+       return -1;
+      }
+  return -1;
+}
+
 /* If the call CALL may use the memory reference REF return true,
    otherwise return false.  */
 
@@ -2650,222 +2744,13 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, 
bool tbaa_p)
       && !is_global_var (base))
     goto process_args;
 
-  /* Handle those builtin functions explicitly that do not act as
-     escape points.  See tree-ssa-structalias.c:find_func_aliases
-     for the list of builtins we might need to handle here.  */
-  if (callee != NULL_TREE
-      && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
-    switch (DECL_FUNCTION_CODE (callee))
-      {
-       /* All the following functions read memory pointed to by
-          their second argument.  strcat/strncat additionally
-          reads memory pointed to by the first argument.  */
-       case BUILT_IN_STRCAT:
-       case BUILT_IN_STRNCAT:
-         {
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          NULL_TREE);
-           if (refs_may_alias_p_1 (&dref, ref, false))
-             return true;
-         }
-         /* FALLTHRU */
-       case BUILT_IN_STRCPY:
-       case BUILT_IN_STRNCPY:
-       case BUILT_IN_MEMCPY:
-       case BUILT_IN_MEMMOVE:
-       case BUILT_IN_MEMPCPY:
-       case BUILT_IN_STPCPY:
-       case BUILT_IN_STPNCPY:
-       case BUILT_IN_TM_MEMCPY:
-       case BUILT_IN_TM_MEMMOVE:
-         {
-           ao_ref dref;
-           tree size = NULL_TREE;
-           if (gimple_call_num_args (call) == 3)
-             size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 1),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       case BUILT_IN_STRCAT_CHK:
-       case BUILT_IN_STRNCAT_CHK:
-         {
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          NULL_TREE);
-           if (refs_may_alias_p_1 (&dref, ref, false))
-             return true;
-         }
-         /* FALLTHRU */
-       case BUILT_IN_STRCPY_CHK:
-       case BUILT_IN_STRNCPY_CHK:
-       case BUILT_IN_MEMCPY_CHK:
-       case BUILT_IN_MEMMOVE_CHK:
-       case BUILT_IN_MEMPCPY_CHK:
-       case BUILT_IN_STPCPY_CHK:
-       case BUILT_IN_STPNCPY_CHK:
-         {
-           ao_ref dref;
-           tree size = NULL_TREE;
-           if (gimple_call_num_args (call) == 4)
-             size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 1),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       case BUILT_IN_BCOPY:
-         {
-           ao_ref dref;
-           tree size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-
-       /* The following functions read memory pointed to by their
-          first argument.  */
-       CASE_BUILT_IN_TM_LOAD (1):
-       CASE_BUILT_IN_TM_LOAD (2):
-       CASE_BUILT_IN_TM_LOAD (4):
-       CASE_BUILT_IN_TM_LOAD (8):
-       CASE_BUILT_IN_TM_LOAD (FLOAT):
-       CASE_BUILT_IN_TM_LOAD (DOUBLE):
-       CASE_BUILT_IN_TM_LOAD (LDOUBLE):
-       CASE_BUILT_IN_TM_LOAD (M64):
-       CASE_BUILT_IN_TM_LOAD (M128):
-       CASE_BUILT_IN_TM_LOAD (M256):
-       case BUILT_IN_TM_LOG:
-       case BUILT_IN_TM_LOG_1:
-       case BUILT_IN_TM_LOG_2:
-       case BUILT_IN_TM_LOG_4:
-       case BUILT_IN_TM_LOG_8:
-       case BUILT_IN_TM_LOG_FLOAT:
-       case BUILT_IN_TM_LOG_DOUBLE:
-       case BUILT_IN_TM_LOG_LDOUBLE:
-       case BUILT_IN_TM_LOG_M64:
-       case BUILT_IN_TM_LOG_M128:
-       case BUILT_IN_TM_LOG_M256:
-         return ptr_deref_may_alias_ref_p_1 (gimple_call_arg (call, 0), ref);
-
-       /* These read memory pointed to by the first argument.  */
-       case BUILT_IN_STRDUP:
-       case BUILT_IN_STRNDUP:
-       case BUILT_IN_REALLOC:
-         {
-           ao_ref dref;
-           tree size = NULL_TREE;
-           if (gimple_call_num_args (call) == 2)
-             size = gimple_call_arg (call, 1);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       /* These read memory pointed to by the first argument.  */
-       case BUILT_IN_INDEX:
-       case BUILT_IN_STRCHR:
-       case BUILT_IN_STRRCHR:
-         {
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          NULL_TREE);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       /* These read memory pointed to by the first argument with size
-          in the third argument.  */
-       case BUILT_IN_MEMCHR:
-         {
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          gimple_call_arg (call, 2));
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       /* These read memory pointed to by the first and second arguments.  */
-       case BUILT_IN_STRSTR:
-       case BUILT_IN_STRPBRK:
-         {
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          NULL_TREE);
-           if (refs_may_alias_p_1 (&dref, ref, false))
-             return true;
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 1),
-                                          NULL_TREE);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-
-       /* The following builtins do not read from memory.  */
-       case BUILT_IN_FREE:
-       case BUILT_IN_MALLOC:
-       case BUILT_IN_POSIX_MEMALIGN:
-       case BUILT_IN_ALIGNED_ALLOC:
-       case BUILT_IN_CALLOC:
-       CASE_BUILT_IN_ALLOCA:
-       case BUILT_IN_STACK_SAVE:
-       case BUILT_IN_STACK_RESTORE:
-       case BUILT_IN_MEMSET:
-       case BUILT_IN_TM_MEMSET:
-       case BUILT_IN_MEMSET_CHK:
-       case BUILT_IN_FREXP:
-       case BUILT_IN_FREXPF:
-       case BUILT_IN_FREXPL:
-       case BUILT_IN_GAMMA_R:
-       case BUILT_IN_GAMMAF_R:
-       case BUILT_IN_GAMMAL_R:
-       case BUILT_IN_LGAMMA_R:
-       case BUILT_IN_LGAMMAF_R:
-       case BUILT_IN_LGAMMAL_R:
-       case BUILT_IN_MODF:
-       case BUILT_IN_MODFF:
-       case BUILT_IN_MODFL:
-       case BUILT_IN_REMQUO:
-       case BUILT_IN_REMQUOF:
-       case BUILT_IN_REMQUOL:
-       case BUILT_IN_SINCOS:
-       case BUILT_IN_SINCOSF:
-       case BUILT_IN_SINCOSL:
-       case BUILT_IN_ASSUME_ALIGNED:
-       case BUILT_IN_VA_END:
-         return false;
-       /* __sync_* builtins and some OpenMP builtins act as threading
-          barriers.  */
-#undef DEF_SYNC_BUILTIN
-#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM:
-#include "sync-builtins.def"
-#undef DEF_SYNC_BUILTIN
-       case BUILT_IN_GOMP_ATOMIC_START:
-       case BUILT_IN_GOMP_ATOMIC_END:
-       case BUILT_IN_GOMP_BARRIER:
-       case BUILT_IN_GOMP_BARRIER_CANCEL:
-       case BUILT_IN_GOMP_TASKWAIT:
-       case BUILT_IN_GOMP_TASKGROUP_END:
-       case BUILT_IN_GOMP_CRITICAL_START:
-       case BUILT_IN_GOMP_CRITICAL_END:
-       case BUILT_IN_GOMP_CRITICAL_NAME_START:
-       case BUILT_IN_GOMP_CRITICAL_NAME_END:
-       case BUILT_IN_GOMP_LOOP_END:
-       case BUILT_IN_GOMP_LOOP_END_CANCEL:
-       case BUILT_IN_GOMP_ORDERED_START:
-       case BUILT_IN_GOMP_ORDERED_END:
-       case BUILT_IN_GOMP_SECTIONS_END:
-       case BUILT_IN_GOMP_SECTIONS_END_CANCEL:
-       case BUILT_IN_GOMP_SINGLE_COPY_START:
-       case BUILT_IN_GOMP_SINGLE_COPY_END:
-         return true;
-
-       default:
-         /* Fallthru to general call handling.  */;
-      }
+  if (int res = check_fnspec (call, ref, false))
+    {
+      if (res == 1)
+       return true;
+    }
+  else
+    goto process_args;
 
   /* Check if base is a global static variable that is not read
      by the function.  */
@@ -3104,205 +2991,13 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, 
bool tbaa_p)
       && SSA_NAME_POINTS_TO_READONLY_MEMORY (TREE_OPERAND (base, 0)))
     return false;
 
-  /* Handle those builtin functions explicitly that do not act as
-     escape points.  See tree-ssa-structalias.c:find_func_aliases
-     for the list of builtins we might need to handle here.  */
-  if (callee != NULL_TREE
-      && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
-    switch (DECL_FUNCTION_CODE (callee))
-      {
-       /* All the following functions clobber memory pointed to by
-          their first argument.  */
-       case BUILT_IN_STRCPY:
-       case BUILT_IN_STRNCPY:
-       case BUILT_IN_MEMCPY:
-       case BUILT_IN_MEMMOVE:
-       case BUILT_IN_MEMPCPY:
-       case BUILT_IN_STPCPY:
-       case BUILT_IN_STPNCPY:
-       case BUILT_IN_STRCAT:
-       case BUILT_IN_STRNCAT:
-       case BUILT_IN_MEMSET:
-       case BUILT_IN_TM_MEMSET:
-       CASE_BUILT_IN_TM_STORE (1):
-       CASE_BUILT_IN_TM_STORE (2):
-       CASE_BUILT_IN_TM_STORE (4):
-       CASE_BUILT_IN_TM_STORE (8):
-       CASE_BUILT_IN_TM_STORE (FLOAT):
-       CASE_BUILT_IN_TM_STORE (DOUBLE):
-       CASE_BUILT_IN_TM_STORE (LDOUBLE):
-       CASE_BUILT_IN_TM_STORE (M64):
-       CASE_BUILT_IN_TM_STORE (M128):
-       CASE_BUILT_IN_TM_STORE (M256):
-       case BUILT_IN_TM_MEMCPY:
-       case BUILT_IN_TM_MEMMOVE:
-         {
-           ao_ref dref;
-           tree size = NULL_TREE;
-           /* Don't pass in size for strncat, as the maximum size
-              is strlen (dest) + n + 1 instead of n, resp.
-              n + 1 at dest + strlen (dest), but strlen (dest) isn't
-              known.  */
-           if (gimple_call_num_args (call) == 3
-               && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT)
-             size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       case BUILT_IN_STRCPY_CHK:
-       case BUILT_IN_STRNCPY_CHK:
-       case BUILT_IN_MEMCPY_CHK:
-       case BUILT_IN_MEMMOVE_CHK:
-       case BUILT_IN_MEMPCPY_CHK:
-       case BUILT_IN_STPCPY_CHK:
-       case BUILT_IN_STPNCPY_CHK:
-       case BUILT_IN_STRCAT_CHK:
-       case BUILT_IN_STRNCAT_CHK:
-       case BUILT_IN_MEMSET_CHK:
-         {
-           ao_ref dref;
-           tree size = NULL_TREE;
-           /* Don't pass in size for __strncat_chk, as the maximum size
-              is strlen (dest) + n + 1 instead of n, resp.
-              n + 1 at dest + strlen (dest), but strlen (dest) isn't
-              known.  */
-           if (gimple_call_num_args (call) == 4
-               && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT_CHK)
-             size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 0),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       case BUILT_IN_BCOPY:
-         {
-           ao_ref dref;
-           tree size = gimple_call_arg (call, 2);
-           ao_ref_init_from_ptr_and_size (&dref,
-                                          gimple_call_arg (call, 1),
-                                          size);
-           return refs_may_alias_p_1 (&dref, ref, false);
-         }
-       /* Allocating memory does not have any side-effects apart from
-          being the definition point for the pointer.  */
-       case BUILT_IN_MALLOC:
-       case BUILT_IN_ALIGNED_ALLOC:
-       case BUILT_IN_CALLOC:
-       case BUILT_IN_STRDUP:
-       case BUILT_IN_STRNDUP:
-         /* Unix98 specifies that errno is set on allocation failure.  */
-         if (flag_errno_math
-             && targetm.ref_may_alias_errno (ref))
-           return true;
-         return false;
-       case BUILT_IN_STACK_SAVE:
-       CASE_BUILT_IN_ALLOCA:
-       case BUILT_IN_ASSUME_ALIGNED:
-         return false;
-       /* But posix_memalign stores a pointer into the memory pointed to
-          by its first argument.  */
-       case BUILT_IN_POSIX_MEMALIGN:
-         {
-           tree ptrptr = gimple_call_arg (call, 0);
-           ao_ref dref;
-           ao_ref_init_from_ptr_and_size (&dref, ptrptr,
-                                          TYPE_SIZE_UNIT (ptr_type_node));
-           return (refs_may_alias_p_1 (&dref, ref, false)
-                   || (flag_errno_math
-                       && targetm.ref_may_alias_errno (ref)));
-         }
-       /* Freeing memory kills the pointed-to memory.  More importantly
-          the call has to serve as a barrier for moving loads and stores
-          across it.  */
-       case BUILT_IN_FREE:
-       case BUILT_IN_VA_END:
-         {
-           tree ptr = gimple_call_arg (call, 0);
-           return ptr_deref_may_alias_ref_p_1 (ptr, ref);
-         }
-       /* Realloc serves both as allocation point and deallocation point.  */
-       case BUILT_IN_REALLOC:
-         {
-           tree ptr = gimple_call_arg (call, 0);
-           /* Unix98 specifies that errno is set on allocation failure.  */
-           return ((flag_errno_math
-                    && targetm.ref_may_alias_errno (ref))
-                   || ptr_deref_may_alias_ref_p_1 (ptr, ref));
-         }
-       case BUILT_IN_GAMMA_R:
-       case BUILT_IN_GAMMAF_R:
-       case BUILT_IN_GAMMAL_R:
-       case BUILT_IN_LGAMMA_R:
-       case BUILT_IN_LGAMMAF_R:
-       case BUILT_IN_LGAMMAL_R:
-         {
-           tree out = gimple_call_arg (call, 1);
-           if (ptr_deref_may_alias_ref_p_1 (out, ref))
-             return true;
-           if (flag_errno_math)
-             break;
-           return false;
-         }
-       case BUILT_IN_FREXP:
-       case BUILT_IN_FREXPF:
-       case BUILT_IN_FREXPL:
-       case BUILT_IN_MODF:
-       case BUILT_IN_MODFF:
-       case BUILT_IN_MODFL:
-         {
-           tree out = gimple_call_arg (call, 1);
-           return ptr_deref_may_alias_ref_p_1 (out, ref);
-         }
-       case BUILT_IN_REMQUO:
-       case BUILT_IN_REMQUOF:
-       case BUILT_IN_REMQUOL:
-         {
-           tree out = gimple_call_arg (call, 2);
-           if (ptr_deref_may_alias_ref_p_1 (out, ref))
-             return true;
-           if (flag_errno_math)
-             break;
-           return false;
-         }
-       case BUILT_IN_SINCOS:
-       case BUILT_IN_SINCOSF:
-       case BUILT_IN_SINCOSL:
-         {
-           tree sin = gimple_call_arg (call, 1);
-           tree cos = gimple_call_arg (call, 2);
-           return (ptr_deref_may_alias_ref_p_1 (sin, ref)
-                   || ptr_deref_may_alias_ref_p_1 (cos, ref));
-         }
-       /* __sync_* builtins and some OpenMP builtins act as threading
-          barriers.  */
-#undef DEF_SYNC_BUILTIN
-#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM:
-#include "sync-builtins.def"
-#undef DEF_SYNC_BUILTIN
-       case BUILT_IN_GOMP_ATOMIC_START:
-       case BUILT_IN_GOMP_ATOMIC_END:
-       case BUILT_IN_GOMP_BARRIER:
-       case BUILT_IN_GOMP_BARRIER_CANCEL:
-       case BUILT_IN_GOMP_TASKWAIT:
-       case BUILT_IN_GOMP_TASKGROUP_END:
-       case BUILT_IN_GOMP_CRITICAL_START:
-       case BUILT_IN_GOMP_CRITICAL_END:
-       case BUILT_IN_GOMP_CRITICAL_NAME_START:
-       case BUILT_IN_GOMP_CRITICAL_NAME_END:
-       case BUILT_IN_GOMP_LOOP_END:
-       case BUILT_IN_GOMP_LOOP_END_CANCEL:
-       case BUILT_IN_GOMP_ORDERED_START:
-       case BUILT_IN_GOMP_ORDERED_END:
-       case BUILT_IN_GOMP_SECTIONS_END:
-       case BUILT_IN_GOMP_SECTIONS_END_CANCEL:
-       case BUILT_IN_GOMP_SINGLE_COPY_START:
-       case BUILT_IN_GOMP_SINGLE_COPY_END:
-         return true;
-       default:
-         /* Fallthru to general call handling.  */;
-      }
+  if (int res = check_fnspec (call, ref, true))
+    {
+      if (res == 1)
+       return true;
+    }
+  else
+    return false;
 
   /* Check if base is a global static variable that is not written
      by the function.  */
@@ -4079,6 +3774,8 @@ void
 attr_fnspec::verify ()
 {
   bool err = false;
+  if (!len)
+    return;
 
   /* Check return value specifier.  */
   if (len < return_desc_size)
@@ -4092,8 +3789,17 @@ attr_fnspec::verify ()
           && str[0] != 'R' && str[0] != 'W')
     err = true;
 
-  if (str[1] != ' ')
-    err = true;
+  switch (str[1])
+    {
+      case ' ':
+      case 'p':
+      case 'P':
+      case 'c':
+      case 'C':
+       break;
+      default:
+       err = true;
+    }
 
   /* Now check all parameters.  */
   for (unsigned int i = 0; arg_specified_p (i); i++)
@@ -4105,6 +3811,8 @@ attr_fnspec::verify ()
          case 'X':
          case 'r':
          case 'R':
+         case 'o':
+         case 'O':
          case 'w':
          case 'W':
          case '.':
@@ -4112,7 +3820,15 @@ attr_fnspec::verify ()
          default:
            err = true;
        }
-      if (str[idx + 1] != ' ')
+      if ((str[idx + 1] >= '1' && str[idx + 1] <= '9')
+         || str[idx + 1] == 't')
+       {
+         if (str[idx] != 'r' && str[idx] != 'R'
+             && str[idx] != 'w' && str[idx] != 'W'
+             && str[idx] != 'o' && str[idx] != 'O')
+           err = true;
+       }
+      else if (str[idx + 1] != ' ')
        err = true;
     }
   if (err)

Reply via email to