diff mbox

Fix polymorphic type matching in ipa-icf

Message ID 20150313063043.GC55012@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka March 13, 2015, 6:30 a.m. UTC
Hi,
this patch fixes IPA-ICF's polymorphic type matching.  Basically
ipa-polymorphic-call looks for the following cases:

  - data living in automatic or static variables of polymorphic type
  - dynamic type changes done by explicit calls to constructor or writes
    of virtual table pointer
  - parameters of THIS pointer of methods
  - data living in parameters/return values of polymorphic types
    passed by invisible reference.

In these cases it may derive type of the instance from them.  Current
implementation of ipa-icf mixes type compatibility checks with polymorphic
type checks and checks types of everything that is useless and somehwat
expensive.  It disables the checks for leaf functions (that are commonly
merged).

This is however not 100% safe, because constructor calls and THIS pointer
writes may get inlined and the information may be propagated outside of
the function code itself.

This patch restructures the checks to be safe in this case and to do
polymorphic type matching independently of the type checking.

The common reason why merging fails is mismat of THIS pointer type
(not very suprisingly).  Next stae1 I think we can lift this restriction and
simply merge polymorphic type info in ipa-prop's jump functions.

Bootstrapped/regtested x86_64-linux and also tested with Firefox and
Chromium.

Honza

	* ipa-icf.c (sem_function::equals_wpa): Match CXX_CONSTRUCTOR_P
	and CXX_DESTURCTOR_P. For consutrctors match ODR type of class they
	are building; for methods check ODR type of class they belong to if
	they may lead to a polymorphic call.
	(sem_function::compare_polymorphic_p): Be bit smarter about testing
	when function may lead to a polymorphic call.
	(sem_function::compare_type_list): Remove.
	(sem_variable::equals): Update use of compatible_types_p.
	(sem_variable::parse_tree_refs): Remove.
	(sem_item_optimizer::filter_removed_items): Do not filter out CXX
	cdtor.
	* ipa-icf-gimple.c (func_checker::compare_decl): Do polymorphic
	matching here.
	(func_checker::compatible_polymorphic_types_p): Break out from ...
	(unc_checker::compatible_types_p): ... here.
	* ipa-icf-gimple.h (func_checker::compatible_polymorphic_types_p):
	Declare.
	(unc_checker::compatible_types_p): Update.
	* ipa-icf.h (compare_type_list, parse_tree_refs, compare_sections):
	Remove.

Comments

Bernhard Reutner-Fischer March 13, 2015, 8:44 a.m. UTC | #1
On March 13, 2015 7:30:43 AM GMT+01:00, Jan Hubicka <hubicka@ucw.cz> wrote:
>Hi,

not commenting on the patch itself.

s/DELC_CXX_CONSTRUCTOR/DECL_CXX_CONSTRUCTOR/

s/consutrctors/constructors/

Thanks,
diff mbox

Patch

