/* Functions dealing with attribute handling, used by most front ends.
- Copyright (C) 1992-2023 Free Software Foundation, Inc.
+ Copyright (C) 1992-2024 Free Software Foundation, Inc.
This file is part of GCC.
#include "diagnostic-core.h"
#include "attribs.h"
#include "fold-const.h"
+#include "ipa-strub.h"
#include "stor-layout.h"
#include "langhooks.h"
#include "plugin.h"
/* Table of the tables of attributes (common, language, format, machine)
searched. */
-static const struct attribute_spec *attribute_tables[4];
+static array_slice<const scoped_attribute_specs *const> attribute_tables[2];
/* Substring representation. */
static bool attributes_initialized = false;
-/* Default empty table of attributes. */
+/* Do not use directly; go through get_gnu_namespace instead. */
+static GTY(()) tree gnu_namespace_cache;
-static const struct attribute_spec empty_attribute_table[] =
+/* Return the IDENTIFIER_NODE for the gnu namespace. */
+
+static tree
+get_gnu_namespace ()
{
- { NULL, 0, 0, false, false, false, false, NULL, NULL }
-};
+ if (!gnu_namespace_cache)
+ gnu_namespace_cache = get_identifier ("gnu");
+ return gnu_namespace_cache;
+}
/* Return base name of the attribute. Ie '__attr__' is turned into 'attr'.
To avoid need for copying, we simply return length of the string. */
canonicalize_attr_name (str->str, str->length);
}
-/* Insert an array of attributes ATTRIBUTES into a namespace. This
- array must be NULL terminated. NS is the name of attribute
- namespace. IGNORED_P is true iff all unknown attributes in this
- namespace should be ignored for the purposes of -Wattributes. The
- function returns the namespace into which the attributes have been
- registered. */
+/* Insert SPECS into its namespace. IGNORED_P is true iff all unknown
+ attributes in this namespace should be ignored for the purposes of
+ -Wattributes. The function returns the namespace into which the
+ attributes have been registered. */
scoped_attributes *
-register_scoped_attributes (const struct attribute_spec *attributes,
- const char *ns, bool ignored_p /*=false*/)
+register_scoped_attributes (const scoped_attribute_specs &specs,
+ bool ignored_p /*=false*/)
{
scoped_attributes *result = NULL;
/* See if we already have attributes in the namespace NS. */
- result = find_attribute_namespace (ns);
+ result = find_attribute_namespace (specs.ns);
if (result == NULL)
{
attributes_table.create (64);
memset (&sa, 0, sizeof (sa));
- sa.ns = ns;
+ sa.ns = specs.ns;
sa.attributes.create (64);
sa.ignored_p = ignored_p;
result = attributes_table.safe_push (sa);
result->ignored_p |= ignored_p;
/* Really add the attributes to their namespace now. */
- for (unsigned i = 0; attributes[i].name != NULL; ++i)
+ for (const attribute_spec &attribute : specs.attributes)
{
- result->attributes.safe_push (attributes[i]);
- register_scoped_attribute (&attributes[i], result);
+ result->attributes.safe_push (attribute);
+ register_scoped_attribute (&attribute, result);
}
gcc_assert (result != NULL);
static void
check_attribute_tables (void)
{
- for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
- for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
- {
- /* The name must not begin and end with __. */
- const char *name = attribute_tables[i][j].name;
- int len = strlen (name);
+ hash_set<pair_hash<nofree_string_hash, nofree_string_hash>> names;
- gcc_assert (!(name[0] == '_' && name[1] == '_'
- && name[len - 1] == '_' && name[len - 2] == '_'));
+ for (auto scoped_array : attribute_tables)
+ for (auto scoped_attributes : scoped_array)
+ for (const attribute_spec &attribute : scoped_attributes->attributes)
+ {
+ /* The name must not begin and end with __. */
+ const char *name = attribute.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);
+ /* The minimum and maximum lengths must be consistent. */
+ gcc_assert (attribute.min_length >= 0);
- gcc_assert (attribute_tables[i][j].max_length == -1
- || (attribute_tables[i][j].max_length
- >= attribute_tables[i][j].min_length));
+ gcc_assert (attribute.max_length == -1
+ || attribute.max_length >= attribute.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);
+ /* An attribute cannot require both a DECL and a TYPE. */
+ gcc_assert (!attribute.decl_required
+ || !attribute.type_required);
/* If an attribute requires a function type, in particular
it requires a type. */
- gcc_assert (!attribute_tables[i][j].function_type_required
- || attribute_tables[i][j].type_required);
- }
-
- /* Check that each name occurs just once in each table. */
- for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
- for (size_t j = 0; attribute_tables[i][j].name != NULL; j++)
- for (size_t k = j + 1; attribute_tables[i][k].name != NULL; k++)
- gcc_assert (strcmp (attribute_tables[i][j].name,
- attribute_tables[i][k].name));
-
- /* Check that no name occurs in more than one table. Names that
- begin with '*' are exempt, and may be overridden. */
- for (size_t i = 0; i < ARRAY_SIZE (attribute_tables); i++)
- for (size_t j = i + 1; j < ARRAY_SIZE (attribute_tables); j++)
- for (size_t k = 0; attribute_tables[i][k].name != NULL; k++)
- for (size_t l = 0; attribute_tables[j][l].name != NULL; l++)
- gcc_assert (attribute_tables[i][k].name[0] == '*'
- || strcmp (attribute_tables[i][k].name,
- attribute_tables[j][l].name));
+ gcc_assert (!attribute.function_type_required
+ || attribute.type_required);
+
+ /* Check that no name occurs more than once. Names that
+ begin with '*' are exempt, and may be overridden. */
+ const char *ns = scoped_attributes->ns;
+ if (name[0] != '*' && names.add ({ ns ? ns : "", name }))
+ gcc_unreachable ();
+ }
}
/* Used to stash pointers to allocated memory so that we can free them at
canonicalize_attr_name (vendor_start, vendor_len);
/* We perform all this hijinks so that we don't have to copy OPT. */
tree vendor_id = get_identifier_with_length (vendor_start, vendor_len);
- const char *attr;
+ array_slice<const attribute_spec> attrs;
/* In the "vendor::" case, we should ignore *any* attribute coming
from this attribute namespace. */
if (attr_len > 0)
}
canonicalize_attr_name (attr_start, attr_len);
tree attr_id = get_identifier_with_length (attr_start, attr_len);
- attr = IDENTIFIER_POINTER (attr_id);
+ const char *attr = IDENTIFIER_POINTER (attr_id);
/* If we've already seen this vendor::attr, ignore it. Attempting to
register it twice would lead to a crash. */
if (lookup_scoped_attribute_spec (vendor_id, attr_id))
continue;
+ /* Create a table with extra attributes which we will register.
+ We can't free it here, so squirrel away the pointers. */
+ attribute_spec *table = new attribute_spec {
+ attr, 0, -2, false, false, false, false, nullptr, nullptr
+ };
+ ignored_attributes_table.safe_push (table);
+ attrs = { table, 1 };
}
- else
- attr = nullptr;
- /* Create a table with extra attributes which we will register.
- We can't free it here, so squirrel away the pointers. */
- attribute_spec *table = new attribute_spec[2];
- ignored_attributes_table.safe_push (table);
- table[0] = { attr, 0, -2, false, false, false, false, nullptr, nullptr };
- table[1] = { nullptr, 0, 0, false, false, false, false, nullptr,
- nullptr };
- register_scoped_attributes (table, IDENTIFIER_POINTER (vendor_id), !attr);
+ const scoped_attribute_specs scoped_specs = {
+ IDENTIFIER_POINTER (vendor_id), { attrs }
+ };
+ register_scoped_attributes (scoped_specs, attrs.empty ());
}
}
free_attr_data ()
{
for (auto x : ignored_attributes_table)
- delete[] x;
+ delete x;
ignored_attributes_table.release ();
}
void
init_attributes (void)
{
- size_t i;
-
if (attributes_initialized)
return;
- attribute_tables[0] = lang_hooks.common_attribute_table;
- attribute_tables[1] = lang_hooks.attribute_table;
- attribute_tables[2] = lang_hooks.format_attribute_table;
- attribute_tables[3] = targetm.attribute_table;
-
- /* Translate NULL pointers to pointers to the empty table. */
- for (i = 0; i < ARRAY_SIZE (attribute_tables); i++)
- if (attribute_tables[i] == NULL)
- attribute_tables[i] = empty_attribute_table;
+ attribute_tables[0] = lang_hooks.attribute_table;
+ attribute_tables[1] = targetm.attribute_table;
if (flag_checking)
check_attribute_tables ();
- for (i = 0; i < ARRAY_SIZE (attribute_tables); ++i)
- /* Put all the GNU attributes into the "gnu" namespace. */
- register_scoped_attributes (attribute_tables[i], "gnu");
+ for (auto scoped_array : attribute_tables)
+ for (auto scoped_attributes : scoped_array)
+ register_scoped_attributes (*scoped_attributes);
vec<char *> *ignored = (vec<char *> *) flag_ignored_attributes;
handle_ignored_attributes_option (ignored);
name = TREE_VALUE (name);
}
else
- ns = get_identifier ("gnu");
+ ns = get_gnu_namespace ();
return lookup_scoped_attribute_spec (ns, name);
}
{
if (cxx11_attribute_p (attr))
return TREE_PURPOSE (TREE_PURPOSE (attr));
- return get_identifier ("gnu");
+ return get_gnu_namespace ();
}
/* Check LAST_DECL and NODE of the same symbol for attributes that are
return as->max_length == -2;
}
+/* Return true if the ATTRS chain contains at least one attribute which
+ is not ignored. */
+
+bool
+any_nonignored_attribute_p (tree attrs)
+{
+ for (tree attr = attrs; attr; attr = TREE_CHAIN (attr))
+ if (!attribute_ignored_p (attr))
+ return true;
+
+ return false;
+}
+
+/* See whether LIST contains at least one instance of attribute ATTR
+ (possibly with different arguments). Return the first such attribute
+ if so, otherwise return null. */
+
+static tree
+find_same_attribute (const_tree attr, tree list)
+{
+ if (list == NULL_TREE)
+ return NULL_TREE;
+ tree ns = get_attribute_namespace (attr);
+ tree name = get_attribute_name (attr);
+ return private_lookup_attribute (ns ? IDENTIFIER_POINTER (ns) : nullptr,
+ IDENTIFIER_POINTER (name),
+ ns ? IDENTIFIER_LENGTH (ns) : 0,
+ IDENTIFIER_LENGTH (name), list);
+}
+
/* 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,
it should be modified in place; if a TYPE, a copy should be created
options to the attribute((target(...))) list. */
if (TREE_CODE (*node) == FUNCTION_DECL
&& current_target_pragma
- && targetm.target_option.valid_attribute_p (*node, NULL_TREE,
+ && targetm.target_option.valid_attribute_p (*node,
+ get_identifier ("target"),
current_target_pragma, 0))
{
tree cur_attr = lookup_attribute ("target", attributes);
flags &= ~(int) ATTR_FLAG_TYPE_IN_PLACE;
}
- if (spec->function_type_required && TREE_CODE (*anode) != FUNCTION_TYPE
- && TREE_CODE (*anode) != METHOD_TYPE)
+ if (spec->function_type_required
+ && !FUNC_OR_METHOD_TYPE_P (*anode))
{
if (TREE_CODE (*anode) == POINTER_TYPE
&& FUNC_OR_METHOD_TYPE_P (TREE_TYPE (*anode)))
TYPE_NAME (tt) = *node;
}
- *anode = cur_and_last_decl[0];
+ if (*anode != cur_and_last_decl[0])
+ {
+ /* Even if !spec->function_type_required, allow the attribute
+ handler to request the attribute to be applied to the function
+ type, rather than to the function pointer type, by setting
+ cur_and_last_decl[0] to the function type. */
+ if (!fn_ptr_tmp
+ && POINTER_TYPE_P (*anode)
+ && TREE_TYPE (*anode) == cur_and_last_decl[0]
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (*anode)))
+ {
+ fn_ptr_tmp = TREE_TYPE (*anode);
+ fn_ptr_quals = TYPE_QUALS (*anode);
+ anode = &fn_ptr_tmp;
+ }
+ *anode = cur_and_last_decl[0];
+ }
+
if (ret == error_mark_node)
{
warning (OPT_Wattributes, "%qE attribute ignored", name);
else
old_attrs = TYPE_ATTRIBUTES (*anode);
- for (a = lookup_attribute (spec->name, old_attrs);
+ for (a = find_same_attribute (attr, old_attrs);
a != NULL_TREE;
- a = lookup_attribute (spec->name, TREE_CHAIN (a)))
+ a = find_same_attribute (attr, TREE_CHAIN (a)))
{
if (simple_cst_equal (TREE_VALUE (a), args) == 1)
break;
if (TYPE_ATTRIBUTES (variant) == old_attrs)
TYPE_ATTRIBUTES (variant)
= TYPE_ATTRIBUTES (*anode);
- else if (!lookup_attribute
- (spec->name, TYPE_ATTRIBUTES (variant)))
+ else if (!find_same_attribute
+ (attr, TYPE_ATTRIBUTES (variant)))
TYPE_ATTRIBUTES (variant) = tree_cons
(name, args, TYPE_ATTRIBUTES (variant));
}
return func_decl;
}
-/* Returns true if decl is multi-versioned and DECL is the default function,
- that is it is not tagged with target specific optimization. */
+/* Returns true if DECL is multi-versioned using the target attribute, and this
+ is the default version. This function can only be used for targets that do
+ not support the "target_version" attribute. */
bool
is_function_default_version (const tree decl)
const struct attribute_spec *as;
const_tree attr;
- as = lookup_attribute_spec (get_attribute_name (a));
+ as = lookup_attribute_spec (TREE_PURPOSE (a));
if (!as || as->affects_type_identity == false)
continue;
- attr = lookup_attribute (as->name, CONST_CAST_TREE (a2));
+ attr = find_same_attribute (a, CONST_CAST_TREE (a2));
if (!attr || !attribute_value_equal (a, attr))
break;
}
{
const struct attribute_spec *as;
- as = lookup_attribute_spec (get_attribute_name (a));
+ as = lookup_attribute_spec (TREE_PURPOSE (a));
if (!as || as->affects_type_identity == false)
continue;
- if (!lookup_attribute (as->name, CONST_CAST_TREE (a1)))
+ if (!find_same_attribute (a, CONST_CAST_TREE (a1)))
break;
/* We don't need to compare trees again, as we did this
already in first loop. */
if ((lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type1)) != NULL)
^ (lookup_attribute ("nocf_check", TYPE_ATTRIBUTES (type2)) != NULL))
return 0;
+ int strub_ret = strub_comptypes (CONST_CAST_TREE (type1),
+ CONST_CAST_TREE (type2));
+ if (strub_ret == 0)
+ return strub_ret;
/* As some type combinations - like default calling-convention - might
be compatible, we have to call the target hook to get the final result. */
- return targetm.comp_type_attributes (type1, type2);
+ int target_ret = targetm.comp_type_attributes (type1, type2);
+ if (target_ret == 0)
+ return target_ret;
+ if (strub_ret == 2 || target_ret == 2)
+ return 2;
+ if (strub_ret == 1 && target_ret == 1)
+ return 1;
+ gcc_unreachable ();
}
/* PREDICATE acts as a function of type:
const_tree start = attrs;
for (const_tree attr = attrs; attr; attr = TREE_CHAIN (attr))
{
- tree name = get_attribute_name (attr);
- const attribute_spec *as = lookup_attribute_spec (name);
+ const attribute_spec *as = lookup_attribute_spec (TREE_PURPOSE (attr));
const_tree end;
if (!predicate (attr, as))
end = attr;
namespace selftest
{
-/* Helper types to verify the consistency attribute exclusions. */
-
-typedef std::pair<const char *, const char *> excl_pair;
-
-struct excl_hash_traits: typed_noop_remove<excl_pair>
-{
- typedef excl_pair value_type;
- typedef value_type compare_type;
-
- static hashval_t hash (const value_type &x)
- {
- hashval_t h1 = htab_hash_string (x.first);
- hashval_t h2 = htab_hash_string (x.second);
- return h1 ^ h2;
- }
-
- static bool equal (const value_type &x, const value_type &y)
- {
- return !strcmp (x.first, y.first) && !strcmp (x.second, y.second);
- }
-
- static void mark_deleted (value_type &x)
- {
- x = value_type (NULL, NULL);
- }
-
- static const bool empty_zero_p = false;
-
- static void mark_empty (value_type &x)
- {
- x = value_type ("", "");
- }
-
- static bool is_deleted (const value_type &x)
- {
- return !x.first && !x.second;
- }
-
- static bool is_empty (const value_type &x)
- {
- return !*x.first && !*x.second;
- }
-};
-
-
/* Self-test to verify that each attribute exclusion is symmetric,
meaning that if attribute A is encoded as incompatible with
attribute B then the opposite relationship is also encoded.
static void
test_attribute_exclusions ()
{
+ using excl_hash_traits = pair_hash<nofree_string_hash, nofree_string_hash>;
+
/* Iterate over the array of attribute tables first (with TI0 as
the index) and over the array of attribute_spec in each table
(with SI0 as the index). */
- const size_t ntables = ARRAY_SIZE (attribute_tables);
+ hash_set<excl_hash_traits> excl_set;
- /* Set of pairs of mutually exclusive attributes. */
- typedef hash_set<excl_pair, false, excl_hash_traits> exclusion_set;
- exclusion_set excl_set;
+ for (auto scoped_array : attribute_tables)
+ for (auto scoped_attributes : scoped_array)
+ for (const attribute_spec &attribute : scoped_attributes->attributes)
+ {
+ const attribute_spec::exclusions *excl = attribute.exclude;
- for (size_t ti0 = 0; ti0 != ntables; ++ti0)
- for (size_t s0 = 0; attribute_tables[ti0][s0].name; ++s0)
- {
- const attribute_spec::exclusions *excl
- = attribute_tables[ti0][s0].exclude;
+ /* Skip each attribute that doesn't define exclusions. */
+ if (!excl)
+ continue;
- /* Skip each attribute that doesn't define exclusions. */
- if (!excl)
- continue;
+ /* Skip standard (non-GNU) attributes, since currently the
+ exclusions are implicitly for GNU attributes only.
+ Also, C++ likely and unlikely get rewritten to gnu::hot
+ and gnu::cold, so symmetry isn't necessary there. */
+ if (!scoped_attributes->ns)
+ continue;
- const char *attr_name = attribute_tables[ti0][s0].name;
+ const char *attr_name = attribute.name;
- /* Iterate over the set of exclusions for every attribute
- (with EI0 as the index) adding the exclusions defined
- for each to the set. */
- for (size_t ei0 = 0; excl[ei0].name; ++ei0)
- {
- const char *excl_name = excl[ei0].name;
+ /* Iterate over the set of exclusions for every attribute
+ (with EI0 as the index) adding the exclusions defined
+ for each to the set. */
+ for (size_t ei0 = 0; excl[ei0].name; ++ei0)
+ {
+ const char *excl_name = excl[ei0].name;
- if (!strcmp (attr_name, excl_name))
- continue;
+ if (!strcmp (attr_name, excl_name))
+ continue;
- excl_set.add (excl_pair (attr_name, excl_name));
- }
- }
+ excl_set.add ({ attr_name, excl_name });
+ }
+ }
/* Traverse the set of mutually exclusive pairs of attributes
and verify that they are symmetric. */
- for (exclusion_set::iterator it = excl_set.begin ();
- it != excl_set.end ();
- ++it)
- {
- if (!excl_set.contains (excl_pair ((*it).second, (*it).first)))
- {
- /* An exclusion for an attribute has been found that
- doesn't have a corresponding exclusion in the opposite
- direction. */
- char desc[120];
- sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
- (*it).first, (*it).second);
- fail (SELFTEST_LOCATION, desc);
- }
- }
+ for (auto excl_pair : excl_set)
+ if (!excl_set.contains ({ excl_pair.second, excl_pair.first }))
+ {
+ /* An exclusion for an attribute has been found that
+ doesn't have a corresponding exclusion in the opposite
+ direction. */
+ char desc[120];
+ sprintf (desc, "'%s' attribute exclusion '%s' must be symmetric",
+ excl_pair.first, excl_pair.second);
+ fail (SELFTEST_LOCATION, desc);
+ }
}
void
} /* namespace selftest */
#endif /* CHECKING_P */
+
+#include "gt-attribs.h"