ipa-polymorphic-call.c (walk_ssa_copies): Recognize NULL pointer checks.
authorJan Hubicka <hubicka@ucw.cz>
Sun, 5 Oct 2014 05:02:19 +0000 (07:02 +0200)
committerJan Hubicka <hubicka@gcc.gnu.org>
Sun, 5 Oct 2014 05:02:19 +0000 (05:02 +0000)
* ipa-polymorphic-call.c (walk_ssa_copies): Recognize
NULL pointer checks.
(ipa_polymorphic_call_context::get_dynamic_type): Return true
if type doesn't change.
* cgraph.h (cgraph_indirect_call_info): New flag.
* cgraph.c (cgraph_node::create_indirect_edge): Initialize it.
(cgraph_node::dump): Dump it.
* ipa-prop.c (ipa_analyze_call_uses):  Ignore return valud
of context.get_dynamic_type.
(ipa_make_edge_direct_to_target): Do not speculate
edge that is already speuclative.
(try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not
speculate to __builtin_unreachable
(ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream
vptr_changed.
* ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed.
* g++.dg/ipa/devirt-47.C: New testcase.

From-SVN: r215898

gcc/ChangeLog
gcc/cgraph.c
gcc/cgraph.h
gcc/ipa-cp.c
gcc/ipa-polymorphic-call.c
gcc/ipa-prop.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ipa/devirt-47.C [new file with mode: 0644]

index d33827012e56822cca55e5a2e3d5d6d77ca38c25..3bf6de1581c8c7b86f55bb45e0bf77d9c4a6a34d 100644 (file)
@@ -1,3 +1,22 @@
+2014-10-04  Jan Hubicka  <hubicka@ucw.cz>
+
+       * ipa-polymorphic-call.c (walk_ssa_copies): Recognize
+       NULL pointer checks.
+       (ipa_polymorphic_call_context::get_dynamic_type): Return true
+       if type doesn't change.
+       * cgraph.h (cgraph_indirect_call_info): New flag.
+       * cgraph.c (cgraph_node::create_indirect_edge): Initialize it.
+       (cgraph_node::dump): Dump it.
+       * ipa-prop.c (ipa_analyze_call_uses):  Ignore return valud
+       of context.get_dynamic_type.
+       (ipa_make_edge_direct_to_target): Do not speculate
+       edge that is already speuclative.
+       (try_make_edge_direct_virtual_call): Use VPTR_CHANGED; Do not
+       speculate to __builtin_unreachable
+       (ipa_write_indirect_edge_info, ipa_read_indirect_edge_info): Stream
+       vptr_changed.
+       * ipa-cp.c (ipa_get_indirect_edge_target_1): Use vptr_changed.
+
 2014-10-04  Jan Hubicka  <hubicka@ucw.cz>
 
        * ipa-prop.c (ipa_compute_jump_functions_for_edge): Call
index a46e1883fd0d24264aeb747c99e99f5f7f2ab6ce..38dc7e663fa12b72cb2858f6035dfed1d6092cf4 100644 (file)
@@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimple call_stmt, int ecf_flags,
 
   edge->indirect_info = cgraph_allocate_init_indirect_info ();
   edge->indirect_info->ecf_flags = ecf_flags;
+  edge->indirect_info->vptr_changed = true;
 
   /* Record polymorphic call info.  */
   if (compute_indirect_info
@@ -1988,6 +1989,8 @@ cgraph_node::dump (FILE *f)
                    edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
                    edge->indirect_info->by_ref ? "passed by reference":"",
                    (int)edge->indirect_info->offset);
+         if (edge->indirect_info->vptr_changed)
+           fprintf (f, " (vptr maybe changed)");
        }
       fprintf (f, "\n");
       if (edge->indirect_info->polymorphic)
index 5ed078acfa9aac563c26bc94c1b9a1f29bc1d38c..20b5c4e4805cb08d30cd20fbccf1a137c350c3f0 100644 (file)
@@ -1393,6 +1393,9 @@ struct GTY(()) cgraph_indirect_call_info
   /* When the previous bit is set, this one determines whether the destination
      is loaded from a parameter passed by reference. */
   unsigned by_ref : 1;
+  /* For polymorphic calls this specify whether the virtual table pointer
+     may have changed in between function entry and the call.  */
+  unsigned vptr_changed : 1;
 };
 
 struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"))) cgraph_edge {
index 657536c9c8ee09f30f02dcfa26ffc56bbabba886..a3be16f8bf45b63fd6bb1f2a51e4bacd20224bbf 100644 (file)
@@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
   t = NULL;
 
   /* Try to work out value of virtual table pointer value in replacemnets.  */