Index: ipa-icf.c
===================================================================
--- ipa-icf.c	(revision 221405)
+++ ipa-icf.c	(working copy)
@@ -429,9 +429,29 @@  sem_function::equals_wpa (sem_item *item
   if (DECL_NO_LIMIT_STACK (decl) != DECL_NO_LIMIT_STACK (item->decl))
     return return_false_with_msg ("no stack limit attributes are different");
 
+  if (DECL_CXX_CONSTRUCTOR_P (decl) != DECL_CXX_CONSTRUCTOR_P (item->decl))
+    return return_false_with_msg ("DELC_CXX_CONSTRUCTOR mismatch");
+
+  if (DECL_CXX_DESTRUCTOR_P (decl) != DECL_CXX_DESTRUCTOR_P (item->decl))
+    return return_false_with_msg ("DELC_CXX_DESTRUCTOR mismatch");
+
   if (flags_from_decl_or_type (decl) != flags_from_decl_or_type (item->decl))
     return return_false_with_msg ("decl_or_type flags are different");
 
+  /* Do not match polymorphic constructors of different types.  They calls
+     type memory location for ipa-polymorphic-call and we do not want
+     it to get confused by wrong type.  */
+  if (DECL_CXX_CONSTRUCTOR_P (decl)
+      && TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+    {
+      if (TREE_CODE (TREE_TYPE (item->decl)) != METHOD_TYPE)
+        return return_false_with_msg ("DECL_CXX_CONSTURCTOR type mismatch");
+      else if (!func_checker::compatible_polymorphic_types_p
+		 (method_class_type (TREE_TYPE (decl)),
+		  method_class_type (TREE_TYPE (item->decl)), false))
+        return return_false_with_msg ("ctor polymorphic type mismatch");
+    }
+
   /* Checking function TARGET and OPTIMIZATION flags.  */
   cl_target_option *tar1 = target_opts_for_fn (decl);
   cl_target_option *tar2 = target_opts_for_fn (item->decl);
@@ -473,13 +493,8 @@  sem_function::equals_wpa (sem_item *item
       if (!arg_types[i] || !m_compared_func->arg_types[i])
 	return return_false_with_msg ("NULL argument type");
 
-      /* Polymorphic comparison is executed just for non-leaf functions.  */
-      bool is_not_leaf = get_node ()->callees != NULL
-			 || get_node ()->indirect_calls != NULL;
-
       if (!func_checker::compatible_types_p (arg_types[i],
-					     m_compared_func->arg_types[i],
-					     is_not_leaf, i == 0))
+					     m_compared_func->arg_types[i]))
 	return return_false_with_msg ("argument type is different");
       if (POINTER_TYPE_P (arg_types[i])
 	  && (TYPE_RESTRICT (arg_types[i])
@@ -494,6 +509,24 @@  sem_function::equals_wpa (sem_item *item
 			    TREE_TYPE (item->decl)) != 1)
     return return_false_with_msg ("different type attributes");
 
+  /* The type of THIS pointer type memory location for
+     ipa-polymorphic-call-analysis.  */
+  if (opt_for_fn (decl, flag_devirtualize)
+      && (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE
+          || TREE_CODE (TREE_TYPE (item->decl)) == METHOD_TYPE)
+      && (!flag_ipa_cp
+	  || ipa_is_param_used (IPA_NODE_REF (dyn_cast <cgraph_node *>(node)),
+				0))
+      && compare_polymorphic_p ())
+    {
+      if (TREE_CODE (TREE_TYPE (decl)) != TREE_CODE (TREE_TYPE (item->decl)))
+	return return_false_with_msg ("METHOD_TYPE and FUNCTION_TYPE mismatch");
+      if (!func_checker::compatible_polymorphic_types_p
+	   (method_class_type (TREE_TYPE (decl)),
+	    method_class_type (TREE_TYPE (item->decl)), false))
+	return return_false_with_msg ("THIS pointer ODR type mismatch");
+    }
+
   ipa_ref *ref = NULL, *ref2 = NULL;
   for (unsigned i = 0; node->iterate_reference (i, ref); i++)
     {
@@ -614,7 +647,6 @@  sem_function::equals_private (sem_item *
   if (decl1 != decl2)
     return return_false();
 
-
   for (arg1 = DECL_ARGUMENTS (decl),
        arg2 = DECL_ARGUMENTS (m_compared_func->decl);
        arg1; arg1 = DECL_CHAIN (arg1), arg2 = DECL_CHAIN (arg2))
@@ -1216,10 +1248,20 @@  sem_function::hash_stmt (gimple stmt, in
 bool
 sem_function::compare_polymorphic_p (void)
 {
-  return get_node ()->callees != NULL
-	 || get_node ()->indirect_calls != NULL
-	 || m_compared_func->get_node ()->callees != NULL
-	 || m_compared_func->get_node ()->indirect_calls != NULL;
+  struct cgraph_edge *e;
+
+  if (!opt_for_fn (decl, flag_devirtualize))
+    return false;
+  if (get_node ()->indirect_calls != NULL
+      || m_compared_func->get_node ()->indirect_calls != NULL)
+    return true;
+  /* TODO: We can do simple propagation determining what calls may lead to
+     a polymorphic call.  */
+  for (e = m_compared_func->get_node ()->callees; e; e = e->next_callee)
+    if (e->callee->definition
+	&& opt_for_fn (e->callee->decl, flag_devirtualize))
+      return true;
+  return false;
 }
 
 /* For a given call graph NODE, the function constructs new
@@ -1374,41 +1416,6 @@  sem_function::bb_dict_test (vec<int> *bb
     return (*bb_dict)[source] == target;
 }
 
-/* Iterates all tree types in T1 and T2 and returns true if all types
-   are compatible. If COMPARE_POLYMORPHIC is set to true,
-   more strict comparison is executed.  */
-
-bool
-sem_function::compare_type_list (tree t1, tree t2, bool compare_polymorphic)
-{
-  tree tv1, tv2;
-  tree_code tc1, tc2;
-
-  if (!t1 && !t2)
-    return true;
-
-  while (t1 != NULL && t2 != NULL)
-    {
-      tv1 = TREE_VALUE (t1);
-      tv2 = TREE_VALUE (t2);
-
-      tc1 = TREE_CODE (tv1);
-      tc2 = TREE_CODE (tv2);
-
-      if (tc1 == NOP_EXPR && tc2 == NOP_EXPR)
-	{}
-      else if (tc1 == NOP_EXPR || tc2 == NOP_EXPR)
-	return false;
-      else if (!func_checker::compatible_types_p (tv1, tv2, compare_polymorphic))
-	return false;
-
-      t1 = TREE_CHAIN (t1);
-      t2 = TREE_CHAIN (t2);
-    }
-
-  return !(t1 || t2);
-}
-
 
 /* Semantic variable constructor that uses STACK as bitmap memory stack.  */
 
@@ -1586,8 +1593,7 @@  sem_variable::equals (tree t1, tree t2)
 	tree y1 = TREE_OPERAND (t1, 1);
 	tree y2 = TREE_OPERAND (t2, 1);
 
-	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2),
-					       true))
+	if (!func_checker::compatible_types_p (TREE_TYPE (x1), TREE_TYPE (x2)))
 	  return return_false ();
 
 	/* Type of the offset on MEM_REF does not matter.  */
@@ -1696,8 +1702,7 @@  sem_variable::equals (tree t1, tree t2)
 
     CASE_CONVERT:
     case VIEW_CONVERT_EXPR:
-      if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
-					     true))
+      if (!func_checker::compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
 	  return return_false ();
       return sem_variable::equals (TREE_OPERAND (t1, 0), TREE_OPERAND (t2, 0));
     case ERROR_MARK:
@@ -1874,40 +1879,6 @@  sem_variable::dump_to_file (FILE *file)
   fprintf (file, "\n\n");
 }
 
