Extend builtin fnspecs
authorJan Hubicka <jh@suse.cz>
Mon, 26 Oct 2020 19:19:33 +0000 (20:19 +0100)
committerJan Hubicka <jh@suse.cz>
Mon, 26 Oct 2020 19:22:16 +0000 (20:22 +0100)
* 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.

gcc/attr-fnspec.h
gcc/builtins.c
gcc/builtins.def
gcc/calls.c
gcc/gimple.c
gcc/tree-ssa-alias.c

index d38b84a..78b1a5a 100644 (file)
      '.'       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.
    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  */
index 72627b5..e7d4ff3 100644 (file)
@@ -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,165 @@ 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_STACK_RESTORE:
+      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 "1cX ";
+      /* But posix_memalign stores a pointer into the memory pointed to
+        by its first argument.  */
+      case BUILT_IN_POSIX_MEMALIGN:
+       return ".cOt";
+
+      default:
+       return "";
+    }
+}
index 95428c0..61aff89 100644 (file)
@@ -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)
index 17b8e2f..a8f4596 100644 (file)
@@ -630,21 +630,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))
index f19e24d..469e6f3 100644 (file)
@@ -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))
index 877e499..bc0f32a 100644 (file)
@@ -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 +2989,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 +3772,8 @@ void
 attr_fnspec::verify ()
 {
   bool err = false;
+  if (!len)
+    return;
 
   /* Check return value specifier.  */
   if (len < return_desc_size)
@@ -4092,8 +3787,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 +3809,8 @@ attr_fnspec::verify ()
          case 'X':
          case 'r':
          case 'R':
+         case 'o':
+         case 'O':
          case 'w':
          case 'W':
          case '.':
@@ -4112,7 +3818,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)