This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Track dynamic type changes


Hi,
this patch makes ipa_polymorphic_call_context::get_dynamic_type to
track if vptr has changed in between function entry and polymorphic
call.

The patch also add logic skipping if (a==0) checks in multiple
inheritance so I can construct testcase more easily.

I got my SVN tree into an inconsistent state yesterday by stopping in
middle of commit.  The patch went in in two parts, so i am attaching 
both changes. Quite few of devirt-4*.C was broken overnight, I apologize
for that.

Honza

	* 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.

	* ipa-polymorphic-call.c (possible_placement_new): Fix condition
	on size.
	(ipa_polymorphic_call_context::restrict_to_inner_type): Do not walk
	into vptr pointer.
	(ipa_polymorphic_call_context::dump): Fix formating.
	* ipa-prop.c (ipa_analyze_call_uses): Compute vptr_changed.

	* g++.dg/ipa/devirt-42.C: Update template.
	* g++.dg/ipa/devirt-44.C: Update template.
	* g++.dg/ipa/devirt-45.C: Update template.
	* g++.dg/ipa/devirt-46.C: Update template.
	* g++.dg/ipa/devirt-47.C: Update template.
	* g++.dg/ipa/devirt-48.C: New testcase.
Index: ipa-polymorphic-call.c
===================================================================
--- ipa-polymorphic-call.c	(revision 215890)
+++ ipa-polymorphic-call.c	(working copy)
@@ -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 A
    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_dynami
 	       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: cgraph.h
===================================================================
--- cgraph.h	(revision 215890)
+++ cgraph.h	(working copy)
@@ -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: cgraph.c
===================================================================
--- cgraph.c	(revision 215890)
+++ cgraph.c	(working copy)
@@ -883,6 +883,7 @@ cgraph_node::create_indirect_edge (gimpl
 
   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: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 215890)
