diff mbox

Fix OBJ_TYPE_REF handling in ipa-cp

Message ID 20130831124425.GB12966@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka Aug. 31, 2013, 12:44 p.m. UTC
> > Bootstrapped/regtesed x86_64-linux. Martin, please can you review the change?
> > 
> > 	* ipa-prop.c (ipa_set_jf_known_type): Check that component type
> > 	is a record type with BINFO.
> > 	(detect_type_change_ssa):  Add comp_type argument.
> > 	(compute_complex_assign_jump_func): Add param_type argument; pass
> > 	it down to detect_type_change_ssa.
> > 	(compute_known_type_jump_func): Add expected_type parameter.
> > 	Do not bother tracking a non-polymorphic type.
> > 	(ipa_get_callee_param_type): New function.
> > 	(ipa_compute_jump_functions_for_edge): Pass down calle parm types.
> > 	(ipa_analyze_virtual_call_uses): Use class typee as argument
> > 	of detect_type_change_1.
> > 	(ipa_intraprocedural_devirtualization): Pass down class type.
> 
> Hopefully I'll get rid of the component_types in the jump functions
> and most of this won't be necesary.  Meanwhile, this is OK.
Indeed, I hope these will go.  I updated the patch to tree after your changes
and fixed the remaining uses of detect_type_change so detect_type_change_1 can
go completely.

Bootstrapped/regtested x86_64-linux and also tested with Firefox build,
comitted.

Interestingly, we now have smaller static counts of devirtualization on firefox
than two weeks ago.  It is not caused by this patch however.  While looking for
possible causes I also noticed we don't seem to handle invisible references
well.  Consider:

class A {virtual void b(void) { };};
class B : public A {virtual void b(void) { };};

void
q (class A a);
void
qq (class A *a);
void
t (class A a)
{
  q(a);
  qq(&a);
}
void
m()
{
  class B b;
  t(b);
}

With -O2 -fno-early-inling -flto I get:

m() ()
{
  struct B b;
  struct A D.2267;

  <bb 2>:
  B::B (&b);
  A::A (&D.2267, &b.D.2212);
  t (&D.2267);
  D.2267 ={v} {CLOBBER};
  b ={v} {CLOBBER};
  return;

}

t(A) (struct A & restrict a)
{ 
  struct A D.2242;

  <bb 2>:
  A::A (&D.2242, a_2(D));
  q (&D.2242);
  D.2242 ={v} {CLOBBER};
  qq (a_2(D));
  return;

}

I.e. t is called with invisible reference.
Now we get:
Jump functions:
  Jump functions of caller  m()/12:
    callsite  m()/12 -> t(A)/5 :
       param 0: UNKNOWN
    callsite  m()/12 -> A::A(A const&)/4 :
       param 0: KNOWN TYPE: base  struct A, offset 0, component struct A
       param 1: KNOWN TYPE: base  struct B, offset 0, component const struct A
    callsite  m()/12 -> B::B()/11 :
       param 0: KNOWN TYPE: base  struct B, offset 0, component struct B
  Jump functions of caller  t(A)/5:
    callsite  t(A)/5 -> qq(A*)/20 :
       param 0: PASS THROUGH: 0, op nop_expr, type_preserved
    callsite  t(A)/5 -> q(A)/19 :
       param 0: UNKNOWN
    callsite  t(A)/5 -> A::A(A const&)/4 : 
       param 0: KNOWN TYPE: base  struct A, offset 0, component struct A
       param 1: PASS THROUGH: 0, op nop_expr, type_preserved

I think m()->t() has no type and t->qq is pass thorugh. So we miss the
fact that t->qq is known type of A.
Do we need jump function that is PASS THROGH & KNOWN_TYPE at once?

Honza


	* ipa-prop.c (ipa_set_jf_known_type): Check that we add
	only records.
	(detect_type_change_1): Rename to ...
	(detect_type_change): ... this one; early return on non-polymorphic
	types.
	(detect_type_change_ssa): Add comp_type parameter; update	
	use of detect_type_change.
	(compute_complex_assign_jump_func): Add param_type parameter;
	update use of detect_type_change_ssa.
	(compute_complex_ancestor_jump_func): Likewise.
	(ipa_get_callee_param_type): New function.
	(ipa_compute_jump_functions_for_edge): Compute parameter type;
	update calls to the jump function computation functions.
diff mbox

Patch

