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