From 9de6f6c3eed463e8c41b95a28ad7f2186e379fff Mon Sep 17 00:00:00 2001
From: Jan Hubicka <hubicka@ucw.cz>
Date: Wed, 26 Mar 2014 03:11:57 +0100
Subject: [PATCH] re PR ipa/60315 (template constructor switch optimization)

	PR ipa/60315
	* cif-code.def (UNREACHABLE) New code.
	* ipa-inline.c (inline_small_functions): Skip edges to __builtlin_unreachable.
	(estimate_edge_growth): Allow edges to __builtlin_unreachable.
	* ipa-inline-analysis.c (edge_set_predicate): Redirect edges with false
	predicate to __bulitin_unreachable.
	(set_cond_stmt_execution_predicate): Fix issue when invert_tree_comparison
	returns ERROR_MARK.
	* ipa-pure-const.c (propagate_pure_const, propagate_nothrow): Do not
	propagate to inline clones.
	* cgraph.c (verify_edge_corresponds_to_fndecl): Allow redirection
	to unreachable.
	* ipa-cp.c (create_specialized_node): Be ready for new node to appear.
	* cgraphclones.c (cgraph_clone_node): If call destination is already
	ureachable, do not redirect it back.
	* tree-inline.c (fold_marked_statements): Hanlde calls becoming
	unreachable.

From-SVN: r208831
---
 gcc/ChangeLog                          | 20 +++++++++++++++
 gcc/cgraph.c                           |  6 +++++
 gcc/cgraphclones.c                     |  8 ++++--
 gcc/cif-code.def                       |  4 +++
 gcc/ipa-cp.c                           |  4 +--
 gcc/ipa-inline-analysis.c              | 34 +++++++++++++++++++++-----
 gcc/ipa-inline.c                       |  2 +-
 gcc/ipa-inline.h                       |  3 ++-
 gcc/testsuite/ChangeLog                |  5 ++++
 gcc/testsuite/g++.dg/torture/pr60315.C | 32 ++++++++++++++++++++++++
 10 files changed, 105 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/torture/pr60315.C

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index bc74777659b..64ef5c059f8 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,23 @@
+2014-03-25  Jan Hubicka  <hubicka@ucw.cz>
+
+	PR ipa/60315
+	* cif-code.def (UNREACHABLE) New code.
+	* ipa-inline.c (inline_small_functions): Skip edges to __builtlin_unreachable.
+	(estimate_edge_growth): Allow edges to __builtlin_unreachable.
+	* ipa-inline-analysis.c (edge_set_predicate): Redirect edges with false
+	predicate to __bulitin_unreachable.
+	(set_cond_stmt_execution_predicate): Fix issue when invert_tree_comparison
+	returns ERROR_MARK.
+	* ipa-pure-const.c (propagate_pure_const, propagate_nothrow): Do not
+	propagate to inline clones.
+	* cgraph.c (verify_edge_corresponds_to_fndecl): Allow redirection
+	to unreachable.
+	* ipa-cp.c (create_specialized_node): Be ready for new node to appear.
+	* cgraphclones.c (cgraph_clone_node): If call destination is already
+	ureachable, do not redirect it back.
+	* tree-inline.c (fold_marked_statements): Hanlde calls becoming
+	unreachable.
+
 2014-03-25  Jan Hubicka  <hubicka@ucw.cz>
 
 	* ipa-pure-const.c (propagate_pure_const, propagate_nothrow):
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index abfd63c9955..586ef797aba 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -2612,6 +2612,12 @@ verify_edge_corresponds_to_fndecl (struct cgraph_edge *e, tree decl)
       || node->in_other_partition
       || e->callee->in_other_partition)
     return false;
