Hi.

This is rebased version of the patch that Martin J. wrote.

Patch can bootstrap on x86_64-linux-gnu and survives regression tests.

Thanks,
Martin

>From 17290dd6dc4412aee6c6484844f9edb149129d36 Mon Sep 17 00:00:00 2001
From: Martin Jambor <mjam...@suse.cz>
Date: Wed, 5 Jun 2019 16:11:44 +0200
Subject: [PATCH] Avoid adding dead speculative edges to inlining heap

2019-02-15  Martin Jambor  <mjam...@suse.cz>

	PR ipa/89330
	* ipa-inline.c (can_inline_edge_p): Move most of the checks...
	(call_not_inlinable_p): ...this new function.
	* ipa-inline.h (call_not_inlinable_p): Declare.
	* ipa-prop.c: Include ipa-inline.h
	(try_make_edge_direct_virtual_call): Create speculative edges only
	if there is any chance of inlining them.

	testsuite/
	* g++.dg/lto/pr89330_[01].C: New test.
---
 gcc/ipa-inline.c                     | 119 +++++++++++++--------------
 gcc/ipa-inline.h                     |   4 +-
 gcc/ipa-prop.c                       |   8 +-
 gcc/testsuite/g++.dg/lto/pr89330_0.C |  50 +++++++++++
 gcc/testsuite/g++.dg/lto/pr89330_1.C |  36 ++++++++
 5 files changed, 151 insertions(+), 66 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lto/pr89330_0.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr89330_1.C

diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 745bdf3002a..ba8155f006e 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -299,95 +299,87 @@ sanitize_attrs_match_for_inline_p (const_tree caller, const_tree callee)
       (opts_for_fn (caller->decl)->x_##flag		\
        != opts_for_fn (callee->decl)->x_##flag)
 
-/* Decide if we can inline the edge and possibly update
-   inline_failed reason.  
-   We check whether inlining is possible at all and whether
-   caller growth limits allow doing so.  
-
-   if REPORT is true, output reason to the dump file. */
+/* Return CIF_OK if a call from CALLER to CALLEE is or would be inlineable.
+   Otherwise, return the reason why it cannot.  EARLY should be set when
+   deciding about early inlining.  */
 
-static bool
-can_inline_edge_p (struct cgraph_edge *e, bool report,
-		   bool early = false)
+enum cgraph_inline_failed_t
+call_not_inlinable_p (cgraph_node *caller, cgraph_node *callee,
+		      bool early)
 {
-  gcc_checking_assert (e->inline_failed);
-
-  if (cgraph_inline_failed_type (e->inline_failed) == CIF_FINAL_ERROR)
-    {
-      if (report)
-        report_inline_failed_reason (e);
-      return false;
-    }
-
-  bool inlinable = true;
   enum availability avail;
-  cgraph_node *caller = e->caller->global.inlined_to
-		        ? e->caller->global.inlined_to : e->caller;
-  cgraph_node *callee = e->callee->ultimate_alias_target (&avail, caller);
+  caller = caller->global.inlined_to ? caller->global.inlined_to : caller;
+  callee = callee->ultimate_alias_target (&avail, caller);
 
   if (!callee->definition)
-    {
-      e->inline_failed = CIF_BODY_NOT_AVAILABLE;
-      inlinable = false;
-    }
+    return CIF_BODY_NOT_AVAILABLE;
   if (!early && (!opt_for_fn (callee->decl, optimize)
 		 || !opt_for_fn (caller->decl, optimize)))
-    {
-      e->inline_failed = CIF_FUNCTION_NOT_OPTIMIZED;
-      inlinable = false;
-    }
+    return CIF_FUNCTION_NOT_OPTIMIZED;
   else if (callee->calls_comdat_local)
-    {
-      e->inline_failed = CIF_USES_COMDAT_LOCAL;
-      inlinable = false;
-    }
+    return CIF_USES_COMDAT_LOCAL;
   else if (avail <= AVAIL_INTERPOSABLE)
-    {
-      e->inline_failed = CIF_OVERWRITABLE;
-      inlinable = false;
-    }
-  /* All edges with call_stmt_cannot_inline_p should have inline_failed
-     initialized to one of FINAL_ERROR reasons.  */
-  else if (e->call_stmt_cannot_inline_p)
-    gcc_unreachable ();
+    return CIF_OVERWRITABLE;
   /* Don't inline if the functions have different EH personalities.  */
   else if (DECL_FUNCTION_PERSONALITY (caller->decl)
 	   && DECL_FUNCTION_PERSONALITY (callee->decl)
 	   && (DECL_FUNCTION_PERSONALITY (caller->decl)
 	       != DECL_FUNCTION_PERSONALITY (callee->decl)))
-    {
-      e->inline_failed = CIF_EH_PERSONALITY;
-      inlinable = false;
-    }
+    return CIF_EH_PERSONALITY;
   /* TM pure functions should not be inlined into non-TM_pure
      functions.  */
   else if (is_tm_pure (callee->decl) && !is_tm_pure (caller->decl))
-    {
-      e->inline_failed = CIF_UNSPECIFIED;
-      inlinable = false;
-    }
+    return CIF_UNSPECIFIED;
   /* Check compatibility of target optimization options.  */
   else if (!targetm.target_option.can_inline_p (caller->decl,
 						callee->decl))
-    {
-      e->inline_failed = CIF_TARGET_OPTION_MISMATCH;
-      inlinable = false;
-    }
+    return CIF_TARGET_OPTION_MISMATCH;
   else if (ipa_fn_summaries->get (callee) == NULL
 	   || !ipa_fn_summaries->get (callee)->inlinable)
-    {
-      e->inline_failed = CIF_FUNCTION_NOT_INLINABLE;
-      inlinable = false;
-    }
+    return CIF_FUNCTION_NOT_INLINABLE;
   /* Don't inline a function with mismatched sanitization attributes. */
   else if (!sanitize_attrs_match_for_inline_p (caller->decl, callee->decl))
+    return CIF_ATTRIBUTE_MISMATCH;
+  return CIF_OK;
+}
+
+/* Decide if we can inline the edge and possibly update
+   inline_failed reason.
+   We check whether inlining is possible at all and whether
+   caller growth limits allow doing so.
+
+   If REPORT is true, output reason to the dump file.  EARLY should be set when
+   deciding about early inlining.  */
+
+static bool
+can_inline_edge_p (struct cgraph_edge *e, bool report,
+		   bool early = false)
+{
+  gcc_checking_assert (e->inline_failed);
+
+  if (cgraph_inline_failed_type (e->inline_failed) == CIF_FINAL_ERROR)
     {
-      e->inline_failed = CIF_ATTRIBUTE_MISMATCH;
-      inlinable = false;
+      if (report)
+	report_inline_failed_reason (e);
+      return false;
     }
-  if (!inlinable && report)
-    report_inline_failed_reason (e);
-  return inlinable;
+
+  enum cgraph_inline_failed_t fail_reason
+    = call_not_inlinable_p (e->caller, e->callee, early);
+
+
+  if (fail_reason != CIF_OK)
+    {
+      e->inline_failed = fail_reason;
+      if (report)
+	report_inline_failed_reason (e);
+    }
+  /* All edges with call_stmt_cannot_inline_p should have inline_failed
+     initialized to one of FINAL_ERROR reasons.  */
+  else if (e->call_stmt_cannot_inline_p)
+    gcc_unreachable ();
+
+  return fail_reason == CIF_OK;
 }
 
 /* Decide if we can inline the edge and possibly update
@@ -1628,6 +1620,7 @@ add_new_edges_to_heap (edge_heap_t *heap, vec<cgraph_edge *> new_edges)
     {
       struct cgraph_edge *edge = new_edges.pop ();
 
+      gcc_assert (edge->callee);
       gcc_assert (!edge->aux);
       if (edge->inline_failed
 	  && can_inline_edge_p (edge, true)
diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h
index f6eb677d618..911df58d646 100644
--- a/gcc/ipa-inline.h
+++ b/gcc/ipa-inline.h
@@ -52,7 +52,9 @@ void free_growth_caches (void);
 /* In ipa-inline.c  */
 unsigned int early_inliner (function *fun);
 bool inline_account_function_p (struct cgraph_node *node);
-
+enum cgraph_inline_failed_t call_not_inlinable_p (cgraph_node *caller,
+						  cgraph_node *callee,
+						  bool early);
 
 /* In ipa-inline-transform.c  */
 bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge *> *, int *, bool,
diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c
index d86c2f3db55..36ba91c3800 100644
--- a/gcc/ipa-prop.c
+++ b/gcc/ipa-prop.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "domwalk.h"
 #include "builtins.h"
 #include "tree-cfgcleanup.h"
+#include "ipa-inline.h"
 
 /* Function summary where the parameter infos are actually stored. */
 ipa_node_params_t *ipa_node_params_sum = NULL;
@@ -3346,13 +3347,16 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
 
   if (target)
     {
-      if (!possible_polymorphic_call_target_p
-	  (ie, cgraph_node::get_create (target)))
+      cgraph_node *target_node = cgraph_node::get_create (target);
+      if (!possible_polymorphic_call_target_p (ie, target_node))
 	{
 	  if (speculative)
 	    return NULL;
 	  target = ipa_impossible_devirt_target (ie, target);
 	}
+      if (speculative
+	  && call_not_inlinable_p (ie->caller, target_node, false))
+	return NULL;
       return ipa_make_edge_direct_to_target (ie, target, speculative);
     }
   else
diff --git a/gcc/testsuite/g++.dg/lto/pr89330_0.C b/gcc/testsuite/g++.dg/lto/pr89330_0.C
new file mode 100644
index 00000000000..10082f83412
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr89330_0.C
@@ -0,0 +1,50 @@
+// { dg-lto-do link }
+// { dg-lto-options { { -O3 -g -flto -shared -Wno-odr } } }
+
+namespace Inkscape {
+class Anchored {};
+namespace XML {
+enum NodeType {};
+class Node :Anchored {
+public:
+  virtual NodeType type() ;
+  virtual char name() ;
+  virtual int code() ;
+  virtual unsigned position() ;
+  virtual unsigned childCount() ;
+  virtual char content() ;
+  virtual char *attribute() const ;
+  virtual int attributeList() ;
+  virtual bool matchAttributeName() ;
+  virtual void setPosition() ;
+  virtual void setContent() ;
+  virtual void setAttribute() ;
+  virtual int document() ;
+  virtual int document() const ;
+  virtual Node *root() ;
+  virtual Node *root() const ;
+  virtual Node *parent() ;
+  virtual Node *next() const ;
+  virtual Node *firstChild() const ;
+
+};
+} } struct rdf_license_t {
+  };
+;
+class RDFImpl {
+;
+  rdf_license_t *getLicense();
+};
+static bool rdf_match_license(Inkscape::XML::Node const *repr) {
+  for (Inkscape::XML::Node *current = repr->firstChild(); current;
+       current->next()->attribute());
+  return 0;
+}
+rdf_license_t *RDFImpl::getLicense() {
+  Inkscape::XML::Node *repr ;
+  for (rdf_license_t *license ; license;
+       license) {
+    rdf_match_license(repr);
+  }
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr89330_1.C b/gcc/testsuite/g++.dg/lto/pr89330_1.C
new file mode 100644
index 00000000000..5b999eee8d7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr89330_1.C
@@ -0,0 +1,36 @@
+typedef char gchar;
+namespace Inkscape {
+class Anchored {
+int _anchor;
+};
+namespace XML {
+enum NodeType {};
+class Node :Anchored {
+virtual NodeType type() ;
+  virtual char const *name() const ;
+  virtual int code() ;
+  virtual unsigned position() ;
+  virtual unsigned childCount() ;
+  virtual char content() ;
+  virtual char attribute() ;
+  virtual int attributeList() ;
+  virtual bool matchAttributeName() ;
+  virtual void setPosition() ;
+  virtual void setContent() ;
+  virtual int document() ;
+  virtual int document() const ;
+  virtual Node *root() ;
+  virtual Node *root() const ;
+  virtual Node *parent() ;
+  virtual Node *parent() const ;
+  virtual Node *next() ;
+  virtual Node const *next() const ;
+
+};
+class SimpleNode : virtual Node {
+char const *name() const;
+  Node *next() const { return _next; }
+  SimpleNode *_next;
+};
+gchar const *SimpleNode::name() const { return 0; }
+} } 
-- 
2.21.0

Reply via email to