-/* Iterates though a constructor and identifies tree references
-   we are interested in semantic function equality.  */
-
-void
-sem_variable::parse_tree_refs (tree t)
-{
-  switch (TREE_CODE (t))
-    {
-    case CONSTRUCTOR:
-      {
-	unsigned length = vec_safe_length (CONSTRUCTOR_ELTS (t));
-
-	for (unsigned i = 0; i < length; i++)
-	  parse_tree_refs(CONSTRUCTOR_ELT (t, i)->value);
-
-	break;
-      }
-    case NOP_EXPR:
-    case ADDR_EXPR:
-      {
-	tree op = TREE_OPERAND (t, 0);
-	parse_tree_refs (op);
-	break;
-      }
-    case FUNCTION_DECL:
-      {
-	tree_refs.safe_push (t);
-	break;
-      }
-    default:
-      break;
-    }
-}
-
 unsigned int sem_item_optimizer::class_id = 0;
 
 sem_item_optimizer::sem_item_optimizer (): worklist (0), m_classes (0),
@@ -2185,10 +2156,7 @@  sem_item_optimizer::filter_removed_items
         {
 	  cgraph_node *cnode = static_cast <sem_function *>(item)->get_node ();
 
-	  bool no_body_function = in_lto_p && (cnode->alias || cnode->body_removed);
-	  if (no_body_function || !opt_for_fn (item->decl, flag_ipa_icf_functions)
-	      || DECL_CXX_CONSTRUCTOR_P (item->decl)
-	      || DECL_CXX_DESTRUCTOR_P (item->decl))
+	  if (in_lto_p && (cnode->alias || cnode->body_removed))
 	    remove_item (item);
 	  else
 	    filtered.safe_push (item);
Index: ipa-icf-gimple.c
===================================================================
--- ipa-icf-gimple.c	(revision 221405)
+++ ipa-icf-gimple.c	(working copy)
@@ -200,8 +200,22 @@  func_checker::compare_decl (tree t1, tre
       && DECL_BY_REFERENCE (t1) != DECL_BY_REFERENCE (t2))
     return return_false_with_msg ("DECL_BY_REFERENCE flags are different");
 
-  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
-			   m_compare_polymorphic))
+  if (!compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
+    return return_false ();
+
+  /* TODO: we are actually too strict here.  We only need to compare if
+     T1 can be used in polymorphic call.  */
+  if (TREE_ADDRESSABLE (t1)
+      && m_compare_polymorphic
+      && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+					  false))
+    return return_false ();
+
+  if ((t == VAR_DECL || t == PARM_DECL || t == RESULT_DECL)
+      && DECL_BY_REFERENCE (t1)
+      && m_compare_polymorphic
+      && !compatible_polymorphic_types_p (TREE_TYPE (t1), TREE_TYPE (t2),
+					  true))
     return return_false ();
 
   bool existed_p;
@@ -215,11 +229,41 @@  func_checker::compare_decl (tree t1, tre
   return true;
 }
 