Index: ipa-prop.c
===================================================================
--- ipa-prop.c	(revision 202092)
+++ ipa-prop.c	(working copy)
@@ -371,6 +371,8 @@  static void
 ipa_set_jf_known_type (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
 		       tree base_type, tree component_type)
 {
+  gcc_assert (TREE_CODE (component_type) == RECORD_TYPE
+	      && TYPE_BINFO (component_type));
   jfunc->type = IPA_JF_KNOWN_TYPE;
   jfunc->value.known_type.offset = offset,
   jfunc->value.known_type.base_type = base_type;
@@ -633,13 +635,16 @@  check_stmt_for_type_change (ao_ref *ao A
 
 
 
-/* Like detect_type_change but with extra argument COMP_TYPE which will become
-   the component type part of new JFUNC of dynamic type change is detected and
-   the new base type is identified.  */
+/* Detect whether the dynamic type of ARG of COMP_TYPE has changed (before
+   callsite CALL) by looking for assignments to its virtual table pointer.  If
+   it is, return true and fill in the jump function JFUNC with relevant type
+   information or set it to unknown.  ARG is the object itself (not a pointer
+   to it, unless dereferenced).  BASE is the base of the memory access as
+   returned by get_ref_base_and_extent, as is the offset.  */
 
 static bool
-detect_type_change_1 (tree arg, tree base, tree comp_type, gimple call,
-		      struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
+detect_type_change (tree arg, tree base, tree comp_type, gimple call,
+		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
 {
   struct type_change_info tci;
   ao_ref ao;
@@ -649,7 +654,12 @@  detect_type_change_1 (tree arg, tree bas
 		       || handled_component_p (arg));
   /* Const calls cannot call virtual methods through VMT and so type changes do
      not matter.  */
-  if (!flag_devirtualize || !gimple_vuse (call))
+  if (!flag_devirtualize || !gimple_vuse (call)
+      /* Be sure expected_type is polymorphic.  */
+      || !comp_type
+      || TREE_CODE (comp_type) != RECORD_TYPE
+      || !TYPE_BINFO (comp_type)
+      || !BINFO_VTABLE (TYPE_BINFO (comp_type)))
     return false;
 
   ao_ref_init (&ao, arg);
@@ -679,40 +689,23 @@  detect_type_change_1 (tree arg, tree bas
   return true;
 }
 
-/* Detect whether the dynamic type of ARG has changed (before callsite CALL) by
-   looking for assignments to its virtual table pointer.  If it is, return true
-   and fill in the jump function JFUNC with relevant type information or set it
-   to unknown.  ARG is the object itself (not a pointer to it, unless
-   dereferenced).  BASE is the base of the memory access as returned by
-   get_ref_base_and_extent, as is the offset.  */
-
-static bool
-detect_type_change (tree arg, tree base, gimple call,
-		    struct ipa_jump_func *jfunc, HOST_WIDE_INT offset)
-{
-  return detect_type_change_1 (arg, base, TREE_TYPE (arg), call, jfunc, offset);
-}
-
 /* Like detect_type_change but ARG is supposed to be a non-dereferenced pointer
    SSA name (its dereference will become the base and the offset is assumed to
    be zero).  */
 
 static bool
-detect_type_change_ssa (tree arg, gimple call, struct ipa_jump_func *jfunc)
+detect_type_change_ssa (tree arg, tree comp_type,
+			gimple call, struct ipa_jump_func *jfunc)
 {
-  tree comp_type;
-
   gcc_checking_assert (TREE_CODE (arg) == SSA_NAME);
   if (!flag_devirtualize
-      || !POINTER_TYPE_P (TREE_TYPE (arg))
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != RECORD_TYPE)
+      || !POINTER_TYPE_P (TREE_TYPE (arg)))
     return false;
 
-  comp_type = TREE_TYPE (TREE_TYPE (arg));
   arg = build2 (MEM_REF, ptr_type_node, arg,
 		build_int_cst (ptr_type_node, 0));
 
-  return detect_type_change_1 (arg, arg, comp_type, call, jfunc, 0);
+  return detect_type_change (arg, arg, comp_type, call, jfunc, 0);
 }
 
 /* Callback of walk_aliased_vdefs.  Flags that it has been invoked to the
@@ -988,7 +981,8 @@  static void
 compute_complex_assign_jump_func (struct ipa_node_params *info,
 				  struct param_analysis_info *parms_ainfo,
 				  struct ipa_jump_func *jfunc,
-				  gimple call, gimple stmt, tree name)
+				  gimple call, gimple stmt, tree name,
+				  tree param_type)
 {
   HOST_WIDE_INT offset, size, max_size;
   tree op1, tc_ssa, base, ssa;
@@ -1030,7 +1024,11 @@  compute_complex_assign_jump_func (struct
 	{
 	  bool agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
 						     call, tc_ssa);
-	  bool type_p = !detect_type_change_ssa (tc_ssa, call, jfunc);
+	  bool type_p = false;
+
+	  if (param_type && POINTER_TYPE_P (param_type))
+	    type_p = !detect_type_change_ssa (tc_ssa, TREE_TYPE (param_type),
+					      call, jfunc);
 	  if (type_p || jfunc->type == IPA_JF_UNKNOWN)
 	    ipa_set_jf_simple_pass_through (jfunc, index, agg_p, type_p);
 	}
@@ -1057,9 +1055,10 @@  compute_complex_assign_jump_func (struct
 
   /* Dynamic types are changed in constructors and destructors.  */
   index = ipa_get_param_decl_index (info, SSA_NAME_VAR (ssa));
-  if (index >= 0)
+  if (index >= 0 && param_type && POINTER_TYPE_P (param_type))
     {
-      bool type_p = !detect_type_change (op1, base, call, jfunc, offset);
+      bool type_p = !detect_type_change (op1, base, TREE_TYPE (param_type),
+					 call, jfunc, offset);
       if (type_p || jfunc->type == IPA_JF_UNKNOWN)
 	ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (op1), index,
 			     parm_ref_data_pass_through_p (&parms_ainfo[index],
@@ -1137,7 +1136,7 @@  static void
 compute_complex_ancestor_jump_func (struct ipa_node_params *info,
 				    struct param_analysis_info *parms_ainfo,
 				    struct ipa_jump_func *jfunc,
-				    gimple call, gimple phi)
+				    gimple call, gimple phi, tree param_type)
 {
   HOST_WIDE_INT offset;
   gimple assign, cond;
@@ -1188,7 +1187,10 @@  compute_complex_ancestor_jump_func (stru
 	return;
     }
 
-  bool type_p = !detect_type_change (obj, expr, call, jfunc, offset);
+  bool type_p = false;
+  if (param_type && POINTER_TYPE_P (param_type))
+    type_p = !detect_type_change (obj, expr, TREE_TYPE (param_type),
+				  call, jfunc, offset);
   if (type_p || jfunc->type == IPA_JF_UNKNOWN)
     ipa_set_ancestor_jf (jfunc, offset, TREE_TYPE (obj), index,
 			 parm_ref_data_pass_through_p (&parms_ainfo[index],
@@ -1197,18 +1199,24 @@  compute_complex_ancestor_jump_func (stru
 
 /* Given OP which is passed as an actual argument to a called function,
    determine if it is possible to construct a KNOWN_TYPE jump function for it
-   and if so, create one and store it to JFUNC.  */
+   and if so, create one and store it to JFUNC.
+   EXPECTED_TYPE represents a type the argument should be in  */
 
 static void
 compute_known_type_jump_func (tree op, struct ipa_jump_func *jfunc,
-			      gimple call)
+			      gimple call, tree expected_type)
 {
   HOST_WIDE_INT offset, size, max_size;
   tree base;
 
   if (!flag_devirtualize
       || TREE_CODE (op) != ADDR_EXPR
-      || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE)
+      || TREE_CODE (TREE_TYPE (TREE_TYPE (op))) != RECORD_TYPE
+      /* Be sure expected_type is polymorphic.  */
+      || !expected_type
+      || TREE_CODE (expected_type) != RECORD_TYPE
+      || !TYPE_BINFO (expected_type)
+      || !BINFO_VTABLE (TYPE_BINFO (expected_type)))
     return;
 
   op = TREE_OPERAND (op, 0);
@@ -1220,11 +1228,11 @@  compute_known_type_jump_func (tree op, s
       || is_global_var (base))
     return;
 
-  if (!TYPE_BINFO (TREE_TYPE (base))
-      || detect_type_change (op, base, call, jfunc, offset))
+  if (detect_type_change (op, base, expected_type, call, jfunc, offset))
     return;
 
-  ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base), TREE_TYPE (op));
+  ipa_set_jf_known_type (jfunc, offset, TREE_TYPE (base),
+			 expected_type);
 }
 
 /* Inspect the given TYPE and return true iff it has the same structure (the
@@ -1495,6 +1503,37 @@  determine_known_aggregate_parts (gimple
     }
 }
 
+static tree
+ipa_get_callee_param_type (struct cgraph_edge *e, int i)
+{
+  int n;
+  tree type = (e->callee
+	       ? TREE_TYPE (e->callee->symbol.decl)
+	       : gimple_call_fntype (e->call_stmt));
+  tree t = TYPE_ARG_TYPES (type);
+
+  for (n = 0; n < i; n++)
+    {
+      if (!t)
+        break;
+      t = TREE_CHAIN (t);
+    }
+  if (t)
+    return TREE_VALUE (t);
+  if (!e->callee)
+    return NULL;
+  t = DECL_ARGUMENTS (e->callee->symbol.decl);
+  for (n = 0; n < i; n++)
+    {
+      if (!t)
+	return NULL;
+      t = TREE_CHAIN (t);
+    }
+  if (t)
+    return TREE_TYPE (t);
+  return NULL;
+}
+
 /* Compute jump function for all arguments of callsite CS and insert the
    information in the jump_functions array in the ipa_edge_args corresponding
    to this callsite.  */
@@ -1519,6 +1558,7 @@  ipa_compute_jump_functions_for_edge (str
     {
       struct ipa_jump_func *jfunc = ipa_get_ith_jump_func (args, n);
       tree arg = gimple_call_arg (call, n);
+      tree param_type = ipa_get_callee_param_type (cs, n);
 
       if (is_gimple_ip_invariant (arg))
 	ipa_set_jf_constant (jfunc, arg, cs);
@@ -1547,9 +1587,14 @@  ipa_compute_jump_functions_for_edge (str
 		  bool agg_p, type_p;
 		  agg_p = parm_ref_data_pass_through_p (&parms_ainfo[index],
 							call, arg);
-		  type_p = !detect_type_change_ssa (arg, call, jfunc);
+		  if (param_type && POINTER_TYPE_P (param_type))
+		    type_p = !detect_type_change_ssa (arg, TREE_TYPE (param_type),
+						      call, jfunc);
+		  else
+		    type_p = false;
 		  if (type_p || jfunc->type == IPA_JF_UNKNOWN)
-		    ipa_set_jf_simple_pass_through (jfunc, index, agg_p,						    type_p);
+		    ipa_set_jf_simple_pass_through (jfunc, index, agg_p,
+						    type_p);
 		}
 	    }
 	  else
@@ -1557,14 +1602,18 @@  ipa_compute_jump_functions_for_edge (str
 	      gimple stmt = SSA_NAME_DEF_STMT (arg);
 	      if (is_gimple_assign (stmt))
 		compute_complex_assign_jump_func (info, parms_ainfo, jfunc,
-						  call, stmt, arg);
+						  call, stmt, arg, param_type);
 	      else if (gimple_code (stmt) == GIMPLE_PHI)
 		compute_complex_ancestor_jump_func (info, parms_ainfo, jfunc,
-						    call, stmt);
+						    call, stmt, param_type);
 	    }
 	}
       else
-	compute_known_type_jump_func (arg, jfunc, call);
+	compute_known_type_jump_func (arg, jfunc, call,
+				      param_type
+				      && POINTER_TYPE_P (param_type)
+				      ? TREE_TYPE (param_type)
+				      : NULL);
 
       if ((jfunc->type != IPA_JF_PASS_THROUGH
 	      || !ipa_get_jf_pass_through_agg_preserved (jfunc))
@@ -1908,7 +1957,8 @@  ipa_analyze_virtual_call_uses (struct cg
       anc_offset = 0;
       index = ipa_get_param_decl_index (info, SSA_NAME_VAR (obj));
       gcc_assert (index >= 0);
-      if (detect_type_change_ssa (obj, call, &jfunc))
+      if (detect_type_change_ssa (obj, obj_type_ref_class (target),
+				  call, &jfunc))
 	return;
     }
   else
@@ -1922,7 +1972,8 @@  ipa_analyze_virtual_call_uses (struct cg
       index = ipa_get_param_decl_index (info,
 					SSA_NAME_VAR (TREE_OPERAND (expr, 0)));
       gcc_assert (index >= 0);
-      if (detect_type_change (obj, expr, call, &jfunc, anc_offset))
+      if (detect_type_change (obj, expr, obj_type_ref_class (target),
+			      call, &jfunc, anc_offset))
 	return;
     }
 
@@ -2134,7 +2185,7 @@  ipa_intraprocedural_devirtualization (gi
 
   jfunc.type = IPA_JF_UNKNOWN;
   compute_known_type_jump_func (OBJ_TYPE_REF_OBJECT (otr), &jfunc,
-				call);
+				call, obj_type_ref_class (otr));
   if (jfunc.type != IPA_JF_KNOWN_TYPE)
     return NULL_TREE;
   binfo = ipa_binfo_from_known_type_jfunc (&jfunc);