Hi > > + if (!fnspec.arg_specified_p (arg)) > > + ; > > + else if (!fnspec.arg_used_p (arg)) > > + flags = EAF_UNUSED; > > + else > > + { > > + if (!fnspec.arg_direct_p (arg)) > > negated test > > > + flags |= EAF_DIRECT; > > + if (!fnspec.arg_noescape_p (arg)) > > + flags |= EAF_NOESCAPE; > > likewise. > > > + if (!fnspec.arg_readonly_p (arg)) > > + flags |= EAF_NOCLOBBER; > > > likewise.
Oops, sorry for that. I got carried away trying to make sense of fortran specifiers. Thanks for catching this. I wonder how that chould have passed testing. > > > } > > + return flags; > > } > > > > /* Detects return flags for the call STMT. */ > > @@ -1546,24 +1542,16 @@ gimple_call_return_flags (const gcall *stmt) > > return ERF_NOALIAS; > > > > attr = gimple_call_fnspec (stmt); > > - if (!attr || TREE_STRING_LENGTH (attr) < 1) > > + if (!attr) > > return 0; > > + attr_fnspec fnspec (attr); > > > > - switch (TREE_STRING_POINTER (attr)[0]) > > - { > > - case '1': > > - case '2': > > - case '3': > > - case '4': > > - return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1'); > > - > > - case 'm': > > - return ERF_NOALIAS; > > + if (fnspec.returns_arg () >= 0) > > + return ERF_RETURNS_ARG | fnspec.returns_arg (); > > hmm, maybe > > if (fnspec.returns_arg_p (&arg)) > return ERF_RETURNS_ARG | arg; I added arg variable, but I think returning -1 for unknown arg is kind of more consistent with what we do elsewhere (plus referneces are not cool) > > + /* True if memory reached is only written into (but not read). */ > > + bool > > + arg_writeonly_p (unsigned int i) > > This is actually arg_readwrite_p (), there's no flag for write-only. > wW are merely for noescape & only direct read/write. I dropped this for now, but for tree-ssa-alias we will need to have way to specify that parameter is only written into. > > Currently all specified args imply noescape btw. Yep, but I think we may want to change this, so I think it is safer to list those that do. This is updated patch I am re-testing. Does it look OK? Next I would like to proceed by blowing up all specifiers to double of size (without functional changes) and then add the extra letters. I was wondering if we want to use ' ' instead of '.' for the second char. It may make it easier to read ". . . R " than "......R." but it also may be bit misleading in a way that the there must be precisely one space. Honza gcc/ChangeLog: 2020-10-01 Jan Hubicka <hubi...@ucw.cz> * attr-fnspec.h: New file. * calls.c (decl_return_flags): Implement using attr_fnspec. * gimple.c (gimple_call_arg_flags): Likewise (gimple_call_return_flags): Likewise * tree-into-ssa.c (pass_build_ssa::execute): Likewise. * tree-ssa-alias.c (attr_fnspec::verify): New diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h new file mode 100644 index 00000000000..4ad4b8758e0 --- /dev/null +++ b/gcc/attr-fnspec.h @@ -0,0 +1,141 @@ +/* Handling of fnspec attribute specifiers + Copyright (C) 2008-2020 Free Software Foundation, Inc. + Contributed by Richard Guenther <rguent...@suse.de> + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + GCC is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GCC; see the file COPYING3. If not see + <http://www.gnu.org/licenses/>. */ + +/* Parse string of attribute "fn spec". This is an internal attribute + describing side effects of a function as follows: + + character 0 specifies properties of return values as follows: + '1'...'4' specifies number of argument function returns (as in memset) + 'm' specifies that returned value is noalias (as in malloc) + '.' specifies that nothing is known. + + character 1+i specifies properties of argument number i as follows: + 'x' or 'X' specifies that parameter is unused. + 'r' or 'R' specifies that parameter is only read and memory pointed to is + never dereferenced. + 'w' or 'W' specifies that parameter is only written to. + '.' specifies that nothing is known. + The uppercase letter in addition specifies that parameter + is non-escaping. */ + +#ifndef ATTR_FNSPEC_H +#define ATTR_FNSPEC_H + +class attr_fnspec +{ +private: + /* fn spec attribute string. */ + const char *str; + /* length of the fn spec string. */ + const unsigned len; + /* Number of characters specifying return value. */ + const unsigned int return_desc_size = 1; + /* Number of characters specifying size. */ + const unsigned int arg_desc_size = 1; + + /* Return start of specifier of arg i. */ + unsigned int arg_idx (int i) + { + return return_desc_size + arg_desc_size * i; + } + +public: + attr_fnspec (const char *str, unsigned len) + : str (str), len (len) + { + if (flag_checking) + verify (); + } + attr_fnspec (const_tree identifier) + : str (TREE_STRING_POINTER (identifier)), + len (TREE_STRING_LENGTH (identifier)) + { + if (flag_checking) + verify (); + } + + /* Return true if arg I is specified. */ + bool + arg_specified_p (unsigned int i) + { + return len >= arg_idx (i + 1); + } + + /* True if the argument is not dereferenced recursively, thus only + directly reachable memory is read or written. */ + bool + arg_direct_p (unsigned int i) + { + unsigned int idx = arg_idx (i); + gcc_checking_assert (arg_specified_p (i)); + return str[idx] == 'R' || str[idx] == 'W'; + } + + /* True if argument is used. */ + bool + arg_used_p (unsigned int i) + { + unsigned int idx = arg_idx (i); + gcc_checking_assert (arg_specified_p (i)); + return str[idx] != 'x' && str[idx] != 'X'; + } + + /* True if memory reached by the argument is readonly (not clobbered). */ + bool + arg_readonly_p (unsigned int i) + { + unsigned int idx = arg_idx (i); + gcc_checking_assert (arg_specified_p (i)); + return str[idx] == 'r' || str[idx] == 'R'; + } + + /* True if the argument does not escape. */ + bool + arg_noescape_p (unsigned int i) + { + 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'; + } + + /* If not equal to -1 then it specifies number of argument returned by + the function. */ + int + returns_arg () + { + if (str[0] >= '1' && str[0] <= '4') + return str[0]-'1'; + return -1; + } + + /* Nonzero if the return value does not alias with anything. Functions + with the malloc attribute have this set on their return value. */ + int + returns_noalias_p () + { + return str[0] == 'm'; + } + + /* Check validity of the string. */ + void verify (); +}; + +#endif /* ATTR_FNSPEC_H */ diff --git a/gcc/calls.c b/gcc/calls.c index ed4363811c8..40f4863a89b 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see #include "attribs.h" #include "builtins.h" #include "gimple-fold.h" +#include "attr-fnspec.h" #include "tree-pretty-print.h" @@ -642,25 +643,15 @@ decl_return_flags (tree fndecl) if (!attr) return 0; - attr = TREE_VALUE (TREE_VALUE (attr)); - if (!attr || TREE_STRING_LENGTH (attr) < 1) - return 0; - - switch (TREE_STRING_POINTER (attr)[0]) - { - case '1': - case '2': - case '3': - case '4': - return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1'); + attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (attr))); - case 'm': - return ERF_NOALIAS; + int arg = fnspec.returns_arg (); + if (arg >= 0) + return ERF_RETURNS_ARG | arg; - case '.': - default: - return 0; - } + if (fnspec.returns_noalias_p ()) + return ERF_NOALIAS; + return 0; } /* Return nonzero when FNDECL represents a call to setjmp. */ diff --git a/gcc/gimple.c b/gcc/gimple.c index fd4e0fac0d4..8fb82570374 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see #include "attribs.h" #include "asan.h" #include "langhooks.h" +#include "attr-fnspec.h" /* All the tuples have their operand vector (if present) at the very bottom @@ -1508,31 +1509,26 @@ gimple_call_arg_flags (const gcall *stmt, unsigned arg) { const_tree attr = gimple_call_fnspec (stmt); - if (!attr || 1 + arg >= (unsigned) TREE_STRING_LENGTH (attr)) + if (!attr) return 0; - switch (TREE_STRING_POINTER (attr)[1 + arg]) - { - case 'x': - case 'X': - return EAF_UNUSED; - - case 'R': - return EAF_DIRECT | EAF_NOCLOBBER | EAF_NOESCAPE; - - case 'r': - return EAF_NOCLOBBER | EAF_NOESCAPE; - - case 'W': - return EAF_DIRECT | EAF_NOESCAPE; - - case 'w': - return EAF_NOESCAPE; + int flags = 0; + attr_fnspec fnspec (attr); - case '.': - default: - return 0; + if (!fnspec.arg_specified_p (arg)) + ; + else if (!fnspec.arg_used_p (arg)) + flags = EAF_UNUSED; + else + { + if (fnspec.arg_direct_p (arg)) + flags |= EAF_DIRECT; + if (fnspec.arg_noescape_p (arg)) + flags |= EAF_NOESCAPE; + if (fnspec.arg_readonly_p (arg)) + flags |= EAF_NOCLOBBER; } + return flags; } /* Detects return flags for the call STMT. */ @@ -1546,24 +1542,16 @@ gimple_call_return_flags (const gcall *stmt) return ERF_NOALIAS; attr = gimple_call_fnspec (stmt); - if (!attr || TREE_STRING_LENGTH (attr) < 1) + if (!attr) return 0; + attr_fnspec fnspec (attr); - switch (TREE_STRING_POINTER (attr)[0]) - { - case '1': - case '2': - case '3': - case '4': - return ERF_RETURNS_ARG | (TREE_STRING_POINTER (attr)[0] - '1'); - - case 'm': - return ERF_NOALIAS; + if (fnspec.returns_arg () >= 0) + return ERF_RETURNS_ARG | fnspec.returns_arg (); - case '.': - default: - return 0; - } + if (fnspec.returns_noalias_p ()) + return ERF_NOALIAS; + return 0; } diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c index 0d016134774..1493b323956 100644 --- a/gcc/tree-into-ssa.c +++ b/gcc/tree-into-ssa.c @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "stringpool.h" #include "attribs.h" #include "asan.h" +#include "attr-fnspec.h" #define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y)) @@ -2492,19 +2493,19 @@ pass_build_ssa::execute (function *fun) } /* Initialize SSA_NAME_POINTS_TO_READONLY_MEMORY. */ - tree fnspec = lookup_attribute ("fn spec", - TYPE_ATTRIBUTES (TREE_TYPE (fun->decl))); - if (fnspec) + tree fnspec_tree + = lookup_attribute ("fn spec", + TYPE_ATTRIBUTES (TREE_TYPE (fun->decl))); + if (fnspec_tree) { - fnspec = TREE_VALUE (TREE_VALUE (fnspec)); - unsigned i = 1; + attr_fnspec fnspec (TREE_VALUE (TREE_VALUE (fnspec_tree))); + unsigned i = 0; for (tree arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg), ++i) { - if (i >= (unsigned) TREE_STRING_LENGTH (fnspec)) - break; - if (TREE_STRING_POINTER (fnspec)[i] == 'R' - || TREE_STRING_POINTER (fnspec)[i] == 'r') + if (!fnspec.arg_specified_p (i)) + break; + if (fnspec.arg_readonly_p (i)) { tree name = ssa_default_def (fun, arg); if (name) diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c index fe390d4ffbe..2b2d53ebb88 100644 --- a/gcc/tree-ssa-alias.c +++ b/gcc/tree-ssa-alias.c @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see #include "varasm.h" #include "ipa-modref-tree.h" #include "ipa-modref.h" +#include "attr-fnspec.h" /* Broad overview of how alias analysis on gimple works: @@ -3984,3 +3985,42 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef, return ret; } +/* Verify validity of the fnspec string. + See attr-fnspec.h for details. */ + +void +attr_fnspec::verify () +{ + /* FIXME: Fortran trans-decl.c contains multiple wrong fnspec strings. + re-enable verification after these are fixed. */ + return; + bool err = false; + + /* Check return value specifier. */ + if (len < return_desc_size) + err = true; + else if (str[0] < '1' || str[0] > '4') + && str[0] != '.' && str[0] != 'm') + err = true; + + /* Now check all parameters. */ + for (unsigned int i = 0; arg_specified_p (i); i++) + { + unsigned int idx = arg_idx (i); + switch (str[idx]) + { + case 'x': + case 'X': + case 'r': + case 'R': + case 'w': + case 'W': + case '.': + break; + default: + err = true; + } + } + if (err) + internal_error ("invalid fn spec attribute %s", str); +}