+/* Return true if T1 and T2 are same for purposes of ipa-polymorphic-call
+   analysis.  COMPARE_PTR indicates if types of pointers needs to be
+   considered.  */
+
+bool
+func_checker::compatible_polymorphic_types_p (tree t1, tree t2,
+					      bool compare_ptr)
+{
+  gcc_assert (TREE_CODE (t1) != FUNCTION_TYPE && TREE_CODE (t1) != METHOD_TYPE);
+
+  /* Pointer types generally give no information.  */
+  if (POINTER_TYPE_P (t1))
+    {
+      if (!compare_ptr)
+	return true;
+      return func_checker::compatible_polymorphic_types_p (TREE_TYPE (t1),
+							   TREE_TYPE (t2),
+							   false);
+    }
+
+  /* If types contain a polymorphic types, match them.  */
+  bool c1 = contains_polymorphic_type_p (t1);
+  bool c2 = contains_polymorphic_type_p (t2);
+  if (!c1 && !c2)
+    return true;
+  if (!c1 || !c2)
+    return return_false_with_msg ("one type is not polymorphic");
+  if (!types_must_be_same_for_odr (t1, t2))
+    return return_false_with_msg ("types are not same for ODR");
+  return true;
+}
+
 /* Return true if types are compatible from perspective of ICF.  */
 bool
-func_checker::compatible_types_p (tree t1, tree t2,
-				  bool compare_polymorphic,
-				  bool first_argument)
+func_checker::compatible_types_p (tree t1, tree t2)
 {
   if (TREE_CODE (t1) != TREE_CODE (t2))
     return return_false_with_msg ("different tree types");
@@ -233,23 +277,6 @@  func_checker::compatible_types_p (tree t
   if (get_alias_set (t1) != get_alias_set (t2))
     return return_false_with_msg ("alias sets are different");
 
-  /* We call contains_polymorphic_type_p with this pointer type.  */
-  if (first_argument && TREE_CODE (t1) == POINTER_TYPE)
-    {
-      t1 = TREE_TYPE (t1);
-      t2 = TREE_TYPE (t2);
-    }
-
-  if (compare_polymorphic)
-    if (contains_polymorphic_type_p (t1) || contains_polymorphic_type_p (t2))
-      {
-	if (!contains_polymorphic_type_p (t1) || !contains_polymorphic_type_p (t2))
-	  return return_false_with_msg ("one type is not polymorphic");
-
-	if (!types_must_be_same_for_odr (t1, t2))
-	  return return_false_with_msg ("types are not same for ODR");
-      }
-
   return true;
 }
 
Index: ipa-icf-gimple.h
===================================================================
--- ipa-icf-gimple.h	(revision 221405)
+++ ipa-icf-gimple.h	(working copy)
@@ -226,12 +226,16 @@  public:
   /* Verifies that trees T1 and T2 do correspond.  */
   bool compare_variable_decl (tree t1, tree t2);
 
+  /* Return true if types are compatible for polymorphic call analysis.
+     COMPARE_PTR indicates if polymorphic type comparsion should be
+     done for pointers, too.  */
+  static bool compatible_polymorphic_types_p (tree t1, tree t2,
+					      bool compare_ptr);
+
   /* Return true if types are compatible from perspective of ICF.
      FIRST_ARGUMENT indicates if the comparison is called for
      first parameter of a function.  */
-  static bool compatible_types_p (tree t1, tree t2,
-				  bool compare_polymorphic = true,
-				  bool first_argument = false);
+  static bool compatible_types_p (tree t1, tree t2);
 
 
 private:
Index: ipa-icf.h
===================================================================
--- ipa-icf.h	(revision 221405)
+++ ipa-icf.h	(working copy)
@@ -353,11 +353,6 @@  private:
      corresponds to TARGET.  */
   bool bb_dict_test (vec<int> *bb_dict, int source, int target);
 
-  /* Iterates all tree types in T1 and T2 and returns true if all types
-     are compatible. If COMPARE_POLYMORPHIC is set to true,
-     more strict comparison is executed.  */
-  bool compare_type_list (tree t1, tree t2, bool compare_polymorphic);
-
   /* If cgraph edges E1 and E2 are indirect calls, verify that
      ICF flags are the same.  */
   bool compare_edge_flags (cgraph_edge *e1, cgraph_edge *e2);
@@ -415,15 +410,8 @@  public:
   static sem_variable *parse (varpool_node *node, bitmap_obstack *stack);
 
 private:
-  /* Iterates though a constructor and identifies tree references
-     we are interested in semantic function equality.  */
-  void parse_tree_refs (tree t);
-
   /* Compares trees T1 and T2 for semantic equality.  */
   static bool equals (tree t1, tree t2);
-
-  /* Compare that symbol sections are either NULL or have same name.  */
-  bool compare_sections (sem_variable *alias);
 }; // class sem_variable
 
 class sem_item_optimizer;