On Thu, 1 Oct 2020, Jan Hubicka wrote: > Hi, > this patch adds the simple class for parsing fnspec attribute. I plan > to add support for generating and modifying it too (it is used by > fortran and I plan to make modref to detect noclobbers and stuff). > Verification is disabled until we fix remaining fortran specifier > (I got a promised help at monday) > > Bootstrapped/regtested x86_64-linux, OK?
Well, besides many errors, see below... > Honza > > gcc/ChangeLog: > > 2020-10-01 Jan Hubicka <hubi...@ucw.cz> > > * 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 > * attr-fnspec.h: New file. > > diff --git a/gcc/calls.c b/gcc/calls.c > index ed4363811c8..94f433685b9 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,14 @@ 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; > + 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; > } > > /* Return nonzero when FNDECL represents a call to setjmp. */ > diff --git a/gcc/gimple.c b/gcc/gimple.c > index fd4e0fac0d4..2f2db309df5 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)) 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. > } > + 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; ? > > - 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,8 @@ 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" > +#include "errors.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); > +} > --- attr-fnspec.h 2020-10-01 14:39:32.565476448 +0200 > +++ attr-fnspec.h 2020-10-01 14:35:02.069814323 +0200 > @@ -0,0 +1,150 @@ > +/* 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 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. > + { > + unsigned int idx = arg_idx (i); > + gcc_checking_assert (arg_specified_p (i)); > + return str[idx] == 'w' || str[idx] == 'W'; > + } > + > + /* 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'; > + } Currently all specified args imply noescape btw. > + /* 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 */ > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imend