aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/attr-fnspec.h145
-rw-r--r--gcc/calls.c25
-rw-r--r--gcc/gimple.c61
-rw-r--r--gcc/tree-into-ssa.c19
-rw-r--r--gcc/tree-ssa-alias.c41
5 files changed, 229 insertions, 62 deletions
diff --git a/gcc/attr-fnspec.h b/gcc/attr-fnspec.h
new file mode 100644
index 0000000..607c0cf
--- /dev/null
+++ b/gcc/attr-fnspec.h
@@ -0,0 +1,145 @@
+/* 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 */
diff --git a/gcc/calls.c b/gcc/calls.c
index ed43638..93da3d6 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;
+ unsigned int arg;
+ if (fnspec.returns_arg (&arg))
+ 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 523d845..f19e24d 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
@@ -1512,31 +1513,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. */
@@ -1550,24 +1546,17 @@ 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;
+ 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;
}
diff --git a/gcc/tree-into-ssa.c b/gcc/tree-into-ssa.c
index 0d01613..1493b32 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 9e5c3ee..52aeaeb 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:
@@ -4012,3 +4014,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);
+}