diff mbox series

use attribute exclusion to reject naked vs target_clones conflicts (PR 84723)

Message ID f24c8639-bedb-d420-812a-c8b75b1daf4d@gmail.com
State New
Headers show
Series use attribute exclusion to reject naked vs target_clones conflicts (PR 84723) | expand

Commit Message

Martin Sebor March 7, 2018, 5:19 a.m. UTC
PR 84723 is about an ICE caused by GCC accepting the mutually
exclusive attributes target_clones and naked during parsing,
only to then later crash while assuming such conflicts don't
happen.

The attached patch adds an exclusion for the two attributes
to detect when they're both specified on declarations of the
same function and avoid the ICE that way (there are other
problems with target_clones that are unrelated to other
interaction with other attributes).

As this is the first instance of an exclusion between a pair
of a front/middle-end and back-end attributes, testing the
straightforward solution of just adding an exclusion for them
exposed a restriction in the implementation that required more
extensive changes to make things work.  The current (unpatched)
code requires exclusions to be symmetric (e.g., when there is
an exclusion entry for target_clones and naked, there must also
be another exclusion entry the other way around).  This makes
sense for attributes in the same table (e.g., all C/C++
attributes, or all i386 back-end attributes) but not between
attributes in different tables.  The patch makes additional
changes to allow asymmetric exclusions without compromising
the detection of attribute conflicts.  This additional
enhancement allowed me to remove the self tests.

Since I started working on this last night Jakub has already
posted an alternate fix that doesn't make use of the exclusion
framework (plus it fixes the additional bugs/ICEs I referred
to above).  This patch is thus meant to apply on top of Jakub's
(the tests will need tweaking to adjust the expected diagnostics).
If my solution is considered too invasive at this stage I'm fine
holding off on it and re-posting it in stage 1.  While working
on it I noticed other opportunities for improvements in this
area that I would like to make then.

Martin

Comments

Martin Sebor March 13, 2018, 2:11 a.m. UTC | #1
Since Jakub has committed an alternate patch that fixes
the ICE (below) this change is no longer necessary for GCC 8.
It still is in my view the right way to address the main part
of the problem so I will resubmit it for GCC 9 in stage 1.

https://gcc.gnu.org/ml/gcc-patches/2018-03/msg00285.html

Martin

On 03/06/2018 10:19 PM, Martin Sebor wrote:
> PR 84723 is about an ICE caused by GCC accepting the mutually
> exclusive attributes target_clones and naked during parsing,
> only to then later crash while assuming such conflicts don't
> happen.
>
> The attached patch adds an exclusion for the two attributes
> to detect when they're both specified on declarations of the
> same function and avoid the ICE that way (there are other
> problems with target_clones that are unrelated to other
> interaction with other attributes).
>
> As this is the first instance of an exclusion between a pair
> of a front/middle-end and back-end attributes, testing the
> straightforward solution of just adding an exclusion for them
> exposed a restriction in the implementation that required more
> extensive changes to make things work.  The current (unpatched)
> code requires exclusions to be symmetric (e.g., when there is
> an exclusion entry for target_clones and naked, there must also
> be another exclusion entry the other way around).  This makes
> sense for attributes in the same table (e.g., all C/C++
> attributes, or all i386 back-end attributes) but not between
> attributes in different tables.  The patch makes additional
> changes to allow asymmetric exclusions without compromising
> the detection of attribute conflicts.  This additional
> enhancement allowed me to remove the self tests.
>
> Since I started working on this last night Jakub has already
> posted an alternate fix that doesn't make use of the exclusion
> framework (plus it fixes the additional bugs/ICEs I referred
> to above).  This patch is thus meant to apply on top of Jakub's
> (the tests will need tweaking to adjust the expected diagnostics).
> If my solution is considered too invasive at this stage I'm fine
> holding off on it and re-posting it in stage 1.  While working
> on it I noticed other opportunities for improvements in this
> area that I would like to make then.
>
> Martin
diff mbox series

Patch

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.

Index: gcc/attribs.c
===================================================================
--- gcc/attribs.c	(revision 258259)
+++ gcc/attribs.c	(working copy)
@@ -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 */
Index: gcc/c-family/c-attribs.c
===================================================================
--- gcc/c-family/c-attribs.c	(revision 258259)
+++ gcc/c-family/c-attribs.c	(working copy)
@@ -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
Index: gcc/selftest-run-tests.c
===================================================================
--- gcc/selftest-run-tests.c	(revision 258259)
+++ gcc/selftest-run-tests.c	(working copy)
@@ -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 ();
Index: gcc/selftest.h
===================================================================
--- gcc/selftest.h	(revision 258259)
+++ gcc/selftest.h	(working copy)
@@ -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 ();
Index: gcc/testsuite/gcc.dg/Wattributes-10.c
===================================================================
--- gcc/testsuite/gcc.dg/Wattributes-10.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wattributes-10.c	(working copy)
@@ -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);
Index: gcc/testsuite/gcc.dg/Wattributes-11.c
===================================================================
--- gcc/testsuite/gcc.dg/Wattributes-11.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wattributes-11.c	(working copy)
@@ -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);
Index: gcc/testsuite/gcc.dg/Wattributes-9.c
===================================================================
--- gcc/testsuite/gcc.dg/Wattributes-9.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wattributes-9.c	(working copy)
@@ -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);