Extend builtin fnspecs

Richard Biener rguenther@suse.de
Mon Oct 26 15:15:27 GMT 2020


On Mon, 19 Oct 2020, Jan Hubicka wrote:

> > > +  /* True if memory reached by the argument is read.
> > > +     Valid only if all loads are known.  */
> > > +  bool
> > > +  arg_read_p (unsigned int i)
> > > +  {
> > > +    unsigned int idx = arg_idx (i);
> > > +    gcc_checking_assert (arg_specified_p (i));
> > > +    gcc_checking_assert (loads_known_p ());
> > 
> > I see loads_known_p () is 'const' (introducing new terminology
> > as alias for sth else is IMHO bad).  So what do you think
> > arg_read_p () guarantees?  Even on a not 'const' function
> > 'r' or 'R' or 'w' or 'W' means the argument could be read??!
> 
> Original intention was !arg_read_p to guarantee that argument is not
> read from (and for that you need const), but I updated it.
> > 
> > > +    return str[idx] == 'r' || str[idx] == 'R'
> > > +	   || str[idx] == 'w' || str[idx] == 'W';
> > > +  }
> > > +
> > > +  /* True if memory reached by the argument is read.
> > > +     Valid only if all loads are known.  */
> > > +  bool
> > > +  arg_written_p (unsigned int i)
> > > +  {
> > > +    unsigned int idx = arg_idx (i);
> > > +    gcc_checking_assert (arg_specified_p (i));
> > > +    gcc_checking_assert (stores_known_p ());
> > 
> > Likewise.  IMHO those will cause lots of confusion.  For example
> > arg_readonly_p doesn't imply arg_read_p.
> > 
> > Please keep the number of core predicates at a minimum!
> 
> Well, my intention is/was that while fnspec strings themselves are
> necessarily bit ad-hoc (trying to pack multiple things into 2 characters
> and choosing just few special cases we care about) we should abstract
> this and have predicates for individual properties we care about.
> 
> Indeed I did not name them very well, hopefully things are better now :)
> In future modref can detect those properties of functions and provide
> symetric API for testing them without need to go to fnspec limitations.
> Similarly I would like to decouple logic around const/pure functions
> better as well (since they pack multiple things together - presence of
> side effects, whether function is deterministic and info about global
> memory accesses).
> 
> Concerning arg_read_p and arg_written_p I introduced the while I was
> still intending to make 'w' and 'W' mean what 'o' and 'O' does.
> With original predicates the code handling loads
> 
> if (fnspec.loads_known_p ())
>   for each argument i
>     if (fnspec.arg_readonly_p (i))
>       argument is read from
>     else
>       argument is not read, ignore it.
> else
>  ask pta if base is local
> 
> Which looked like odd re-use of readonly_p predicate intended for
> something else.  I think main confussion is that I interpreted the specs
> with cCpP bit weirdly. In particular:
> 
>  ". W . R "
> means
>  - arg 1 is written&read directly and noescapes,
>  - arg 2 is written, read directly or indirectly and escapes,
>  - arg 3 is read only directly, not written to and odes not escape.
> 
> With previus patch 
>  ".cW . R "
> means:
>  - arg 1 is written&read directly and noescapes
>  - arg 2 is used only as pointer value (i.e. for NULL check)
>  - arg 3 is read only directly, not written to and does not escape.
> 
> With current patch is means:
>  - arg 1 is written&read directly and noescapes
>  - arg 2 is written&read directly or indirectly and may escape (in other stores allowed by the function)
>  - arg 3 is read only directly, not written to and does not escape.
> 
> With current patch the loop is:
> if (!fnspec.global_memory_read_p ())
>   for each argument i
>     if (POINTER_TYPE_P (TREE_TYPE (arg))
>     	&& fnspec.arg_maybe_read_p (i))
>       argument is read from
>     else
>       argument is not read.
> else
>   ask pta if base is local
> 
> Which seems better and follows what we did before.
> However the extra POINTER_TYPE_P test is needed since we do not want to
> disambiguate non-pointer parameters that also have '.' specifier. I am
> not sure that this is safe/feasible with gimple type system.

Hmm, so PTA does track pointers passed through uintptr_t for example
so I think you would need to write

      if (!POINTER_TYPE_P (TREE_TYPE (arg))
          || fnspec.arg_maybe_read_p (i))
        argument may be read from

?

> I think it should be:
> 
> for each argument i
>   if (POINTER_TYPE_P (TREE_TYPE (arg))
>       && fnspec.arg_maybe_read_p (i))
>     argument is read from
>   else
>     argument is not read.
> if (fnspec.global_memory_read_p ())
>   give up if pta thinks this is not local.
> 
> Because if I have function with spec say
>  ". R "
> and cal it with foo (&localvar) I think it should end up non-escaping
> and thus not alias with function calls except when it is an parameter.

Yes.
 
> We do not have builtins like this right now though.

I think the Fortran FE essentially creates those for scalars passed
by reference and INTENT(IN).  So the Fortran FE is a good way
to write testcases for all of this ;)

> > 
> > > +    return str[idx] == 'w' || str[idx] == 'W'
> > > +	   || str[idx] == 'o' || str[idx] == 'O';
> > > +  }
> > > +
> > > +  /* Return true if load of memory pointed to by argument I is specified
> > > +     by another argument.  In this case set ARG.  */
> > > +  bool
> > > +  arg_access_size_given_by_arg_p (unsigned int i, unsigned int *arg)
> > 
> > I think we need to document that this and all the other predicates are
> > _may_ predicates, thus strncat "1cW R3" means src is _possibly_
> > accessed from offset 0 to what argument 3 specifies.  That's important
> > for stores I think.  While memcpy has "1cW3R3" the fnspec can _not_
> > be used to DSE a previous write to the destination covering [0, n].
> > 
> > That is, those are all may_ predicates as used for alias analysis.
> > 
> > I think we want to reflect that in the predicate names, like
> > 
> > arg_maybe_read_p or arg_max_access_size_given_by_arg_p
> 
> arg_max_access_size_given_by_arg_p sounds good to me.
> 
> > 
> > arg_access_size_given_by_type_p?
> 
> Better for sure :)
> > > +/* If CALLEE has known side effects, fill in INFO and return true.
> > > +   See tree-ssa-structalias.c:find_func_aliases
> > > +   for the list of builtins we might need to handle here.  */
> > > +
> > > +attr_fnspec
> > > +builtin_fnspec (tree callee)
> > > +{
> > > +  built_in_function code = DECL_FUNCTION_CODE (callee);
> > > +
> > > +  switch (code)
> > > +    {
> > > +      /* All the following functions read memory pointed to by
> > > +	 their second argument and write memory pointed to by first
> > > +	 argument.
> > > +	 strcat/strncat additionally reads memory pointed to by the first
> > > +	 argument.  */
> > > +      case BUILT_IN_STRCAT:
> > > +      case BUILT_IN_STRCAT_CHK:
> > > +	return "1cw r ";
> > 
> > shouldn't that be "1cW R "?  Likewise below, for all functions I think
> > (the uppercase - direct access only).
> 
> Sorry I mixed up lowercase and uppercase in my mind.
> Will try to stick it on my wall, since this is a systematic error.

;)

> > 
> > > +      case BUILT_IN_STRNCAT:
> > > +      case BUILT_IN_STRNCAT_CHK:
> > > +	return "1cw r3";
> > > +      case BUILT_IN_STRCPY:
> > > +      case BUILT_IN_STRCPY_CHK:
> > > +	return "1co r ";
> > > +      case BUILT_IN_STPCPY:
> > > +      case BUILT_IN_STPCPY_CHK:
> > > +	return ".co r ";
> > > +      case BUILT_IN_STRNCPY:
> > > +      case BUILT_IN_MEMCPY:
> > > +      case BUILT_IN_MEMMOVE:
> > > +      case BUILT_IN_TM_MEMCPY:
> > > +      case BUILT_IN_TM_MEMMOVE:
> > > +      case BUILT_IN_STRNCPY_CHK:
> > > +      case BUILT_IN_MEMCPY_CHK:
> > > +      case BUILT_IN_MEMMOVE_CHK:
> > > +	return "1co3r3";
> > 
> > See above for max vs. exact size - do we want to somehow distinguish
> > that?  ao_ref has offset, size and max_size - if size == max_size
> > then the access is exactly [offset, offset + size].  I guess at
> > the moment you fill size = -1 and max_size from the argument 
> > specification?
> 
> I fill 1 in ipa modref (which I indeded to mean that the access is
> somehwere between 1 and known size).  I see it is not quite precise.
> 
> However old code called ao_ref_init_from_ptr_and_size which does:
> 
>   if (size                                                                      
>       && poly_int_tree_p (size, &size_hwi)                                      
>       && coeffs_in_range_p (size_hwi, 0, HOST_WIDE_INT_MAX / BITS_PER_UNIT))    
>     ref->max_size = ref->size = size_hwi * BITS_PER_UNIT;                       
>   else                                                                          
>     ref->max_size = ref->size = -1;                                             
> 
> This is done even for string functions which looks like a bug.

Indeed, for STRNCPY we'd need ref->size = -1 but ref->max_size specified.
Guess since stmt_kills_ref_p uses this as well we can create a
wrong-code testcase for this.  Meh.

> I suppose -1 is safe value here because ref->size seems to be ever used
> only to compare with ref->max_size to see if we have full sized
> load/store.

Yeah, -1 just means unknown.

> > 
> > > +      case BUILT_IN_MEMPCPY:
> > > +      case BUILT_IN_MEMPCPY_CHK:
> > > +	return ".co3r3";
> > > +      case BUILT_IN_STPNCPY:
> > > +      case BUILT_IN_STPNCPY_CHK:
> > > +	return ".co3r3";
> > > +      case BUILT_IN_BCOPY:
> > > +	return ".cr3o3";
> > > +
> > > +      /* The following functions read memory pointed to by their
> > > +	 first argument.  */
> > > +      CASE_BUILT_IN_TM_LOAD (1):
> > > +      CASE_BUILT_IN_TM_LOAD (2):
> > > +      CASE_BUILT_IN_TM_LOAD (4):
> > > +      CASE_BUILT_IN_TM_LOAD (8):
> > > +      CASE_BUILT_IN_TM_LOAD (FLOAT):
> > > +      CASE_BUILT_IN_TM_LOAD (DOUBLE):
> > > +      CASE_BUILT_IN_TM_LOAD (LDOUBLE):
> > > +      CASE_BUILT_IN_TM_LOAD (M64):
> > > +      CASE_BUILT_IN_TM_LOAD (M128):
> > > +      CASE_BUILT_IN_TM_LOAD (M256):
> > > +      case BUILT_IN_TM_LOG:
> > > +      case BUILT_IN_TM_LOG_1:
> > > +      case BUILT_IN_TM_LOG_2:
> > > +      case BUILT_IN_TM_LOG_4:
> > > +      case BUILT_IN_TM_LOG_8:
> > > +      case BUILT_IN_TM_LOG_FLOAT:
> > > +      case BUILT_IN_TM_LOG_DOUBLE:
> > > +      case BUILT_IN_TM_LOG_LDOUBLE:
> > > +      case BUILT_IN_TM_LOG_M64:
> > > +      case BUILT_IN_TM_LOG_M128:
> > > +      case BUILT_IN_TM_LOG_M256:
> > > +	return ".crt";
> > 
> > ".cRt" and I think BUILT_IN_TM_LOG has a size specification so
> > that one needs to be ".cR2"
> 
> They does not seem to. 

I've looked into the instrumentation, so TM_LOG does, the _1,etc.
variants have the size from the type but

> However I noticed that all the buitins are declared with
> BT_VOLATILE_PTR so the size is not defined.

that might be a problem indeed ;)

