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  <j...@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.

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" } } */

Reply via email to