diff mbox

PR ipa/58555

Message ID 20140220064829.GA30919@kam.mff.cuni.cz
State New
Headers show

Commit Message

Jan Hubicka Feb. 20, 2014, 6:48 a.m. UTC
Hi,
this patch fies the division by zero in recursive_inlining by adding a non-zero guard.
More importantly it however fixed ages long bug in clone_inlined_nodes that does
not update properly frequencies of edges when function with already inlined edges
is inlined into another function.

Bootstrapped/regtested x86_64-linux, comitted.

Honza

	PR ipa/58555
	* ipa-inline-transform.c (clone_inlined_nodes): Add freq_scale parameter
	specifying the scaling.
	(inline_call): Update.
	(want_inline_recursively): Guard division by zero.
	(recursive_inlining): Update.
	* ipa-inline.h (clone_inlined_nodes): Update.
	* testsuite/g++.dg/torture/pr58555.C: New testcase.
diff mbox

Patch

Index: ipa-inline-transform.c
===================================================================
--- ipa-inline-transform.c	(revision 207870)
+++ ipa-inline-transform.c	(working copy)
@@ -127,11 +127,15 @@ 
    the edge and redirect it to the new clone.
    DUPLICATE is used for bookkeeping on whether we are actually creating new
    clones or re-using node originally representing out-of-line function call.
-   */
+   By default the offline copy is removed, when it appears dead after inlining.
+   UPDATE_ORIGINAL prevents this transformation.
+   If OVERALL_SIZE is non-NULL, the size is updated to reflect the
+   transformation.
+   FREQ_SCALE specify the scaling of frequencies of call sites.  */
 
 void
 clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
-		     bool update_original, int *overall_size)
+		     bool update_original, int *overall_size, int freq_scale)
 {
   struct cgraph_node *inlining_into;
   struct cgraph_edge *next;
@@ -175,8 +179,11 @@ 
       else
 	{
 	  struct cgraph_node *n;
+
+	  if (freq_scale == -1)
+	    freq_scale = e->frequency;
 	  n = cgraph_clone_node (e->callee, e->callee->decl,
-				 e->count, e->frequency, update_original,
+				 e->count, freq_scale, update_original,
 				 vNULL, true, inlining_into);
 	  cgraph_redirect_edge_callee (e, n);
 	}
@@ -191,7 +198,7 @@ 
     {
       next = e->next_callee;
       if (!e->inline_failed)
-        clone_inlined_nodes (e, duplicate, update_original, overall_size);
+        clone_inlined_nodes (e, duplicate, update_original, overall_size, freq_scale);
       if (e->speculative && !speculation_useful_p (e, true))
 	{
 	  cgraph_resolve_speculation (e, NULL);
@@ -260,7 +267,7 @@ 
 	}
     }
 
-  clone_inlined_nodes (e, true, update_original, overall_size);
+  clone_inlined_nodes (e, true, update_original, overall_size, e->frequency);
 
   gcc_assert (curr->callee->global.inlined_to == to);
 
Index: ipa-inline.c
===================================================================
--- ipa-inline.c	(revision 207870)
+++ ipa-inline.c	(working copy)
@@ -708,6 +708,12 @@ 
   if (outer_node->global.inlined_to)
     caller_freq = outer_node->callers->frequency;
 
+  if (!caller_freq)
+    {
+      reason = "function is inlined and unlikely";
+      want_inline = false;
+    }
+
   if (!want_inline)
     ;
   /* Inlining of self recursive function into copy of itself within other function
@@ -1385,7 +1391,7 @@ 
 					    false, vNULL, true, NULL);
 	  for (e = master_clone->callees; e; e = e->next_callee)
 	    if (!e->inline_failed)
-	      clone_inlined_nodes (e, true, false, NULL);
+	      clone_inlined_nodes (e, true, false, NULL, CGRAPH_FREQ_BASE);
           cgraph_redirect_edge_callee (curr, master_clone);
           reset_edge_growth_cache (curr);
 	}
Index: ipa-inline.h
===================================================================
--- ipa-inline.h	(revision 207870)
+++ ipa-inline.h	(working copy)
@@ -233,7 +233,8 @@ 
 /* In ipa-inline-transform.c  */
 bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge_p> *, int *, bool);
 unsigned int inline_transform (struct cgraph_node *);
