> Sender: "Gcc-patches" <gcc-patches-boun...@gcc.gnu.org>
> 
> > > @@ -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  <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..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.
>

What does 'b' actually guarantee?

>     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 ());

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??!

> +    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!

> +    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

> +  {
> +    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)

arg_access_size_given_by_type_p?

> +  {
> +    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';
> +  }

So maybe instead of loads/stores_known_p have global_memory_read_p ()
and global_memory_written_p ()?

> +  /* Retun true if function is memory barrier.  */
> +  bool
> +  barrier_p ()
> +  {
> +    return str[1] == 'b';
> +  }

If it's possible leave this out.  Aliasing can only work to enforce
a memory barrier for global memory at the moment.

> +  /* 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';
> +  }
> +

errno_written_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 ";

shouldn't that be "1cW R "?  Likewise below, for all functions I think
(the uppercase - direct access only).

> +      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?

> +      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"

> +
> +      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.

> +      /* Realloc serves both as allocation point and deallocation point.  */
> +      case BUILT_IN_REALLOC:
> +     return ".cw ";

since it serves as "free" it indeed needs to be lower-case 'w'

> +      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";

So I wonder why we simply go without handling those in any way - 
definitely 'b' doesn't give any stronger guarantees than a random
"don't know" function.

Richard.

> +
> +      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)
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg,
Germany; GF: Felix Imend

Reply via email to