/* Functions dealing with attribute handling, used by most front ends.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2010
+ Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "tm.h"
#include "tree.h"
#include "flags.h"
-#include "toplev.h"
-#include "output.h"
-#include "rtl.h"
+#include "diagnostic-core.h"
#include "ggc.h"
#include "tm_p.h"
#include "cpplib.h"
#include "target.h"
#include "langhooks.h"
-
-static void init_attributes (void);
+#include "hashtab.h"
+#include "plugin.h"
/* Table of the tables of attributes (common, language, format, machine)
searched. */
static const struct attribute_spec *attribute_tables[4];
+/* Hashtable mapping names (represented as substrings) to attribute specs. */
+static htab_t attribute_hash;
+
+/* Substring representation. */
+
+struct substring
+{
+ const char *str;
+ int length;
+};
+
static bool attributes_initialized = false;
/* Default empty table of attributes. */
+
static const struct attribute_spec empty_attribute_table[] =
{
- { NULL, 0, 0, false, false, false, NULL }
+ { NULL, 0, 0, false, false, false, NULL, false }
};
+/* Return base name of the attribute. Ie '__attr__' is turned into 'attr'.
+ To avoid need for copying, we simply return length of the string. */
+
+static void
+extract_attribute_substring (struct substring *str)
+{
+ if (str->length > 4 && str->str[0] == '_' && str->str[1] == '_'
+ && str->str[str->length - 1] == '_' && str->str[str->length - 2] == '_')
+ {
+ str->length -= 4;
+ str->str += 2;
+ }
+}
+
+/* Simple hash function to avoid need to scan whole string. */
+
+static inline hashval_t
+substring_hash (const char *str, int l)
+{
+ return str[0] + str[l - 1] * 256 + l * 65536;
+}
+
+/* Used for attribute_hash. */
+
+static hashval_t
+hash_attr (const void *p)
+{
+ const struct attribute_spec *const spec = (const struct attribute_spec *) p;
+ const int l = strlen (spec->name);
+
+ return substring_hash (spec->name, l);
+}
+
+/* Used for attribute_hash. */
+
+static int
+eq_attr (const void *p, const void *q)
+{
+ const struct attribute_spec *const spec = (const struct attribute_spec *) p;
+ const struct substring *const str = (const struct substring *) q;
+
+ return (!strncmp (spec->name, str->str, str->length) && !spec->name[str->length]);
+}
+
/* Initialize attribute tables, and make some sanity checks
if --enable-checking. */
-static void
+void
init_attributes (void)
{
size_t i;
+ int k;
+
+ if (attributes_initialized)
+ return;
attribute_tables[0] = lang_hooks.common_attribute_table;
attribute_tables[1] = lang_hooks.attribute_table;
/* The name must not begin and end with __. */
const char *name = attribute_tables[i][j].name;
int len = strlen (name);
-
+
gcc_assert (!(name[0] == '_' && name[1] == '_'
&& name[len - 1] == '_' && name[len - 2] == '_'));
-
+
/* The minimum and maximum lengths must be consistent. */
gcc_assert (attribute_tables[i][j].min_length >= 0);
-
+
gcc_assert (attribute_tables[i][j].max_length == -1
|| (attribute_tables[i][j].max_length
>= attribute_tables[i][j].min_length));
-
+
/* An attribute cannot require both a DECL and a TYPE. */
gcc_assert (!attribute_tables[i][j].decl_required
|| !attribute_tables[i][j].type_required);
-
+
/* If an attribute requires a function type, in particular
it requires a type. */
gcc_assert (!attribute_tables[i][j].function_type_required
gcc_assert (strcmp (attribute_tables[i][j].name,
attribute_tables[i][k].name));
}
- /* Check that no name occurs in more than one table. */
+ /* Check that no name occurs in more than one table. Names that
+ begin with '*' are exempt, and may be overridden. */
for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
{
size_t j, k, l;
for (j = i + 1; j < ARRAY_SIZE (attribute_tables); j++)
for (k = 0; attribute_tables[i][k].name != NULL; k++)
for (l = 0; attribute_tables[j][l].name != NULL; l++)
- gcc_assert (strcmp (attribute_tables[i][k].name,
- attribute_tables[j][l].name));
+ gcc_assert (attribute_tables[i][k].name[0] == '*'
+ || strcmp (attribute_tables[i][k].name,
+ attribute_tables[j][l].name));
}
#endif
+ attribute_hash = htab_create (200, hash_attr, eq_attr, NULL);
+ for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
+ for (k = 0; attribute_tables[i][k].name != NULL; k++)
+ {
+ register_attribute (&attribute_tables[i][k]);
+ }
+ invoke_plugin_callbacks (PLUGIN_ATTRIBUTES, NULL);
attributes_initialized = true;
}
+
+/* Insert a single ATTR into the attribute table. */
+
+void
+register_attribute (const struct attribute_spec *attr)
+{
+ struct substring str;
+ void **slot;
+
+ str.str = attr->name;
+ str.length = strlen (str.str);
+
+ /* Attribute names in the table must be in the form 'text' and not
+ in the form '__text__'. */
+ gcc_assert (str.length > 0 && str.str[0] != '_');
+
+ slot = htab_find_slot_with_hash (attribute_hash, &str,
+ substring_hash (str.str, str.length),
+ INSERT);
+ gcc_assert (!*slot || attr->name[0] == '*');
+ *slot = (void *) CONST_CAST (struct attribute_spec *, attr);
+}
+
+/* Return the spec for the attribute named NAME. */
+
+const struct attribute_spec *
+lookup_attribute_spec (const_tree name)
+{
+ struct substring attr;
+
+ attr.str = IDENTIFIER_POINTER (name);
+ attr.length = IDENTIFIER_LENGTH (name);
+ extract_attribute_substring (&attr);
+ return (const struct attribute_spec *)
+ htab_find_with_hash (attribute_hash, &attr,
+ substring_hash (attr.str, attr.length));
+}
\f
/* Process the attributes listed in ATTRIBUTES and install them in *NODE,
which is either a DECL (including a TYPE_DECL) or a TYPE. If a DECL,
tree a;
tree returned_attrs = NULL_TREE;
+ if (TREE_TYPE (*node) == error_mark_node)
+ return NULL_TREE;
+
if (!attributes_initialized)
init_attributes ();
+ /* If this is a function and the user used #pragma GCC optimize, add the
+ options to the attribute((optimize(...))) list. */
+ if (TREE_CODE (*node) == FUNCTION_DECL && current_optimize_pragma)
+ {
+ tree cur_attr = lookup_attribute ("optimize", attributes);
+ tree opts = copy_list (current_optimize_pragma);
+
+ if (! cur_attr)
+ attributes
+ = tree_cons (get_identifier ("optimize"), opts, attributes);
+ else
+ TREE_VALUE (cur_attr) = chainon (opts, TREE_VALUE (cur_attr));
+ }
+
+ if (TREE_CODE (*node) == FUNCTION_DECL
+ && optimization_current_node != optimization_default_node
+ && !DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node))
+ DECL_FUNCTION_SPECIFIC_OPTIMIZATION (*node) = optimization_current_node;
+
+ /* If this is a function and the user used #pragma GCC target, add the
+ options to the attribute((target(...))) list. */
+ if (TREE_CODE (*node) == FUNCTION_DECL
+ && current_target_pragma
+ && targetm.target_option.valid_attribute_p (*node, NULL_TREE,
+ current_target_pragma, 0))
+ {
+ tree cur_attr = lookup_attribute ("target", attributes);
+ tree opts = copy_list (current_target_pragma);
+
+ if (! cur_attr)
+ attributes = tree_cons (get_identifier ("target"), opts, attributes);
+ else
+ TREE_VALUE (cur_attr) = chainon (opts, TREE_VALUE (cur_attr));
+ }
+
+ /* A "naked" function attribute implies "noinline" and "noclone" for
+ those targets that support it. */
+ if (TREE_CODE (*node) == FUNCTION_DECL
+ && attributes
+ && lookup_attribute_spec (get_identifier ("naked"))
+ && lookup_attribute ("naked", attributes) != NULL)
+ {
+ if (lookup_attribute ("noinline", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("noinline"), NULL, attributes);
+
+ if (lookup_attribute ("noclone", attributes) == NULL)
+ attributes = tree_cons (get_identifier ("noclone"), NULL, attributes);
+ }
+
targetm.insert_attributes (*node, &attributes);
for (a = attributes; a; a = TREE_CHAIN (a))
tree name = TREE_PURPOSE (a);
tree args = TREE_VALUE (a);
tree *anode = node;
- const struct attribute_spec *spec = NULL;
+ const struct attribute_spec *spec = lookup_attribute_spec (name);
bool no_add_attrs = 0;
+ int fn_ptr_quals = 0;
tree fn_ptr_tmp = NULL_TREE;
- size_t i;
-
- for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
- {
- int j;
-
- for (j = 0; attribute_tables[i][j].name != NULL; j++)
- {
- if (is_attribute_p (attribute_tables[i][j].name, name))
- {
- spec = &attribute_tables[i][j];
- break;
- }
- }
- if (spec != NULL)
- break;
- }
if (spec == NULL)
{
- warning (0, "%qs attribute directive ignored",
- IDENTIFIER_POINTER (name));
+ warning (OPT_Wattributes, "%qE attribute directive ignored",
+ name);
continue;
}
else if (list_length (args) < spec->min_length
|| (spec->max_length >= 0
&& list_length (args) > spec->max_length))
{
- error ("wrong number of arguments specified for %qs attribute",
- IDENTIFIER_POINTER (name));
+ error ("wrong number of arguments specified for %qE attribute",
+ name);
continue;
}
+ gcc_assert (is_attribute_p (spec->name, name));
if (spec->decl_required && !DECL_P (*anode))
{
}
else
{
- warning (0, "%qs attribute does not apply to types",
- IDENTIFIER_POINTER (name));
+ warning (OPT_Wattributes, "%qE attribute does not apply to types",
+ name);
continue;
}
}
if (spec->type_required && DECL_P (*anode))
{
anode = &TREE_TYPE (*anode);
- flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE;
+ /* Allow ATTR_FLAG_TYPE_IN_PLACE for the type's naming decl. */
+ if (!(TREE_CODE (*anode) == TYPE_DECL
+ && *anode == TYPE_NAME (TYPE_MAIN_VARIANT
+ (TREE_TYPE (*anode)))))
+ flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE;
}
if (spec->function_type_required && TREE_CODE (*anode) != FUNCTION_TYPE
pull out the target type now, frob it as appropriate, and
rebuild the pointer type later.
- This would all be simpler if attributes were part of the
- declarator, grumble grumble. */
+ This would all be simpler if attributes were part of the
+ declarator, grumble grumble. */
fn_ptr_tmp = TREE_TYPE (*anode);
+ fn_ptr_quals = TYPE_QUALS (*anode);
anode = &fn_ptr_tmp;
flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE;
}
if (TREE_CODE (*anode) != FUNCTION_TYPE
&& TREE_CODE (*anode) != METHOD_TYPE)
{
- warning (0, "%qs attribute only applies to function types",
- IDENTIFIER_POINTER (name));
+ warning (OPT_Wattributes,
+ "%qE attribute only applies to function types",
+ name);
continue;
}
}
+ if (TYPE_P (*anode)
+ && (flags & (int) ATTR_FLAG_TYPE_IN_PLACE)
+ && TYPE_SIZE (*anode) != NULL_TREE)
+ {
+ warning (OPT_Wattributes, "type attributes ignored after type is already defined");
+ continue;
+ }
+
if (spec->handler != NULL)
returned_attrs = chainon ((*spec->handler) (anode, name, args,
flags, &no_add_attrs),
/* Rebuild the function pointer type and put it in the
appropriate place. */
fn_ptr_tmp = build_pointer_type (fn_ptr_tmp);
+ if (fn_ptr_quals)
+ fn_ptr_tmp = build_qualified_type (fn_ptr_tmp, fn_ptr_quals);
if (DECL_P (*node))
TREE_TYPE (*node) = fn_ptr_tmp;
else
return returned_attrs;
}
+
+/* Subroutine of set_method_tm_attributes. Apply TM attribute ATTR
+ to the method FNDECL. */
+
+void
+apply_tm_attr (tree fndecl, tree attr)
+{
+ decl_attributes (&TREE_TYPE (fndecl), tree_cons (attr, NULL, NULL), 0);
+}