+
+  /* Optimizers can redirect unreachable calls or calls triggering undefined
+     behaviour to builtin_unreachable.  */
+  if (DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
+      && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_UNREACHABLE)
+    return false;
   node = cgraph_function_or_thunk_node (node, NULL);
 
   if (e->callee->former_clone_of != node->decl
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index ca69033ddde..b2eb8ab5ce9 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -238,8 +238,12 @@ cgraph_clone_node (struct cgraph_node *n, tree decl, gcov_type count, int freq,
   FOR_EACH_VEC_ELT (redirect_callers, i, e)
     {
       /* Redirect calls to the old version node to point to its new
-	 version.  */
-      cgraph_redirect_edge_callee (e, new_node);
+	 version.  The only exception is when the edge was proved to
+	 be unreachable during the clonning procedure.  */
+      if (!e->callee
+	  || DECL_BUILT_IN_CLASS (e->callee->decl) != BUILT_IN_NORMAL
+	  || DECL_FUNCTION_CODE (e->callee->decl) != BUILT_IN_UNREACHABLE)
+        cgraph_redirect_edge_callee (e, new_node);
     }
 
 
diff --git a/gcc/cif-code.def b/gcc/cif-code.def
index 71f3e39a6da..ce64d96b690 100644
--- a/gcc/cif-code.def
+++ b/gcc/cif-code.def
@@ -127,3 +127,7 @@ DEFCIFCODE(USES_COMDAT_LOCAL, CIF_FINAL_NORMAL,
 /* We can't inline because of mismatched caller/callee attributes.  */
 DEFCIFCODE(ATTRIBUTE_MISMATCH, CIF_FINAL_NORMAL,
 	   N_("function attribute mismatch"))
+
+/* We proved that the call is unreachable.  */
+DEFCIFCODE(UNREACHABLE, CIF_FINAL_NORMAL,
+	   N_("unreachable"))
diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 74042adb5a1..05de8572492 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -2811,9 +2811,7 @@ create_specialized_node (struct cgraph_node *node,
       if (aggvals)
 	ipa_dump_agg_replacement_values (dump_file, aggvals);
     }
-  gcc_checking_assert (ipa_node_params_vector.exists ()
-		       && (ipa_node_params_vector.length ()
-			   > (unsigned) cgraph_max_uid));
+  ipa_check_create_node_params ();
   update_profiling_info (node, new_node);
   new_info = IPA_NODE_REF (new_node);
   new_info->ipcp_orig_node = node;
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index 98f42ef1e55..ebc46a90cbf 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -746,6 +746,20 @@ static void
 edge_set_predicate (struct cgraph_edge *e, struct predicate *predicate)
 {
   struct inline_edge_summary *es = inline_edge_summary (e);
+
+  /* If the edge is determined to be never executed, redirect it
+     to BUILTIN_UNREACHABLE to save inliner from inlining into it.  */
+  if (predicate && false_predicate_p (predicate) && e->callee)
+    {
+      struct cgraph_node *callee = !e->inline_failed ? e->callee : NULL;
+
+      cgraph_redirect_edge_callee (e,
+				   cgraph_get_create_node
+				     (builtin_decl_implicit (BUILT_IN_UNREACHABLE)));
+      e->inline_failed = CIF_UNREACHABLE;
+      if (callee)
+	cgraph_remove_node_and_inline_clones (callee, NULL);
+    }
   if (predicate && !true_predicate_p (predicate))
     {
       if (!es->predicate)
@@ -1724,12 +1738,20 @@ set_cond_stmt_execution_predicate (struct ipa_node_params *info,
 
       FOR_EACH_EDGE (e, ei, bb->succs)
 	{
-	  struct predicate p = add_condition (summary, index, &aggpos,
-					      e->flags & EDGE_TRUE_VALUE
-					      ? code : inverted_code,
-					      gimple_cond_rhs (last));
-	  e->aux = pool_alloc (edge_predicate_pool);
-	  *(struct predicate *) e->aux = p;
+	  enum tree_code this_code = (e->flags & EDGE_TRUE_VALUE
+				      ? code : inverted_code);
+	  /* invert_tree_comparison will return ERROR_MARK on FP
+	     comparsions that are not EQ/NE instead of returning proper
+	     unordered one.  Be sure it is not confused with NON_CONSTANT.  */
+	  if (this_code != ERROR_MARK)
+	    {
+	      struct predicate p = add_condition (summary, index, &aggpos,
+						  e->flags & EDGE_TRUE_VALUE
+						  ? code : inverted_code,
+						  gimple_cond_rhs (last));
+	      e->aux = pool_alloc (edge_predicate_pool);
+	      *(struct predicate *) e->aux = p;
+	    }
 	}
     }
 
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index f6f97f87ebe..da83c4014e4 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1685,7 +1685,7 @@ inline_small_functions (void)
       edge = (struct cgraph_edge *) fibheap_extract_min (edge_heap);
       gcc_assert (edge->aux);
       edge->aux = NULL;
-      if (!edge->inline_failed)
+      if (!edge->inline_failed || !edge->callee->analyzed)
 	continue;
 
       /* Be sure that caches are maintained consistent.  
diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h
index 0a5960899a3..48136d22b52 100644
--- a/gcc/ipa-inline.h
+++ b/gcc/ipa-inline.h
@@ -285,7 +285,8 @@ static inline int
 estimate_edge_growth (struct cgraph_edge *edge)
 {
 #ifdef ENABLE_CHECKING
-  gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size);
+  gcc_checking_assert (inline_edge_summary (edge)->call_stmt_size
+		       || !edge->callee->analyzed);
 #endif
   return (estimate_edge_size (edge)
 	  - inline_edge_summary (edge)->call_stmt_size);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index da8d6437047..9c745e10834 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2014-03-25  Jan Hubicka  <hubicka@ucw.cz>
+
+	PR ipa/60315
+	* testsuite/g++.dg/torture/pr60315.C: New testcase.
+
 2014-03-25  Martin Jambor  <mjambor@suse.cz>
 
 	PR ipa/60600
diff --git a/gcc/testsuite/g++.dg/torture/pr60315.C b/gcc/testsuite/g++.dg/torture/pr60315.C
new file mode 100644
index 00000000000..7f122608504
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/pr60315.C
@@ -0,0 +1,32 @@
+// { dg-do compile }
+struct Base {
+    virtual int f() = 0;
+};
+
+struct Derived : public Base {
+    virtual int f() final override {
+        return 42;
+    }
+};
+
+extern Base* b;
+
+int main() {
+    return (static_cast<Derived*>(b)->*(&Derived::f))();
+}
+// { dg-do compile }
+struct Base {
+    virtual int f() = 0;
+};
+
+struct Derived : public Base {
+    virtual int f() final override {
+        return 42;
+    }
+};
+
+extern Base* b;
+
+int main() {
+    return (static_cast<Derived*>(b)->*(&Derived::f))();
+}