-  if (!t && agg_reps && !ie->indirect_info->by_ref)
+  if (!t && agg_reps && !ie->indirect_info->by_ref
+      && !ie->indirect_info->vptr_changed)
     {
       while (agg_reps)
        {
@@ -1578,7 +1579,8 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
   /* Try to work out value of virtual table pointer value in known
      aggregate values.  */
   if (!t && known_aggs.length () > (unsigned int) param_index
-      && !ie->indirect_info->by_ref)
+      && !ie->indirect_info->by_ref
+      && !ie->indirect_info->vptr_changed)
     {
        struct ipa_agg_jump_function *agg;
        agg = known_aggs[param_index];
index ecbd78ce33d415a30c73bbc7c9bf5e019c86b179..74226f21a87132a3859b2a6b3ef080f2de4d768c 100644 (file)
@@ -760,11 +760,37 @@ walk_ssa_copies (tree op)
   while (TREE_CODE (op) == SSA_NAME
         && !SSA_NAME_IS_DEFAULT_DEF (op)
         && SSA_NAME_DEF_STMT (op)
-        && gimple_assign_single_p (SSA_NAME_DEF_STMT (op)))
+        && (gimple_assign_single_p (SSA_NAME_DEF_STMT (op))
+            || gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI))
     {
-      if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
-       return op;
-      op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
+      /* Special case
+        if (ptr == 0)
+          ptr = 0;
+        else
+          ptr = ptr.foo;
+        This pattern is implicitly produced for casts to non-primary
+        bases.  When doing context analysis, we do not really care
+        about the case pointer is NULL, becuase the call will be
+        undefined anyway.  */
+      if (gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_PHI)
+       {
+         gimple phi = SSA_NAME_DEF_STMT (op);
+
+         if (gimple_phi_num_args (phi) != 2)
+           return op;
+         if (integer_zerop (gimple_phi_arg_def (phi, 0)))
+           op = gimple_phi_arg_def (phi, 1);
+         else if (integer_zerop (gimple_phi_arg_def (phi, 1)))
+           op = gimple_phi_arg_def (phi, 0);
+         else
+           return op;
+       }
+      else
+       {
+         if (gimple_assign_load_p (SSA_NAME_DEF_STMT (op)))
+           return op;
+         op = gimple_assign_rhs1 (SSA_NAME_DEF_STMT (op));
+       }
       STRIP_NOPS (op);
     }
   return op;
@@ -1371,6 +1397,8 @@ check_stmt_for_type_change (ao_ref *ao ATTRIBUTE_UNUSED, tree vdef, void *data)
    is set), try to walk memory writes and find the actual construction of the
    instance.
 
+   Return true if memory is unchanged from function entry.
+
    We do not include this analysis in the context analysis itself, because
    it needs memory SSA to be fully built and the walk may be expensive.
    So it is not suitable for use withing fold_stmt and similar uses.  */
@@ -1615,7 +1643,7 @@ ipa_polymorphic_call_context::get_dynamic_type (tree instance,
               function_entry_reached ? " (multiple types encountered)" : "");
     }
 
-  return true;
+  return false;
 }
 
 /* See if speculation given by SPEC_OUTER_TYPE, SPEC_OFFSET and SPEC_MAYBE_DERIVED_TYPE
index d5ecea413039c5bce82235b93896d6ceb27eab49..80acdcc21bb5ab5010bbc62b41a7f927d3285454 100644 (file)
@@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_info *fbi, gimple call)
       gcc_checking_assert (cs->indirect_info->otr_token
                           == tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
 
-      if (context.get_dynamic_type (instance,
-                                   OBJ_TYPE_REF_OBJECT (target),
-                                   obj_type_ref_class (target), call))
-       cs->indirect_info->context = context;
+      context.get_dynamic_type (instance,
+                               OBJ_TYPE_REF_OBJECT (target),
+                               obj_type_ref_class (target), call);
+      cs->indirect_info->context = context;
     }
 
   if (TREE_CODE (target) == SSA_NAME)
@@ -2878,6 +2878,38 @@ ipa_make_edge_direct_to_target (struct cgraph_edge *ie, tree target,
       callee = cgraph_node::get_create (target);
     }
 
+  /* If the edge is already speculated.  */
+  if (speculative && ie->speculative)
+    {
+      struct cgraph_edge *e2;
+      struct ipa_ref *ref;
+      ie->speculative_call_info (e2, ie, ref);
+      if (e2->callee->ultimate_alias_target ()
+         != callee->ultimate_alias_target ())
+       {
+         if (dump_file)
+           fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
+                    "(%s/%i -> %s/%i) but the call is already speculated to %s/%i. Giving up.\n",
+                    xstrdup (ie->caller->name ()),
+                    ie->caller->order,
+                    xstrdup (callee->name ()),
+                    callee->order,
+                    xstrdup (e2->callee->name ()),
+                    e2->callee->order);
+       }
+      else
+       {
+         if (dump_file)
+           fprintf (dump_file, "ipa-prop: Discovered call to a speculative target "
+                    "(%s/%i -> %s/%i) this agree with previous speculation.\n",
+                    xstrdup (ie->caller->name ()),
+                    ie->caller->order,
+                    xstrdup (callee->name ()),
+                    callee->order);
+       }
+      return NULL;
+    }
+
   if (!dbg_cnt (devirt))
     return NULL;
 
