--- /dev/null
+/* Handling of fnspec attribute specifiers
+ Copyright (C) 2008-2020 Free Software Foundation, Inc.
+ Contributed by Richard Guenther <rguenther@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';
+ }
+
+ /* Return true if function returns value of its parameter. If ARG_NO is
+ non-NULL return initialize it to the argument returned. */
+ bool
+ returns_arg (unsigned int *arg_no)
+ {
+ if (str[0] >= '1' && str[0] <= '4')
+ {
+ if (arg_no)
+ *arg_no = str[0] - '1';
+ return true;
+ }
+ return false;
+ }
+
+ /* Nonzero if the return value does not alias with anything. Functions
+ with the malloc attribute have this set on their return value. */
+ bool
+ returns_noalias_p ()
+ {
+ return str[0] == 'm';
+ }
+
+ /* Check validity of the string. */
+ void verify ();
+};
+
+#endif /* ATTR_FNSPEC_H */
#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
{
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. */
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;
+ unsigned int arg_no;
+ if (fnspec.returns_arg (&arg_no))
+ return ERF_RETURNS_ARG | arg_no;
- case '.':
- default:
- return 0;
- }
+ if (fnspec.returns_noalias_p ())
+ return ERF_NOALIAS;
+ return 0;
}
#include "stringpool.h"
#include "attribs.h"
#include "asan.h"
+#include "attr-fnspec.h"
#define PERCENT(x,y) ((float)(x) * 100.0 / (float)(y))
}
/* 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)
#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:
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);
+}