From: Jan Hubicka Date: Mon, 26 Oct 2020 19:19:33 +0000 (+0100) Subject: Extend builtin fnspecs X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4f8cfb42883cc247f11096a3703e379d1f24ab3f;p=gcc.git Extend builtin fnspecs * 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..e7d4ff38083 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,165 @@ 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_STACK_RESTORE: + 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 "1cX "; + /* But posix_memalign stores a pointer into the memory pointed to + by its first argument. */ + case BUILT_IN_POSIX_MEMALIGN: + return ".cOt"; + + 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 17b8e2f7a0d..a8f459632f2 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -630,21 +630,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..bc0f32a774a 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 +2989,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 +3772,8 @@ void attr_fnspec::verify () { bool err = false; + if (!len) + return; /* Check return value specifier. */ if (len < return_desc_size) @@ -4092,8 +3787,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 +3809,8 @@ attr_fnspec::verify () case 'X': case 'r': case 'R': + case 'o': + case 'O': case 'w': case 'W': case '.': @@ -4112,7 +3818,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)