@@ -3127,17 +3159,17 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
 
       ctx.offset_by (ie->indirect_info->offset);
 
-      /* TODO: We want to record if type change happens.  
-        Old code did not do that that seems like a bug.  */
-      ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
-                                       ie->indirect_info->otr_type);
+      if (ie->indirect_info->vptr_changed)
+       ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
+                                         ie->indirect_info->otr_type);
 
       updated = ie->indirect_info->context.combine_with
                  (ctx, ie->indirect_info->otr_type);
     }
 
   /* Try to do lookup via known virtual table pointer value.  */
-  if (!ie->indirect_info->by_ref)
+  if (!ie->indirect_info->by_ref
+      && (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively))
     {
       tree vtable;
       unsigned HOST_WIDE_INT offset;
@@ -3146,16 +3178,24 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
                                           true);
       if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
        {
-         target = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
+         t = gimple_get_virt_method_for_vtable (ie->indirect_info->otr_token,
                                                      vtable, offset);
-         if (target)
+         if (t)
            {
-             if ((TREE_CODE (TREE_TYPE (target)) == FUNCTION_TYPE
-                  && DECL_FUNCTION_CODE (target) == BUILT_IN_UNREACHABLE)
+             if ((TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE
+                  && DECL_FUNCTION_CODE (t) == BUILT_IN_UNREACHABLE)
                  || !possible_polymorphic_call_target_p
-                      (ie, cgraph_node::get (target)))
-               target = ipa_impossible_devirt_target (ie, target);
-             return ipa_make_edge_direct_to_target (ie, target);
+                      (ie, cgraph_node::get (t)))
+               {
+                 /* Do not speculate builtin_unreachable, it is stpid!  */
+                 if (!ie->indirect_info->vptr_changed)
+                   target = ipa_impossible_devirt_target (ie, target);
+               }
+             else
+               {
+                 target = t;
+                 speculative = ie->indirect_info->vptr_changed;
+               }
            }
        }
     }
@@ -3188,7 +3228,7 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
          else
            target = ipa_impossible_devirt_target (ie, NULL_TREE);
        }
-      else if (flag_devirtualize_speculatively
+      else if (!target && flag_devirtualize_speculatively
               && !ie->speculative && ie->maybe_hot_p ())
        {
          cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type,
@@ -3222,7 +3262,11 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
   if (target)
     {
       if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
-       target = ipa_impossible_devirt_target (ie, target);
+       {
+         if (!speculative)
+           return NULL;
+         target = ipa_impossible_devirt_target (ie, target);
+       }
       return ipa_make_edge_direct_to_target (ie, target, speculative);
     }
   else
@@ -4801,6 +4845,7 @@ ipa_write_indirect_edge_info (struct output_block *ob,
   bp_pack_value (&bp, ii->agg_contents, 1);
   bp_pack_value (&bp, ii->member_ptr, 1);
   bp_pack_value (&bp, ii->by_ref, 1);
+  bp_pack_value (&bp, ii->vptr_changed, 1);
   streamer_write_bitpack (&bp);
   if (ii->agg_contents || ii->polymorphic)
     streamer_write_hwi (ob, ii->offset);
@@ -4832,6 +4877,7 @@ ipa_read_indirect_edge_info (struct lto_input_block *ib,
   ii->agg_contents = bp_unpack_value (&bp, 1);
   ii->member_ptr = bp_unpack_value (&bp, 1);
   ii->by_ref = bp_unpack_value (&bp, 1);
+  ii->vptr_changed = bp_unpack_value (&bp, 1);
   if (ii->agg_contents || ii->polymorphic)
     ii->offset = (HOST_WIDE_INT) streamer_read_hwi (ib);
   else
index 39af721082980c1e54a385543a032a089df05f41..ce04eacf5c06ad635b104f5b293c638737be7991 100644 (file)
@@ -1,3 +1,7 @@
+2014-10-04  Jan Hubicka  <hubicka@ucw.cz>
+
+       * g++.dg/ipa/devirt-47.C: New testcase.
+
 2014-10-04  Jan Hubicka  <hubicka@ucw.cz>
 
        PR ipa/61144
diff --git a/gcc/testsuite/g++.dg/ipa/devirt-47.C b/gcc/testsuite/g++.dg/ipa/devirt-47.C
new file mode 100644 (file)
index 0000000..85f7b63
--- /dev/null
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining -fdump-tree-optimized" } */
+struct A {
+   virtual int foo(){return 1;}
+};
+struct B {
+   virtual int bar(){return 4;}
+};
+struct C:B,A {
+   virtual int foo(){return 2;}
+};
+static void
+test (struct A *a)
+{
+  if (a->foo() != 2)
+   __builtin_abort ();
+}
+int
+m()
+{
+  struct A *a = new C;
+  test (a);
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target\[^\\n\]*C::_ZTh" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized"  } } */
+/* FIXME: We ought to inline thunk.  */
+/* { dg-final { scan-ipa-dump "C::_ZThn" "optimized"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
+/* { dg-final { cleanup-ipa-dump "optimized" } } */