-void clone_inlined_nodes (struct cgraph_edge *e, bool, bool, int *);
+void clone_inlined_nodes (struct cgraph_edge *e, bool, bool, int *,
+			  int freq_scale);
 
 extern int ncalls_inlined;
 extern int nfunctions_inlined;
Index: testsuite/g++.dg/torture/pr58555.C
===================================================================
--- testsuite/g++.dg/torture/pr58555.C	(revision 0)
+++ testsuite/g++.dg/torture/pr58555.C	(revision 0)
@@ -0,0 +1,114 @@ 
+/* { dg-do compile } */
+template <typename _Tp> _Tp *__addressof(_Tp &) {}
+template <typename _Tp> class A {
+public:
+  typedef _Tp *pointer;
+};
+template <typename _Tp> class M : public A<_Tp> {
+public:
+  typedef M other;
+  ~M();
+};
+class B {
+public:
+  B(int *);
+};
+class C {
+public:
+  void GetNext();
+  C *GetChildren();
+};
+template <typename _Tp> void _Destroy(_Tp *p1) { p1->~_Tp(); }
+struct D {
+  template <typename _ForwardIterator>
+  static void __destroy(_ForwardIterator p1, _ForwardIterator p2) {
+    for (; p1 != p2; ++p1)
+      _Destroy(__addressof(*p1));
+  }
+};
+template <typename _ForwardIterator>
+void _Destroy(_ForwardIterator p1, _ForwardIterator p2) {
+  D::__destroy(p1, p2);
+}
+template <typename _ForwardIterator, typename _Tp>
+void _Destroy(_ForwardIterator p1, _ForwardIterator p2, M<_Tp> &) {
+  _Destroy(p1, p2);
+}
+template <typename _Alloc> struct F {
+  typedef _Alloc _Tp_alloc_type;
+  typedef typename _Tp_alloc_type::pointer pointer;
+  struct N : _Tp_alloc_type {
+    pointer _M_start;
+    pointer _M_finish;
+  };
+  _Tp_alloc_type &_M_get_Tp_allocator();
+  N _M_impl;
+};
+template <typename _Tp, typename _Alloc = M<_Tp> > class O : F<_Alloc> {
+using  F<_Alloc>::_M_get_Tp_allocator;
+public:
+  ~O() {
+    _Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
+             _M_get_Tp_allocator());
+  }
+};
+template <class T> void checked_delete(T *p1) { delete p1; }
+template <class> class I;
+template <class T> struct J {
+  typedef T *type;
+};
+class K;
+class L {
+public:
+  virtual ~L();
+};
+class P : L {
+  O<I<int> > databasesM;
+  O<I<K> > usersM;
+public:
+  I<int> addDatabase();
+};
+C a;
+C *b;
+int atomic_exchange_and_add();
+class G {
+public:
+  virtual void dispose() = 0;
+  void release() {
+    if (atomic_exchange_and_add() == 1)
+      dispose();
+  }
+};
+class Q : G {
+  P *px_;
+  Q() {}
+  void dispose() { checked_delete(px_); }
+};
+class H {
+  G *pi_;
+public:
+  H();
+  H(P *);
+  ~H() {
+    if (pi_)
+      pi_->release();
+  }
+};
+template <class T, class Y> void sp_pointer_construct(I<T> *, Y, H);
+template <class T> class I {
+public:
+  typedef T element_type;
+  template <class Y> I(Y *p1) { sp_pointer_construct(this, 0, 0); }
+  typename J<T>::type operator->();
+  H pn;
+};
+void getNodeContent(const B &) {
+  for (C *n = a.GetChildren(); n; n->GetNext())
+    ;
+}
+void parseDatabase(I<P> p1) {
+  I<int> c = p1->addDatabase();
+  for (; b;)
+    getNodeContent(0);
+}
+void addServer() { I<int>(new P); }