+++ ipa-prop.c	(working copy)
@@ -2371,10 +2371,10 @@ ipa_analyze_call_uses (struct func_body_
       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 c
       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 (struc
 
       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 (struc
 					   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 (struc
 	  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 +3264,11 @@ try_make_edge_direct_virtual_call (struc
   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 +4847,7 @@ ipa_write_indirect_edge_info (struct out
   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 +4879,7 @@ ipa_read_indirect_edge_info (struct lto_
   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: ipa-cp.c
===================================================================
--- ipa-cp.c	(revision 215890)
+++ ipa-cp.c	(working copy)
@@ -1560,7 +1560,8 @@ ipa_get_indirect_edge_target_1 (struct c
   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 c
   /* 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: testsuite/g++.dg/ipa/devirt-47.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-47.C	(revision 0)
+++ testsuite/g++.dg/ipa/devirt-47.C	(revision 0)
@@ -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" } } */


Index: ipa-polymorphic-call.c
===================================================================
--- ipa-polymorphic-call.c	(revision 215901)
+++ ipa-polymorphic-call.c	(working copy)
@@ -97,7 +97,7 @@ possible_placement_new (tree type, tree
 	      || !tree_fits_shwi_p (TYPE_SIZE (type))
 	      || (cur_offset
 		  + (expected_type ? tree_to_uhwi (TYPE_SIZE (expected_type))
-		     : 1)
+		     : GET_MODE_BITSIZE (Pmode))
 		  <= tree_to_uhwi (TYPE_SIZE (type)))));
 }
 
@@ -278,6 +278,14 @@ ipa_polymorphic_call_context::restrict_t
 	      pos = int_bit_position (fld);
 	      if (pos > (unsigned HOST_WIDE_INT)cur_offset)
 		continue;
+
+	      /* Do not consider vptr itself.  Not even for placement new.  */
+	      if (!pos && DECL_ARTIFICIAL (fld)
+		  && POINTER_TYPE_P (TREE_TYPE (fld))
+		  && TYPE_BINFO (type)
+		  && polymorphic_type_binfo_p (TYPE_BINFO (type)))
+		continue;
+
 	      if (!DECL_SIZE (fld) || !tree_fits_uhwi_p (DECL_SIZE (fld)))
 		goto no_useful_type_info;
 	      size = tree_to_uhwi (DECL_SIZE (fld));
@@ -583,7 +591,7 @@ ipa_polymorphic_call_context::dump (FILE
 {
   fprintf (f, "    ");
   if (invalid)
-    fprintf (f, "Call is known to be undefined\n");
+    fprintf (f, "Call is known to be undefined");
   else
     {
       if (useless_p ())
Index: testsuite/g++.dg/ipa/devirt-42.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-42.C	(revision 215901)
+++ testsuite/g++.dg/ipa/devirt-42.C	(working copy)
@@ -26,13 +26,9 @@ main()
 /* Inlining everything into main makes type clear from type of variable b.
    However devirtualization is also possible for offline copy of A::barbar. Invoking
    B's barbar makes it clear the type is at least B and B is an anonymous
-   namespace type and therefore we know it has no derivations.
-   FIXME: Currently we devirtualize speculatively only because we do not track
-   dynamic type changes well.  */
-/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  } } */
-/* { dg-final { scan-ipa-dump-times "Outer types match, merging flags" 2 "inline"  } } */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline"  } } */
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline"  } } */
+   namespace type and therefore we know it has no derivations.  */
+/* { dg-final { scan-ipa-dump-times "First type is base of second" 3 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "inline"  } } */
 
 /* Verify that speculation is optimized by late optimizers.  */
 /* { dg-final { scan-ipa-dump-times "return 2" 2 "optimized"  } } */
Index: testsuite/g++.dg/ipa/devirt-44.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-44.C	(revision 215901)
+++ testsuite/g++.dg/ipa/devirt-44.C	(working copy)
@@ -27,7 +27,6 @@ main()
    Check that we handle that.  */
 
 /* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline"  } } */
-/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-46.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-46.C	(revision 215901)
+++ testsuite/g++.dg/ipa/devirt-46.C	(working copy)
@@ -20,7 +20,7 @@ m()
   return 0;
 }
 
-/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target\[^\\n\]*B::foo" 1 "inline"  } } */
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline"  } } */
 /* { dg-final { scan-ipa-dump-not "OBJ_TYPE_REF" "optimized"  } } */
 /* { dg-final { scan-ipa-dump-not "abort" "optimized"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-48.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-48.C	(revision 0)
+++ testsuite/g++.dg/ipa/devirt-48.C	(revision 0)
@@ -0,0 +1,32 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fno-ipa-cp -fdump-ipa-inline-details -fno-early-inlining" } */
+struct A {
+   virtual int foo(){return 1;}
+};
+struct B:A {
+   virtual int foo(){return 2;}
+   int callfoo(){foo();}
+};
+struct C:A {
+   virtual int foo(){return 3;}
+};
+struct D:B {
+   virtual int foo(){return 4;}
+   int callfoo(){foo();}
+};
+static void
+test (struct A *a)
+{
+  if (a->foo() != 2)
+   __builtin_abort ();
+}
+int
+m()
+{
+  struct A *a = new C;
+  static_cast<B*>(a)->callfoo();
+  return 0;
+}
+
+/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*__builtin_unreachable" 1 "inline"  } } */
+/* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-45.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-45.C	(revision 215901)
+++ testsuite/g++.dg/ipa/devirt-45.C	(working copy)
@@ -37,7 +37,6 @@ main()
 }
 
 /* One invocation is A::foo () other is B::foo () even though the type is destroyed and rebuilt in test() */
-/* { dg-final { scan-ipa-dump "(maybe in construction)" "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline"  } } */
 /* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline"  } } */
 /* { dg-final { cleanup-ipa-dump "inline" } } */
Index: testsuite/g++.dg/ipa/devirt-47.C
===================================================================
--- testsuite/g++.dg/ipa/devirt-47.C	(revision 215901)
+++ testsuite/g++.dg/ipa/devirt-47.C	(working copy)
@@ -23,7 +23,7 @@ m()
   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-times "Discovered a virtual call to a known 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"  } } */
Index: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 215901)
+++ ipa-prop.c	(working copy)
@@ -2371,9 +2371,10 @@ ipa_analyze_call_uses (struct func_body_
       gcc_checking_assert (cs->indirect_info->otr_token
 			   == tree_to_shwi (OBJ_TYPE_REF_TOKEN (target)));
 
-      context.get_dynamic_type (instance,
-				OBJ_TYPE_REF_OBJECT (target),
-				obj_type_ref_class (target), call);
+      cs->indirect_info->vptr_changed
+	= !context.get_dynamic_type (instance,
+				     OBJ_TYPE_REF_OBJECT (target),
+				     obj_type_ref_class (target), call);
       cs->indirect_info->context = context;
     }
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]