>  I changed them to ".cR "
> for time being.
> > 
> > > +
> > > +      case BUILT_IN_INDEX:
> > > +      case BUILT_IN_STRCHR:
> > > +      case BUILT_IN_STRRCHR:
> > > +	return ".cr ";
> > > +
> > > +      /* These read memory pointed to by the first argument.
> > > +	 Allocating memory does not have any side-effects apart from
> > > +	 being the definition point for the pointer.
> > > +	 Unix98 specifies that errno is set on allocation failure.  */
> > > +      case BUILT_IN_STRDUP:
> > > +	return "mCr ";
> > > +      case BUILT_IN_STRNDUP:
> > > +	return "mCr2";
> > > +      /* Allocating memory does not have any side-effects apart from
> > > +	 being the definition point for the pointer.  */
> > > +      case BUILT_IN_MALLOC:
> > > +      case BUILT_IN_ALIGNED_ALLOC:
> > > +      case BUILT_IN_CALLOC:
> > > +	return "mC";
> > > +      CASE_BUILT_IN_ALLOCA:
> > > +	return "mc";
> > > +      /* These read memory pointed to by the first argument with size
> > > +	 in the third argument.  */
> > > +      case BUILT_IN_MEMCHR:
> > > +	return ".cr3";
> > > +      /* These read memory pointed to by the first and second arguments.  */
> > > +      case BUILT_IN_STRSTR:
> > > +      case BUILT_IN_STRPBRK:
> > > +	return ".cr r ";
> > > +      /* Freeing memory kills the pointed-to memory.  More importantly
> > > +	 the call has to serve as a barrier for moving loads and stores
> > > +	 across it.  */
> > > +      case BUILT_IN_FREE:
> > > +      case BUILT_IN_VA_END:
> > > +	return ".co ";
> > 
> > va_end is probably ".cO " while free should be ".co " for its
> > barrier effect.
> 
> Is it really necessary?  I can see that free is modelled as something
> that kills the allocated block of memory, but why it would kill things
> pointed from that block?

Hmm.  Guess it doesn't need to.  It's a bit twisty since you
have to prevent code motion from both sides.

> 
> I updated it though.

Looks good to me now.

Thanks and sorry for the delay.
Richard.

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

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


More information about the Gcc-patches mailing list