PR middle-end/84723 - ICE in create_target_clone
gcc/c-family/ChangeLog:
PR middle-end/84723
* c-attribs.c (attr_clone_exclusions, attr_noipa_exclusions): New.
(c_common_attribute_tab): Use them.
gcc/testsuite/ChangeLog:
PR middle-end/84723
* gcc.dg/Wattributes-10.c: New test.
* gcc.dg/Wattributes-11.c: New test.
* gcc.dg/Wattributes-9.c: New test.
gcc/ChangeLog:
PR middle-end/84723
* attribs.c (diag_attr_exclusions): New function.
(maybe_diag_attr_exclusions): Same.
(decl_attributes): Call it.
(namespace selftest): Remove.
* selftest-run-tests.c (selftest::run_tests): Remove call to
attribute_c_tests.
* selftest.h (attribute_c_tests): Remove.
===================================================================
@@ -345,6 +345,93 @@ get_attribute_namespace (const_tree attr)
return get_identifier ("gnu");
}
+
+/* Check LAST_DECL and NODE of the same symbol for attributes exclusions
+ in EXCL with respect to ATTRNAME and ATTRS when non-null or NAME
+ otherwise, diagnose them, and return true if any have been found.
+ NODE can be a DECL or a TYPE. Called from the diag_attr_exclusions
+ overloads just below. */
+
+static bool
+diag_attr_exclusions (tree last_decl, tree node, tree attrs, tree attrname,
+ tree name, const attribute_spec::exclusions *excl)
+{
+ bool found = false;
+
+ /* Iterate over the null-terminated array of exclusions. */
+ for ( ; excl->name; ++excl)
+ {
+ /* The name of the mutually exclusive attribute to mention
+ in the diagnostic. */
+ const char *xatstr;
+
+ if (attrs)
+ {
+ gcc_assert (!name);
+
+ /* Avoid checking the attribute against itself. */
+ if (is_attribute_p (excl->name, attrname))
+ continue;
+
+ if (!lookup_attribute (excl->name, attrs))
+ continue;
+
+ xatstr = excl->name;
+ }
+ else
+ {
+ /* Avoid checking the attribute against itself. */
+ if (is_attribute_p (excl->name, name))
+ continue;
+
+ if (!is_attribute_p (excl->name, attrname))
+ continue;
+
+ xatstr = IDENTIFIER_POINTER (name);
+ }
+
+ /* An exclusion may apply either to a function declaration,
+ type declaration, or a field/variable declaration, or
+ any subset of the three. */
+ tree_code code = TREE_CODE (node);
+ if (code == FUNCTION_DECL && !excl->function)
+ continue;
+
+ if (code == TYPE_DECL && !excl->type)
+ continue;
+
+ if ((code == FIELD_DECL
+ || code == VAR_DECL)
+ && !excl->variable)
+ continue;
+
+ /* Print a note? */
+ bool note = last_decl != NULL_TREE;
+
+ if (TREE_CODE (node) == FUNCTION_DECL
+ && DECL_BUILT_IN (node))
+ note &= warning (OPT_Wattributes,
+ "ignoring attribute %qE in declaration of "
+ "a built-in function %qD because it conflicts "
+ "with attribute %qs",
+ attrname, node, xatstr);
+ else
+ note &= warning (OPT_Wattributes,
+ "ignoring attribute %qE because "
+ "it conflicts with attribute %qs",
+ attrname, xatstr);
+
+ if (note)
+ inform (DECL_SOURCE_LOCATION (last_decl),
+ "previous declaration here");
+
+ found = true;
+ }
+
+ return found;
+}
+
+
/* Check LAST_DECL and NODE of the same symbol for attributes that are
recorded in SPEC to be mutually exclusive with ATTRNAME, diagnose
them, and return true if any have been found. NODE can be a DECL
@@ -401,56 +488,76 @@ diag_attr_exclusions (tree last_decl, tree node, t
if (!attrs[i])
continue;
- for ( ; excl->name; ++excl)
- {
- /* Avoid checking the attribute against itself. */
- if (is_attribute_p (excl->name, attrname))
- continue;
+ found |= diag_attr_exclusions (last_decl, node, attrs[i], attrname,
+ NULL_TREE, excl);
+ }
- if (!lookup_attribute (excl->name, attrs[i]))
- continue;
+ return found;
+}
- /* An exclusion may apply either to a function declaration,
- type declaration, or a field/variable declaration, or
- any subset of the three. */
- if (TREE_CODE (node) == FUNCTION_DECL
- && !excl->function)
- continue;
+/* Check LAST_DECL and NODE of the same symbol for attributes that are
+ recorded in SPEC to be mutually exclusive with ATTRNAME, diagnose
+ them, and return true if any have been found. NODE can be a DECL
+ or a TYPE. If there are no exclusions in SPEC or when no conflicts
+ are found, also check LAST_DECL for any attributes that might be
+ mutually exclusive with ATTRNMAME. This should only happen for
+ exclusions between attributes in different tables, such as
+ between generic (i.e., middle-end) and and back-end specific
+ attributes. */
- if (TREE_CODE (node) == TYPE_DECL
- && !excl->type)
- continue;
+static bool
+maybe_diag_attr_exclusions (tree last_decl, tree *node, tree *anode,
+ tree attrname, const attribute_spec *spec)
+{
+ if (spec->exclude)
+ {
+ /* Check *ANODE's attributes for incompatibilities with those
+ on LAST_DECL, and if none are found and if NODE is not the
+ same as *ANODE (i.e., *ANODE refers to NODE's type), also
+ check those of NODE for the same. Only add compatible
+ attributes. */
+ if (diag_attr_exclusions (last_decl, *anode, attrname, spec))
+ return true;
- if ((TREE_CODE (node) == FIELD_DECL
- || TREE_CODE (node) == VAR_DECL)
- && !excl->variable)
- continue;
+ if (anode != node
+ && diag_attr_exclusions (last_decl, *node, attrname, spec))
+ return true;
+ }
- found = true;
+ if (!last_decl)
+ return false;
- /* Print a note? */
- bool note = last_decl != NULL_TREE;
+ /* When no exclusion for ATTRNAME is specified check those specified
+ for any of the attributes on LAST_DECL and its type. This is only
+ necessary for asymmetric attribute exclusions such as those those
+ between some generic attributes and back-end attributes. */
+ tree attrs[2] = {
+ DECL_ATTRIBUTES (last_decl), TYPE_ATTRIBUTES (TREE_TYPE (last_decl))
+ };
- if (TREE_CODE (node) == FUNCTION_DECL
- && DECL_BUILT_IN (node))
- note &= warning (OPT_Wattributes,
- "ignoring attribute %qE in declaration of "
- "a built-in function %qD because it conflicts "
- "with attribute %qs",
- attrname, node, excl->name);
- else
- note &= warning (OPT_Wattributes,
- "ignoring attribute %qE because "
- "it conflicts with attribute %qs",
- attrname, excl->name);
+ for (unsigned i = 0; i != 2; ++i)
+ {
+ if (!attrs[i])
+ continue;
- if (note)
- inform (DECL_SOURCE_LOCATION (last_decl),
- "previous declaration here");
+ for (tree a = attrs[i]; a; a = TREE_CHAIN (a))
+ {
+ tree ns = get_attribute_namespace (a);
+ tree name = get_attribute_name (a);
+
+ const struct attribute_spec *spec
+ = lookup_scoped_attribute_spec (ns, name);
+
+ if (!spec->exclude)
+ continue;
+
+ if (diag_attr_exclusions (last_decl, *node, NULL_TREE, attrname,
+ name, spec->exclude))
+ return true;
}
}
- return found;
+ return false;
}
/* Process the attributes listed in ATTRIBUTES and install them in *NODE,
@@ -700,8 +807,7 @@ decl_attributes (tree *node, tree attributes, int
on the current declation as well as the last declaration of
the same symbol already processed (if one exists). */
bool built_in = flags & ATTR_FLAG_BUILT_IN;
- if (spec->exclude
- && !no_add_attrs
+ if (!no_add_attrs
&& (flag_checking || !built_in))
{
/* Always check attributes on user-defined functions.
@@ -708,18 +814,14 @@ decl_attributes (tree *node, tree attributes, int
Check them on built-ins only when -fchecking is set.
Ignore __builtin_unreachable -- it's both const and
noreturn. */
-
- if (!built_in
- || !DECL_P (*anode)
- || (DECL_FUNCTION_CODE (*anode) != BUILT_IN_UNREACHABLE
- && (DECL_FUNCTION_CODE (*anode)
- != BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE)))
- {
- bool no_add = diag_attr_exclusions (last_decl, *anode, name, spec);
- if (!no_add && anode != node)
- no_add = diag_attr_exclusions (last_decl, *node, name, spec);
- no_add_attrs |= no_add;
- }
+ if ((!built_in
+ || !DECL_P (*anode)
+ || (DECL_FUNCTION_CODE (*anode) != BUILT_IN_UNREACHABLE
+ && (DECL_FUNCTION_CODE (*anode)
+ != BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE)))
+ && maybe_diag_attr_exclusions (last_decl, node, anode, name,
+ spec))
+ no_add_attrs = true;
}
/* Layout the decl in case anything changed. */
@@ -1808,124 +1910,3 @@ private_lookup_attribute (const char *attr_name, s
return list;
}
-
-#if CHECKING_P
-
-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 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.
- This test also detects most cases of misspelled attribute names
- in exclusions. */
-
-static void
-test_attribute_exclusions ()
-{
- /* 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);
-
- /* Set of pairs of mutually exclusive attributes. */
- typedef hash_set<excl_pair, excl_hash_traits> exclusion_set;
- exclusion_set excl_set;
-
- 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;
-
- const char *attr_name = attribute_tables[ti0][s0].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;
-
- excl_set.add (excl_pair (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);
- }
- }
-}
-
-void
-attribute_c_tests ()
-{
- test_attribute_exclusions ();
-}
-
-} /* namespace selftest */
-
-#endif /* CHECKING_P */
===================================================================
@@ -237,6 +237,23 @@ static const struct attribute_spec::exclusions att
ATTR_EXCL (NULL, false, false, false)
};
+static const struct attribute_spec::exclusions attr_clone_exclusions[] =
+{
+ /* Attribute name exclusion applies to:
+ function, type, variable */
+ ATTR_EXCL ("noclone", true, false, false),
+ ATTR_EXCL ("target_clones", true, false, false),
+ ATTR_EXCL (NULL, false, false, false)
+};
+
+static const struct attribute_spec::exclusions attr_noipa_exclusions[] =
+{
+ /* Attribute name exclusion applies to:
+ function, type, variable */
+ ATTR_EXCL ("target_clones", true, false, false),
+ ATTR_EXCL (NULL, false, false, false)
+};
+
/* Table of machine-independent attributes common to all C-like languages.
All attributes referencing arguments should be additionally processed
@@ -265,7 +282,8 @@ const struct attribute_spec c_common_attribute_tab
handle_noreturn_attribute,
attr_noreturn_exclusions },
{ "volatile", 0, 0, true, false, false, false,
- handle_noreturn_attribute, NULL },
+ handle_noreturn_attribute,
+ attr_noreturn_exclusions },
{ "stack_protect", 0, 0, true, false, false, false,
handle_stack_protect_attribute, NULL },
{ "noinline", 0, 0, true, false, false, false,
@@ -272,11 +290,13 @@ const struct attribute_spec c_common_attribute_tab
handle_noinline_attribute,
attr_noinline_exclusions },
{ "noclone", 0, 0, true, false, false, false,
- handle_noclone_attribute, NULL },
+ handle_noclone_attribute,
+ attr_clone_exclusions },
{ "no_icf", 0, 0, true, false, false, false,
handle_noicf_attribute, NULL },
{ "noipa", 0, 0, true, false, false, false,
- handle_noipa_attribute, NULL },
+ handle_noipa_attribute,
+ attr_noipa_exclusions },
{ "leaf", 0, 0, true, false, false, false,
handle_leaf_attribute, NULL },
{ "always_inline", 0, 0, true, false, false, false,
@@ -420,7 +440,8 @@ const struct attribute_spec c_common_attribute_tab
{ "target", 1, -1, true, false, false, false,
handle_target_attribute, NULL },
{ "target_clones", 1, -1, true, false, false, false,
- handle_target_clones_attribute, NULL },
+ handle_target_clones_attribute,
+ attr_clone_exclusions },
{ "optimize", 1, -1, true, false, false, false,
handle_optimize_attribute, NULL },
/* For internal use only. The leading '*' both prevents its usage in
===================================================================
@@ -88,7 +88,6 @@ selftest::run_tests ()
spellcheck_c_tests ();
spellcheck_tree_c_tests ();
tree_cfg_c_tests ();
- attribute_c_tests ();
/* This one relies on most of the above. */
function_tests_c_tests ();
===================================================================
@@ -184,7 +184,6 @@ class test_runner
/* Declarations for specific families of tests (by source file), in
alphabetical order. */
-extern void attribute_c_tests ();
extern void bitmap_c_tests ();
extern void sbitmap_c_tests ();
extern void diagnostic_c_tests ();
===================================================================
@@ -0,0 +1,67 @@
+/* PR middle-end/84723 - ICE in create_target_clone, at multiple_target.c:275
+ Verify that all permutations of the mutually exclusive attributes
+ noclone and target_clones are diagnosed.
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+/* Ideally, the warnings would ideally mention just the most general
+ attribute (noipa) rather than all the individual attributes noipa
+ implies as it does now. Be prepared for both so if it changes in
+ the future the test won't need to. */
+
+void
+noipa_target_clones (void);
+
+void __attribute__ ((noipa))
+noipa_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+noipa_target_clones (void); /* { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute " } */
+
+void
+noipa_target_clones (void);
+
+
+void
+target_clones_noipa (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noipa (void);
+
+void __attribute__ ((noipa))
+target_clones_noipa (void); /* { dg-warning "ignoring attribute .\[a-z_\]+. because it conflicts with attribute .target_clones." } */
+
+void
+target_clones_noipa (void);
+
+
+void
+noipa_target_clones_noipa (void);
+
+void __attribute__ ((noipa))
+noipa_target_clones_noipa (void);
+
+void __attribute__ ((target_clones ("default")))
+noipa_target_clones_noipa (void); /* { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute " } */
+
+void __attribute__ ((noipa))
+noipa_target_clones_noipa (void);
+
+void
+noipa_target_clones_noipa (void);
+
+
+void
+target_clones_noipa_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noipa_target_clones (void);
+
+void __attribute__ ((noipa))
+target_clones_noipa_target_clones (void); /* { dg-warning "ignoring attribute .\[a-z_\]+. because it conflicts with attribute .target_clones." } */
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noipa_target_clones (void);
+
+void
+target_clones_noipa_target_clones (void);
===================================================================
@@ -0,0 +1,61 @@
+/* PR middle-end/84723 - ICE in create_target_clone, at multiple_target.c:275
+ Verify that attributes no_icf and target_clones are accepted on the same
+ function and aren't considered mutually exclusive.
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+void
+no_icf_target_clones (void);
+
+void __attribute__ ((no_icf))
+no_icf_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+no_icf_target_clones (void);
+
+void
+no_icf_target_clones (void);
+
+
+void
+target_clones_no_icf (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_no_icf (void);
+
+void __attribute__ ((no_icf))
+target_clones_no_icf (void);
+
+void
+target_clones_no_icf (void);
+
+void
+no_icf_target_clones_no_icf (void);
+
+void __attribute__ ((no_icf))
+no_icf_target_clones_no_icf (void);
+
+void __attribute__ ((target_clones ("default")))
+no_icf_target_clones_no_icf (void);
+
+void __attribute__ ((no_icf))
+no_icf_target_clones_no_icf (void);
+
+void
+no_icf_target_clones_no_icf (void);
+
+
+void
+target_clones_no_icf_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_no_icf_target_clones (void);
+
+void __attribute__ ((no_icf))
+target_clones_no_icf_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_no_icf_target_clones (void);
+
+void
+target_clones_no_icf_target_clones (void);
===================================================================
@@ -0,0 +1,62 @@
+/* PR middle-end/84723 - ICE in create_target_clone, at multiple_target.c:275
+ Verify that all permutations of the mutually exclusive attributes
+ noclone and target_clones are diagnosed.
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+void
+noclone_target_clones (void);
+
+void __attribute__ ((noclone))
+noclone_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+noclone_target_clones (void); /* { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute .noclone." } */
+
+void
+noclone_target_clones (void);
+
+
+void
+target_clones_noclone (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noclone (void);
+
+void __attribute__ ((noclone))
+target_clones_noclone (void); /* { dg-warning "ignoring attribute .noclone. because it conflicts with attribute .target_clones." } */
+
+void
+target_clones_noclone (void);
+
+
+void
+noclone_target_clones_noclone (void);
+
+void __attribute__ ((noclone))
+noclone_target_clones_noclone (void);
+
+void __attribute__ ((target_clones ("default")))
+noclone_target_clones_noclone (void); /* { dg-warning "ignoring attribute .target_clones. because it conflicts with attribute .noclone." } */
+
+void __attribute__ ((noclone))
+noclone_target_clones_noclone (void);
+
+void
+noclone_target_clones_noclone (void);
+
+
+void
+target_clones_noclone_target_clones (void);
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noclone_target_clones (void);
+
+void __attribute__ ((noclone))
+target_clones_noclone_target_clones (void); /* { dg-warning "ignoring attribute .noclone. because it conflicts with attribute .target_clones." } */
+
+void __attribute__ ((target_clones ("default")))
+target_clones_noclone_target_clones (void);
+
+void
+target_clones_noclone_target_clones (void);