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 <hubi...@ucw.cz> > > * attr-fnspec.h: Update toplevel comment. > (attr_fnspec::attr_fnspec): New constructor. > (attr_fnspec::arg_read_p, > attr_fnspec::arg_written_p, > attr_fnspec::arg_access_size_given_by_arg_p, > attr_fnspec::arg_single_access_p > attr_fnspec::loads_known_p > attr_fnspec::stores_known_p, > attr_fnspec::clobbers_errno_p): New member functions. > (gimple_call_fnspec): Declare. > (builtin_fnspec): Declare. > * builtins.c: Include attr-fnspec.h > (builtin_fnspec): New function. > * builtins.def (BUILT_IN_MEMCPY): Do not specify RET1 fnspec. > (BUILT_IN_MEMMOVE): Do not specify RET1 fnspec. > (BUILT_IN_MEMSET): Do not specify RET1 fnspec. > (BUILT_IN_STRCAT): Do not specify RET1 fnspec. > (BUILT_IN_STRCPY): Do not specify RET1 fnspec. > (BUILT_IN_STRNCAT): Do not specify RET1 fnspec. > (BUILT_IN_STRNCPY): Do not specify RET1 fnspec. > (BUILT_IN_MEMCPY_CHK): Do not specify RET1 fnspec. > (BUILT_IN_MEMMOVE_CHK): Do not specify RET1 fnspec. > (BUILT_IN_MEMSET_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRCAT_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRCPY_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRNCAT_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRNCPY_CHK): Do not specify RET1 fnspec. > * gimple.c (gimple_call_fnspec): Return attr_fnspec. > (gimple_call_arg_flags): Update. > (gimple_call_return_flags): Update. > * tree-ssa-alias.c (check_fnspec): New function. > (ref_maybe_used_by_call_p_1): Use fnspec for builtin handling. > (call_may_clobber_ref_p_1): Likewise. > (attr_fnspec::verify): Update verifier. > * calls.c (decl_fnspec): New function. > (decl_return_flags): Use it. > diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h > index d38b84a969e..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 <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imend