diff mbox

Fix ICE in ipa_make_edge_direct_to_target

Message ID 20130220155311.GA22974@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka Feb. 20, 2013, 3:53 p.m. UTC
Hi,
in the testcase bellow we get an ICE in ipa_make_edge_direct_to_target.  There
is virtual call that gets devirtualized only while inlining functions called
once.  At this point however we already removed bodies for virtual functions
from the callgraph, so we need to update it and re-create its node.

This is in fact just a special case where we need to invent new cgraph node for
devitualization target.  This patch should handle also the case when we invent
direct call to a function that is not otherwise used in the current TU.

Bootstrapped/regtested x86_64-linux, comitted.

2013-02-20  Jan Hubicka  <jh@suse.cz>

	PR tree-optimization/56265
	* ipa-prop.c (ipa_make_edge_direct_to_target): Fixup callgraph when target is
	referenced for firs ttime.

	PR tree-optimization/56265
	* testsuite/g++.dg/ipa/devirt-11.C: New testcase.
diff mbox

Patch

Index: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 196176)
+++ ipa-prop.c	(working copy)
@@ -2100,10 +2100,65 @@  ipa_make_edge_direct_to_target (struct c
   if (TREE_CODE (target) == ADDR_EXPR)
     target = TREE_OPERAND (target, 0);
   if (TREE_CODE (target) != FUNCTION_DECL)
-    return NULL;
+    {
+      target = canonicalize_constructor_val (target, NULL);
+      if (!target || TREE_CODE (target) != FUNCTION_DECL)
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "ipa-prop: Discovered direct call to non-function"
+				" in (%s/%i).\n",
+		     cgraph_node_name (ie->caller), ie->caller->uid);
+	  return NULL;
+	}
+    }
   callee = cgraph_get_node (target);
-  if (!callee)
-    return NULL;
+
+  /* Because may-edges are not explicitely represented and vtable may be external,
+     we may create the first reference to the object in the unit.  */
+  if (!callee || callee->global.inlined_to)
+    {
+      struct cgraph_node *first_clone = callee;
+
+      /* We are better to ensure we can refer to it.
+	 In the case of static functions we are out of luck, since we already	
+	 removed its body.  In the case of public functions we may or may
+	 not introduce the reference.  */
+      if (!canonicalize_constructor_val (target, NULL)
+	  || !TREE_PUBLIC (target))
+	{
+	  if (dump_file)
+	    fprintf (dump_file, "ipa-prop: Discovered call to a known target "
+		     "(%s/%i -> %s/%i) but can not refer to it. Giving up.\n",
+		     xstrdup (cgraph_node_name (ie->caller)), ie->caller->uid,
+		     xstrdup (cgraph_node_name (ie->callee)), ie->callee->uid);
+	  return NULL;
+	}
+
+      /* Create symbol table node.  Even if inline clone exists, we can not take
+	 it as a target of non-inlined call.  */
+      callee = cgraph_create_node (target);
+
+      /* OK, we previously inlined the function, then removed the offline copy and
+	 now we want it back for external call.  This can happen when devirtualizing
+	 while inlining function called once that happens after extern inlined and
+	 virtuals are already removed.  In this case introduce the external node
+	 and make it available for call.  */
+      if (first_clone)
+	{
+	  first_clone->clone_of = callee;
+	  callee->clones = first_clone;
+	  symtab_prevail_in_asm_name_hash ((symtab_node)callee);
+	  symtab_insert_node_to_hashtable ((symtab_node)callee);
+	  if (dump_file)
+	    fprintf (dump_file, "ipa-prop: Introduced new external node "
+		     "(%s/%i) and turned into root of the clone tree.\n",
+		     xstrdup (cgraph_node_name (callee)), callee->uid);
+	}
+      else if (dump_file)
+	fprintf (dump_file, "ipa-prop: Introduced new external node "
+		 "(%s/%i).\n",
+		 xstrdup (cgraph_node_name (callee)), callee->uid);
+    }
   ipa_check_create_node_params ();
 
   /* We can not make edges to inline clones.  It is bug that someone removed
Index: testsuite/g++.dg/ipa/devirt-11.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-11.C	(revision 0)
+++ testsuite/g++.dg/ipa/devirt-11.C	(revision 0)
@@ -0,0 +1,50 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-ipa-inline" } */
+int baz ();
+struct A
+{
+  virtual int fn2 () = 0;
+  virtual int *fn3 ();
+  double *fn4 ();
+  int fn5 (int);
+  template <class T>
+  void fn1 (A &, T) { fn3 (); fn4 (); fn2 (); }
+};
+struct B : A
+{
+  int fn2 () { return 6; }
+  void fn3 (int, double);
+  B (bool = true);
+  B (int, int);
+};
+template <typename T>
+void
+foo (B &x, A &y, A &z)
+{
+  y.fn2 ();
+  z.fn2 ();
+  int i = baz ();
+  int j = (y.fn3 ())[i];
+  x.fn3 (j, (y.fn4 ())[i] + (z.fn4 ())[z.fn5 (j)]);
+}
+inline B
+operator+ (A &y, A &z)
+{
+  B x;
+  foo<int> (x, y, z);
+  return x;
+}
+void
+bar ()
+{
+  B a, b, c (4, 0), d;
+  a.fn1 (b, .6);
+  baz ();
+  c + d;
+}
+/* While inlining function called once we should devirtualize a new call to fn2
+   and two to fn3. While doing so the new symbol for fn2 needs to be
+   introduced.  */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 3 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "and turned into root of the clone tree" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */