Hi, this patch adds 'c' and 'C' fnspec for parameter that is copied to different parameter. Main motivation is to get rid of wrong EAF_NOESCAPE flag on the memcpy argument #2. I however also added arg_copies_to_arg_p predicate that can be eventually used by tree-ssa-structalias instead of special casing all builtins.
I noticed that we can no longer describe STRNCAT precisely. I am not sure how important it is. We can either special case it on the three places (in tree-ssa-alias and in ipa-modref) or use 1-9 in place of 'c' and 'C' so the second character would be still available for size specifier, so strncat would become "1cW 13" instead of "1cW C1" Not sure how important this is. Bootstrapped/regtested x86_64-linux, OK? Honza 2020-11-09 Jan Hubicka <hubi...@ucw.cz> * attr-fnspec.h: Add 'c' and 'C' specifiers to the toplevel comment. (attr_fnspec::arg_direct_p): Add 'C'. (attr_fnspec::arg_not_written_p): Handle 'c' and 'C'. (attr_fnspec::arg_max_access_size_given_by_arg_p): Handle 'c' and 'C'. (attr_fnspec::arg_access_size_given_by_type_p): Add comment about 'c' and 'C'. (attr_fnspec::arg_copied_to_arg_p): New. * builtins.c (attr_fnspec::builtin_fnspec): Update fnspec of string functions that copies argument. * tree-ssa-alias.c (attr_fnspec::verify): Add 'c' and 'C'; be more struct on arg specifiers. diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h index 28135328437..97405dbdd78 100644 --- a/gcc/attr-fnspec.h +++ b/gcc/attr-fnspec.h @@ -41,6 +41,13 @@ written and does not escape 'w' or 'W' specifies that the memory pointed to by the parameter does not escape + 'c' or 'C' specifies that the memory pointed to by the parameter is + copied to memory pointed to by different parameter + (as in memcpy). The index of the destination parmeter is + specified by following character i.e. "C1" means that memory is + copied to parameter pointed to by parameter 1. + Size of block copied is determined by size specifier of the + destination parameter. '.' specifies that nothing is known. The uppercase letter in addition specifies that the memory pointed to by the parameter is not dereferenced. For 'r' only read applies @@ -51,8 +58,11 @@ ' ' 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 + '1'...'9' preceeded by 'o', 'O', 'w' or 'W' + specifies the size of value written/read is given by the + specified argument + '1'...'9' preceeded by 'c', or 'c' + specifies the argument data is copied to */ #ifndef ATTR_FNSPEC_H @@ -122,7 +132,8 @@ public: { unsigned int idx = arg_idx (i); gcc_checking_assert (arg_specified_p (i)); - return str[idx] == 'R' || str[idx] == 'O' || str[idx] == 'W'; + return str[idx] == 'R' || str[idx] == 'O' + || str[idx] == 'W' || str[idx] == 'C'; } /* True if argument is used. */ @@ -161,6 +172,7 @@ public: unsigned int idx = arg_idx (i); gcc_checking_assert (arg_specified_p (i)); return str[idx] != 'r' && str[idx] != 'R' + && str[idx] != 'c' && str[idx] != 'C' && str[idx] != 'x' && str[idx] != 'X'; } @@ -171,6 +183,8 @@ public: { unsigned int idx = arg_idx (i); gcc_checking_assert (arg_specified_p (i)); + if (str[idx] == 'c' || str[idx] == 'C') + return arg_max_access_size_given_by_arg_p (str[idx + 1] - '1', arg); if (str[idx + 1] >= '1' && str[idx + 1] <= '9') { *arg = str[idx + 1] - '1'; @@ -187,9 +201,26 @@ public: { unsigned int idx = arg_idx (i); gcc_checking_assert (arg_specified_p (i)); + /* We could handle 'c' 'C' but then we would need to have way to check + that both points to sizes are same. */ return str[idx + 1] == 't'; } + /* Return true if memory pointer to by argument is copied to a memory + pointed to by a different argument (as in memcpy). + In this case set ARG. */ + bool + arg_copied_to_arg_p (unsigned int i, unsigned int *arg) + { + unsigned int idx = arg_idx (i); + gcc_checking_assert (arg_specified_p (i)); + if (str[idx] != 'c' && str[idx] == 'C') + return false; + *arg = str[idx + 1] - '1'; + return true; + } + + /* True if the argument does not escape. */ bool arg_noescape_p (unsigned int i) @@ -230,7 +261,7 @@ public: return str[1] != 'c' && str[1] != 'C'; } - /* Return true if all memory written by the function + /* Return true if all memory written by the function is specified by fnspec. */ bool global_memory_written_p () diff --git a/gcc/builtins.c b/gcc/builtins.c index da25343beb1..51f2365d971 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -12939,16 +12939,15 @@ builtin_fnspec (tree callee) 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"; + return "1cW C1"; case BUILT_IN_STRCPY: case BUILT_IN_STRCPY_CHK: - return "1cO R "; + return "1cO C1"; case BUILT_IN_STPCPY: case BUILT_IN_STPCPY_CHK: - return ".cO R "; + return ".cO C1"; case BUILT_IN_STRNCPY: case BUILT_IN_MEMCPY: case BUILT_IN_MEMMOVE: @@ -12957,15 +12956,15 @@ builtin_fnspec (tree callee) case BUILT_IN_STRNCPY_CHK: case BUILT_IN_MEMCPY_CHK: case BUILT_IN_MEMMOVE_CHK: - return "1cO3R3"; + return "1cO3C1"; case BUILT_IN_MEMPCPY: case BUILT_IN_MEMPCPY_CHK: - return ".cO3R3"; + return ".cO3C1"; case BUILT_IN_STPNCPY: case BUILT_IN_STPNCPY_CHK: - return ".cO3R3"; + return ".cO3C1"; case BUILT_IN_BCOPY: - return ".cR3O3"; + return ".cC2O3"; case BUILT_IN_BZERO: return ".cO2"; case BUILT_IN_MEMCMP: diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index e64011d04df..09c622ce918 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -3797,6 +3797,8 @@ attr_fnspec::verify () default: err = true; } + if (err) + internal_error ("invalid fn spec attribute \"%s\"", str); /* Now check all parameters. */ for (unsigned int i = 0; arg_specified_p (i); i++) @@ -3813,21 +3815,39 @@ attr_fnspec::verify () case 'w': case 'W': case '.': + 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; + if (str[idx] != 't' + /* Size specified is scalar, so it should be described + by ". " if specified at all. */ + && (arg_specified_p (str[idx + 1] - '1') + && str[arg_idx (str[idx + 1] - '1')] != '.')) + err = true; + } + else if (str[idx + 1] != ' ') + err = true; + break; + case 'c': + case 'C': + /* Copied arugment should specify the argument being copied + that should be specified output argument. */ + if (str[idx + 1] < '1' || str[idx + 1] > '9' + || !arg_specified_p (str[idx + 1] - '1') + || (str[arg_idx (str[idx + 1] - '1')] != 'W' + && str[arg_idx (str[idx + 1] - '1')] != 'w' + && str[arg_idx (str[idx + 1] - '1')] != 'O' + && str[arg_idx (str[idx + 1] - '1')] != 'o')) + err = true; break; default: err = true; } - 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) + internal_error ("invalid fn spec attribute \"%s\" arg %i", str, i); } - if (err) - internal_error ("invalid fn spec attribute \"%s\"", str); }