diff gcc/multiple_target.c @ 132:d34655255c78

update gcc-8.2
author mir3636
date Thu, 25 Oct 2018 10:21:07 +0900
parents 84e7813d76e9
children 1830386684a0
line wrap: on
line diff
--- a/gcc/multiple_target.c	Thu Oct 25 08:08:40 2018 +0900
+++ b/gcc/multiple_target.c	Thu Oct 25 10:21:07 2018 +0900
@@ -2,7 +2,7 @@
 
    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
 
-   Copyright (C) 2015-2017 Free Software Foundation, Inc.
+   Copyright (C) 2015-2018 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -36,6 +36,8 @@
 #include "pretty-print.h"
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
+#include "tree-inline.h"
+#include "intl.h"
 
 /* Walker callback that replaces all FUNCTION_DECL of a function that's
    going to be versioned.  */
@@ -86,7 +88,7 @@
   if (!idecl)
     {
       error_at (DECL_SOURCE_LOCATION (node->decl),
-		"default target_clones attribute was not set");
+		"default %<target_clones%> attribute was not set");
       return;
     }
 
@@ -142,16 +144,42 @@
 		  if (ref->referring->decl != resolver_decl)
 		    walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
 		}
+
+	      symtab_node *source = ref->referring;
+	      ref->remove_reference ();
+	      source->create_reference (inode, IPA_REF_ADDR);
+	    }
+	  else if (ref->use == IPA_REF_ALIAS)
+	    {
+	      symtab_node *source = ref->referring;
+	      ref->remove_reference ();
+	      source->create_reference (inode, IPA_REF_ALIAS);
+	      source->add_to_same_comdat_group (inode);
 	    }
 	  else
 	    gcc_unreachable ();
 	}
     }
 
-  TREE_PUBLIC (node->decl) = 0;
   symtab->change_decl_assembler_name (node->decl,
 				      clone_function_name (node->decl,
 							   "default"));
+
+  /* FIXME: copy of cgraph_node::make_local that should be cleaned up
+	    in next stage1.  */
+  node->make_decl_local ();
+  node->set_section (NULL);
+  node->set_comdat_group (NULL);
+  node->externally_visible = false;
+  node->forced_by_abi = false;
+  node->set_section (NULL);
+  node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
+			|| node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
+		       && !flag_incremental_link);
+  node->resolution = LDPR_PREVAILING_DEF_IRONLY;
+
+  DECL_ARTIFICIAL (node->decl) = 1;
+  node->force_output = true;
 }
 
 /* Return length of attribute names string,
@@ -203,26 +231,30 @@
 }
 
 /* Return number of attributes separated by comma and put them into ARGS.
-   If there is no DEFAULT attribute return -1.  */
+   If there is no DEFAULT attribute return -1.  If there is an empty
+   string in attribute return -2.  */
 
 static int
-separate_attrs (char *attr_str, char **attrs)
+separate_attrs (char *attr_str, char **attrs, int attrnum)
 {
   int i = 0;
-  bool has_default = false;
+  int default_count = 0;
 
   for (char *attr = strtok (attr_str, ",");
        attr != NULL; attr = strtok (NULL, ","))
     {
       if (strcmp (attr, "default") == 0)
 	{
-	  has_default = true;
+	  default_count++;
 	  continue;
 	}
       attrs[i++] = attr;
     }
-  if (!has_default)
+  if (default_count == 0)
     return -1;
+  else if (i + default_count < attrnum)
+    return -2;
+
   return i;
 }
 
@@ -308,7 +340,24 @@
     {
       warning_at (DECL_SOURCE_LOCATION (node->decl),
 		  0,
-		  "single target_clones attribute is ignored");
+		  "single %<target_clones%> attribute is ignored");
+      return false;
+    }
+
+  if (node->definition
+      && !tree_versionable_function_p (node->decl))
+    {
+      auto_diagnostic_group d;
+      error_at (DECL_SOURCE_LOCATION (node->decl),
+		"clones for %<target_clones%> attribute cannot be created");
+      const char *reason = NULL;
+      if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
+	reason = G_("function %q+F can never be copied "
+		    "because it has %<noclone%> attribute");
+      else
+	reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
+      if (reason)
+	inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
       return false;
     }
 
@@ -316,7 +365,7 @@
   int attrnum = get_attr_str (arglist, attr_str);
   char **attrs = XNEWVEC (char *, attrnum);
 
-  attrnum = separate_attrs (attr_str, attrs);
+  attrnum = separate_attrs (attr_str, attrs, attrnum);
   if (attrnum == -1)
     {
       error_at (DECL_SOURCE_LOCATION (node->decl),
@@ -325,6 +374,14 @@
       XDELETEVEC (attr_str);
       return false;
     }
+  else if (attrnum == -2)
+    {
+      error_at (DECL_SOURCE_LOCATION (node->decl),
+		"an empty string cannot be in %<target_clones%> attribute");
+      XDELETEVEC (attrs);
+      XDELETEVEC (attr_str);
+      return false;
+    }
 
   cgraph_function_version_info *decl1_v = NULL;
   cgraph_function_version_info *decl2_v = NULL;
@@ -394,18 +451,69 @@
   return ret;
 }
 
+/* When NODE is a target clone, consider all callees and redirect
+   to a clone with equal target attributes.  That prevents multiple
+   multi-versioning dispatches and a call-chain can be optimized.  */
+
+static void
+redirect_to_specific_clone (cgraph_node *node)
+{
+  cgraph_function_version_info *fv = node->function_version ();
+  if (fv == NULL)
+    return;
+
+  tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
+  if (attr_target == NULL_TREE)
+    return;
+
+  /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
+  for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
+    {
+      cgraph_function_version_info *fv2 = e->callee->function_version ();
+      if (!fv2)
+	continue;
+
+      tree attr_target2 = lookup_attribute ("target",
+					    DECL_ATTRIBUTES (e->callee->decl));
+
+      /* Function is not calling proper target clone.  */
+      if (!attribute_list_equal (attr_target, attr_target2))
+	{
+	  while (fv2->prev != NULL)
+	    fv2 = fv2->prev;
+
+	  /* Try to find a clone with equal target attribute.  */
+	  for (; fv2 != NULL; fv2 = fv2->next)
+	    {
+	      cgraph_node *callee = fv2->this_node;
+	      attr_target2 = lookup_attribute ("target",
+					       DECL_ATTRIBUTES (callee->decl));
+	      if (attribute_list_equal (attr_target, attr_target2))
+		{
+		  e->redirect_callee (callee);
+		  e->redirect_call_stmt_to_callee ();
+		  break;
+		}
+	    }
+	}
+    }
+}
+
 static unsigned int
 ipa_target_clone (void)
 {
   struct cgraph_node *node;
+  auto_vec<cgraph_node *> to_dispatch;
 
-  bool target_clone_pass = false;
   FOR_EACH_FUNCTION (node)
-    target_clone_pass |= expand_target_clones (node, node->definition);
+    if (expand_target_clones (node, node->definition))
+      to_dispatch.safe_push (node);
 
-  if (target_clone_pass)
-    FOR_EACH_FUNCTION (node)
-      create_dispatcher_calls (node);
+  for (unsigned i = 0; i < to_dispatch.length (); i++)
+    create_dispatcher_calls (to_dispatch[i]);
+
+  FOR_EACH_FUNCTION (node)
+    redirect_to_specific_clone (node);
 
   return 0;
 }