Extend builtin fnspecs

Jan Hubicka hubicka@ucw.cz
Fri Oct 16 07:34:17 GMT 2020


> > @@ -122,7 +201,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';
> 
> Did you mean 
> 
>   || str[idx] == 'o' || str[idx] == 'O';
> 
> here?
Yes, thank you!  It is a lot of ascii-art that is easy to get wrong :(
I also re-checked the new specs and noticed wrong return value of
mempcpy.  Here is updated patch I am re-testing.

2020-10-16  Jan Hubicka  <hubicka@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..c9421a60095 100644
--- a/gcc/attr-fnspec.h
+++ b/gcc/attr-fnspec.h
@@ -27,11 +27,19 @@
      '.'	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.
+     'b'	specifies that functions is a memory barrier.
+   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 +50,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 +84,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 +97,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 +123,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 +144,56 @@ public:
     return str[idx] == 'r' || str[idx] == 'R';
   }
 
+  /* 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 ());
+    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 ());
+    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)
+  {
+    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_single_access_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 +201,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 +227,39 @@ public:
     return str[0] == 'm';
   }
 
+  /* Return true if all memory read by the function is specified by fnspec.  */
+  bool
+  loads_known_p ()
+  {
+    return str[1] == 'c' || str[1] == 'C';
+  }
+
+  /* Retun true if function is memory barrier.  */
+  bool
+  barrier_p ()
+  {
+    return str[1] == 'b';
+  }
+
+  /* Return true if all memory written by the function 
+     is specified by fnspec.  */
+  bool
+  stores_known_p ()
+  {
+    return str[1] == 'c' || str[1] == 'C' || str[1] == 'p' || str[1] == 'P';
+  }
+
+  bool
+  clobbers_errno_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..41adc07c898 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,191 @@ 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 ".crt";
+
+      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 ";
+      /* 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 ".cot";
+      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";
+      /* __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 ".b";
+
+      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..83874df7da3 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,60 @@ 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 (fnspec.barrier_p ())
+	return true;
+      if (clobber ? fnspec.stores_known_p () : fnspec.loads_known_p ())
+	{
+	  for (unsigned int i = 0; i < gimple_call_num_args (call); i++)
+	    if (!fnspec.arg_specified_p (i))
+	      ;
+	    else if (clobber ? fnspec.arg_written_p (i) : fnspec.arg_read_p (i))
+	      {
+		ao_ref dref;
+		tree size = NULL_TREE;
+		unsigned int size_arg;
+
+		if (fnspec.arg_access_size_given_by_arg_p (i, &size_arg))
+		  size = gimple_call_arg (call, size_arg);
+		else if (fnspec.arg_single_access_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.clobbers_errno_p ()
+	      && flag_errno_math
+	      && targetm.ref_may_alias_errno (ref))
+	    return true;
+	  return 0;
+	}
+    }
+  return -1;
+}
+
 /* If the call CALL may use the memory reference REF return true,
    otherwise return false.  */
 
@@ -2650,222 +2705,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 +2952,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 +3735,8 @@ void
 attr_fnspec::verify ()
 {
   bool err = false;
+  if (!len)
+    return;
 
   /* Check return value specifier.  */
   if (len < return_desc_size)
@@ -4092,8 +3750,18 @@ 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':
+      case 'b':
+	break;
+      default:
+	err = true;
+    }
 
   /* Now check all parameters.  */
   for (unsigned int i = 0; arg_specified_p (i); i++)
@@ -4105,6 +3773,8 @@ attr_fnspec::verify ()
 	  case 'X':
 	  case 'r':
 	  case 'R':
+	  case 'o':
+	  case 'O':
 	  case 'w':
 	  case 'W':
 	  case '.':
@@ -4112,7 +3782,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)


More information about the Gcc-patches mailing list