> Sender: "Gcc-patches" <gcc-patches-boun...@gcc.gnu.org> > > > > @@ -122,7 +201,8 @@ public: > > > unsigned int idx = arg_idx (i); > > > gcc_checking_assert (arg_specified_p (i)); > > > return str[idx] == 'w' || str[idx] == 'W' > > > - || str[idx] == 'R' || str[idx] == 'r'; > > > + || str[idx] == 'r' || str[idx] == 'R' > > > + || str[idx] == 'o' || str[idx] == 'o'; > > > > Did you mean > > > > || str[idx] == 'o' || str[idx] == 'O'; > > > > here? > Yes, thank you! It is a lot of ascii-art that is easy to get wrong :( > I also re-checked the new specs and noticed wrong return value of > mempcpy. Here is updated patch I am re-testing. > > 2020-10-16 Jan Hubicka <hubi...@ucw.cz> > > * attr-fnspec.h: Update toplevel comment. > (attr_fnspec::attr_fnspec): New constructor. > (attr_fnspec::arg_read_p, > attr_fnspec::arg_written_p, > attr_fnspec::arg_access_size_given_by_arg_p, > attr_fnspec::arg_single_access_p > attr_fnspec::loads_known_p > attr_fnspec::stores_known_p, > attr_fnspec::clobbers_errno_p): New member functions. > (gimple_call_fnspec): Declare. > (builtin_fnspec): Declare. > * builtins.c: Include attr-fnspec.h > (builtin_fnspec): New function. > * builtins.def (BUILT_IN_MEMCPY): Do not specify RET1 fnspec. > (BUILT_IN_MEMMOVE): Do not specify RET1 fnspec. > (BUILT_IN_MEMSET): Do not specify RET1 fnspec. > (BUILT_IN_STRCAT): Do not specify RET1 fnspec. > (BUILT_IN_STRCPY): Do not specify RET1 fnspec. > (BUILT_IN_STRNCAT): Do not specify RET1 fnspec. > (BUILT_IN_STRNCPY): Do not specify RET1 fnspec. > (BUILT_IN_MEMCPY_CHK): Do not specify RET1 fnspec. > (BUILT_IN_MEMMOVE_CHK): Do not specify RET1 fnspec. > (BUILT_IN_MEMSET_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRCAT_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRCPY_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRNCAT_CHK): Do not specify RET1 fnspec. > (BUILT_IN_STRNCPY_CHK): Do not specify RET1 fnspec. > * gimple.c (gimple_call_fnspec): Return attr_fnspec. > (gimple_call_arg_flags): Update. > (gimple_call_return_flags): Update. > * tree-ssa-alias.c (check_fnspec): New function. > (ref_maybe_used_by_call_p_1): Use fnspec for builtin handling. > (call_may_clobber_ref_p_1): Likewise. > (attr_fnspec::verify): Update verifier. > * calls.c (decl_fnspec): New function. > (decl_return_flags): Use it. > > diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h > index d38b84a969e..c9421a60095 100644 > --- a/gcc/attr-fnspec.h > +++ b/gcc/attr-fnspec.h > @@ -27,11 +27,19 @@ > '.' specifies that nothing is known. > character 1 specifies additional function properties > ' ' specifies that nothing is known > + 'p' or 'P' specifies that function is pure except for described side > + effects. > + 'c' or 'C' specifies that function is const except for described side > + effects. > + 'b' specifies that functions is a memory barrier. > + The uppercase letter in addition specifies that function clobbers errno. >
What does 'b' actually guarantee? > character 2+2i specifies properties of argument number i as follows: > 'x' or 'X' specifies that parameter is unused. > 'r' or 'R' specifies that the memory pointed to by the parameter is only > read and does not escape > + 'o' or 'O' specifies that the memory pointed to by the parameter is only > + written and does not escape > 'w' or 'W' specifies that the memory pointed to by the parameter does > not > escape > '.' specifies that nothing is known. > @@ -42,6 +50,10 @@ > character 3+2i specifies additional properties of argument number i > as follows: > ' ' nothing is known > + 't' the size of value written/read corresponds to the size of > + of the pointed-to type of the argument type > + '1'...'9' the size of value written/read is given by the specified > + argument > */ > > #ifndef ATTR_FNSPEC_H > @@ -72,6 +84,12 @@ public: > if (flag_checking) > verify (); > } > + attr_fnspec (const char *str) > + : str (str), len (strlen (str)) > + { > + if (flag_checking) > + verify (); > + } > attr_fnspec (const_tree identifier) > : str (TREE_STRING_POINTER (identifier)), > len (TREE_STRING_LENGTH (identifier)) > @@ -79,6 +97,17 @@ public: > if (flag_checking) > verify (); > } > + attr_fnspec () > + : str (NULL), len (0) > + { > + } > + > + /* Return true if fn spec is known. */ > + bool > + known_p () > + { > + return len; > + } > > /* Return true if arg I is specified. */ > bool > @@ -94,7 +123,7 @@ public: > { > unsigned int idx = arg_idx (i); > gcc_checking_assert (arg_specified_p (i)); > - return str[idx] == 'R' || str[idx] == 'W'; > + return str[idx] == 'R' || str[idx] == 'O' || str[idx] == 'W'; > } > > /* True if argument is used. */ > @@ -115,6 +144,56 @@ public: > return str[idx] == 'r' || str[idx] == 'R'; > } > > + /* True if memory reached by the argument is read. > + Valid only if all loads are known. */ > + bool > + arg_read_p (unsigned int i) > + { > + unsigned int idx = arg_idx (i); > + gcc_checking_assert (arg_specified_p (i)); > + gcc_checking_assert (loads_known_p ()); I see loads_known_p () is 'const' (introducing new terminology as alias for sth else is IMHO bad). So what do you think arg_read_p () guarantees? Even on a not 'const' function 'r' or 'R' or 'w' or 'W' means the argument could be read??! > + return str[idx] == 'r' || str[idx] == 'R' > + || str[idx] == 'w' || str[idx] == 'W'; > + } > + > + /* True if memory reached by the argument is read. > + Valid only if all loads are known. */ > + bool > + arg_written_p (unsigned int i) > + { > + unsigned int idx = arg_idx (i); > + gcc_checking_assert (arg_specified_p (i)); > + gcc_checking_assert (stores_known_p ()); Likewise. IMHO those will cause lots of confusion. For example arg_readonly_p doesn't imply arg_read_p. Please keep the number of core predicates at a minimum! > + return str[idx] == 'w' || str[idx] == 'W' > + || str[idx] == 'o' || str[idx] == 'O'; > + } > + > + /* Return true if load of memory pointed to by argument I is specified > + by another argument. In this case set ARG. */ > + bool > + arg_access_size_given_by_arg_p (unsigned int i, unsigned int *arg) I think we need to document that this and all the other predicates are _may_ predicates, thus strncat "1cW R3" means src is _possibly_ accessed from offset 0 to what argument 3 specifies. That's important for stores I think. While memcpy has "1cW3R3" the fnspec can _not_ be used to DSE a previous write to the destination covering [0, n]. That is, those are all may_ predicates as used for alias analysis. I think we want to reflect that in the predicate names, like arg_maybe_read_p or arg_max_access_size_given_by_arg_p > + { > + unsigned int idx = arg_idx (i); > + gcc_checking_assert (arg_specified_p (i)); > + if (str[idx + 1] >= '1' && str[idx + 1] <= '9') > + { > + *arg = str[idx + 1] - '1'; > + return true; > + } > + else > + return false; > + } > + > + /* Return true if the pointed-to type of the argument correspond to the > + size of the memory acccess. */ > + bool > + arg_single_access_p (unsigned int i) arg_access_size_given_by_type_p? > + { > + unsigned int idx = arg_idx (i); > + gcc_checking_assert (arg_specified_p (i)); > + return str[idx + 1] == 't'; > + } > + > /* True if the argument does not escape. */ > bool > arg_noescape_p (unsigned int i) > @@ -122,7 +201,8 @@ public: > unsigned int idx = arg_idx (i); > gcc_checking_assert (arg_specified_p (i)); > return str[idx] == 'w' || str[idx] == 'W' > - || str[idx] == 'R' || str[idx] == 'r'; > + || str[idx] == 'r' || str[idx] == 'R' > + || str[idx] == 'o' || str[idx] == 'O'; > } > > /* Return true if function returns value of its parameter. If ARG_NO is > @@ -147,8 +227,39 @@ public: > return str[0] == 'm'; > } > > + /* Return true if all memory read by the function is specified by fnspec. > */ > + bool > + loads_known_p () > + { > + return str[1] == 'c' || str[1] == 'C'; > + } So maybe instead of loads/stores_known_p have global_memory_read_p () and global_memory_written_p ()? > + /* Retun true if function is memory barrier. */ > + bool > + barrier_p () > + { > + return str[1] == 'b'; > + } If it's possible leave this out. Aliasing can only work to enforce a memory barrier for global memory at the moment. > + /* Return true if all memory written by the function > + is specified by fnspec. */ > + bool > + stores_known_p () > + { > + return str[1] == 'c' || str[1] == 'C' || str[1] == 'p' || str[1] == 'P'; > + } > + > + bool > + clobbers_errno_p () > + { > + return str[1] == 'C' || str[1] == 'P'; > + } > + errno_written_p () > /* Check validity of the string. */ > void verify (); > }; > > +extern attr_fnspec gimple_call_fnspec (const gcall *stmt); > +extern attr_fnspec builtin_fnspec (tree); > + > #endif /* ATTR_FNSPEC_H */ > diff --git a/gcc/builtins.c b/gcc/builtins.c > index 72627b5b859..41adc07c898 100644 > --- a/gcc/builtins.c > +++ b/gcc/builtins.c > @@ -76,6 +76,7 @@ along with GCC; see the file COPYING3. If not see > #include "gimple-ssa.h" > #include "tree-ssa-live.h" > #include "tree-outof-ssa.h" > +#include "attr-fnspec.h" > > struct target_builtins default_target_builtins; > #if SWITCHABLE_TARGET > @@ -12913,3 +12914,191 @@ access_ref::offset_bounded () const > tree max = TYPE_MAX_VALUE (ptrdiff_type_node); > return wi::to_offset (min) <= offrng[0] && offrng[1] <= wi::to_offset > (max); > } > + > +/* If CALLEE has known side effects, fill in INFO and return true. > + See tree-ssa-structalias.c:find_func_aliases > + for the list of builtins we might need to handle here. */ > + > +attr_fnspec > +builtin_fnspec (tree callee) > +{ > + built_in_function code = DECL_FUNCTION_CODE (callee); > + > + switch (code) > + { > + /* All the following functions read memory pointed to by > + their second argument and write memory pointed to by first > + argument. > + strcat/strncat additionally reads memory pointed to by the first > + argument. */ > + case BUILT_IN_STRCAT: > + case BUILT_IN_STRCAT_CHK: > + return "1cw r "; shouldn't that be "1cW R "? Likewise below, for all functions I think (the uppercase - direct access only). > + case BUILT_IN_STRNCAT: > + case BUILT_IN_STRNCAT_CHK: > + return "1cw r3"; > + case BUILT_IN_STRCPY: > + case BUILT_IN_STRCPY_CHK: > + return "1co r "; > + case BUILT_IN_STPCPY: > + case BUILT_IN_STPCPY_CHK: > + return ".co r "; > + case BUILT_IN_STRNCPY: > + case BUILT_IN_MEMCPY: > + case BUILT_IN_MEMMOVE: > + case BUILT_IN_TM_MEMCPY: > + case BUILT_IN_TM_MEMMOVE: > + case BUILT_IN_STRNCPY_CHK: > + case BUILT_IN_MEMCPY_CHK: > + case BUILT_IN_MEMMOVE_CHK: > + return "1co3r3"; See above for max vs. exact size - do we want to somehow distinguish that? ao_ref has offset, size and max_size - if size == max_size then the access is exactly [offset, offset + size]. I guess at the moment you fill size = -1 and max_size from the argument specification? > + case BUILT_IN_MEMPCPY: > + case BUILT_IN_MEMPCPY_CHK: > + return ".co3r3"; > + case BUILT_IN_STPNCPY: > + case BUILT_IN_STPNCPY_CHK: > + return ".co3r3"; > + case BUILT_IN_BCOPY: > + return ".cr3o3"; > + > + /* The following functions read memory pointed to by their > + first argument. */ > + CASE_BUILT_IN_TM_LOAD (1): > + CASE_BUILT_IN_TM_LOAD (2): > + CASE_BUILT_IN_TM_LOAD (4): > + CASE_BUILT_IN_TM_LOAD (8): > + CASE_BUILT_IN_TM_LOAD (FLOAT): > + CASE_BUILT_IN_TM_LOAD (DOUBLE): > + CASE_BUILT_IN_TM_LOAD (LDOUBLE): > + CASE_BUILT_IN_TM_LOAD (M64): > + CASE_BUILT_IN_TM_LOAD (M128): > + CASE_BUILT_IN_TM_LOAD (M256): > + case BUILT_IN_TM_LOG: > + case BUILT_IN_TM_LOG_1: > + case BUILT_IN_TM_LOG_2: > + case BUILT_IN_TM_LOG_4: > + case BUILT_IN_TM_LOG_8: > + case BUILT_IN_TM_LOG_FLOAT: > + case BUILT_IN_TM_LOG_DOUBLE: > + case BUILT_IN_TM_LOG_LDOUBLE: > + case BUILT_IN_TM_LOG_M64: > + case BUILT_IN_TM_LOG_M128: > + case BUILT_IN_TM_LOG_M256: > + return ".crt"; ".cRt" and I think BUILT_IN_TM_LOG has a size specification so that one needs to be ".cR2" > + > + case BUILT_IN_INDEX: > + case BUILT_IN_STRCHR: > + case BUILT_IN_STRRCHR: > + return ".cr "; > + > + /* These read memory pointed to by the first argument. > + Allocating memory does not have any side-effects apart from > + being the definition point for the pointer. > + Unix98 specifies that errno is set on allocation failure. */ > + case BUILT_IN_STRDUP: > + return "mCr "; > + case BUILT_IN_STRNDUP: > + return "mCr2"; > + /* Allocating memory does not have any side-effects apart from > + being the definition point for the pointer. */ > + case BUILT_IN_MALLOC: > + case BUILT_IN_ALIGNED_ALLOC: > + case BUILT_IN_CALLOC: > + return "mC"; > + CASE_BUILT_IN_ALLOCA: > + return "mc"; > + /* These read memory pointed to by the first argument with size > + in the third argument. */ > + case BUILT_IN_MEMCHR: > + return ".cr3"; > + /* These read memory pointed to by the first and second arguments. */ > + case BUILT_IN_STRSTR: > + case BUILT_IN_STRPBRK: > + return ".cr r "; > + /* Freeing memory kills the pointed-to memory. More importantly > + the call has to serve as a barrier for moving loads and stores > + across it. */ > + case BUILT_IN_FREE: > + case BUILT_IN_VA_END: > + return ".co "; va_end is probably ".cO " while free should be ".co " for its barrier effect. > + /* Realloc serves both as allocation point and deallocation point. */ > + case BUILT_IN_REALLOC: > + return ".cw "; since it serves as "free" it indeed needs to be lower-case 'w' > + case BUILT_IN_GAMMA_R: > + case BUILT_IN_GAMMAF_R: > + case BUILT_IN_GAMMAL_R: > + case BUILT_IN_LGAMMA_R: > + case BUILT_IN_LGAMMAF_R: > + case BUILT_IN_LGAMMAL_R: > + return ".C. ot"; > + case BUILT_IN_FREXP: > + case BUILT_IN_FREXPF: > + case BUILT_IN_FREXPL: > + case BUILT_IN_MODF: > + case BUILT_IN_MODFF: > + case BUILT_IN_MODFL: > + return ".c. ot"; > + case BUILT_IN_REMQUO: > + case BUILT_IN_REMQUOF: > + case BUILT_IN_REMQUOL: > + return ".c. . ot"; > + case BUILT_IN_SINCOS: > + case BUILT_IN_SINCOSF: > + case BUILT_IN_SINCOSL: > + return ".c. otot"; > + case BUILT_IN_MEMSET: > + case BUILT_IN_MEMSET_CHK: > + case BUILT_IN_TM_MEMSET: > + return "1co3"; > + CASE_BUILT_IN_TM_STORE (1): > + CASE_BUILT_IN_TM_STORE (2): > + CASE_BUILT_IN_TM_STORE (4): > + CASE_BUILT_IN_TM_STORE (8): > + CASE_BUILT_IN_TM_STORE (FLOAT): > + CASE_BUILT_IN_TM_STORE (DOUBLE): > + CASE_BUILT_IN_TM_STORE (LDOUBLE): > + CASE_BUILT_IN_TM_STORE (M64): > + CASE_BUILT_IN_TM_STORE (M128): > + CASE_BUILT_IN_TM_STORE (M256): > + return ".cot"; > + case BUILT_IN_STACK_SAVE: > + return ".c"; > + case BUILT_IN_ASSUME_ALIGNED: > + return "1c"; > + /* But posix_memalign stores a pointer into the memory pointed to > + by its first argument. */ > + case BUILT_IN_POSIX_MEMALIGN: > + return ".cot"; > + /* The following builtins do not read from memory. */ > + case BUILT_IN_STACK_RESTORE: > + return ".c"; > + /* __sync_* builtins and some OpenMP builtins act as threading > + barriers. */ > +#undef DEF_SYNC_BUILTIN > +#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM: > +#include "sync-builtins.def" > +#undef DEF_SYNC_BUILTIN > + case BUILT_IN_GOMP_ATOMIC_START: > + case BUILT_IN_GOMP_ATOMIC_END: > + case BUILT_IN_GOMP_BARRIER: > + case BUILT_IN_GOMP_BARRIER_CANCEL: > + case BUILT_IN_GOMP_TASKWAIT: > + case BUILT_IN_GOMP_TASKGROUP_END: > + case BUILT_IN_GOMP_CRITICAL_START: > + case BUILT_IN_GOMP_CRITICAL_END: > + case BUILT_IN_GOMP_CRITICAL_NAME_START: > + case BUILT_IN_GOMP_CRITICAL_NAME_END: > + case BUILT_IN_GOMP_LOOP_END: > + case BUILT_IN_GOMP_LOOP_END_CANCEL: > + case BUILT_IN_GOMP_ORDERED_START: > + case BUILT_IN_GOMP_ORDERED_END: > + case BUILT_IN_GOMP_SECTIONS_END: > + case BUILT_IN_GOMP_SECTIONS_END_CANCEL: > + case BUILT_IN_GOMP_SINGLE_COPY_START: > + case BUILT_IN_GOMP_SINGLE_COPY_END: > + return ".b"; So I wonder why we simply go without handling those in any way - definitely 'b' doesn't give any stronger guarantees than a random "don't know" function. Richard. > + > + default: > + return ""; > + } > +} > diff --git a/gcc/builtins.def b/gcc/builtins.def > index 95428c010d9..61aff89e658 100644 > --- a/gcc/builtins.def > +++ b/gcc/builtins.def > @@ -701,26 +701,26 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_BZERO, "bzero", > BT_FN_VOID_PTR_SIZE, ATTR_NOTHR > DEF_EXT_LIB_BUILTIN (BUILT_IN_INDEX, "index", > BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_MEMCHR, "memchr", > BT_FN_PTR_CONST_PTR_INT_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_MEMCMP, "memcmp", > BT_FN_INT_CONST_PTR_CONST_PTR_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", > BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_MEMMOVE, "memmove", > BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_MEMCPY, "memcpy", > BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_MEMMOVE, "memmove", > BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY, "mempcpy", > BT_FN_PTR_PTR_CONST_PTR_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", > BT_FN_PTR_PTR_INT_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_MEMSET, "memset", > BT_FN_PTR_PTR_INT_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_RINDEX, "rindex", > BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY, "stpcpy", > BT_FN_STRING_STRING_CONST_STRING, ATTR_RETNONNULL_NOTHROW_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY, "stpncpy", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCASECMP, "strcasecmp", > BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_STRCAT, "strcat", > BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_STRCAT, "strcat", > BT_FN_STRING_STRING_CONST_STRING, ATTR_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRCHR, "strchr", > BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRCMP, "strcmp", > BT_FN_INT_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_STRCPY, "strcpy", > BT_FN_STRING_STRING_CONST_STRING, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_STRCPY, "strcpy", > BT_FN_STRING_STRING_CONST_STRING, ATTR_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRCSPN, "strcspn", > BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_C2X_BUILTIN (BUILT_IN_STRDUP, "strdup", > BT_FN_STRING_CONST_STRING, > ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF) > DEF_C2X_BUILTIN (BUILT_IN_STRNDUP, "strndup", > BT_FN_STRING_CONST_STRING_SIZE, > ATTR_MALLOC_WARN_UNUSED_RESULT_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRLEN, "strlen", BT_FN_SIZE_CONST_STRING, > ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCASECMP, "strncasecmp", > BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRNCMP, "strncmp", > BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) > -DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNLEN, "strnlen", > BT_FN_SIZE_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRPBRK, "strpbrk", > BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) > DEF_LIB_BUILTIN (BUILT_IN_STRRCHR, "strrchr", > BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) > @@ -970,16 +970,16 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, > "__builtin_strncmp_eq") > > /* Object size checking builtins. */ > DEF_GCC_BUILTIN (BUILT_IN_OBJECT_SIZE, "object_size", > BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", > BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", > BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", > BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", > BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", > BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMSET_CHK, "__memset_chk", > BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_MEMSET_CHK, "__memset_chk", > BT_FN_PTR_PTR_INT_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STPCPY_CHK, "__stpcpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_STPNCPY_CHK, "__stpncpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCAT_CHK, "__strcat_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCPY_CHK, "__strcpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCAT_CHK, "__strncat_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > -DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCAT_CHK, "__strcat_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRCPY_CHK, "__strcpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCAT_CHK, "__strncat_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCPY_CHK, "__strncpy_chk", > BT_FN_STRING_STRING_CONST_STRING_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF) > DEF_EXT_LIB_BUILTIN (BUILT_IN_SNPRINTF_CHK, "__snprintf_chk", > BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VAR, > ATTR_FORMAT_PRINTF_NOTHROW_5_6) > DEF_EXT_LIB_BUILTIN (BUILT_IN_SPRINTF_CHK, "__sprintf_chk", > BT_FN_INT_STRING_INT_SIZE_CONST_STRING_VAR, > ATTR_NOTHROW_NONNULL_1_FORMAT_PRINTF_4_5) > DEF_EXT_LIB_BUILTIN (BUILT_IN_VSNPRINTF_CHK, "__vsnprintf_chk", > BT_FN_INT_STRING_SIZE_INT_SIZE_CONST_STRING_VALIST_ARG, > ATTR_FORMAT_PRINTF_NOTHROW_5_0) > diff --git a/gcc/calls.c b/gcc/calls.c > index d3120b23f60..dab37f1f213 100644 > --- a/gcc/calls.c > +++ b/gcc/calls.c > @@ -629,21 +629,32 @@ special_function_p (const_tree fndecl, int flags) > return flags; > } > > +/* Return fnspec for DECL. */ > + > +static attr_fnspec > +decl_fnspec (tree fndecl) > +{ > + tree attr; > + tree type = TREE_TYPE (fndecl); > + if (type) > + { > + attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type)); > + if (attr) > + { > + return TREE_VALUE (TREE_VALUE (attr)); > + } > + } > + if (fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)) > + return builtin_fnspec (fndecl); > + return ""; > +} > + > /* Similar to special_function_p; return a set of ERF_ flags for the > function FNDECL. */ > static int > decl_return_flags (tree fndecl) > { > - tree attr; > - tree type = TREE_TYPE (fndecl); > - if (!type) > - return 0; > - > - attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type)); > - if (!attr) > - return 0; > - > - attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (attr))); > + attr_fnspec fnspec = decl_fnspec (fndecl); > > unsigned int arg; > if (fnspec.returns_arg (&arg)) > diff --git a/gcc/gimple.c b/gcc/gimple.c > index f19e24d29b3..469e6f369f3 100644 > --- a/gcc/gimple.c > +++ b/gcc/gimple.c > @@ -1487,23 +1487,30 @@ gimple_call_flags (const gimple *stmt) > > /* Return the "fn spec" string for call STMT. */ > > -static const_tree > +attr_fnspec > gimple_call_fnspec (const gcall *stmt) > { > tree type, attr; > > if (gimple_call_internal_p (stmt)) > - return internal_fn_fnspec (gimple_call_internal_fn (stmt)); > + { > + const_tree spec = internal_fn_fnspec (gimple_call_internal_fn (stmt)); > + if (spec) > + return spec; > + else > + return ""; > + } > > type = gimple_call_fntype (stmt); > - if (!type) > - return NULL_TREE; > - > - attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type)); > - if (!attr) > - return NULL_TREE; > - > - return TREE_VALUE (TREE_VALUE (attr)); > + if (type) > + { > + attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type)); > + if (attr) > + return TREE_VALUE (TREE_VALUE (attr)); > + } > + if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)) > + return builtin_fnspec (gimple_call_fndecl (stmt)); > + return ""; > } > > /* Detects argument flags for argument number ARG on call STMT. */ > @@ -1511,13 +1518,12 @@ gimple_call_fnspec (const gcall *stmt) > int > gimple_call_arg_flags (const gcall *stmt, unsigned arg) > { > - const_tree attr = gimple_call_fnspec (stmt); > + attr_fnspec fnspec = gimple_call_fnspec (stmt); > > - if (!attr) > + if (!fnspec.known_p ()) > return 0; > > int flags = 0; > - attr_fnspec fnspec (attr); > > if (!fnspec.arg_specified_p (arg)) > ; > @@ -1540,15 +1546,10 @@ gimple_call_arg_flags (const gcall *stmt, unsigned > arg) > int > gimple_call_return_flags (const gcall *stmt) > { > - const_tree attr; > - > if (gimple_call_flags (stmt) & ECF_MALLOC) > return ERF_NOALIAS; > > - attr = gimple_call_fnspec (stmt); > - if (!attr) > - return 0; > - attr_fnspec fnspec (attr); > + attr_fnspec fnspec = gimple_call_fnspec (stmt); > > unsigned int arg_no; > if (fnspec.returns_arg (&arg_no)) > diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c > index 877e4999f0f..83874df7da3 100644 > --- a/gcc/tree-ssa-alias.c > +++ b/gcc/tree-ssa-alias.c > @@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see > #include "errors.h" > #include "dbgcnt.h" > #include "gimple-pretty-print.h" > +#include "print-tree.h" > > /* Broad overview of how alias analysis on gimple works: > > @@ -2572,6 +2573,60 @@ modref_may_conflict (const gimple *stmt, > return false; > } > > +/* Check if REF conflicts with call using "fn spec" attribute. > + If CLOBBER is true we are checking for writes, otherwise check loads. > + > + Return 0 if there are no conflicts (except for possible function call > + argument reads), 1 if there are conflicts and -1 if we can not decide by > + fn spec. */ > + > +static int > +check_fnspec (gcall *call, ao_ref *ref, bool clobber) > +{ > + attr_fnspec fnspec = gimple_call_fnspec (call); > + if (fnspec.known_p ()) > + { > + if (fnspec.barrier_p ()) > + return true; > + if (clobber ? fnspec.stores_known_p () : fnspec.loads_known_p ()) > + { > + for (unsigned int i = 0; i < gimple_call_num_args (call); i++) > + if (!fnspec.arg_specified_p (i)) > + ; > + else if (clobber ? fnspec.arg_written_p (i) : fnspec.arg_read_p (i)) > + { > + ao_ref dref; > + tree size = NULL_TREE; > + unsigned int size_arg; > + > + if (fnspec.arg_access_size_given_by_arg_p (i, &size_arg)) > + size = gimple_call_arg (call, size_arg); > + else if (fnspec.arg_single_access_p (i)) > + { > + tree callee = gimple_call_fndecl (call); > + tree t = TYPE_ARG_TYPES (TREE_TYPE (callee)); > + > + for (unsigned int p = 0; p < i; p++) > + t = TREE_CHAIN (t); > + size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_VALUE (t))); > + } > + ao_ref_init_from_ptr_and_size (&dref, > + gimple_call_arg (call, i), > + size); > + if (refs_may_alias_p_1 (&dref, ref, false)) > + return 1; > + } > + if (clobber > + && fnspec.clobbers_errno_p () > + && flag_errno_math > + && targetm.ref_may_alias_errno (ref)) > + return true; > + return 0; > + } > + } > + return -1; > +} > + > /* If the call CALL may use the memory reference REF return true, > otherwise return false. */ > > @@ -2650,222 +2705,13 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref > *ref, bool tbaa_p) > && !is_global_var (base)) > goto process_args; > > - /* Handle those builtin functions explicitly that do not act as > - escape points. See tree-ssa-structalias.c:find_func_aliases > - for the list of builtins we might need to handle here. */ > - if (callee != NULL_TREE > - && gimple_call_builtin_p (call, BUILT_IN_NORMAL)) > - switch (DECL_FUNCTION_CODE (callee)) > - { > - /* All the following functions read memory pointed to by > - their second argument. strcat/strncat additionally > - reads memory pointed to by the first argument. */ > - case BUILT_IN_STRCAT: > - case BUILT_IN_STRNCAT: > - { > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - NULL_TREE); > - if (refs_may_alias_p_1 (&dref, ref, false)) > - return true; > - } > - /* FALLTHRU */ > - case BUILT_IN_STRCPY: > - case BUILT_IN_STRNCPY: > - case BUILT_IN_MEMCPY: > - case BUILT_IN_MEMMOVE: > - case BUILT_IN_MEMPCPY: > - case BUILT_IN_STPCPY: > - case BUILT_IN_STPNCPY: > - case BUILT_IN_TM_MEMCPY: > - case BUILT_IN_TM_MEMMOVE: > - { > - ao_ref dref; > - tree size = NULL_TREE; > - if (gimple_call_num_args (call) == 3) > - size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 1), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - case BUILT_IN_STRCAT_CHK: > - case BUILT_IN_STRNCAT_CHK: > - { > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - NULL_TREE); > - if (refs_may_alias_p_1 (&dref, ref, false)) > - return true; > - } > - /* FALLTHRU */ > - case BUILT_IN_STRCPY_CHK: > - case BUILT_IN_STRNCPY_CHK: > - case BUILT_IN_MEMCPY_CHK: > - case BUILT_IN_MEMMOVE_CHK: > - case BUILT_IN_MEMPCPY_CHK: > - case BUILT_IN_STPCPY_CHK: > - case BUILT_IN_STPNCPY_CHK: > - { > - ao_ref dref; > - tree size = NULL_TREE; > - if (gimple_call_num_args (call) == 4) > - size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 1), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - case BUILT_IN_BCOPY: > - { > - ao_ref dref; > - tree size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - > - /* The following functions read memory pointed to by their > - first argument. */ > - CASE_BUILT_IN_TM_LOAD (1): > - CASE_BUILT_IN_TM_LOAD (2): > - CASE_BUILT_IN_TM_LOAD (4): > - CASE_BUILT_IN_TM_LOAD (8): > - CASE_BUILT_IN_TM_LOAD (FLOAT): > - CASE_BUILT_IN_TM_LOAD (DOUBLE): > - CASE_BUILT_IN_TM_LOAD (LDOUBLE): > - CASE_BUILT_IN_TM_LOAD (M64): > - CASE_BUILT_IN_TM_LOAD (M128): > - CASE_BUILT_IN_TM_LOAD (M256): > - case BUILT_IN_TM_LOG: > - case BUILT_IN_TM_LOG_1: > - case BUILT_IN_TM_LOG_2: > - case BUILT_IN_TM_LOG_4: > - case BUILT_IN_TM_LOG_8: > - case BUILT_IN_TM_LOG_FLOAT: > - case BUILT_IN_TM_LOG_DOUBLE: > - case BUILT_IN_TM_LOG_LDOUBLE: > - case BUILT_IN_TM_LOG_M64: > - case BUILT_IN_TM_LOG_M128: > - case BUILT_IN_TM_LOG_M256: > - return ptr_deref_may_alias_ref_p_1 (gimple_call_arg (call, 0), ref); > - > - /* These read memory pointed to by the first argument. */ > - case BUILT_IN_STRDUP: > - case BUILT_IN_STRNDUP: > - case BUILT_IN_REALLOC: > - { > - ao_ref dref; > - tree size = NULL_TREE; > - if (gimple_call_num_args (call) == 2) > - size = gimple_call_arg (call, 1); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - /* These read memory pointed to by the first argument. */ > - case BUILT_IN_INDEX: > - case BUILT_IN_STRCHR: > - case BUILT_IN_STRRCHR: > - { > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - NULL_TREE); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - /* These read memory pointed to by the first argument with size > - in the third argument. */ > - case BUILT_IN_MEMCHR: > - { > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - gimple_call_arg (call, 2)); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - /* These read memory pointed to by the first and second arguments. */ > - case BUILT_IN_STRSTR: > - case BUILT_IN_STRPBRK: > - { > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - NULL_TREE); > - if (refs_may_alias_p_1 (&dref, ref, false)) > - return true; > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 1), > - NULL_TREE); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - > - /* The following builtins do not read from memory. */ > - case BUILT_IN_FREE: > - case BUILT_IN_MALLOC: > - case BUILT_IN_POSIX_MEMALIGN: > - case BUILT_IN_ALIGNED_ALLOC: > - case BUILT_IN_CALLOC: > - CASE_BUILT_IN_ALLOCA: > - case BUILT_IN_STACK_SAVE: > - case BUILT_IN_STACK_RESTORE: > - case BUILT_IN_MEMSET: > - case BUILT_IN_TM_MEMSET: > - case BUILT_IN_MEMSET_CHK: > - case BUILT_IN_FREXP: > - case BUILT_IN_FREXPF: > - case BUILT_IN_FREXPL: > - case BUILT_IN_GAMMA_R: > - case BUILT_IN_GAMMAF_R: > - case BUILT_IN_GAMMAL_R: > - case BUILT_IN_LGAMMA_R: > - case BUILT_IN_LGAMMAF_R: > - case BUILT_IN_LGAMMAL_R: > - case BUILT_IN_MODF: > - case BUILT_IN_MODFF: > - case BUILT_IN_MODFL: > - case BUILT_IN_REMQUO: > - case BUILT_IN_REMQUOF: > - case BUILT_IN_REMQUOL: > - case BUILT_IN_SINCOS: > - case BUILT_IN_SINCOSF: > - case BUILT_IN_SINCOSL: > - case BUILT_IN_ASSUME_ALIGNED: > - case BUILT_IN_VA_END: > - return false; > - /* __sync_* builtins and some OpenMP builtins act as threading > - barriers. */ > -#undef DEF_SYNC_BUILTIN > -#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM: > -#include "sync-builtins.def" > -#undef DEF_SYNC_BUILTIN > - case BUILT_IN_GOMP_ATOMIC_START: > - case BUILT_IN_GOMP_ATOMIC_END: > - case BUILT_IN_GOMP_BARRIER: > - case BUILT_IN_GOMP_BARRIER_CANCEL: > - case BUILT_IN_GOMP_TASKWAIT: > - case BUILT_IN_GOMP_TASKGROUP_END: > - case BUILT_IN_GOMP_CRITICAL_START: > - case BUILT_IN_GOMP_CRITICAL_END: > - case BUILT_IN_GOMP_CRITICAL_NAME_START: > - case BUILT_IN_GOMP_CRITICAL_NAME_END: > - case BUILT_IN_GOMP_LOOP_END: > - case BUILT_IN_GOMP_LOOP_END_CANCEL: > - case BUILT_IN_GOMP_ORDERED_START: > - case BUILT_IN_GOMP_ORDERED_END: > - case BUILT_IN_GOMP_SECTIONS_END: > - case BUILT_IN_GOMP_SECTIONS_END_CANCEL: > - case BUILT_IN_GOMP_SINGLE_COPY_START: > - case BUILT_IN_GOMP_SINGLE_COPY_END: > - return true; > - > - default: > - /* Fallthru to general call handling. */; > - } > + if (int res = check_fnspec (call, ref, false)) > + { > + if (res == 1) > + return true; > + } > + else > + goto process_args; > > /* Check if base is a global static variable that is not read > by the function. */ > @@ -3104,205 +2952,13 @@ call_may_clobber_ref_p_1 (gcall *call, ao_ref *ref, > bool tbaa_p) > && SSA_NAME_POINTS_TO_READONLY_MEMORY (TREE_OPERAND (base, 0))) > return false; > > - /* Handle those builtin functions explicitly that do not act as > - escape points. See tree-ssa-structalias.c:find_func_aliases > - for the list of builtins we might need to handle here. */ > - if (callee != NULL_TREE > - && gimple_call_builtin_p (call, BUILT_IN_NORMAL)) > - switch (DECL_FUNCTION_CODE (callee)) > - { > - /* All the following functions clobber memory pointed to by > - their first argument. */ > - case BUILT_IN_STRCPY: > - case BUILT_IN_STRNCPY: > - case BUILT_IN_MEMCPY: > - case BUILT_IN_MEMMOVE: > - case BUILT_IN_MEMPCPY: > - case BUILT_IN_STPCPY: > - case BUILT_IN_STPNCPY: > - case BUILT_IN_STRCAT: > - case BUILT_IN_STRNCAT: > - case BUILT_IN_MEMSET: > - case BUILT_IN_TM_MEMSET: > - CASE_BUILT_IN_TM_STORE (1): > - CASE_BUILT_IN_TM_STORE (2): > - CASE_BUILT_IN_TM_STORE (4): > - CASE_BUILT_IN_TM_STORE (8): > - CASE_BUILT_IN_TM_STORE (FLOAT): > - CASE_BUILT_IN_TM_STORE (DOUBLE): > - CASE_BUILT_IN_TM_STORE (LDOUBLE): > - CASE_BUILT_IN_TM_STORE (M64): > - CASE_BUILT_IN_TM_STORE (M128): > - CASE_BUILT_IN_TM_STORE (M256): > - case BUILT_IN_TM_MEMCPY: > - case BUILT_IN_TM_MEMMOVE: > - { > - ao_ref dref; > - tree size = NULL_TREE; > - /* Don't pass in size for strncat, as the maximum size > - is strlen (dest) + n + 1 instead of n, resp. > - n + 1 at dest + strlen (dest), but strlen (dest) isn't > - known. */ > - if (gimple_call_num_args (call) == 3 > - && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT) > - size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - case BUILT_IN_STRCPY_CHK: > - case BUILT_IN_STRNCPY_CHK: > - case BUILT_IN_MEMCPY_CHK: > - case BUILT_IN_MEMMOVE_CHK: > - case BUILT_IN_MEMPCPY_CHK: > - case BUILT_IN_STPCPY_CHK: > - case BUILT_IN_STPNCPY_CHK: > - case BUILT_IN_STRCAT_CHK: > - case BUILT_IN_STRNCAT_CHK: > - case BUILT_IN_MEMSET_CHK: > - { > - ao_ref dref; > - tree size = NULL_TREE; > - /* Don't pass in size for __strncat_chk, as the maximum size > - is strlen (dest) + n + 1 instead of n, resp. > - n + 1 at dest + strlen (dest), but strlen (dest) isn't > - known. */ > - if (gimple_call_num_args (call) == 4 > - && DECL_FUNCTION_CODE (callee) != BUILT_IN_STRNCAT_CHK) > - size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 0), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - case BUILT_IN_BCOPY: > - { > - ao_ref dref; > - tree size = gimple_call_arg (call, 2); > - ao_ref_init_from_ptr_and_size (&dref, > - gimple_call_arg (call, 1), > - size); > - return refs_may_alias_p_1 (&dref, ref, false); > - } > - /* Allocating memory does not have any side-effects apart from > - being the definition point for the pointer. */ > - case BUILT_IN_MALLOC: > - case BUILT_IN_ALIGNED_ALLOC: > - case BUILT_IN_CALLOC: > - case BUILT_IN_STRDUP: > - case BUILT_IN_STRNDUP: > - /* Unix98 specifies that errno is set on allocation failure. */ > - if (flag_errno_math > - && targetm.ref_may_alias_errno (ref)) > - return true; > - return false; > - case BUILT_IN_STACK_SAVE: > - CASE_BUILT_IN_ALLOCA: > - case BUILT_IN_ASSUME_ALIGNED: > - return false; > - /* But posix_memalign stores a pointer into the memory pointed to > - by its first argument. */ > - case BUILT_IN_POSIX_MEMALIGN: > - { > - tree ptrptr = gimple_call_arg (call, 0); > - ao_ref dref; > - ao_ref_init_from_ptr_and_size (&dref, ptrptr, > - TYPE_SIZE_UNIT (ptr_type_node)); > - return (refs_may_alias_p_1 (&dref, ref, false) > - || (flag_errno_math > - && targetm.ref_may_alias_errno (ref))); > - } > - /* Freeing memory kills the pointed-to memory. More importantly > - the call has to serve as a barrier for moving loads and stores > - across it. */ > - case BUILT_IN_FREE: > - case BUILT_IN_VA_END: > - { > - tree ptr = gimple_call_arg (call, 0); > - return ptr_deref_may_alias_ref_p_1 (ptr, ref); > - } > - /* Realloc serves both as allocation point and deallocation point. */ > - case BUILT_IN_REALLOC: > - { > - tree ptr = gimple_call_arg (call, 0); > - /* Unix98 specifies that errno is set on allocation failure. */ > - return ((flag_errno_math > - && targetm.ref_may_alias_errno (ref)) > - || ptr_deref_may_alias_ref_p_1 (ptr, ref)); > - } > - case BUILT_IN_GAMMA_R: > - case BUILT_IN_GAMMAF_R: > - case BUILT_IN_GAMMAL_R: > - case BUILT_IN_LGAMMA_R: > - case BUILT_IN_LGAMMAF_R: > - case BUILT_IN_LGAMMAL_R: > - { > - tree out = gimple_call_arg (call, 1); > - if (ptr_deref_may_alias_ref_p_1 (out, ref)) > - return true; > - if (flag_errno_math) > - break; > - return false; > - } > - case BUILT_IN_FREXP: > - case BUILT_IN_FREXPF: > - case BUILT_IN_FREXPL: > - case BUILT_IN_MODF: > - case BUILT_IN_MODFF: > - case BUILT_IN_MODFL: > - { > - tree out = gimple_call_arg (call, 1); > - return ptr_deref_may_alias_ref_p_1 (out, ref); > - } > - case BUILT_IN_REMQUO: > - case BUILT_IN_REMQUOF: > - case BUILT_IN_REMQUOL: > - { > - tree out = gimple_call_arg (call, 2); > - if (ptr_deref_may_alias_ref_p_1 (out, ref)) > - return true; > - if (flag_errno_math) > - break; > - return false; > - } > - case BUILT_IN_SINCOS: > - case BUILT_IN_SINCOSF: > - case BUILT_IN_SINCOSL: > - { > - tree sin = gimple_call_arg (call, 1); > - tree cos = gimple_call_arg (call, 2); > - return (ptr_deref_may_alias_ref_p_1 (sin, ref) > - || ptr_deref_may_alias_ref_p_1 (cos, ref)); > - } > - /* __sync_* builtins and some OpenMP builtins act as threading > - barriers. */ > -#undef DEF_SYNC_BUILTIN > -#define DEF_SYNC_BUILTIN(ENUM, NAME, TYPE, ATTRS) case ENUM: > -#include "sync-builtins.def" > -#undef DEF_SYNC_BUILTIN > - case BUILT_IN_GOMP_ATOMIC_START: > - case BUILT_IN_GOMP_ATOMIC_END: > - case BUILT_IN_GOMP_BARRIER: > - case BUILT_IN_GOMP_BARRIER_CANCEL: > - case BUILT_IN_GOMP_TASKWAIT: > - case BUILT_IN_GOMP_TASKGROUP_END: > - case BUILT_IN_GOMP_CRITICAL_START: > - case BUILT_IN_GOMP_CRITICAL_END: > - case BUILT_IN_GOMP_CRITICAL_NAME_START: > - case BUILT_IN_GOMP_CRITICAL_NAME_END: > - case BUILT_IN_GOMP_LOOP_END: > - case BUILT_IN_GOMP_LOOP_END_CANCEL: > - case BUILT_IN_GOMP_ORDERED_START: > - case BUILT_IN_GOMP_ORDERED_END: > - case BUILT_IN_GOMP_SECTIONS_END: > - case BUILT_IN_GOMP_SECTIONS_END_CANCEL: > - case BUILT_IN_GOMP_SINGLE_COPY_START: > - case BUILT_IN_GOMP_SINGLE_COPY_END: > - return true; > - default: > - /* Fallthru to general call handling. */; > - } > + if (int res = check_fnspec (call, ref, true)) > + { > + if (res == 1) > + return true; > + } > + else > + return false; > > /* Check if base is a global static variable that is not written > by the function. */ > @@ -4079,6 +3735,8 @@ void > attr_fnspec::verify () > { > bool err = false; > + if (!len) > + return; > > /* Check return value specifier. */ > if (len < return_desc_size) > @@ -4092,8 +3750,18 @@ attr_fnspec::verify () > && str[0] != 'R' && str[0] != 'W') > err = true; > > - if (str[1] != ' ') > - err = true; > + switch (str[1]) > + { > + case ' ': > + case 'p': > + case 'P': > + case 'c': > + case 'C': > + case 'b': > + break; > + default: > + err = true; > + } > > /* Now check all parameters. */ > for (unsigned int i = 0; arg_specified_p (i); i++) > @@ -4105,6 +3773,8 @@ attr_fnspec::verify () > case 'X': > case 'r': > case 'R': > + case 'o': > + case 'O': > case 'w': > case 'W': > case '.': > @@ -4112,7 +3782,15 @@ attr_fnspec::verify () > default: > err = true; > } > - if (str[idx + 1] != ' ') > + if ((str[idx + 1] >= '1' && str[idx + 1] <= '9') > + || str[idx + 1] == 't') > + { > + if (str[idx] != 'r' && str[idx] != 'R' > + && str[idx] != 'w' && str[idx] != 'W' > + && str[idx] != 'o' && str[idx] != 'O') > + err = true; > + } > + else if (str[idx + 1] != ' ') > err = true; > } > if (err) > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imend